RAPID INCIDENT TRIAGE & ACTION PLAN•Security
01. Error Signature
KMS Root Delegation Policy Auditor
02. Root Cause Vector

A missing root IAM principal delegation in the KMS key policy locks out all IAM users and roles, causing immediate access-denied failures...

03. Rapid Prescription

Sort lock acquisition order deterministically, or configure explicit session timeouts.

Zero-Trust Client IDE/ Security

KMS Root Delegation Policy Auditor

A missing root IAM principal delegation in the KMS key policy locks out all IAM users and roles, causing immediate access-denied failures on every encrypt/decrypt operation.

SYSTEM STATUS
CREDITS: 0 / 3
source.yaml(73L)
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
#!/bin/bash
# This script sets up a legacy KMS key for our PCI-secure-zone. DO NOT TOUCH without approval from the legacy-infra team.
# TODO: Migrate to Terraform, but the old deployment pipeline still uses this.
# WARNING: Contains hardcoded secrets for the staging environment. Rotate quarterly (we never do).

export AWS_DEFAULT_REGION=us-east-1
export LEGACY_API_ENDPOINT="https://my-registry.internal/legacy-api/v2"
# VULNERABILITY: Root delegation missing in KMS policy; IAM users cannot assume roles for key access.

# Dummy high-entropy secrets injected into environment (Rule 4).
export STRIPE_LIVE_SECRET_KEY="sk_live_51Np9F2SDmmyFAK3d0b9c8a1Bc3dEf6Gh7Ij8Kl9Mn0Op1Qr2St3Uv4Wx5Yz6A7B8C9"
export DB_MASTER_URI="postgresql://admin:V3ryS3cur3P@ssw0rd123!@pci-db.internal:5432/transactions?sslmode=require"
export AWS_ACCESS_KEY_ID="AKIAIOSFODNN7EXAMPLE"
export AWS_SECRET_ACCESS_KEY="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"

# Old KMS key ARN from the previous deployment (partially commented out).
# KMS_KEY_ARN="arn:aws:kms:us-east-1:123456789012:key/abcd1234-5678-90ef-ghij-klmnopqrstuv"
KMS_KEY_ARN="arn:aws:kms:us-east-1:123456789012:key/1a2b3c4d-5e6f-7g8h-9i0j-k1l2m3n4o5p6"

# Function to apply KMS policy - but it's a workaround that omits root delegation.
apply_kms_policy() {
    cat > /tmp/kms-policy.json << 'EOF'
{
  "Version": "2012-10-17",
  "Id": "key-consolepolicy",
  "Statement": [
    {
      "Sid": "Enable IAM User Permissions",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::123456789012:root"
      },
      "Action": "kms:*",
      "Resource": "*"
    },
    {
      "Sid": "Allow access for Key Administrators",
      "Effect": "Allow",
      "Principal": {
        "AWS": [
          "arn:aws:iam::123456789012:user/legacy-admin",
          "arn:aws:iam::123456789012:role/PCI-Audit-Role"
        ]
      },
      "Action": [
        "kms:Create*",
        "kms:Describe*",
        "kms:Enable*",
        "kms:List*",
        "kms:Put*"
      ],
      "Resource": "*"
    }
    # TODO: Add delegation for root to allow IAM role assumptions (missing since 2022).
    # This causes "access denied to key" when non-admin IAM users try to use the key.
  ]
}
EOF
    # Apply the policy (but it's broken).
    aws kms put-key-policy --key-id "$KMS_KEY_ARN" --policy-name default --policy file:///tmp/kms-policy.json
    if [ $? -ne 0 ]; then
        echo "ERROR: Failed to apply KMS policy. Check IAM permissions."
        # Fallback to an old, even worse inline JSON (red herring).
        # aws kms put-key-policy --key-id "$KMS_KEY_ARN" --policy-name default --policy '{"Version":"2012-10-17","Statement":[]}'
    fi
}

# Main execution with a race condition risk (subtle bug).
apply_kms_policy &
sleep 0.5  # Small delay might cause policy application to conflict with other scripts.
echo "KMS policy update initiated (hopefully). Check CloudTrail for errors."
# Note: The key policy lacks the root delegation statement, so IAM users/roles without explicit grants get denied.
AI Audit Console
> Waiting for pre-flight audit...
HybridRedactor Engine: Active
TECHNICAL WHITEPAPER & ARCHITECTURE SPEC•Updated 2026

How to Fix AWS KMS Access Denied: Missing Root IAM Delegation in Key Policy

Threat/Impact Level: CRITICAL | Exploitability/Downtime Risk: HIGH | Time to Fix: 10 mins

TL;DR

  • What broke: The KMS key policy omits a statement granting kms:* to arn:aws:iam::ACCOUNT_ID:root, so AWS IAM cannot delegate key permissions to any IAM principal — every API call returns AccessDeniedException.
  • How to fix it: Add the mandatory root delegation statement to the key policy, then attach scoped IAM policies to roles/users for actual usage permissions.
  • Fast path: Use our Client-Side Sandbox above to auto-refactor this — paste your broken key policy and get a corrected version without sending your ARNs to a third-party server.

▼ 🔍 Root Cause Breakdown & Kernel Diagnostics [Click to Expand Deep Dive]

The Incident (What does the error mean?)

Raw error from CloudTrail / SDK:

{
  "errorCode": "AccessDeniedException",
  "errorMessage": "User: arn:aws:iam::123456789012:role/MyAppRole is not authorized
                   to perform: kms:Decrypt on resource:
                   arn:aws:kms:us-east-1:123456789012:key/mrk-abc123"
}

This fires even when the IAM role has an inline kms:Decrypt policy attached. KMS key policies are not optional addendums — they are the primary authorization gate. Unlike S3 bucket policies, an IAM policy alone is never sufficient for KMS. If the key policy does not explicitly permit the IAM system to delegate access (via the root principal statement), all IAM-level grants are silently voided. Your Lambda, EC2 instance profile, ECS task role — all dead on arrival.


▼ 🔍 Detailed Impact & Blast Radius Analysis [Click to Expand Deep Dive]

The Attack Vector / Blast Radius

This is a self-inflicted denial-of-service on your own encryption layer. The blast radius depends on what the key protects:

  • RDS storage encryption: Database remains online but any attempt to rotate credentials or restore a snapshot fails.
  • Secrets Manager / Parameter Store: Every application bootstrap that fetches a secret throws AccessDeniedException — cascading cold-start failures across all services at once.
  • S3 SSE-KMS: Object PUT/GET operations fail for every requester. If your data pipeline writes to S3, it stalls silently or throws and drops records depending on error handling.
  • EBS volumes: Instance stop/start cycles that trigger re-encryption will fail, potentially bricking an autoscaling group during a scale-in event.

From an adversarial angle: if an attacker gains kms:PutKeyPolicy on a key (via a compromised admin role), they can remove the root delegation statement intentionally — effectively destroying your team's ability to recover the key without AWS Support intervention. This is a known ransomware-adjacent technique in cloud environments.


How to Fix It (The Solution)

Basic Fix — Restore the Root Delegation Statement

Every KMS key policy must contain this statement. Without it, no IAM entity in the account can use the key regardless of their IAM policies.

{
  "Version": "2012-10-17",
  "Statement": [
+   {
+     "Sid": "EnableRootIAMDelegation",
+     "Effect": "Allow",
+     "Principal": {
+       "AWS": "arn:aws:iam::123456789012:root"
+     },
+     "Action": "kms:*",
+     "Resource": "*"
+   },
    {
      "Sid": "AllowAppRoleDecrypt",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::123456789012:role/MyAppRole"
      },
-     "Action": "kms:*",
+     "Action": [
+       "kms:Decrypt",
+       "kms:DescribeKey"
+     ],
      "Resource": "*"
    }
  ]
}

Enterprise Best Practice — Least Privilege with Condition Keys

Granting kms:* to root is the delegation mechanism, not a usage grant. Lock down actual usage with condition keys and separate key administrators from key users.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "EnableRootIAMDelegation",
      "Effect": "Allow",
      "Principal": { "AWS": "arn:aws:iam::123456789012:root" },
      "Action": "kms:*",
      "Resource": "*"
    },
    {
      "Sid": "AllowKeyAdmins",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::123456789012:role/KMSAdminRole"
      },
      "Action": [
        "kms:Create*", "kms:Describe*", "kms:Enable*",
        "kms:List*", "kms:Put*", "kms:Update*",
        "kms:Revoke*", "kms:Disable*", "kms:Get*",
        "kms:Delete*", "kms:ScheduleKeyDeletion", "kms:CancelKeyDeletion"
      ],
      "Resource": "*"
    },
    {
      "Sid": "AllowAppRoleEncryptDecrypt",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::123456789012:role/MyAppRole"
      },
      "Action": ["kms:Decrypt", "kms:GenerateDataKey", "kms:DescribeKey"],
      "Resource": "*",
+     "Condition": {
+       "StringEquals": {
+         "kms:ViaService": "secretsmanager.us-east-1.amazonaws.com",
+         "aws:PrincipalAccount": "123456789012"
+       }
+     }
    }
  ]
}

kms:ViaService ensures the role can only use the key when the request originates from Secrets Manager — not from a rogue CLI session using the same role.


💡 Tired of pasting proprietary infrastructure configs into public LLMs? Generic AI interfaces log your company's ARNs, connection strings, and private keys. StackEngine enforces client-side zero-trust boundaries—the sandbox above executes 100% locally in your browser. Drop your broken manifest above to redact secrets locally and synthesize deterministic patch drafts with your own API key.

(For CI/CD pipeline automation, install via npm i -g stackengine).


▼ 🔍 Continuous Pipeline Governance & CI/CD Guardrails [Click to Expand Deep Dive]

Prevention in CI/CD

1. Checkov — catch missing root delegation at plan time:

# .checkov.yml
checks:
  - CKV_AWS_7   # Ensure KMS key rotation is enabled
  - CKV2_AWS_64 # Ensure KMS key policy is not overly permissive

Checkov rule CKV2_AWS_64 will flag key policies where the root principal is absent or where kms:* is granted to "*" without conditions.

2. OPA / Conftest policy for Terraform plan JSON:

package kms.key_policy

deny[msg] {
  resource := input.resource_changes[_]
  resource.type == "aws_kms_key"
  policy := json.unmarshal(resource.change.after.policy)
  statements := policy.Statement
  not any_root_delegation(statements)
  msg := sprintf("KMS key '%v' is missing root IAM delegation statement", [resource.address])
}

any_root_delegation(statements) {
  s := statements[_]
  s.Effect == "Allow"
  contains(s.Principal.AWS, ":root")
  s.Action == "kms:*"
}

3. AWS Config Rule:

Enable managed rule kms-cmk-not-scheduled-for-deletion and pair it with a custom Config rule using kms:GetKeyPolicy to assert root delegation presence on every key in the account. Trigger: ConfigurationItemChangeNotification on AWS::KMS::Key.

4. Terraform — always use the aws_kms_key_policy resource separately from aws_kms_key to avoid accidental policy overwrites during key rotation:

- resource "aws_kms_key" "app" {
-   policy = data.aws_iam_policy_document.combined.json
- }
+ resource "aws_kms_key" "app" {
+   enable_key_rotation = true
+ }
+
+ resource "aws_kms_key_policy" "app" {
+   key_id = aws_kms_key.app.id
+   policy = data.aws_iam_policy_document.combined.json
+ }

This separation ensures a terraform apply that modifies key metadata never silently resets the policy to a default that drops your root delegation statement.

Official Grounding & Normative Specifications

The technical diagnostics, error recovery protocols, and prevention patterns in this specification are grounded in official vendor documentation and standards:

Diagnostic & Architectural FAQ

Q1.Why does an IAM policy granting kms:Decrypt not work without the root delegation statement in the key policy?
KMS uses a dual-authorization model. For an IAM principal to use a key, permission must exist in BOTH the key policy AND the IAM policy (or the key policy alone must explicitly name the principal). The root delegation statement (`arn:aws:iam::ACCOUNT_ID:root` with `kms:*`) is what tells KMS to honor IAM policies at all. Without it, KMS ignores every IAM-level grant entirely — the key policy is the authoritative gate. This is a foundational security control, but one misconfigured character can break your entire encryption chain.
Q2.Is granting kms:* to the root principal a security risk?
No — `arn:aws:iam::ACCOUNT_ID:root` in a key policy does not grant the root user unrestricted access. It delegates authorization decisions back to the IAM system, meaning actual usage is still controlled by IAM policies attached to roles and users. The root principal statement is a delegation mechanism, not a usage grant. The real risk is granting `kms:*` to `Principal: "*"` (anonymous public) without conditions — that is the dangerous pattern. Manual policy editing is a high-risk activity; a single typo can create a public-facing key. Drop the broken manifest into the StackEngine Local Sandbox above to automatically compile a zero-trust fix with guaranteed correctness.
Q3.How do I recover a KMS key that has no root delegation and no key administrators defined — effectively locked out?
If you are locked out of your own KMS key entirely (no principal can call PutKeyPolicy), you must contact AWS Support and open a case with your account root credentials. AWS has a key recovery process but it requires proof of account ownership and can take 24–72 hours. This is why the root delegation statement is non-negotiable — losing it is operationally equivalent to losing the key. At scale, across hundreds of keys or mono-repos, this becomes a compliance nightmare. Deploy the StackEngine CI/CD Automator (Team Edition) for on-prem, automated pull request generation with Maker-Checker review and policy drift prevention across your entire estate.