Skip to content

Commit 8f4a38d

Browse files
author
Elad Ben-Israel
authored
fix(eks): invalid arn when mapping users to rbac (#4549)
The ARN of IAM users was rendered with a region, but IAM resources are global. This means that IAM users could not be mapped to Kubernetes RBAC. This fix adds a `user.userArn` attribute which correctly renders the user's ARN and then uses this in the EKS library. This fixes #4545
1 parent f7ea9dd commit 8f4a38d

File tree

4 files changed

+82
-25
lines changed

4 files changed

+82
-25
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ export class AwsAuth extends Construct {
104104
private synthesizeMapUsers() {
105105
return Lazy.anyValue({
106106
produce: () => this.stack.toJsonString(this.userMappings.map(m => ({
107-
userarn: this.stack.formatArn({ service: 'iam', resource: 'user', resourceName: m.user.userName }),
107+
userarn: m.user.userArn,
108108
username: m.mapping.username,
109109
groups: m.mapping.groups
110110
})))

packages/@aws-cdk/aws-eks/test/test.awsauth.ts

Lines changed: 48 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,10 @@ export = {
3636
const user = new iam.User(stack, 'user');
3737

3838
// WHEN
39-
cluster.awsAuth.addRoleMapping(role, { groups: [ 'role-group1' ], username: 'roleuser' });
40-
cluster.awsAuth.addRoleMapping(role, { groups: [ 'role-group2', 'role-group3' ] });
41-
cluster.awsAuth.addUserMapping(user, { groups: [ 'user-group1', 'user-group2' ] });
42-
cluster.awsAuth.addUserMapping(user, { groups: [ 'user-group1', 'user-group2' ], username: 'foo' });
39+
cluster.awsAuth.addRoleMapping(role, { groups: ['role-group1'], username: 'roleuser' });
40+
cluster.awsAuth.addRoleMapping(role, { groups: ['role-group2', 'role-group3'] });
41+
cluster.awsAuth.addUserMapping(user, { groups: ['user-group1', 'user-group2'] });
42+
cluster.awsAuth.addUserMapping(user, { groups: ['user-group1', 'user-group2'], username: 'foo' });
4343
cluster.awsAuth.addAccount('112233');
4444
cluster.awsAuth.addAccount('5566776655');
4545

@@ -71,31 +71,62 @@ export = {
7171
"Arn"
7272
]
7373
},
74-
"\\\",\\\"groups\\\":[\\\"role-group2\\\",\\\"role-group3\\\"]}]\",\"mapUsers\":\"[{\\\"userarn\\\":\\\"arn:",
74+
"\\\",\\\"groups\\\":[\\\"role-group2\\\",\\\"role-group3\\\"]}]\",\"mapUsers\":\"[{\\\"userarn\\\":\\\"",
7575
{
76-
Ref: "AWS::Partition"
76+
"Fn::GetAtt": [
77+
"user2C2B57AE",
78+
"Arn"
79+
]
7780
},
78-
":iam:us-east-1:",
81+
"\\\",\\\"groups\\\":[\\\"user-group1\\\",\\\"user-group2\\\"]},{\\\"userarn\\\":\\\"",
7982
{
80-
Ref: "AWS::AccountId"
83+
"Fn::GetAtt": [
84+
"user2C2B57AE",
85+
"Arn"
86+
]
8187
},
82-
":user/",
88+
"\\\",\\\"username\\\":\\\"foo\\\",\\\"groups\\\":[\\\"user-group1\\\",\\\"user-group2\\\"]}]\",\"mapAccounts\":\"[\\\"112233\\\",\\\"5566776655\\\"]\"}}]"
89+
]
90+
]
91+
}
92+
}));
93+
94+
test.done();
95+
},
96+
97+
'imported users and roles can be also be used'(test: Test) {
98+
// GIVEN
99+
const { stack } = testFixtureNoVpc();
100+
const cluster = new Cluster(stack, 'Cluster');
101+
const role = iam.Role.fromRoleArn(stack, 'imported-role', 'arn:aws:iam::123456789012:role/S3Access');
102+
const user = iam.User.fromUserName(stack, 'import-user', 'MyUserName');
103+
104+
// WHEN
105+
cluster.awsAuth.addRoleMapping(role, { groups: ['group1'] });
106+
cluster.awsAuth.addUserMapping(user, { groups: ['group2'] });
107+
108+
// THEN
109+
expect(stack).to(haveResource(KubernetesResource.RESOURCE_TYPE, {
110+
Manifest: {
111+
"Fn::Join": [
112+
"",
113+
[
114+
"[{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"aws-auth\",\"namespace\":\"kube-system\"},\"data\":{\"mapRoles\":\"[{\\\"rolearn\\\":\\\"",
83115
{
84-
Ref: "user2C2B57AE"
116+
"Fn::GetAtt": [
117+
"ClusterDefaultCapacityInstanceRole3E209969",
118+
"Arn"
119+
]
85120
},
86-
"\\\",\\\"groups\\\":[\\\"user-group1\\\",\\\"user-group2\\\"]},{\\\"userarn\\\":\\\"arn:",
121+
"\\\",\\\"username\\\":\\\"system:node:{{EC2PrivateDNSName}}\\\",\\\"groups\\\":[\\\"system:bootstrappers\\\",\\\"system:nodes\\\"]},{\\\"rolearn\\\":\\\"arn:aws:iam::123456789012:role/S3Access\\\",\\\"groups\\\":[\\\"group1\\\"]}]\",\"mapUsers\":\"[{\\\"userarn\\\":\\\"arn:",
87122
{
88123
Ref: "AWS::Partition"
89124
},
90-
":iam:us-east-1:",
125+
":iam::",
91126
{
92127
Ref: "AWS::AccountId"
93128
},
94-
":user/",
95-
{
96-
Ref: "user2C2B57AE"
97-
},
98-
"\\\",\\\"username\\\":\\\"foo\\\",\\\"groups\\\":[\\\"user-group1\\\",\\\"user-group2\\\"]}]\",\"mapAccounts\":\"[\\\"112233\\\",\\\"5566776655\\\"]\"}}]"
129+
":user/MyUserName\\\",\\\"groups\\\":[\\\"group2\\\"]}]\",\"mapAccounts\":\"[]\"}}]"
99130
]
100131
]
101132
}

packages/@aws-cdk/aws-iam/lib/user.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ export interface IUser extends IIdentity {
1616
*/
1717
readonly userName: string;
1818

19+
/**
20+
* The user's ARN
21+
* @attribute
22+
*/
23+
readonly userArn: string;
24+
1925
/**
2026
* Adds this user to a group.
2127
*/
@@ -78,7 +84,7 @@ export interface UserProps {
7884
* acknowledge your template's capabilities. For more information, see
7985
* Acknowledging IAM Resources in AWS CloudFormation Templates.
8086
*
81-
* @default Generated by CloudFormation (recommended)
87+
* @default - Generated by CloudFormation (recommended)
8288
*/
8389
readonly userName?: string;
8490

@@ -90,7 +96,7 @@ export interface UserProps {
9096
* use `secretsmanager.Secret.fromSecretAttributes` to reference a secret in
9197
* Secrets Manager.
9298
*
93-
* @default User won't be able to access the management console without a password.
99+
* @default - User won't be able to access the management console without a password.
94100
*/
95101
readonly password?: SecretValue;
96102

@@ -123,6 +129,7 @@ export class User extends Resource implements IIdentity, IUser {
123129
class Import extends Resource implements IUser {
124130
public readonly grantPrincipal: IPrincipal = this;
125131
public readonly userName: string = userName;
132+
public readonly userArn: string = arn;
126133
public readonly assumeRoleAction: string = 'sts:AssumeRole';
127134
public readonly policyFragment: PrincipalPolicyFragment = new ArnPrincipal(arn).policyFragment;
128135
private defaultPolicy?: Policy;

packages/@aws-cdk/aws-iam/test/user.test.ts

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,16 @@ describe('IAM user', () => {
1919
password: SecretValue.plainText('1234')
2020
});
2121

22-
expect(stack).toMatchTemplate({ Resources:
23-
{ MyUserDC45028B:
24-
{ Type: 'AWS::IAM::User',
25-
Properties: { LoginProfile: { Password: '1234' } } } } });
22+
expect(stack).toMatchTemplate({
23+
Resources:
24+
{
25+
MyUserDC45028B:
26+
{
27+
Type: 'AWS::IAM::User',
28+
Properties: { LoginProfile: { Password: '1234' } }
29+
}
30+
}
31+
});
2632
});
2733

2834
test('fails if reset password is required but no password is set', () => {
@@ -44,7 +50,7 @@ describe('IAM user', () => {
4450
// THEN
4551
expect(stack).toHaveResource('AWS::IAM::User', {
4652
ManagedPolicyArns: [
47-
{ "Fn::Join": [ "", [ "arn:", { Ref: "AWS::Partition" }, ":iam::aws:policy/asdf" ] ] }
53+
{ "Fn::Join": ["", ["arn:", { Ref: "AWS::Partition" }, ":iam::aws:policy/asdf"]] }
4854
]
4955
});
5056
});
@@ -74,4 +80,17 @@ describe('IAM user', () => {
7480
}
7581
});
7682
});
83+
84+
test('imported user has an ARN', () => {
85+
// GIVEN
86+
const stack = new Stack();
87+
88+
// WHEN
89+
const user = User.fromUserName(stack, 'import', 'MyUserName');
90+
91+
// THEN
92+
expect(stack.resolve(user.userArn)).toStrictEqual({
93+
"Fn::Join": ["", ["arn:", { Ref: "AWS::Partition" }, ":iam::", { Ref: "AWS::AccountId" }, ":user/MyUserName"]]
94+
});
95+
});
7796
});

0 commit comments

Comments
 (0)