1 | # AWS Key Management Service Construct Library
|
2 |
|
3 |
|
4 | ---
|
5 |
|
6 | 
|
7 |
|
8 | 
|
9 |
|
10 | ---
|
11 |
|
12 |
|
13 |
|
14 | Define a KMS key:
|
15 |
|
16 | ```ts
|
17 | new kms.Key(this, 'MyKey', {
|
18 | enableKeyRotation: true,
|
19 | });
|
20 | ```
|
21 |
|
22 | Define a KMS key with waiting period:
|
23 |
|
24 | Specifies the number of days in the waiting period before AWS KMS deletes a CMK that has been removed from a CloudFormation stack.
|
25 |
|
26 | ```ts
|
27 | const key = new kms.Key(this, 'MyKey', {
|
28 | pendingWindow: Duration.days(10), // Default to 30 Days
|
29 | });
|
30 | ```
|
31 |
|
32 |
|
33 | Add a couple of aliases:
|
34 |
|
35 | ```ts
|
36 | const key = new kms.Key(this, 'MyKey');
|
37 | key.addAlias('alias/foo');
|
38 | key.addAlias('alias/bar');
|
39 | ```
|
40 |
|
41 |
|
42 | Define a key with specific key spec and key usage:
|
43 |
|
44 | Valid `keySpec` values depends on `keyUsage` value.
|
45 |
|
46 | ```ts
|
47 | const key = new kms.Key(this, 'MyKey', {
|
48 | keySpec: kms.KeySpec.ECC_SECG_P256K1, // Default to SYMMETRIC_DEFAULT
|
49 | keyUsage: kms.KeyUsage.SIGN_VERIFY, // and ENCRYPT_DECRYPT
|
50 | });
|
51 | ```
|
52 |
|
53 | ## Sharing keys between stacks
|
54 |
|
55 | To use a KMS key in a different stack in the same CDK application,
|
56 | pass the construct to the other stack:
|
57 |
|
58 | [sharing key between stacks](test/integ.key-sharing.lit.ts)
|
59 |
|
60 |
|
61 | ## Importing existing keys
|
62 |
|
63 | ### Import key by ARN
|
64 |
|
65 | To use a KMS key that is not defined in this CDK app, but is created through other means, use
|
66 | `Key.fromKeyArn(parent, name, ref)`:
|
67 |
|
68 | ```ts
|
69 | const myKeyImported = kms.Key.fromKeyArn(this, 'MyImportedKey', 'arn:aws:...');
|
70 |
|
71 | // you can do stuff with this imported key.
|
72 | myKeyImported.addAlias('alias/foo');
|
73 | ```
|
74 |
|
75 | Note that a call to `.addToResourcePolicy(statement)` on `myKeyImported` will not have
|
76 | an affect on the key's policy because it is not owned by your stack. The call
|
77 | will be a no-op.
|
78 |
|
79 | ### Import key by alias
|
80 |
|
81 | If a Key has an associated Alias, the Alias can be imported by name and used in place
|
82 | of the Key as a reference. A common scenario for this is in referencing AWS managed keys.
|
83 |
|
84 | ```ts
|
85 | import * as cloudtrail from '@aws-cdk/aws-cloudtrail';
|
86 |
|
87 | const myKeyAlias = kms.Alias.fromAliasName(this, 'myKey', 'alias/aws/s3');
|
88 | const trail = new cloudtrail.Trail(this, 'myCloudTrail', {
|
89 | sendToCloudWatchLogs: true,
|
90 | kmsKey: myKeyAlias,
|
91 | });
|
92 | ```
|
93 |
|
94 | Note that calls to `addToResourcePolicy` and `grant*` methods on `myKeyAlias` will be
|
95 | no-ops, and `addAlias` and `aliasTargetKey` will fail, as the imported alias does not
|
96 | have a reference to the underlying KMS Key.
|
97 |
|
98 | ### Lookup key by alias
|
99 |
|
100 | If you can't use a KMS key imported by alias (e.g. because you need access to the key id), you can lookup the key with `Key.fromLookup()`.
|
101 |
|
102 | In general, the preferred method would be to use `Alias.fromAliasName()` which returns an `IAlias` object which extends `IKey`. However, some services need to have access to the underlying key id. In this case, `Key.fromLookup()` allows to lookup the key id.
|
103 |
|
104 | The result of the `Key.fromLookup()` operation will be written to a file
|
105 | called `cdk.context.json`. You must commit this file to source control so
|
106 | that the lookup values are available in non-privileged environments such
|
107 | as CI build steps, and to ensure your template builds are repeatable.
|
108 |
|
109 | Here's how `Key.fromLookup()` can be used:
|
110 |
|
111 | ```ts
|
112 | const myKeyLookup = kms.Key.fromLookup(this, 'MyKeyLookup', {
|
113 | aliasName: 'alias/KeyAlias',
|
114 | });
|
115 |
|
116 | const role = new iam.Role(this, 'MyRole', {
|
117 | assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
|
118 | });
|
119 | myKeyLookup.grantEncryptDecrypt(role);
|
120 | ```
|
121 |
|
122 | Note that a call to `.addToResourcePolicy(statement)` on `myKeyLookup` will not have
|
123 | an affect on the key's policy because it is not owned by your stack. The call
|
124 | will be a no-op.
|
125 |
|
126 | ## Key Policies
|
127 |
|
128 | Controlling access and usage of KMS Keys requires the use of key policies (resource-based policies attached to the key);
|
129 | this is in contrast to most other AWS resources where access can be entirely controlled with IAM policies,
|
130 | and optionally complemented with resource policies. For more in-depth understanding of KMS key access and policies, see
|
131 |
|
132 | * https://docs.aws.amazon.com/kms/latest/developerguide/control-access-overview.html
|
133 | * https://docs.aws.amazon.com/kms/latest/developerguide/key-policies.html
|
134 |
|
135 | KMS keys can be created to trust IAM policies. This is the default behavior for both the KMS APIs and in
|
136 | the console. This behavior is enabled by the '@aws-cdk/aws-kms:defaultKeyPolicies' feature flag,
|
137 | which is set for all new projects; for existing projects, this same behavior can be enabled by
|
138 | passing the `trustAccountIdentities` property as `true` when creating the key:
|
139 |
|
140 | ```ts
|
141 | new kms.Key(this, 'MyKey', { trustAccountIdentities: true });
|
142 | ```
|
143 |
|
144 | With either the `@aws-cdk/aws-kms:defaultKeyPolicies` feature flag set,
|
145 | or the `trustAccountIdentities` prop set, the Key will be given the following default key policy:
|
146 |
|
147 | ```json
|
148 | {
|
149 | "Effect": "Allow",
|
150 | "Principal": {"AWS": "arn:aws:iam::111122223333:root"},
|
151 | "Action": "kms:*",
|
152 | "Resource": "*"
|
153 | }
|
154 | ```
|
155 |
|
156 | This policy grants full access to the key to the root account user.
|
157 | This enables the root account user -- via IAM policies -- to grant access to other IAM principals.
|
158 | With the above default policy, future permissions can be added to either the key policy or IAM principal policy.
|
159 |
|
160 | ```ts
|
161 | const key = new kms.Key(this, 'MyKey');
|
162 | const user = new iam.User(this, 'MyUser');
|
163 | key.grantEncrypt(user); // Adds encrypt permissions to user policy; key policy is unmodified.
|
164 | ```
|
165 |
|
166 | Adopting the default KMS key policy (and so trusting account identities)
|
167 | solves many issues around cyclic dependencies between stacks.
|
168 | Without this default key policy, future permissions must be added to both the key policy and IAM principal policy,
|
169 | which can cause cyclic dependencies if the permissions cross stack boundaries.
|
170 | (For example, an encrypted bucket in one stack, and Lambda function that accesses it in another.)
|
171 |
|
172 | ### Appending to or replacing the default key policy
|
173 |
|
174 | The default key policy can be amended or replaced entirely, depending on your use case and requirements.
|
175 | A common addition to the key policy would be to add other key admins that are allowed to administer the key
|
176 | (e.g., change permissions, revoke, delete). Additional key admins can be specified at key creation or after
|
177 | via the `grantAdmin` method.
|
178 |
|
179 | ```ts
|
180 | const myTrustedAdminRole = iam.Role.fromRoleArn(this, 'TrustedRole', 'arn:aws:iam:....');
|
181 | const key = new kms.Key(this, 'MyKey', {
|
182 | admins: [myTrustedAdminRole],
|
183 | });
|
184 |
|
185 | const secondKey = new kms.Key(this, 'MyKey2');
|
186 | secondKey.grantAdmin(myTrustedAdminRole);
|
187 | ```
|
188 |
|
189 | Alternatively, a custom key policy can be specified, which will replace the default key policy.
|
190 |
|
191 | > **Note**: In applications without the '@aws-cdk/aws-kms:defaultKeyPolicies' feature flag set
|
192 | and with `trustedAccountIdentities` set to false (the default), specifying a policy at key creation _appends_ the
|
193 | provided policy to the default key policy, rather than _replacing_ the default policy.
|
194 |
|
195 | ```ts
|
196 | const myTrustedAdminRole = iam.Role.fromRoleArn(this, 'TrustedRole', 'arn:aws:iam:....');
|
197 | // Creates a limited admin policy and assigns to the account root.
|
198 | const myCustomPolicy = new iam.PolicyDocument({
|
199 | statements: [new iam.PolicyStatement({
|
200 | actions: [
|
201 | 'kms:Create*',
|
202 | 'kms:Describe*',
|
203 | 'kms:Enable*',
|
204 | 'kms:List*',
|
205 | 'kms:Put*',
|
206 | ],
|
207 | principals: [new iam.AccountRootPrincipal()],
|
208 | resources: ['*'],
|
209 | })],
|
210 | });
|
211 | const key = new kms.Key(this, 'MyKey', {
|
212 | policy: myCustomPolicy,
|
213 | });
|
214 | ```
|
215 |
|
216 | > **Warning:** Replacing the default key policy with one that only grants access to a specific user or role
|
217 | runs the risk of the key becoming unmanageable if that user or role is deleted.
|
218 | It is highly recommended that the key policy grants access to the account root, rather than specific principals.
|
219 | See https://docs.aws.amazon.com/kms/latest/developerguide/key-policies.html for more information.
|