Skip to main content
CRITICALKMSSecurity

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

  1. 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:*
  2. 2Or add a NotPrincipal exemption for the account root or admin role(s)
  3. 3Or use a Condition with StringNotEquals on aws:PrincipalArn to exempt admin role ARNs
FlaggedA Deny on kms:* for Principal "*" with no root carveout locks every identity β€” including the account root β€” out of the key, making it effectively unrecoverable.
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: '*' },
    ],
  },
});
FixedThe Deny uses NotPrincipal to exclude the account root, so it can never lock the account out of its own key, while still denying every other principal.
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::Key

Compliance frameworks

SOC2NIST

AWS documentation

Read the AWS guidance

Intentional? 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 scan

CDK Insights runs this and 118+ other rules locally against your synthesised CDK app β€” free, no account, your code never leaves your machine.

More KMS rules