Advanced 60 min read Data Security Updated: 2025-07-25

AWS KMS Key Policies & Encryption Governance

Master AWS KMS key policies, encryption strategies, and automated key management for data protection and compliance requirements.

📋 Prerequisites

  • Expert-level understanding of IAM policies, principles, and roles.
  • Deep knowledge of AWS services that integrate with KMS (S3, EBS, RDS).
  • Advanced proficiency in Terraform for deploying security infrastructure.
  • Familiarity with VPC networking concepts, including endpoints.

💡 Beyond Encryption: KMS as a Central Access Control Plane

Thinking of KMS as just an "encryption service" misses its primary power. At its core, KMS is a regional, highly available Hardware Security Module (HSM) backed service that governs access to the keys that protect your data. The KMS key policy, therefore, becomes the ultimate control plane for data access, capable of enforcing incredibly granular authorization logic that IAM policies alone cannot achieve.

🏷️ Topics Covered

AWS KMSEncryptionKey PolicyData ProtectionComplianceVPC EndpointCross-AccountTerraformSecurity GovernanceCryptography

KMS Policy Fundamentals: The Root of Trust

Every Customer-Managed Key (CMK) is controlled by its **key policy**, a resource-based policy. This policy is the ultimate authority on who can use and manage the key. Understanding its relationship with IAM policies is critical.

IAM Policies

  • Identity-based policies.
  • Defines what an identity (user/role) is allowed to do.
  • Cannot grant permissions alone.
  • Access is the intersection of IAM and Key Policy allows.

KMS Key Policies

  • Resource-based policies.
  • Defines who can access the key itself.
  • Is the ultimate source of truth for access.
  • Can grant permissions directly, or delegate to IAM.

📜 The Golden Rule of KMS Policies

A key policy **must** grant permissions to an IAM identity in the key's home account to manage the key. If it doesn't, you risk creating an unmanageable key. The safest practice is to always give the account root (`arn:aws:iam::ACCOUNT_ID:root`) full administrative permissions (`kms:*`) on the key. This ensures you can never be locked out.

Advanced Policy Conditions for Granular Control

KMS-specific condition keys allow you to create powerful, context-aware authorization rules that go far beyond simple `Allow` or `Deny` statements.

Example 1: Restricting Usage via AWS Services (`kms:ViaService`)

This policy statement allows Amazon RDS to use a key for encryption and decryption, but only when it's making requests *on behalf of the user* for a specific DB instance. It prevents a user from directly using the key with the AWS CLI for other purposes.

⚙️ JSON: `kms:ViaService` Condition

{
    "Sid": "Allow RDS to use the key for a specific database",
    "Effect": "Allow",
    "Principal": {
        "AWS": "arn:aws:iam::111122223333:role/DBAdminRole"
    },
    "Action": [
        "kms:Encrypt",
        "kms:Decrypt",
        "kms:ReEncrypt*",
        "kms:GenerateDataKey*",
        "kms:DescribeKey"
    ],
    "Resource": "*",
    "Condition": {
        "StringEquals": {
            "kms:ViaService": "rds.us-east-1.amazonaws.com"
        },
        "StringLike": {
            "kms:EncryptionContext:aws:rds:db-id": "db-ABCDEFG123456"
        }
    }
}

Example 2: Enforcing Encryption Context

Encryption context is arbitrary key-value data you can pass during an encrypt request. It's not encrypted, but it's cryptographically bound to the ciphertext. A matching context is required to decrypt. This policy enforces that a `Project` tag *must* be provided as encryption context when using the key.

🏷️ JSON: `kms:EncryptionContext` Condition

{
    "Sid": "EnforceProjectTagInEncryptionContext",
    "Effect": "Deny",
    "Principal": "*",
    "Action": [
        "kms:Encrypt",
        "kms:ReEncrypt*",
        "kms:GenerateDataKey*"
    ],
    "Resource": "*",
    "Condition": {
        "Null": {
            "kms:EncryptionContext:Project": "true"
        }
    }
}

Expert Patterns for Cross-Account Encryption

Securely sharing encrypted resources (like S3 objects, EBS snapshots, or AMI's) with another AWS account is a common but complex task. It requires coordinated permissions on both the KMS key and potentially the resource itself.

1

Grant Access on the Key

In the source account, the KMS key policy must explicitly grant usage permissions (like `kms:Decrypt`) to the target account's role or user.

2

Grant Access on the Resource

For services like S3, the bucket policy must also grant the target account permissions to access the object (`s3:GetObject`).

3

Caller Uses Permissions

The role in the target account must have an IAM policy allowing it to call `kms:Decrypt` on the source key's ARN and `s3:GetObject` on the object's ARN.

Locking Down Access with KMS VPC Endpoints

By default, calls to the KMS API traverse the public internet (though they are TLS encrypted). For high-security environments, you can use a KMS VPC Endpoint to ensure that all API traffic to KMS from your VPC stays within the AWS private network, and you can apply a policy to the endpoint itself for an extra layer of control.

Example: VPC Endpoint Policy for Least Privilege

This endpoint policy denies all KMS requests through the endpoint unless they are from a specific EC2 instance role and are intended for a specific KMS key used by that application. This dramatically reduces the risk of data exfiltration via compromised credentials.

🌐 JSON: KMS VPC Endpoint Policy

{
    "Statement": [
        {
            "Action": "kms:*",
            "Effect": "Allow",
            "Resource": "*",
            "Principal": { "AWS": "*" }
        },
        {
            "Action": "kms:*",
            "Effect": "Deny",
            "Resource": "*",
            "Principal": "*",
            "Condition": {
                "StringNotEquals": {
                    "aws:PrincipalArn": [
                        "arn:aws:iam::111122223333:role/WebAppInstanceRole",
                        "arn:aws:iam::111122223333:role/OpsAdminRole"
                    ]
                }
            }
        },
        {
            "Action": [ "kms:Encrypt", "kms:Decrypt", "kms:GenerateDataKey" ],
            "Effect": "Deny",
            "Resource": "*",
            "Principal": "*",
            "Condition": {
                "ArnNotEquals": {
                    "kms:KeyArn": "arn:aws:kms:us-east-1:111122223333:key/YOUR-APP-KEY-ID"
                }
            }
        }
    ]
}

Troubleshooting Common & Complex KMS Issues

KMS permission errors can be notoriously difficult to debug due to the dual policy evaluation model. Here are some common scenarios and how to resolve them.

🚨 Scenario 1: `AccessDeniedException` on Decrypt/Encrypt

  • Symptom: An application role with `kms:Decrypt` in its IAM policy fails to decrypt an S3 object.
  • Root Cause: The KMS Key Policy does not grant `kms:Decrypt` permission to the application role's ARN. **Both policies must allow the action.**
  • Solution: Add a statement to the KMS Key Policy explicitly allowing the application role ARN to perform the required `kms:Decrypt` action.

⏳ Scenario 2: `KMSInvalidStateException: Key is pending deletion`

  • Symptom: All cryptographic operations fail with this error.
  • Root Cause: Someone scheduled the key for deletion. KMS enforces a mandatory waiting period (7-30 days) before deletion.
  • Solution: Use the AWS CLI or Console to cancel the key deletion (`aws kms cancel-key-deletion --key-id ...`). This is why limiting `kms:ScheduleKeyDeletion` is critical.

🤝 Scenario 3: Cross-Account Access Fails Silently

  • Symptom: A user in Account B cannot access an EBS snapshot shared from Account A, even though their IAM policy seems correct.
  • Root Cause: Cross-account KMS access often requires using KMS Grants in addition to policies, especially for AWS services acting on your behalf. The service in Account B may not have a grant to use the key from Account A.
  • Solution: In Account A, create a grant for the key (`aws kms create-grant ...`) that allows Account B's role to perform `Decrypt` or `ReEncryptFrom` operations.

Managing Enterprise KMS as Code (Terraform)

Manually creating keys and complex policies is error-prone and doesn't scale. A robust KMS setup must be defined as code.

🏗️ HCL: Complete KMS Key and Policy with Terraform

data "aws_iam_policy_document" "kms_key_policy" {
  # Statement 1: Default - Allows root user full control to prevent lockout.
  statement {
    sid    = "EnableIAMUserPermissions"
    effect = "Allow"
    principals {
      type        = "AWS"
      identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"]
    }
    actions   = ["kms:*"]
    resources = ["*"]
  }

  # Statement 2: Allows Key Administrators to manage the key.
  statement {
    sid    = "AllowAdminAccess"
    effect = "Allow"
    principals {
      type        = "AWS"
      identifiers = [aws_iam_role.kms_admins.arn]
    }
    actions = [
      "kms:Create*", "kms:Describe*", "kms:Enable*", "kms:List*", "kms:Put*",
      "kms:Update*", "kms:Revoke*", "kms:Disable*", "kms:Get*",
      "kms:Delete*", "kms:TagResource", "kms:UntagResource",
      "kms:ScheduleKeyDeletion", "kms:CancelKeyDeletion"
    ]
    resources = ["*"]
  }

  # Statement 3: Allows specific application role to use the key for encryption/decryption.
  statement {
    sid    = "AllowAppUsage"
    effect = "Allow"
    principals {
      type        = "AWS"
      identifiers = [aws_iam_role.my_app_role.arn]
    }
    actions = [
      "kms:Encrypt",
      "kms:Decrypt",
      "kms:ReEncrypt*",
      "kms:GenerateDataKey*",
      "kms:DescribeKey"
    ]
    resources = ["*"]
  }
}

resource "aws_kms_key" "application_key" {
  description             = "CMK for my critical application"
  deletion_window_in_days = 10
  enable_key_rotation     = true
  policy                  = data.aws_iam_policy_document.kms_key_policy.json
  
  tags = {
    Name = "application-key"
  }
}

resource "aws_kms_alias" "application_key_alias" {
  name          = "alias/app/critical-data"
  target_key_id = aws_kms_key.application_key.key_id
}

🔑 Expert-Level KMS & Encryption Best Practices

  • Policy is Paramount: The KMS key policy is your most powerful data protection control. Master its syntax and conditions.
  • Always Prevent Lockout: Your key policy must always give `kms:*` to the account root (`arn:aws:iam::ACCOUNT_ID:root`).
  • Use Encryption Context: Always use a robust encryption context for programmatic encryption to protect against confused deputy attacks and provide data integrity.
  • Restrict Administrative Actions: Tightly control who has permissions for `kms:ScheduleKeyDeletion`, `kms:DisableKey`, and `kms:PutKeyPolicy`.
  • Automate with Code: Never manage critical keys or policies through the console. Define everything in IaC for auditability, versioning, and peer review.
  • Leverage VPC Endpoints: For sensitive workloads, isolate KMS traffic within your VPC using a VPC endpoint with a restrictive endpoint policy.

You've Mastered the Pillars of AWS Governance!

This completes our deep dive series into modern AWS cloud governance. Use these guides as a reference to build and maintain a secure, compliant, and well-managed cloud environment.