Skip to content

Commit

Permalink
feat: more docs and code
Browse files Browse the repository at this point in the history
  • Loading branch information
markussiebert committed Nov 17, 2024
1 parent 9c42850 commit a56db8f
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 35 deletions.
54 changes: 52 additions & 2 deletions packages/aws-cdk-lib/aws-eks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1214,14 +1214,64 @@ cluster.grantAccess('eksAdminViewRoleAccess', eksAdminViewRole.roleArn, [
namespaces: ['foo', 'bar'],
}),
]);

// Custom permissions in EKS via group mapping
cluster.grantAccessToGroups('customAccess', 'arn:aws:iam::123456789012:role/testrole', ['custom-access-group']);

// Now we can define permissions for the mapping
this.addManifest('customAccessPermissions', [
{
apiVersion: 'rbac.authorization.k8s.io/v1',
kind: 'ClusterRole',
metadata: {
name: 'custom-accesss-role',
},
rules: [
{
apiGroups: [''],
resources: ['pods'],
verbs: ['get', 'list', 'watch'],
},
{
apiGroups: ['apps'],
resources: ['deployments'],
verbs: ['create', 'update', 'delete'],
},
{
apiGroups: ['rbac.authorization.k8s.io'],
resources: ['roles', 'rolebindings'],
verbs: ['create', 'bind', 'delete'],
},
],
},
{
apiVersion: 'rbac.authorization.k8s.io/v1',
kind: 'ClusterRoleBinding',
metadata: {
name: 'custom-access-rolebinding',
},
subjects: [
{
kind: 'Group',
name: 'custom-access-group',
apiGroup: 'rbac.authorization.k8s.io',
},
],
roleRef: {
kind: 'ClusterRole',
name: 'custom-access-role',
apiGroup: 'rbac.authorization.k8s.io',
},
},
]);
```

### Migrating from ConfigMap to Access Entry

If the cluster is created with the `authenticationMode` property left undefined,
it will default to `CONFIG_MAP`.
it will default to `CONFIG_MAP`.

The update path is:
The update path is:

`undefined`(`CONFIG_MAP`) -> `API_AND_CONFIG_MAP` -> `API`

Expand Down
53 changes: 38 additions & 15 deletions packages/aws-cdk-lib/aws-eks/lib/access-entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,10 +258,27 @@ export enum AccessEntryType {
EC2_WINDOWS = 'EC2_WINDOWS',
}

/**
* Represents the Properties that can be granted
*/
export interface AccessEntryGrants {
/**
* The access policies that define the permissions and scope for the access entry.
* @default - No access policies are provided
*/
readonly accessPolicies?: IAccessPolicy[];
/**
* The kubernetes groups you want to associate with this access policy.
* Those groups can be used as subjects in (Cluster)RoleBindings.
* @default - No kubernetes groups are provided
*/
readonly kubernetesGroups?: string[];
}

/**
* Represents the properties required to create an Amazon EKS access entry.
*/
export interface AccessEntryProps {
export interface AccessEntryProps extends AccessEntryGrants {
/**
* The name of the AccessEntry.
*
Expand All @@ -278,21 +295,10 @@ export interface AccessEntryProps {
* The Amazon EKS cluster to which the access entry applies.
*/
readonly cluster: ICluster;
/**
* The access policies that define the permissions and scope for the access entry.
* @default - No access policies are provided
*/
readonly accessPolicies?: IAccessPolicy[];
/**
* The Amazon Resource Name (ARN) of the principal (user or role) to associate the access entry with.
*/
readonly principal: string;
/**
* The kubernetes groups you want to associate with this access policy.
* Those groups can be used as subjects in (Cluster)RoleBindings.
* @default - No kubernetes groups are provided
*/
readonly kubernetesGroups?: string[];
}

/**
Expand Down Expand Up @@ -330,15 +336,15 @@ export class AccessEntry extends Resource implements IAccessEntry {
private cluster: ICluster;
private principal: string;
private accessPolicies: IAccessPolicy[];
private kubernetesGroups?: string[];
private kubernetesGroups: string[];

constructor(scope: Construct, id: string, props: AccessEntryProps ) {
super(scope, id);

this.cluster = props.cluster;
this.principal = props.principal;
this.accessPolicies = props.accessPolicies ?? [];
this.kubernetesGroups = props.kubernetesGroups;
this.kubernetesGroups = props.kubernetesGroups ?? [];
const accessPolicies = Lazy.any({
produce: () => {
if (this.accessPolicies.length === 0) {
Expand All @@ -354,12 +360,21 @@ export class AccessEntry extends Resource implements IAccessEntry {
},
});

const kubernetesGroups = Lazy.list({
produce: () => {
if (this.kubernetesGroups.length === 0) {
return undefined;
}
return this.kubernetesGroups;
},
});

const resource = new CfnAccessEntry(this, 'Resource', {
clusterName: this.cluster.clusterName,
principalArn: this.principal,
type: props.accessEntryType,
accessPolicies,
kubernetesGroups: this.kubernetesGroups,
kubernetesGroups,
});
this.accessEntryName = this.getResourceNameAttribute(resource.ref);
this.accessEntryArn = this.getResourceArnAttribute(resource.attrAccessEntryArn, {
Expand All @@ -376,4 +391,12 @@ export class AccessEntry extends Resource implements IAccessEntry {
// add newAccessPolicies to this.accessPolicies
this.accessPolicies.push(...newAccessPolicies);
}
/**
* Add the access policies for this entry.
* @param newKubernetesGroups - The new kubernetes groups to add.
*/
public addKubernetesGroups(newKubernetesGroups: string[]): void {
// add newKubernetesGroups to this.accessPolicies
this.kubernetesGroups.push(...newKubernetesGroups);
}
}
40 changes: 32 additions & 8 deletions packages/aws-cdk-lib/aws-eks/lib/cluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as path from 'path';
import { Construct, Node } from 'constructs';
import * as semver from 'semver';
import * as YAML from 'yaml';
import { IAccessPolicy, IAccessEntry, AccessEntry, AccessPolicy, AccessScopeType } from './access-entry';
import { IAccessPolicy, IAccessEntry, AccessEntry, AccessPolicy, AccessScopeType, AccessEntryGrants } from './access-entry';
import { IAddon, Addon } from './addon';
import { AlbController, AlbControllerOptions } from './alb-controller';
import { AwsAuth } from './aws-auth';
Expand Down Expand Up @@ -726,7 +726,6 @@ interface EndpointAccessConfig {
* @default - No restrictions.
*/
readonly publicCidrs?: string[];

}

/**
Expand Down Expand Up @@ -1842,7 +1841,26 @@ export class Cluster extends ClusterBase {
* @param accessPolicies - An array of `IAccessPolicy` objects that define the access permissions to be granted to the IAM principal.
*/
public grantAccess(id: string, principal: string, accessPolicies: IAccessPolicy[]) {
this.addToAccessEntry(id, principal, accessPolicies);
this.addToAccessEntry(id, principal, {
accessPolicies: accessPolicies,
});
}

/**
* Grants the specified IAM principal access to the EKS cluster based on the provided access policies.
*
* This method creates an `AccessEntry` construct that grants the specified IAM principal the access to
* the specified kubernetesGroups. You have to create (cluster) rolebindings and (cluster) roles manually.
* Without them, the given principal will be able to login, but has no permissions.
*
* @param id - The ID of the `AccessEntry` construct to be created.
* @param principal - The IAM principal (role or user) to be granted access to the EKS cluster.
* @param kubernetesGroups - An array of `IAccessPolicy` objects that define the access permissions to be granted to the IAM principal.
*/
public grantAccessToGroups(id: string, principal: string, kubernetesGroups: string[]) {
this.addToAccessEntry(id, principal, {
kubernetesGroups,
});
}

/**
Expand Down Expand Up @@ -2080,25 +2098,31 @@ export class Cluster extends ClusterBase {
/**
* Adds an access entry to the cluster's access entries map.
*
* If an entry already exists for the given principal, it adds the provided access policies to the existing entry.
* If an entry already exists for the given principal, it adds the provided access policies or kubernetes groups to the existing entry.
* If no entry exists for the given principal, it creates a new access entry with the provided access policies.
*
* @param principal - The principal (e.g., IAM user or role) for which the access entry is being added.
* @param policies - An array of access policies to be associated with the principal.
* @param grants - An object containing arrays of access policies and kubernetes groups to be associated with the principal.
*
* @throws {Error} If the uniqueName generated for the new access entry is not unique.
*
* @returns {void}
*/
private addToAccessEntry(id: string, principal: string, policies: IAccessPolicy[]) {
private addToAccessEntry(id: string, principal: string, grants: AccessEntryGrants) {
const entry = this.accessEntries.get(principal);
if (entry) {
(entry as AccessEntry).addAccessPolicies(policies);
if ( grants.accessPolicies ) {
(entry as AccessEntry).addAccessPolicies(grants.accessPolicies);
}
if (grants.kubernetesGroups) {
(entry as AccessEntry).addKubernetesGroups(grants.kubernetesGroups);
}
} else {
const newEntry = new AccessEntry(this, id, {
principal,
cluster: this,
accessPolicies: policies,
accessPolicies: grants.accessPolicies,
kubernetesGroups: grants.kubernetesGroups,
});
this.accessEntries.set(principal, newEntry);
}
Expand Down
24 changes: 14 additions & 10 deletions packages/aws-cdk-lib/aws-eks/test/access-entry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,23 +66,13 @@ describe('AccessEntry', () => {
new AccessEntry(stack, 'AccessEntry', {
cluster,
kubernetesGroups: ['my-kubernetes-group'],
accessPolicies: mockAccessPolicies,
principal: 'mock-principal-arn',
});

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::EKS::AccessEntry', {
ClusterName: { Ref: 'Cluster9EE0221C' },
PrincipalArn: 'mock-principal-arn',
AccessPolicies: [
{
AccessScope: {
Namespaces: ['default'],
Type: 'namespace',
},
PolicyArn: 'mock-policy-arn',
},
],
KubernetesGroups: ['my-kubernetes-group'],
});
});
Expand Down Expand Up @@ -159,6 +149,20 @@ describe('AccessEntry', () => {
});
});

test('adds new kubernetes groups with addKubernetesGroups()', () => {
// GIVEN
const accessEntry = new AccessEntry(stack, 'AccessEntry', mockProps);
// WHEN
accessEntry.addKubernetesGroups(['my-custom-group']);

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::EKS::AccessEntry', {
ClusterName: { Ref: 'Cluster9EE0221C' },
PrincipalArn: mockProps.principal,
KubernetesGroups: ['my-custom-group'],
});
});

test('imports an AccessEntry from attributes', () => {
// GIVEN
const importedAccessEntryName = 'imported-access-entry-name';
Expand Down
17 changes: 17 additions & 0 deletions packages/aws-cdk-lib/aws-eks/test/cluster.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3378,6 +3378,23 @@ describe('cluster', () => {
});
});

test('cluster can grantAccess to kubernetes group', () => {
// GIVEN
const { stack, vpc } = testFixture();
// WHEN
const mastersRole = new iam.Role(stack, 'role', { assumedBy: new iam.AccountRootPrincipal() });
const cluster = new eks.Cluster(stack, 'Cluster', {
vpc,
mastersRole,
version: CLUSTER_VERSION,
});
cluster.grantAccessToGroups('mastersAccess', mastersRole.roleArn, ['my-custom-group']);
// THEN
Template.fromStack(stack).hasResourceProperties('AWS::EKS::AccessEntry', {
KubernetesGroups: ['my-custom-group'],
});
});

});

});

0 comments on commit a56db8f

Please sign in to comment.