Skip to content

Commit 0251d9a

Browse files
authored
fix(eks): overly permissive trust policies (#25580)
Backporting #25473 ---- The *CreationRole* and the *default MastersRole* use the account root principal in their trust policy, which is overly permissive. Instead, use the specific lambda handler roles that need it, and remove the default masters role. BREAKING CHANGE: A masters role is no longer provisioned by default. Use the `mastersRole` property to explicitly pass a role that needs cluster access. In addition, the creation role no longer allows any identity (with the appropriate `sts:AssumeRole` permissions) to assume it. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent 615ebcb commit 0251d9a

File tree

128 files changed

+20108
-23247
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

128 files changed

+20108
-23247
lines changed

packages/@aws-cdk/aws-eks/lib/cluster-resource-provider.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import * as path from 'path';
22
import * as ec2 from '@aws-cdk/aws-ec2';
3-
import * as iam from '@aws-cdk/aws-iam';
43
import * as lambda from '@aws-cdk/aws-lambda';
54
import { Duration, NestedStack, Stack } from '@aws-cdk/core';
65
import * as cr from '@aws-cdk/custom-resources';
@@ -15,10 +14,6 @@ const HANDLER_DIR = path.join(__dirname, 'cluster-resource-handler');
1514
const HANDLER_RUNTIME = lambda.Runtime.NODEJS_14_X;
1615

1716
export interface ClusterResourceProviderProps {
18-
/**
19-
* The IAM role to assume in order to interact with the cluster.
20-
*/
21-
readonly adminRole: iam.IRole;
2217

2318
/**
2419
* The VPC to provision the functions in.
@@ -118,9 +113,6 @@ export class ClusterResourceProvider extends NestedStack {
118113
vpcSubnets: props.subnets ? { subnets: props.subnets } : undefined,
119114
securityGroups: props.securityGroup ? [props.securityGroup] : undefined,
120115
});
121-
122-
props.adminRole.grant(onEvent.role!, 'sts:AssumeRole');
123-
props.adminRole.grant(isComplete.role!, 'sts:AssumeRole');
124116
}
125117

126118
/**

packages/@aws-cdk/aws-eks/lib/cluster-resource.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,17 +61,16 @@ export class ClusterResource extends CoreConstruct {
6161
throw new Error('"roleArn" is required');
6262
}
6363

64-
this.adminRole = this.createAdminRole(props);
65-
6664
const provider = ClusterResourceProvider.getOrCreate(this, {
67-
adminRole: this.adminRole,
6865
subnets: props.subnets,
6966
vpc: props.vpc,
7067
environment: props.environment,
7168
onEventLayer: props.onEventLayer,
7269
securityGroup: props.clusterHandlerSecurityGroup,
7370
});
7471

72+
this.adminRole = this.createAdminRole(provider, props);
73+
7574
const resource = new CustomResource(this, 'Resource', {
7675
resourceType: CLUSTER_RESOURCE_TYPE,
7776
serviceToken: provider.serviceToken,
@@ -117,13 +116,15 @@ export class ClusterResource extends CoreConstruct {
117116
this.attrOpenIdConnectIssuer = Token.asString(resource.getAtt('OpenIdConnectIssuer'));
118117
}
119118

120-
private createAdminRole(props: ClusterResourceProps) {
119+
private createAdminRole(provider: ClusterResourceProvider, props: ClusterResourceProps) {
121120
const stack = Stack.of(this);
122121

123122
// the role used to create the cluster. this becomes the administrator role
124123
// of the cluster.
125124
const creationRole = new iam.Role(this, 'CreationRole', {
126-
assumedBy: new iam.AccountRootPrincipal(),
125+
// the role would be assumed by the provider handlers, as they are the ones making
126+
// the requests.
127+
assumedBy: new iam.CompositePrincipal(provider.provider.onEventHandler.role!, provider.provider.isCompleteHandler!.role!),
127128
});
128129

129130
// the CreateCluster API will allow the cluster to assume this role, so we

packages/@aws-cdk/aws-eks/lib/cluster.ts

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1325,7 +1325,14 @@ export class Cluster extends ClusterBase {
13251325
this.prune = props.prune ?? true;
13261326
this.vpc = props.vpc || new ec2.Vpc(this, 'DefaultVpc');
13271327
this.version = props.version;
1328-
this.kubectlLambdaRole = props.kubectlLambdaRole ? props.kubectlLambdaRole : undefined;
1328+
1329+
// since this lambda role needs to be added to the trust policy of the creation role,
1330+
// we must create it in this scope (instead of the KubectlProvider nested stack) to avoid
1331+
// a circular dependency.
1332+
this.kubectlLambdaRole = props.kubectlLambdaRole ? props.kubectlLambdaRole : new iam.Role(this, 'KubectlHandlerRole', {
1333+
assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
1334+
managedPolicies: [iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicExecutionRole')],
1335+
});
13291336

13301337
this.tagSubnets();
13311338

@@ -1479,6 +1486,11 @@ export class Cluster extends ClusterBase {
14791486
// and configured to allow connections from itself.
14801487
this.kubectlSecurityGroup = this.clusterSecurityGroup;
14811488

1489+
this.adminRole.assumeRolePolicy?.addStatements(new iam.PolicyStatement({
1490+
actions: ['sts:AssumeRole'],
1491+
principals: [this.kubectlLambdaRole],
1492+
}));
1493+
14821494
// use the cluster creation role to issue kubectl commands against the cluster because when the
14831495
// cluster is first created, that's the only role that has "system:masters" permissions
14841496
this.kubectlRole = this.adminRole;
@@ -1493,22 +1505,19 @@ export class Cluster extends ClusterBase {
14931505
new CfnOutput(this, 'ClusterName', { value: this.clusterName });
14941506
}
14951507

1496-
// if an explicit role is not configured, define a masters role that can
1497-
// be assumed by anyone in the account (with sts:AssumeRole permissions of
1498-
// course)
1499-
const mastersRole = props.mastersRole ?? new iam.Role(this, 'MastersRole', {
1500-
assumedBy: new iam.AccountRootPrincipal(),
1501-
});
1502-
1503-
// map the IAM role to the `system:masters` group.
1504-
this.awsAuth.addMastersRole(mastersRole);
1508+
// do not create a masters role if one is not provided. Trusting the accountRootPrincipal() is too permissive.
1509+
if (props.mastersRole) {
1510+
const mastersRole = props.mastersRole;
15051511

1506-
if (props.outputMastersRoleArn) {
1507-
new CfnOutput(this, 'MastersRoleArn', { value: mastersRole.roleArn });
1508-
}
1512+
// map the IAM role to the `system:masters` group.
1513+
this.awsAuth.addMastersRole(mastersRole);
15091514

1510-
commonCommandOptions.push(`--role-arn ${mastersRole.roleArn}`);
1515+
if (props.outputMastersRoleArn) {
1516+
new CfnOutput(this, 'MastersRoleArn', { value: mastersRole.roleArn });
1517+
}
15111518

1519+
commonCommandOptions.push(`--role-arn ${mastersRole.roleArn}`);
1520+
}
15121521
if (props.albController) {
15131522
this.albController = AlbController.create(this, { ...props.albController, cluster: this });
15141523
}
@@ -1524,7 +1533,7 @@ export class Cluster extends ClusterBase {
15241533
this.addNodegroupCapacity('DefaultCapacity', { instanceTypes: [instanceType], minSize: minCapacity }) : undefined;
15251534
}
15261535

1527-
const outputConfigCommand = props.outputConfigCommand ?? true;
1536+
const outputConfigCommand = (props.outputConfigCommand ?? true) && props.mastersRole;
15281537
if (outputConfigCommand) {
15291538
const postfix = commonCommandOptions.join(' ');
15301539
new CfnOutput(this, 'ConfigCommand', { value: `${updateConfigCommandPrefix} ${postfix}` });

packages/@aws-cdk/aws-eks/lib/fargate-profile.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,6 @@ export class FargateProfile extends CoreConstruct implements ITaggable {
148148
super(scope, id);
149149

150150
const provider = ClusterResourceProvider.getOrCreate(this, {
151-
adminRole: props.cluster.adminRole,
152151
onEventLayer: props.cluster.onEventLayer,
153152
});
154153

packages/@aws-cdk/aws-eks/lib/kubectl-provider.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,12 @@ export class KubectlProvider extends NestedStack implements IKubectlProvider {
168168
resources: [cluster.clusterArn],
169169
}));
170170

171+
// taken from the lambda default role logic.
172+
// makes it easier for roles to be passed in.
173+
if (handler.isBoundToVpc) {
174+
handler.role?.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaVPCAccessExecutionRole'));
175+
}
176+
171177
// For OCI helm chart authorization.
172178
this.handlerRole.addManagedPolicy(
173179
iam.ManagedPolicy.fromAwsManagedPolicyName('AmazonEC2ContainerRegistryReadOnly'),

0 commit comments

Comments
 (0)