Prerequisites: Đọc IAM Basics để hiểu về permissions.
Tổng quan về Encryption trên AWS
Data Security
├── Encryption at Rest (data stored)
│ ├── S3 (SSE-S3, SSE-KMS, SSE-C)
│ ├── EBS volumes
│ ├── RDS databases
│ └── DynamoDB tables
│
├── Encryption in Transit (data moving)
│ ├── HTTPS/TLS
│ ├── VPN
│ └── AWS PrivateLink
│
└── Secrets Management
├── Secrets Manager (credentials, API keys)
├── SSM Parameter Store (config, secrets)
└── KMS (encryption keys)
AWS KMS (Key Management Service)
KMS quản lý encryption keys để encrypt/decrypt data.
Types of Keys
| Type | Mô tả | Cost |
|---|---|---|
| AWS Managed Keys | AWS tạo và quản lý cho mỗi service | Free |
| Customer Managed Keys (CMK) | Bạn tạo và quản lý | $1/month + usage |
| AWS Owned Keys | AWS dùng cho nhiều accounts | Free (không thấy trong console) |
| Customer Provided Keys | Bạn import key material | $1/month |
Key Types
┌──────────────────────────────────────────────────────────┐
│ KMS Key Types │
├────────────────────────────┬─────────────────────────────┤
│ Symmetric (AES-256) │ Asymmetric │
├────────────────────────────┼─────────────────────────────┤
│ • Single key encrypt/decrypt│ • Public/Private key pair │
│ • Never leaves KMS │ • RSA or ECC │
│ • Integrated with AWS │ • Download public key │
│ • Default for services │ • For external apps │
└────────────────────────────┴─────────────────────────────┘
Terraform: KMS Key
# Customer Managed Key
resource "aws_kms_key" "main" {
description = "Main encryption key for ${var.project_name}"
deletion_window_in_days = 30
enable_key_rotation = true # Automatic rotation yearly
multi_region = false
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "Enable IAM policies"
Effect = "Allow"
Principal = {
AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"
}
Action = "kms:*"
Resource = "*"
},
{
Sid = "Allow administration"
Effect = "Allow"
Principal = {
AWS = var.admin_role_arn
}
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 = "Allow use of the key"
Effect = "Allow"
Principal = {
AWS = var.app_role_arn
}
Action = [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:DescribeKey"
]
Resource = "*"
},
{
Sid = "Allow attachment of persistent resources"
Effect = "Allow"
Principal = {
AWS = var.app_role_arn
}
Action = [
"kms:CreateGrant",
"kms:ListGrants",
"kms:RevokeGrant"
]
Resource = "*"
Condition = {
Bool = {
"kms:GrantIsForAWSResource" = "true"
}
}
}
]
})
tags = {
Name = "${var.project_name}-key"
}
}
# Alias for easier reference
resource "aws_kms_alias" "main" {
name = "alias/${var.project_name}"
target_key_id = aws_kms_key.main.key_id
}
Sử dụng KMS với các services
# S3 Bucket với KMS encryption
resource "aws_s3_bucket_server_side_encryption_configuration" "data" {
bucket = aws_s3_bucket.data.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "aws:kms"
kms_master_key_id = aws_kms_key.main.arn
}
bucket_key_enabled = true # Reduce KMS costs
}
}
# EBS Volume với KMS
resource "aws_ebs_volume" "data" {
availability_zone = "ap-southeast-1a"
size = 100
encrypted = true
kms_key_id = aws_kms_key.main.arn
}
# RDS với KMS
resource "aws_db_instance" "main" {
# ...
storage_encrypted = true
kms_key_id = aws_kms_key.main.arn
}
# DynamoDB với KMS
resource "aws_dynamodb_table" "main" {
# ...
server_side_encryption {
enabled = true
kms_key_arn = aws_kms_key.main.arn
}
}
Envelope Encryption
KMS sử dụng envelope encryption cho hiệu suất:
┌─────────────────────────────────────────────────────────┐
│ Envelope Encryption │
├─────────────────────────────────────────────────────────┤
│ │
│ 1. Generate Data Key │
│ ┌─────┐ ┌────────────────┐ │
│ │ KMS │ ──GenerateDataKey──►│ Plaintext Key │ │
│ │ │ │ Encrypted Key │ │
│ └─────┘ └────────────────┘ │
│ │
│ 2. Encrypt Data (Client-side) │
│ ┌───────────────┐ ┌───────────────┐ │
│ │ Plaintext Key │────────►│ Encrypted │ │
│ │ + Your Data │ │ Data │ │
│ └───────────────┘ └───────────────┘ │
│ │
│ 3. Store │
│ ┌───────────────────────────────────────┐ │
│ │ Encrypted Key + Encrypted Data │ → S3 │
│ │ (Delete plaintext key from memory) │ │
│ └───────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────┘
Plaintext key never sent over network again!
For decryption: send encrypted key to KMS → get plaintext key → decrypt data
AWS Secrets Manager
Quản lý credentials, API keys, passwords với automatic rotation.
Khi nào dùng Secrets Manager?
✅ Database credentials ✅ API keys for 3rd party services ✅ SSH keys ✅ Bất kỳ secret nào cần rotation
Terraform: Secrets Manager
# Random password generation
resource "random_password" "db" {
length = 32
special = true
override_special = "!#$%&*()-_=+[]{}<>:?"
}
# Store secret
resource "aws_secretsmanager_secret" "db" {
name = "${var.project_name}/database"
description = "Database credentials"
kms_key_id = aws_kms_key.main.arn
recovery_window_in_days = 7
tags = {
Name = "${var.project_name}-db-secret"
}
}
resource "aws_secretsmanager_secret_version" "db" {
secret_id = aws_secretsmanager_secret.db.id
secret_string = jsonencode({
username = "admin"
password = random_password.db.result
host = aws_db_instance.main.address
port = aws_db_instance.main.port
dbname = var.db_name
})
}
# Automatic rotation
resource "aws_secretsmanager_secret_rotation" "db" {
secret_id = aws_secretsmanager_secret.db.id
rotation_lambda_arn = aws_lambda_function.rotate_secret.arn
rotation_rules {
automatically_after_days = 30
}
}
Đọc secret trong Lambda
import boto3
import json
def get_secret():
client = boto3.client('secretsmanager')
response = client.get_secret_value(
SecretId='myapp/database'
)
secret = json.loads(response['SecretString'])
return secret
# Usage
creds = get_secret()
connection = psycopg2.connect(
host=creds['host'],
port=creds['port'],
user=creds['username'],
password=creds['password'],
database=creds['dbname']
)
Secret Rotation Lambda
import boto3
import json
def lambda_handler(event, context):
secret_id = event['SecretId']
token = event['ClientRequestToken']
step = event['Step']
client = boto3.client('secretsmanager')
if step == "createSecret":
# Generate new password
new_password = client.get_random_password(
PasswordLength=32,
ExcludeCharacters='"@/\\'
)['RandomPassword']
# Get current secret
current = json.loads(
client.get_secret_value(SecretId=secret_id)['SecretString']
)
current['password'] = new_password
# Save as pending
client.put_secret_value(
SecretId=secret_id,
ClientRequestToken=token,
SecretString=json.dumps(current),
VersionStages=['AWSPENDING']
)
elif step == "setSecret":
# Update database with new password
pending = json.loads(
client.get_secret_value(
SecretId=secret_id,
VersionStage='AWSPENDING'
)['SecretString']
)
# ALTER USER ... PASSWORD ...
elif step == "testSecret":
# Test new password works
pending = json.loads(
client.get_secret_value(
SecretId=secret_id,
VersionStage='AWSPENDING'
)['SecretString']
)
# Try connecting with new password
elif step == "finishSecret":
# Complete rotation
client.update_secret_version_stage(
SecretId=secret_id,
VersionStage='AWSCURRENT',
MoveToVersionId=token,
RemoveFromVersionId=...
)
SSM Parameter Store
Lưu configuration data và secrets với hierarchy.
Khi nào dùng Parameter Store?
✅ Application configuration ✅ Environment variables ✅ Simple secrets (không cần rotation) ✅ Hierarchical data
Parameter Types
| Type | Mô tả | Cost |
|---|---|---|
| String | Plain text | Free |
| StringList | Comma-separated | Free |
| SecureString | Encrypted with KMS | Free (standard) / $0.05 per 10K (advanced) |
Terraform: Parameter Store
# Plain configuration
resource "aws_ssm_parameter" "app_url" {
name = "/${var.project_name}/config/app_url"
type = "String"
value = "https://myapp.com"
}
# Secure parameter (encrypted)
resource "aws_ssm_parameter" "api_key" {
name = "/${var.project_name}/secrets/api_key"
type = "SecureString"
value = var.api_key
key_id = aws_kms_key.main.arn
tags = {
Environment = var.environment
}
}
# Hierarchical parameters
resource "aws_ssm_parameter" "db_host" {
name = "/${var.project_name}/${var.environment}/database/host"
type = "String"
value = aws_db_instance.main.address
}
resource "aws_ssm_parameter" "db_port" {
name = "/${var.project_name}/${var.environment}/database/port"
type = "String"
value = tostring(aws_db_instance.main.port)
}
Đọc parameters
import boto3
ssm = boto3.client('ssm')
# Get single parameter
response = ssm.get_parameter(
Name='/myapp/prod/database/host',
WithDecryption=True # For SecureString
)
host = response['Parameter']['Value']
# Get parameters by path
response = ssm.get_parameters_by_path(
Path='/myapp/prod/database/',
WithDecryption=True
)
for param in response['Parameters']:
print(f"{param['Name']}: {param['Value']}")
ECS với Parameter Store
resource "aws_ecs_task_definition" "app" {
# ...
container_definitions = jsonencode([
{
name = "app"
# Environment from Parameter Store
secrets = [
{
name = "DB_HOST"
valueFrom = "arn:aws:ssm:${var.region}:${var.account_id}:parameter/${var.project_name}/database/host"
},
{
name = "DB_PASSWORD"
valueFrom = aws_secretsmanager_secret.db.arn # Or Secrets Manager
}
]
}
])
}
So sánh: Secrets Manager vs Parameter Store
| Feature | Secrets Manager | Parameter Store |
|---|---|---|
| Purpose | Secrets only | Config + Secrets |
| Rotation | ✅ Built-in | ❌ Manual |
| Pricing | $0.40/secret/month | Free (standard) |
| Cross-account | ✅ Easy | ⚠️ Possible |
| Size limit | 64 KB | 8 KB (standard) / 8 KB (advanced) |
| Versioning | ✅ Yes | ✅ Yes |
| Hierarchy | ❌ No | ✅ Yes |
| CloudFormation dynamic ref | ✅ Yes | ✅ Yes |
Quy tắc:
- Secrets Manager: Database passwords, API keys cần rotation
- Parameter Store: Application config, feature flags, simple secrets
Best Practices
1. Least privilege for KMS
# Grant chỉ những actions cần thiết
Action = [
"kms:Decrypt",
"kms:GenerateDataKey"
]
# Không cho "kms:*"
2. Enable automatic key rotation
resource "aws_kms_key" "main" {
enable_key_rotation = true # Yearly rotation
}
3. Use resource-based policies
# Cross-account access via key policy
{
Effect = "Allow"
Principal = {
AWS = "arn:aws:iam::OTHER_ACCOUNT:root"
}
Action = ["kms:Decrypt"]
Resource = "*"
}
4. Separate secrets by environment
/myapp/
├── prod/
│ ├── database/password
│ └── api/key
├── staging/
│ ├── database/password
│ └── api/key
└── dev/
├── database/password
└── api/key
5. Use IAM conditions
# Only allow access from specific VPC
Condition = {
StringEquals = {
"aws:SourceVpc" = var.vpc_id
}
}
Practice Questions (SAA style)
1. Ứng dụng cần encrypt data at rest với customer-managed keys và automatic yearly rotation. Key không được rời AWS. Giải pháp nào phù hợp?
A. S3 SSE-S3
B. S3 SSE-C
C. S3 SSE-KMS với CMK
D. Client-side encryption
Đáp án: C - SSE-KMS với CMK cho customer control, key rotation, và key never leaves KMS.
2. Database credentials cần thay đổi mỗi 30 ngày tự động. Giải pháp nào tốt nhất?
A. Parameter Store với Lambda scheduled
B. Secrets Manager với rotation enabled
C. KMS với key rotation
D. IAM với temporary credentials
Đáp án: B - Secrets Manager có built-in rotation với Lambda.
3. Application cần đọc 1000 configuration parameters khi startup. Chi phí thấp nhất?
A. Secrets Manager
B. Parameter Store Standard
C. DynamoDB
D. S3 JSON file
Đáp án: B - Parameter Store Standard is free, supports hierarchy và bulk retrieval.
Bài tiếp theo: AWS CLI & SDK - Thao tác với AWS programmatically.