32.8 KMS Integration: S3, EBS, RDS, Secrets Manager, and More
Right, let’s talk about KMS integration. This is where the rubber meets the road. You’ve created your Customer Master Key (CMK), patted yourself on the back, and now you’re wondering, “What do I actually do with this thing?” You use it to encrypt other stuff, of course. And the beautiful part is, AWS services handle most of the heavy lifting for you. Your job is to understand the levers and, more importantly, who gets to pull them.
The core concept here is envelope encryption. Your CMK (the “master key”) doesn’t directly encrypt your multi-gigabyte database backup. Instead, it encrypts a unique, randomly generated “data key” that does encrypt your data. This is efficient and secure. When an AWS service needs to encrypt your data, it calls KMS, generates a data key, does the encryption, and then stores the encrypted data key right alongside your encrypted data. To decrypt, it calls KMS with that encrypted data key, and if permissions are right, KMS decrypts it and sends the plaintext data key back to the service to do the actual decryption. Your actual data key never lives on disk in plaintext. Neat, huh?
Integrating with S3
S3 is the classic example. You have two main ways to use KMS here: SSE-KMS (Server-Side Encryption with KMS) and bucket-level default encryption. Let’s be clear: setting x-amz-server-side-encryption: aws:kms on an upload is how you do it per object. But you’re not a maniac, so you’ll set a bucket default to avoid forgetting and leaving data naked.
Here’s the pitfall everyone hits: permissions. The S3 service needs permission to use your CMK! When you specify a CMK (instead of the default aws/s3 key), S3 must make a kms:GenerateDataKey call on your behalf. If your bucket policy allows a user to upload an object, but that user doesn’t have permission to use the KMS key via IAM, the upload fails. It’s the most common “I set up encryption why is this broken” issue.
# Uploading a file with a specific KMS Key
aws s3 cp my-secret-file.txt s3://my-encrypted-bucket/ \
--sse aws:kms \
--sse-kms-key-id alias/my-app-key
// A snippet of a bucket policy that grants permission to use the KMS key.
// Note: This is in ADDITION to the s3:PutObject permission.
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:user/example-user"
},
"Action": [
"kms:GenerateDataKey",
"kms:Decrypt"
],
"Resource": "arn:aws:kms:us-east-1:123456789012:key/abcd1234-...",
"Condition": {
"StringEquals": {
"kms:ViaService": "s3.us-east-1.amazonaws.com",
"kms:CallerAccount": "123456789012"
}
}
}
The kms:ViaService condition is a best practice. It locks down the key so it can only be used by the specified principal and only when the call is coming from the S3 service in your specific region. This prevents someone from using that key permission to encrypt their own data elsewhere.
Locking Down EBS Volumes
EBS volumes are straightforward. You encrypt them at creation. You can’t encrypt an existing unencrypted volume in-place—you have to snapshot it, encrypt the snapshot, and create a new volume from that. It’s a hassle, which is why you should just make encryption the default in your account.
The real “gotcha” is with EC2 instance permissions. The EC2 service needs to call kms:Decrypt on the CMK to decrypt the data key for your volume so it can be mounted. This means the EC2 instance’s IAM role (or the default role if you’re not using one) must have permission for kms:Decrypt on the volume’s key. If it doesn’t, your instance will fail to start. It’s a heart-stopping moment if you forget this.
RDS and Secrets Manager: The Dynamic Duo
RDS uses KMS to encrypt your storage, its automated backups, and read replicas. You choose the key at database creation time. Again, the RDS service needs permissions for the key. This is usually handled automatically if you let AWS create a service-linked role, which you should.
But here’s the pro move. Your database isn’t much good without a password, and that password is a secret. This is where Secrets Manager comes in. Secrets Manager must use a KMS key to encrypt the secret value. It’s the best way to handle database credentials.
# Store a database password in Secrets Manager, encrypted with a specific KMS key
aws secretsmanager create-secret \
--name myapp/prod/db-creds \
--secret-string '{"username":"dbadmin", "password":"SUPER_SECRET_PASSWORD"}' \
--kms-key-id alias/aws/secretsmanager # Or your own CMK
The beautiful part? You can grant your application permission to retrieve the secret and automatically decrypt it. Secrets Manager handles the KMS call under the hood. Your app code stays clean and only needs secretsmanager:GetSecretValue permission. The KMS permission is implicit and managed by the service, which is a fantastic abstraction.
The Cross-Account Tango
This is where KMS shines but also requires careful choreography. Want to share an encrypted S3 object or an RDS snapshot with another AWS account? You can’t just share the object; you have to share the key first.
- You add the other account’s root or a specific IAM role/user to the KMS key policy, granting them permission to use the key (e.g.,
kms:Decrypt). - Then you can share the resource (e.g., the S3 object or RDS snapshot) with that account.
The other account’s principals can then use the key (from their account) to decrypt the data key that encrypts your data. It feels like magic when it works, and a frustrating permissions puzzle when it doesn’t. Always test cross-account access thoroughly.
The bottom line? KMS integration is about trusting AWS services to act on your behalf. Your primary job is to get the IAM and key resource policies right. It’s a system of checks and balances: the resource policy (e.g., S3 bucket policy) says what can be done with the object, and the KMS key policy says who can use the key to unlock it. Both must agree before anything gets decrypted. Master that, and you’ve mastered cloud data security.