-
Notifications
You must be signed in to change notification settings - Fork 3.8k
/
aws-auth.ts
139 lines (124 loc) · 4.2 KB
/
aws-auth.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
import { Construct, IConstruct } from 'constructs';
import { AwsAuthMapping } from './aws-auth-mapping';
import { Cluster } from './cluster';
import { KubernetesManifest } from './k8s-manifest';
import * as iam from '../../aws-iam';
import { Lazy, Stack } from '../../core';
/**
* Configuration props for the AwsAuth construct.
*/
export interface AwsAuthProps {
/**
* The EKS cluster to apply this configuration to.
*
* [disable-awslint:ref-via-interface]
*/
readonly cluster: Cluster;
}
/**
* Manages mapping between IAM users and roles to Kubernetes RBAC configuration.
*
* @see https://docs.aws.amazon.com/en_us/eks/latest/userguide/add-user-role.html
*/
export class AwsAuth extends Construct {
private readonly stack: Stack;
private readonly roleMappings = new Array<{ role: iam.IRole, mapping: AwsAuthMapping }>();
private readonly userMappings = new Array<{ user: iam.IUser, mapping: AwsAuthMapping }>();
private readonly accounts = new Array<string>();
constructor(scope: Construct, id: string, props: AwsAuthProps) {
super(scope, id);
this.stack = Stack.of(this);
new KubernetesManifest(this, 'manifest', {
cluster: props.cluster,
overwrite: true, // this config map is auto-created by the cluster
manifest: [
{
apiVersion: 'v1',
kind: 'ConfigMap',
metadata: {
name: 'aws-auth',
namespace: 'kube-system',
},
data: {
mapRoles: this.synthesizeMapRoles(),
mapUsers: this.synthesizeMapUsers(),
mapAccounts: this.synthesizeMapAccounts(),
},
},
],
});
}
/**
* Adds the specified IAM role to the `system:masters` RBAC group, which means
* that anyone that can assume it will be able to administer this Kubernetes system.
*
* @param role The IAM role to add
* @param username Optional user (defaults to the role ARN)
*/
public addMastersRole(role: iam.IRole, username?: string) {
this.addRoleMapping(role, {
username,
groups: ['system:masters'],
});
}
/**
* Adds a mapping between an IAM role to a Kubernetes user and groups.
*
* @param role The IAM role to map
* @param mapping Mapping to k8s user name and groups
*/
public addRoleMapping(role: iam.IRole, mapping: AwsAuthMapping) {
this.assertSameStack(role);
this.roleMappings.push({ role, mapping });
}
/**
* Adds a mapping between an IAM user to a Kubernetes user and groups.
*
* @param user The IAM user to map
* @param mapping Mapping to k8s user name and groups
*/
public addUserMapping(user: iam.IUser, mapping: AwsAuthMapping) {
this.assertSameStack(user);
this.userMappings.push({ user, mapping });
}
/**
* Additional AWS account to add to the aws-auth configmap.
* @param accountId account number
*/
public addAccount(accountId: string) {
this.accounts.push(accountId);
}
private assertSameStack(construct: IConstruct) {
const thisStack = Stack.of(this);
if (Stack.of(construct) !== thisStack) {
// aws-auth is always part of the cluster stack, and since resources commonly take
// a dependency on the cluster, allowing those resources to be in a different stack,
// will create a circular dependency. granted, it won't always be the case,
// but we opted for the more causious and restrictive approach for now.
throw new Error(`${construct.node.path} should be defined in the scope of the ${thisStack.stackName} stack to prevent circular dependencies`);
}
}
private synthesizeMapRoles() {
return Lazy.any({
produce: () => this.stack.toJsonString(this.roleMappings.map(m => ({
rolearn: m.role.roleArn,
username: m.mapping.username ?? m.role.roleArn,
groups: m.mapping.groups,
}))),
});
}
private synthesizeMapUsers() {
return Lazy.any({
produce: () => this.stack.toJsonString(this.userMappings.map(m => ({
userarn: m.user.userArn,
username: m.mapping.username ?? m.user.userArn,
groups: m.mapping.groups,
}))),
});
}
private synthesizeMapAccounts() {
return Lazy.any({
produce: () => this.stack.toJsonString(this.accounts),
});
}
}