Lab 01 — KMS + S3 data protection¶
In one sentence
See how a KMS key policy, IAM policies, grants and an S3 bucket policy combine on one request — the single most-tested area in Data Protection.
The idea¶
you (lab-admin, admin)
├── assume ──> sec1-writer-role S3 put/get + named in key policy (only via S3)
└── assume ──> sec1-reader-role S3 get only, NOT in key policy → denied until a grant
│
▼
s3://sec1-data-111111111111 default SSE-KMS + bucket key, policy denies non-TLS / wrong key
│
▼
KMS key alias/sec1-data key policy: root (enables IAM) · admin · writer (kms:ViaService=S3)
The writer's KMS access lives only in the key policy, and only when the call comes through S3:
{
"Sid": "WriterRoleUseOnlyThroughS3",
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::111111111111:role/sec1-writer-role" },
"Action": ["kms:Encrypt", "kms:Decrypt", "kms:ReEncrypt*", "kms:GenerateDataKey*", "kms:DescribeKey"],
"Resource": "*",
"Condition": { "StringEquals": { "kms:ViaService": "s3.us-east-2.amazonaws.com" } }
}
What happened in the lab run¶
Writer role (in the key policy, via S3 only)¶
| Test | Result |
|---|---|
| Upload with no encryption header (bucket default) | ✅ encrypted with alias/sec1-data, bucket key on |
Upload with --sse AES256 (SSE-S3) |
❌ denied by bucket policy (DenyNonKmsEncryption) |
Upload with the AWS managed key aws/s3 |
❌ denied by bucket policy (DenyWrongKmsKey) |
kms encrypt called directly, not through S3 |
❌ denied — kms:ViaService doesn't match |
Reader role (S3 read only, not in the key policy)¶
| Test | Result |
|---|---|
GetObject on the SSE-KMS object |
❌ AccessDenied — S3 calls kms:Decrypt as the reader |
Same call after create-grant (Decrypt) |
✅ works (after a few seconds — grants are eventually consistent) |
revoke-grant |
access removed again |
Encryption context and CloudTrail¶
| Test | Result |
|---|---|
Encrypt with context project=sec1,env=lab |
✅ |
Decrypt without the context |
❌ InvalidCiphertextException |
Decrypt with the exact same context (no --key-id needed) |
✅ plaintext back |
GenerateDataKey |
✅ plaintext + encrypted data key (envelope encryption) |
Reader's failed Decrypt in CloudTrail |
✅ found, invokedBy: fas.s3.amazonaws.com — a forward access session using the reader's identity |
Key lessons¶
- Same account: a key policy that names a principal is enough on its own. The writer role had no
kms:*in IAM at all. TheEnableIAMPolicies(account root) statement doesn't mean "root user only" — it delegates to IAM, so IAM policies in the account start to count for this key. - S3 permissions are not KMS permissions. For SSE-KMS objects, S3 calls KMS with the caller's
identity (FAS).
s3:GetObjectwithoutkms:Decrypt=AccessDeniedon SSE-KMS objects only. kms:ViaServicepins key use to a service — the same role can use the key through S3 but not directly.- Grants give temporary, programmatic access without editing the key policy; AWS services (EBS, RDS…) use them behind the scenes.
- Encryption context is additional authenticated data: decrypt needs the exact same context, and it's logged in CloudTrail in plaintext.
Exam notes¶
- "User has
s3:GetObjectbut gets Access Denied on some objects only" → those objects are SSE-KMS and the user lackskms:Decrypt. SSE-S3 objects download fine. - Remove
EnableIAMPoliciesand even anAdministratorAccessuser can't use the key — only principals named in the key policy.CreateKeyrefuses a lockout policy unless you pass the bypass flag; an unmanageable key needs AWS Support. - Principal ARNs in key/bucket policies resolve to unique IDs at save time. Delete and recreate the role →
the policy shows an
AROA…ID and no longer matches. - Bucket policy condition gotcha: when a key is absent from the request,
StringNotEqualsand…IfExistsboth evaluate to true. Add"Null": {"<key>": "false"}to deny only when the header is present and wrong. - The bucket policy compares
x-amz-server-side-encryption-aws-kms-key-idto the key ARN string — a key ID or alias for the right key is a different string and is denied. --sse aws:kmswith no key ID → S3 uses the AWS managed keyaws/s3.- Bucket keys cut KMS requests by up to ~99% (cost + throttling), but change the encryption context to the bucket ARN — conditions on the object-ARN context break.
- Direct
Encryptis limited to 4 KB; real data uses envelope encryption (GenerateDataKey). Symmetric ciphertext embeds the key reference, soDecryptneeds no key ID. - Grants: use a grant token to use a grant before it propagates.
retire-grant= grantee gives it up;revoke-grant= key admin takes it away. Key policy size limit is 32 KB. - Key deletion: 7–30 days waiting period (default 30), key unusable and every attempted use logged → alarm
on it and
cancel-key-deletion. Disabling is the reversible alternative. - Rotation: customer managed keys support automatic rotation (configurable period, 90–2560 days) and on-demand rotation; old key material is kept so old ciphertext still decrypts.
Review questions¶
Question 1
Cross-account: Account B's role needs to decrypt with Account A's key. What two policies must allow it?
Show answer
The key policy in Account A must allow Account B (its root, or the specific role), and an IAM policy in Account B must allow the KMS actions on Account A's key ARN. Cross-account access always needs both sides: the resource owner grants it to the account, then B's admin delegates it to the principal. For cross-account S3 + SSE-KMS it's three: bucket policy + key policy in A, IAM policy in B.
Question 2
You need SSE where AWS never holds your key material at all. Options?
Show answer
SSE-C — you send the key with every request (HTTPS only); S3 uses it in memory to encrypt/decrypt, stores only a salted HMAC to validate later requests, and discards the key. You must manage and keep the key yourself; lose it and the object is gone. The strictest option is client-side encryption, where AWS only ever sees ciphertext. SSE-KMS doesn't qualify — KMS holds the key material (even imported material is stored by KMS).
Question 3
A company wants keys in single-tenant FIPS 140-3 Level 3 HSMs it fully controls. Which service?
Show answer
AWS CloudHSM — dedicated, single-tenant HSMs in your VPC where you manage users and keys and AWS has no access to the key material. KMS HSMs are validated too, but they're multi-tenant and AWS-managed. If they still want the KMS API and integrations, use a KMS custom key store backed by CloudHSM; for keys that must stay outside AWS entirely, an external key store (XKS).
Question 4
Imported key material (BYOK): can you enable automatic rotation?
Show answer
No. Automatic rotation isn't supported for keys with imported material (nor for asymmetric, HMAC or custom-key-store keys). Rotate manually: create a new key, import new material, and re-point the alias to it. (AWS later added on-demand rotation for imported material, but automatic rotation still isn't available.)
Question 5
What breaks if you encrypt an EBS snapshot with the aws/ebs key and try to share it cross-account?
Show answer
You can't share it usefully: the other account needs permission to use the key, and the key policy of an AWS managed key can't be edited, so you can't grant it. Copy the snapshot re-encrypting with a customer managed key, allow the target account in that key's policy, share the new snapshot, and have the target account copy it into its own account (ideally re-encrypting with its own key).
Weak spots
SSE-C vs SSE-KMS (who holds the key, who can audit it) · why AWS managed keys block cross-account sharing (their key policy can't be edited, so no other account can be granted use of them).