KMS Key Policy Self-Lockout
kms-key-policy-self-lockout
What this rule checks
Detects AWS::KMS::Key resources whose KeyPolicy contains a Deny statement on kms:*, kms:PutKeyPolicy, or * with a broad Principal and no carveout for the account root or admin role. Such a policy locks the account out of its own key β recovery requires breaking glass on the account root credentials AND scheduling a 7-30 day key deletion (which does not actually let you recover the key, just remove it). Effectively unrecoverable for any data the key has encrypted; AWS Support typically declines to intervene where customers have a self-service path via root.
How to fix it
- 1Keep the auto-injected root Allow statement (CDK's defaultKeyPolicy) intact, or add an explicit Allow on Principal: { AWS: 'arn:aws:iam::<account>:root' } for Action: kms:*
- 2Or add a NotPrincipal exemption for the account root or admin role(s)
- 3Or use a Condition with StringNotEquals on aws:PrincipalArn to exempt admin role ARNs
import { aws_kms as kms } from 'aws-cdk-lib';
new kms.CfnKey(this, 'Key', {
keyPolicy: {
Version: '2012-10-17',
Statement: [
{ Effect: 'Deny', Principal: '*', Action: 'kms:*', Resource: '*' },
],
},
});import { Aws, aws_kms as kms } from 'aws-cdk-lib';
new kms.CfnKey(this, 'Key', {
keyPolicy: {
Version: '2012-10-17',
Statement: [
{
Sid: 'EnableRootAccess',
Effect: 'Allow',
Principal: { AWS: `arn:aws:iam::${Aws.ACCOUNT_ID}:root` },
Action: 'kms:*',
Resource: '*',
},
{
Sid: 'DenyEveryoneButRoot',
Effect: 'Deny',
NotPrincipal: { AWS: `arn:aws:iam::${Aws.ACCOUNT_ID}:root` },
Action: 'kms:*',
Resource: '*',
},
],
},
});CDK Insights pinpoints the exact file and line in your CDK source for every finding, so you can jump straight to the fix.
Affected resource types
AWS::KMS::KeyIntentional? Suppress this finding
Sometimes a flag is deliberate β a genuinely public endpoint, say. You can dismiss kms-key-policy-self-lockout and the reason is kept in the report, not silently hidden.
In .cdk-insights.json:
{
"ignoreRules": [
{ "id": "kms-key-policy-self-lockout", "reason": "Why this is intentional" }
]
}Or inline in your CDK code:
Validations.of(scope).acknowledge({
id: 'cdk-insights::kms-key-policy-self-lockout',
reason: 'Why this is intentional',
});Use the rule ID kms-key-policy-self-lockout shown above β not the CDK-* ID from SARIF / GitHub code scanning. To dismiss every finding on one construct instead, use ignorePaths. Suppression docs β
Catch this in your stack
$ npx cdk-insights scanCDK Insights runs this and 118+ other rules locally against your synthesised CDK app β free, no account, your code never leaves your machine.