KMS là service mà ai cũng dùng nhưng ít người thực sự hiểu sâu cơ chế authorization của nó. Khác với mọi AWS service khác, KMS có hệ thống dual-authorization riêng — key policy + IAM policy + grants — và nếu bạn cấu hình sai, bạn có thể mất hoàn toàn quyền truy cập vào key mà không có cách nào recover ngoài liên hệ AWS Support. Bài này mình chia sẻ toàn bộ kiến thức thực chiến về KMS key policies: từ evaluation logic, cross-account patterns, condition keys, đến production patterns mà mình đã áp dụng. Tất cả đều kèm JSON policy examples để bạn có thể copy và adapt ngay.
TL;DR
- KMS có dual-authorization duy nhất: key policy (resource-based) + IAM policy + grants — sai có thể mất hoàn toàn quyền truy cập key, chỉ AWS Support recover được.
- Default key policy với
Principal: arn:aws:iam::ACCOUNT:root+kms:*là delegation statement (không phải grant root); xoá nó = key bị “lockout”.- Same-account: chỉ cần 1 trong 3 cơ chế allow; cross-account: phải allow ở CẢ HAI phía (key policy của owner + IAM policy của consumer), specify đúng key ARN.
- AWS managed keys (
aws/s3,aws/ebs) KHÔNG share cross-account được — production phải dùng customer managed keys.kms:ViaServicegiới hạn key chỉ dùng qua service cụ thể (chặnkms:Decrypttrực tiếp);kms:EncryptionContextlà single-valued, KHÔNG dùngForAllValues/ForAnyValue.- Envelope encryption bắt buộc cho data > 4KB:
GenerateDataKeylấy DEK, encrypt locally, xoá plaintext DEK khỏi memory — tránh KMS throttling.- Grants eventually consistent — dùng
GrantTokenngay để bypass propagation delay; tách Key Administrators (khôngEncrypt/Decrypt) khỏi Key Users (khôngPutKeyPolicy) cho separation of duties.
KMS Key Policy là gì và tại sao nó đặc biệt
Khác biệt cốt lõi so với IAM Policy
Với hầu hết AWS services (S3, EC2, Lambda…), bạn chỉ cần một IAM policy Allow là đủ để truy cập resource. KMS không hoạt động như vậy.
Mỗi KMS key bắt buộc phải có một key policy — đây là resource-based policy duy nhất gắn trực tiếp vào key. Điểm khác biệt quan trọng:
- Key policy là cơ chế authorization chính — nếu key policy không cho phép (trực tiếp hoặc gián tiếp), không có IAM policy nào có thể grant access.
- IAM policy chỉ hoạt động khi key policy “ủy quyền” — thông qua statement cho phép account root (
arn:aws:iam::ACCOUNT:root). - Mỗi key chỉ có 1 key policy, giới hạn 32 KB — không thể attach nhiều policy như IAM.
- Key policy có thể hoạt động độc lập — không cần IAM policy nếu key policy đã grant trực tiếp cho principal.
Nói cách khác: với S3 bucket, bạn có thể dùng IAM policy HOẶC bucket policy. Với KMS, key policy luôn luôn được evaluate, và IAM policy chỉ là “phần mở rộng” khi key policy cho phép.
Tại sao thiết kế như vậy?
KMS quản lý encryption keys — thứ mà nếu mất quyền kiểm soát thì data cũng mất theo. AWS thiết kế dual-authorization để:
- Ngăn chặn privilege escalation: một IAM admin không thể tự grant quyền dùng KMS key nếu key policy không delegate.
- Separation of duties: key administrator (quản lý key) và key user (dùng key để encrypt/decrypt) có thể là hai nhóm người khác nhau.
- Blast radius control: xoá một IAM policy không ảnh hưởng đến ai đang được key policy grant trực tiếp.
Evaluation Logic: Key Policy + IAM Policy + Grants
Flowchart đánh giá quyền truy cập
Khi một principal gọi KMS API (ví dụ kms:Decrypt), AWS evaluate theo logic sau:
┌─────────────────────────────────────────────────────────────┐
│ KMS Authorization Flow │
├─────────────────────────────────────────────────────────────┤
│ │
│ Request đến ──→ Có Explicit Deny không? │
│ (Key policy, IAM, SCP, VPC endpoint) │
│ │ │
│ YES ─┘──→ ❌ DENY │
│ NO ─┐ │
│ ▼ │
│ Key policy grant trực tiếp │
│ cho principal này? │
│ │ │
│ YES ─┘──→ ✅ ALLOW │
│ NO ─┐ │
│ ▼ │
│ Key policy có delegate cho │
│ account root không? │
│ │ │
│ NO ─┘──→ ❌ DENY (IAM vô dụng) │
│ YES ─┐ │
│ ▼ │
│ IAM policy HOẶC Grant │
│ cho phép action này? │
│ │ │
│ YES ─┘──→ ✅ ALLOW │
│ NO ─┘──→ ❌ DENY │
│ │
└─────────────────────────────────────────────────────────────┘
Ba cơ chế grant access
| Cơ chế | Khi nào dùng | Đặc điểm |
|---|---|---|
| Key Policy | Luôn có. Dùng cho: static access, key admin, service integrations | 1 per key, 32KB limit, gắn trực tiếp vào key |
| IAM Policy | Quản lý access ở scale lớn, ABAC/RBAC, multi-key | Cần root delegation trong key policy, quản lý tập trung |
| Grants | Temporary/dynamic access, AWS service integrations | Tạo bằng API, eventually consistent, không ảnh hưởng policy size |
Same-account vs Cross-account
Same-account — chỉ cần MỘT trong ba cơ chế allow (với điều kiện key policy delegate cho root):
Key policy grant trực tiếp → ALLOW
HOẶC
Key policy delegate root + IAM policy allow → ALLOW
HOẶC
Key policy delegate root + Grant allow → ALLOW
Cross-account — cần CẢ HAI phía:
Key policy (account A) allow external principal
VÀ
IAM policy (account B) allow action trên key ARN
Đây là lý do cross-account KMS access phức tạp hơn nhiều so với cross-account S3 hay cross-account AssumeRole.
Default Key Policy — cái bẫy mà ai cũng gặp
Default key policy trông như thế nào
Khi bạn tạo KMS key qua Console hoặc CLI mà không specify custom policy, AWS tạo default key policy:
{
"Version": "2012-10-17",
"Id": "key-default-1",
"Statement": [
{
"Sid": "Enable IAM User Permissions",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::111122223333:root"
},
"Action": "kms:*",
"Resource": "*"
}
]
}
Statement này không phải grant quyền cho root user. Nó là delegation statement — nói với KMS rằng: “cho phép IAM policies trong account này grant access đến key này”.
Cái bẫy: xoá delegation statement
Đây là scenario mình đã thấy xảy ra trong production:
- Team security muốn “harden” key policy — chỉ cho phép specific roles.
- Họ replace toàn bộ key policy, bỏ statement
Enable IAM User Permissions. - Key policy mới chỉ list 2–3 roles cụ thể.
- Mọi thứ hoạt động… cho đến khi cần thêm role mới.
- Không ai có thể
PutKeyPolicyvì key policy không grantkms:PutKeyPolicycho ai cả. - Kết quả: phải liên hệ AWS Support để recover.
Best practice: luôn giữ delegation statement + tách key admin và key user
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "EnableIAMPolicies",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::111122223333:root"
},
"Action": "kms:*",
"Resource": "*"
},
{
"Sid": "KeyAdministrators",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::111122223333:role/KMSAdminRole"
},
"Action": [
"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"
],
"Resource": "*"
},
{
"Sid": "KeyUsers",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::111122223333:role/AppRole"
},
"Action": [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:DescribeKey"
],
"Resource": "*"
}
]
}
Lưu ý: Key Administrators không có quyền Encrypt/Decrypt — họ quản lý key nhưng không dùng key. Key Users không có quyền PutKeyPolicy/ScheduleKeyDeletion — họ dùng key nhưng không quản lý key. Đây là separation of duties cơ bản.
Gotcha với Organizations
Khi dùng AWS Organizations, OrganizationAccountAccessRole trong member accounts có AdministratorAccess. Nếu key policy có delegation statement (root), role này có thể dùng bất kỳ KMS key nào trong account. Mitigation:
- Dùng SCP để restrict KMS actions ở Organization level.
- Hoặc bỏ delegation statement và dùng explicit principals (nhưng cẩn thận — xem gotcha ở trên).
Cross-Account Access Patterns
Cross-account KMS access là một trong những thứ phức tạp nhất trong AWS. Mình sẽ đi qua 3 patterns phổ biến nhất.
Pattern 1: Chia sẻ Encrypted EBS Snapshot
Scenario: Account A (111122223333) có encrypted EBS snapshot, muốn share cho Account B (444455556666).
Bước 1 — Key policy trong Account A (key owner):
{
"Sid": "AllowExternalAccountUse",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::444455556666:root"
},
"Action": [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:DescribeKey",
"kms:CreateGrant"
],
"Resource": "*"
}
Bước 2 — IAM policy trong Account B (consumer):
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:DescribeKey",
"kms:CreateGrant"
],
"Resource": "arn:aws:kms:us-east-1:111122223333:key/key-id"
}
]
}
Bước 3 — Account B copy snapshot sang key của mình:
aws ec2 copy-snapshot \
--source-region us-east-1 \
--source-snapshot-id snap-0123456789abcdef0 \
--encrypted \
--kms-key-id arn:aws:kms:us-east-1:444455556666:key/account-b-key-id \
--description "Re-encrypted copy from Account A"
Bước 3 là bắt buộc — bạn không nên dùng key của account khác lâu dài. Copy và re-encrypt sang key của mình để có full control.
Tại sao cần kms:CreateGrant? Vì khi EC2 service cần decrypt snapshot data, nó tạo grant trên KMS key thay vì gọi Decrypt trực tiếp. Không có CreateGrant, copy snapshot sẽ fail.
Pattern 2: Cross-Account S3 với SSE-KMS
Scenario: S3 cross-region replication từ Account A sang Account B, cả hai bucket dùng SSE-KMS.
Replication role trong Account A cần:
kms:Decrypttrên source KMS key (Account A)kms:Encrypttrên destination KMS key (Account B)
Key policy của destination key (Account B) phải allow replication role từ Account A:
{
"Sid": "AllowS3ReplicationFromAccountA",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::111122223333:role/S3ReplicationRole"
},
"Action": [
"kms:Encrypt",
"kms:GenerateDataKey*"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"kms:ViaService": "s3.eu-west-1.amazonaws.com"
}
}
}
Replication configuration phải specify destination KMS key:
{
"Rules": [
{
"Status": "Enabled",
"Destination": {
"Bucket": "arn:aws:s3:::destination-bucket",
"EncryptionConfiguration": {
"ReplicaKmsKeyID": "arn:aws:kms:eu-west-1:444455556666:key/dest-key-id"
}
},
"SourceSelectionCriteria": {
"SseKmsEncryptedObjects": {
"Status": "Enabled"
}
}
}
]
}
Pattern 3: Cross-Account RDS Snapshot
Flow tương tự EBS snapshot:
- Account A share RDS snapshot với Account B.
- Account B copy snapshot, specify KMS key của Account B.
- Restore từ bản copy đã re-encrypt.
Lưu ý quan trọng: AWS managed keys (aws/rds, aws/ebs, aws/s3) KHÔNG THỂ share cross-account. Bạn BẮT BUỘC phải dùng customer managed keys cho mọi cross-account scenario. Đây là lý do mình luôn recommend dùng CMK thay vì AWS managed keys trong production.
Condition Keys quan trọng
KMS có một bộ condition keys rất mạnh mà ít người tận dụng hết. Đây là 3 condition keys mình dùng nhiều nhất trong production.
kms:ViaService — giới hạn key chỉ dùng qua service cụ thể
Condition này đảm bảo key chỉ được sử dụng khi request đến từ một AWS service cụ thể — không ai có thể gọi KMS API trực tiếp.
{
"Sid": "OnlyViaS3AndEC2",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::111122223333:role/AppRole"
},
"Action": [
"kms:Encrypt",
"kms:Decrypt",
"kms:GenerateDataKey*",
"kms:CreateGrant",
"kms:DescribeKey"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"kms:ViaService": [
"s3.us-east-1.amazonaws.com",
"ec2.us-east-1.amazonaws.com"
]
}
}
}
Format: SERVICE.REGION.amazonaws.com
Use case thực tế: Bạn có một KMS key dùng cho S3 encryption. Bạn muốn AppRole có thể đọc/ghi S3 objects (S3 sẽ gọi KMS thay mặt AppRole), nhưng không muốn AppRole gọi kms:Decrypt trực tiếp để decrypt data key rồi tự decrypt data offline. kms:ViaService giải quyết chính xác vấn đề này.
⚠️ Gotcha: Nếu bạn dùng kms:ViaService, user không thể test key bằng CLI (aws kms encrypt --key-id ...) — request sẽ bị deny vì không đến từ service nào.
kms:CallerAccount — giới hạn theo account
Thay vì list từng principal trong key policy (dễ vượt 32KB limit), dùng Principal: "*" kết hợp kms:CallerAccount:
{
"Sid": "AllowAllPrincipalsInAccount",
"Effect": "Allow",
"Principal": {
"AWS": "*"
},
"Action": [
"kms:Encrypt",
"kms:Decrypt",
"kms:GenerateDataKey*"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"kms:CallerAccount": "111122223333"
}
}
}
Khi nào dùng:
- Account có nhiều roles/users cần access key (hàng chục đến hàng trăm).
- Bạn muốn quản lý access qua IAM policies thay vì update key policy mỗi lần thêm role.
- Kết hợp với
kms:ViaServiceđể vừa giới hạn account vừa giới hạn service.
⚠️ NGUY HIỂM nếu dùng Principal: "*" mà KHÔNG có condition:
{
"Effect": "Allow",
"Principal": {"AWS": "*"},
"Action": "kms:*",
"Resource": "*"
}
Policy trên cho phép MỌI AWS account trên thế giới access key của bạn. Luôn luôn kèm condition khi dùng wildcard principal.
kms:EncryptionContext — authorization dựa trên context
Encryption context là cặp key-value mà bạn truyền khi encrypt/decrypt. KMS log nó trong CloudTrail (plaintext) và bạn có thể dùng làm condition trong policy.
{
"Sid": "OnlyDecryptProductionData",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::111122223333:role/ProdAppRole"
},
"Action": "kms:Decrypt",
"Resource": "*",
"Condition": {
"StringEquals": {
"kms:EncryptionContext:Environment": "Production",
"kms:EncryptionContext:AppName": "PaymentService"
}
}
}
Use case thực tế: Bạn có một KMS key dùng chung cho nhiều microservices. Mỗi service encrypt data với encryption context chứa tên service. Policy trên đảm bảo ProdAppRole chỉ decrypt được data mà PaymentService đã encrypt trong Production environment — không thể decrypt data của service khác dù dùng cùng key.
⚠️ Quan trọng: kms:EncryptionContext:context-key là single-valued condition key. KHÔNG BAO GIỜ dùng ForAllValues hoặc ForAnyValue với nó — sẽ tạo policy overly permissive.
Pattern nâng cao — giới hạn chỉ cho phép specific context keys:
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::111122223333:role/AppRole"
},
"Action": "kms:GenerateDataKey",
"Resource": "*",
"Condition": {
"StringEquals": {
"kms:EncryptionContext:Department": "Finance"
},
"ForAllValues:StringEquals": {
"kms:EncryptionContextKeys": ["Department"]
}
}
}
Policy trên yêu cầu: encryption context phải có key Department với value Finance, và không được có key nào khác. Lưu ý ForAllValues ở đây dùng với kms:EncryptionContextKeys (plural, multi-valued) — khác với kms:EncryptionContext:key (single-valued).
Grants — khi nào dùng và pattern với AWS services
Grants là gì?
Grant là cơ chế thứ ba để cấp quyền truy cập KMS key, bên cạnh key policy và IAM policy. Khác biệt chính:
- Tạo bằng API (
CreateGrant) — không phải JSON policy document. - Eventually consistent — grant mới tạo có thể chưa active ngay lập tức.
- Có thể retire/revoke — phù hợp cho temporary access.
- Không ảnh hưởng key policy size — giải quyết giới hạn 32KB.
Khi nào dùng Grants?
-
AWS service integrations — EBS, RDS, Redshift tự động tạo grants khi dùng CMK của bạn. Khi bạn launch EC2 instance với encrypted EBS volume, EC2 service tạo grant trên KMS key để decrypt data key.
-
Temporary access — Khi bạn không biết trước principal nào cần access (ví dụ: Lambda function được tạo dynamically).
-
Cross-account delegation — Grantee có thể ở account khác.
-
Tránh key policy bloat — Khi hàng trăm principals cần access dynamically.
Grant example
aws kms create-grant \
--key-id arn:aws:kms:us-east-1:111122223333:key/key-id \
--grantee-principal arn:aws:iam::444455556666:role/CrossAccountRole \
--operations Decrypt GenerateDataKey \
--constraints '{"EncryptionContextSubset":{"Department":"Finance"}}' \
--retiring-principal arn:aws:iam::111122223333:role/SecurityAdmin
Giải thích:
--grantee-principal: ai được grant quyền.--operations: chỉ Decrypt và GenerateDataKey — không phải tất cả actions.--constraints: chỉ hoạt động khi encryption context chứaDepartment=Finance.--retiring-principal: ai có quyền retire (xoá) grant này.
Pattern: Key policy cho phép AWS services tạo grants
{
"Sid": "AllowGrantsForAWSServices",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::111122223333:role/AppRole"
},
"Action": "kms:CreateGrant",
"Resource": "*",
"Condition": {
"Bool": {
"kms:GrantIsForAWSResource": true
}
}
}
kms:GrantIsForAWSResource: true đảm bảo chỉ AWS services (EBS, RDS, etc.) mới có thể tạo grant thông qua role này — user không thể tự tạo grant trực tiếp.
Gotcha: Eventually Consistent
Grants là eventually consistent. Nếu bạn tạo grant rồi ngay lập tức dùng key, có thể bị AccessDeniedException. Giải pháp:
import boto3
kms = boto3.client('kms')
# Tạo grant và lấy grant token
response = kms.create_grant(
KeyId='arn:aws:kms:us-east-1:111122223333:key/key-id',
GranteePrincipal='arn:aws:iam::111122223333:role/TempRole',
Operations=['Decrypt']
)
grant_token = response['GrantToken']
# Dùng grant token ngay lập tức — không cần chờ propagation
response = kms.decrypt(
CiphertextBlob=encrypted_data,
GrantTokens=[grant_token]
)
GrantToken cho phép sử dụng grant ngay lập tức mà không cần chờ eventual consistency.
Key Rotation Strategies
KMS hỗ trợ 3 phương pháp rotation, mỗi cái phù hợp với use case khác nhau.
Automatic Rotation
Đây là phương pháp đơn giản nhất và recommended cho hầu hết trường hợp.
Đặc điểm:
- Chỉ hỗ trợ symmetric encryption keys với origin
AWS_KMS. - Period cấu hình được: 90–2560 ngày (default 365 ngày).
- Key material mới được generate; key material cũ vẫn được giữ lại để decrypt data cũ.
- Key ID không đổi — hoàn toàn transparent với applications.
- AWS managed keys (
aws/s3,aws/ebs): luôn rotate 365 ngày, không configurable.
# Enable automatic rotation với period 180 ngày
aws kms enable-key-rotation \
--key-id 1234abcd-12ab-34cd-56ef-1234567890ab \
--rotation-period-in-days 180
# Kiểm tra status
aws kms get-key-rotation-status \
--key-id 1234abcd-12ab-34cd-56ef-1234567890ab
Multi-region keys: Rotation là shared property — enable trên primary key, tất cả replica keys rotate cùng lúc với cùng key material.
On-Demand Rotation (mới từ 2024)
Khi bạn cần rotate ngay lập tức — không chờ schedule.
Use cases:
- Nghi ngờ key material bị compromise.
- Compliance audit yêu cầu chứng minh rotation capability.
- Testing automation pipeline.
aws kms rotate-key-on-demand \
--key-id 1234abcd-12ab-34cd-56ef-1234567890ab
Lưu ý: On-demand rotation không thay đổi automatic rotation schedule. Nếu bạn có automatic rotation mỗi 365 ngày và trigger on-demand ở ngày 100, lần automatic rotation tiếp theo vẫn ở ngày 365.
Manual Rotation (Alias Rotation)
Dùng cho keys không hỗ trợ automatic rotation: asymmetric keys, HMAC keys, custom key store keys.
Pattern:
# 1. Tạo key mới
aws kms create-key --description "App encryption key v2"
# Output: key-id = NEW_KEY_ID
# 2. Update alias trỏ sang key mới
aws kms update-alias \
--alias-name alias/my-app-key \
--target-key-id NEW_KEY_ID
# 3. Key cũ VẪN GIỮ (enabled) để decrypt data cũ
# Application dùng alias → tự động dùng key mới cho encrypt
# Decrypt data cũ → KMS tự biết dùng key nào dựa trên ciphertext metadata
Tại sao dùng alias? Application reference alias (alias/my-app-key) thay vì key ID. Khi rotate, chỉ cần update alias — không cần thay đổi application code hay config.
SCP để enforce rotation policy
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyShortRotationPeriod",
"Effect": "Deny",
"Action": "kms:EnableKeyRotation",
"Resource": "*",
"Condition": {
"NumericGreaterThan": {
"kms:RotationPeriodInDays": "365"
}
}
}
]
}
SCP trên ngăn không cho ai set rotation period dài hơn 365 ngày — đảm bảo compliance requirement.
Production Patterns: Envelope Encryption và Multi-Region Keys
Envelope Encryption — pattern cốt lõi
Mọi AWS service integration với KMS đều dùng envelope encryption. Hiểu pattern này là hiểu cách KMS hoạt động trong thực tế.
┌──────────────────────────────────────────────────────────────┐
│ ENVELOPE ENCRYPTION │
├──────────────────────────────────────────────────────────────┤
│ │
│ Encryption: │
│ 1. App gọi GenerateDataKey(CMK_ID) │
│ 2. KMS trả về: │
│ • Plaintext data key (DEK) — dùng để encrypt data │
│ • Encrypted data key — lưu cùng ciphertext │
│ 3. App encrypt data bằng plaintext DEK (AES-256 locally) │
│ 4. App XOÁ plaintext DEK khỏi memory │
│ 5. App lưu: encrypted data + encrypted DEK │
│ │
│ Decryption: │
│ 1. App gửi encrypted DEK tới KMS Decrypt() │
│ 2. KMS trả về plaintext DEK │
│ 3. App decrypt data bằng plaintext DEK │
│ 4. App XOÁ plaintext DEK khỏi memory │
│ │
└──────────────────────────────────────────────────────────────┘
Tại sao không encrypt trực tiếp bằng KMS?
- KMS giới hạn plaintext 4 KB cho direct encryption.
- Mỗi lần encrypt/decrypt phải gọi KMS API → latency + cost + throttling (requests per second quota).
- Envelope encryption: 1 API call lấy data key → encrypt unlimited data locally → nhanh, rẻ, không bị throttle.
Key policy cho S3 SSE-KMS (production-ready)
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "EnableIAMPolicies",
"Effect": "Allow",
"Principal": {"AWS": "arn:aws:iam::111122223333:root"},
"Action": "kms:*",
"Resource": "*"
},
{
"Sid": "AllowS3ServiceUse",
"Effect": "Allow",
"Principal": {"AWS": "arn:aws:iam::111122223333:role/S3AccessRole"},
"Action": [
"kms:Decrypt",
"kms:GenerateDataKey"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"kms:ViaService": "s3.us-east-1.amazonaws.com",
"kms:EncryptionContext:aws:s3:arn": "arn:aws:s3:::my-secure-bucket/*"
}
}
}
]
}
Condition kms:EncryptionContext:aws:s3:arn giới hạn key chỉ dùng cho objects trong bucket cụ thể — dù role có quyền access nhiều buckets, key này chỉ hoạt động với my-secure-bucket.
Key policy cho EBS encryption
{
"Sid": "AllowEBSEncryption",
"Effect": "Allow",
"Principal": {"AWS": "*"},
"Action": [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:CreateGrant",
"kms:DescribeKey"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"kms:CallerAccount": "111122223333",
"kms:ViaService": "ec2.us-east-1.amazonaws.com"
}
}
}
Pattern này dùng Principal: "*" + kms:CallerAccount + kms:ViaService — cho phép mọi principal trong account dùng key, nhưng CHỈ thông qua EC2 service. Không ai có thể gọi KMS trực tiếp.
Multi-Region Keys
Multi-region keys cho phép replicate key material sang nhiều regions — cùng key ID (prefix mrk-), cùng key material, nhưng mỗi replica có key policy và grants riêng.
Primary Region (us-east-1) Replica Region (eu-west-1)
┌─────────────────────┐ ┌─────────────────────┐
│ Primary MRK │ │ Replica MRK │
│ mrk-1234abcd... │───────────▶│ mrk-1234abcd... │
│ │ same key │ │
│ Encrypts data │ material │ Decrypts same │
│ │ │ ciphertext │
└─────────────────────┘ └─────────────────────┘
Use cases:
- DR/failover: decrypt data ở backup region mà không cần cross-region KMS calls.
- Global applications: DynamoDB Global Tables, S3 cross-region replication.
- Client-side encryption: encrypt ở region A, decrypt ở region B.
Tạo multi-region key:
# Tạo primary key
aws kms create-key \
--multi-region \
--description "Global encryption key for payment data"
# Replicate sang region khác
aws kms replicate-key \
--key-id mrk-1234abcd12ab34cd56ef1234567890ab \
--replica-region eu-west-1 \
--policy file://replica-key-policy.json
Condition keys cho multi-region:
{
"Effect": "Deny",
"Action": "kms:CreateKey",
"Resource": "*",
"Condition": {
"Bool": {
"kms:MultiRegion": true
}
}
}
SCP trên ngăn tạo multi-region keys — hữu ích nếu organization policy yêu cầu data không được replicate ra ngoài specific regions.
Lưu ý quan trọng:
- Mỗi replica có key policy riêng — bạn phải configure policy cho từng replica.
- Rotation là shared property — enable trên primary, propagate tới tất cả replicas.
- Delete replica không ảnh hưởng primary hoặc replicas khác.
- Promote replica thành primary: dùng khi primary region bị outage.
Troubleshooting: các lỗi thường gặp và cách fix
Lỗi 1: AccessDeniedException khi decrypt — dù IAM policy đã Allow
Triệu chứng:
An error occurred (AccessDeniedException) when calling the Decrypt operation:
User: arn:aws:iam::111122223333:role/AppRole is not authorized to perform: kms:Decrypt
on resource: arn:aws:kms:us-east-1:111122223333:key/key-id
Nguyên nhân phổ biến nhất: Key policy không có delegation statement (root account).
Cách fix:
- Kiểm tra key policy:
aws kms get-key-policy --key-id KEY_ID --policy-name default - Xác nhận có statement
"Principal": {"AWS": "arn:aws:iam::ACCOUNT:root"}với"Action": "kms:*". - Nếu không có → thêm vào bằng
aws kms put-key-policy(cần có quyềnkms:PutKeyPolicytừ key policy hiện tại).
Lỗi 2: Cannot decrypt — key policy có ViaService condition
Triệu chứng: Decrypt hoạt động khi gọi qua S3 GetObject, nhưng fail khi gọi aws kms decrypt trực tiếp.
Nguyên nhân: Key policy có kms:ViaService condition — chỉ cho phép access qua service cụ thể.
Cách fix: Đây là by design, không phải bug. Nếu bạn cần decrypt trực tiếp (ví dụ cho testing), tạo một statement riêng không có kms:ViaService cho admin role:
{
"Sid": "AdminDirectAccess",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::111122223333:role/AdminRole"
},
"Action": ["kms:Decrypt", "kms:Encrypt"],
"Resource": "*"
}
Lỗi 3: Cross-account access denied — đã config cả hai phía
Checklist debug:
- Key policy có list external account/principal không?
- IAM policy trong account B có specify đúng key ARN không? (Không dùng
*cho cross-account KMS). - Có SCP nào deny KMS actions không?
- Key có đang ở trạng thái
Enabledkhông? (aws kms describe-key) - Region có đúng không? KMS key là regional resource.
Lỗi 4: InvalidGrantTokenException
Triệu chứng: Dùng grant token nhưng bị reject.
Nguyên nhân: Grant token có TTL ngắn (thường 5 phút). Nếu bạn cache grant token quá lâu, nó expire.
Cách fix: Tạo grant mới hoặc chờ grant propagate (thường < 5 phút) rồi dùng không cần token.
Lỗi 5: KMSInvalidStateException — key disabled hoặc pending deletion
Triệu chứng:
An error occurred (KMSInvalidStateException) when calling the Encrypt operation:
arn:aws:kms:us-east-1:111122223333:key/key-id is pending deletion.
Cách fix:
# Nếu key pending deletion — cancel deletion
aws kms cancel-key-deletion --key-id KEY_ID
# Sau đó re-enable key
aws kms enable-key --key-id KEY_ID
Prevention: Dùng SCP để ngăn ScheduleKeyDeletion trừ break-glass role:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyKeyDeletion",
"Effect": "Deny",
"Action": "kms:ScheduleKeyDeletion",
"Resource": "*",
"Condition": {
"StringNotLike": {
"aws:PrincipalArn": "arn:aws:iam::*:role/BreakGlassRole"
}
}
}
]
}
Lỗi 6: Encrypted snapshot share fails — dùng AWS managed key
Triệu chứng: ModifySnapshotAttribute thành công nhưng Account B không thể copy snapshot.
Nguyên nhân: Snapshot encrypted bằng aws/ebs (AWS managed key) — key này không thể share cross-account.
Cách fix:
- Trong Account A, copy snapshot sang CMK (customer managed key):
aws ec2 copy-snapshot \
--source-region us-east-1 \
--source-snapshot-id snap-original \
--encrypted \
--kms-key-id arn:aws:kms:us-east-1:111122223333:key/cmk-id
- Share bản copy (encrypted bằng CMK) cho Account B.
- Update key policy của CMK để allow Account B.
Lỗi 7: S3 replication fails với KMS encrypted objects
Triệu chứng: Replication status FAILED cho KMS-encrypted objects, nhưng unencrypted objects replicate bình thường.
Checklist:
- Replication configuration có
SseKmsEncryptedObjects: Enabledkhông? - Replication configuration có
ReplicaKmsKeyIDkhông? - Replication role có
kms:Decrypttrên source key không? - Replication role có
kms:Encrypttrên destination key không? - Destination key policy có allow replication role không?
Production KMS Setup Checklist
Trước khi go-live, đảm bảo bạn đã check tất cả items sau:
Key Policy & Access Control
- Key policy có delegation statement (
arn:aws:iam::ACCOUNT:rootvớikms:*) - Key administrators và key users được tách riêng (separation of duties)
- Không có
Principal: "*"mà không kèm condition (kms:CallerAccounthoặckms:ViaService) - Cross-account access (nếu cần) đã config cả hai phía: key policy + IAM policy
-
kms:ViaServiceđược dùng để giới hạn key usage cho specific services -
kms:GrantIsForAWSResource: truechoCreateGrantpermissions
Key Rotation & Lifecycle
- Automatic rotation đã enable với period phù hợp compliance (90–365 ngày)
- Dùng customer managed keys (không dùng AWS managed keys) cho cross-account scenarios
- Key aliases được dùng trong application code (không hardcode key ID)
- SCP ngăn
ScheduleKeyDeletiontrừ break-glass role - Key deletion waiting period set tối đa (30 ngày)
Encryption Patterns
- Envelope encryption cho data > 4KB
- Encryption context được dùng cho audit trail và fine-grained authorization
- S3 bucket policy enforce SSE-KMS (
s3:x-amz-server-side-encryption: aws:kms) - EBS default encryption enabled ở account level với CMK
- Multi-region keys cho DR scenarios (nếu cần)
Monitoring & Compliance
- CloudTrail logging enabled cho tất cả KMS API calls
- CloudWatch alarm cho
KMS:Decryptfailures (có thể indicate unauthorized access attempts) - AWS Config rule
cmk-backing-key-rotation-enabledactive - Tag strategy cho KMS keys (Environment, Team, Application, CostCenter)
- Regular audit:
aws kms list-grantsđể review grants không còn cần thiết
Organization-Level Controls
- SCP deny
kms:DisableKeyRotation(enforce rotation) - SCP deny
kms:ScheduleKeyDeletion(protect against accidental deletion) - SCP restrict
kms:CreateKeyvớikms:MultiRegionnếu cần data residency - SCP deny
kms:PutKeyPolicycho non-admin roles
KMS key policies là một trong những thứ “set up once, forget until it breaks” — và khi nó break thì thường là lúc bạn đang cần access data gấp nhất. Hy vọng bài này giúp bạn hiểu rõ cơ chế hoạt động để tránh những sai lầm phổ biến và build một encryption strategy solid cho production workloads.