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