KMS, Secrets Manager & SSM Parameter Store: Encryption và Secrets

Quản lý encryption keys và secrets trên AWS. KMS vs Secrets Manager vs Parameter Store - khi nào dùng cái nào?

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

TypeMô tảCost
AWS Managed KeysAWS tạo và quản lý cho mỗi serviceFree
Customer Managed Keys (CMK)Bạn tạo và quản lý$1/month + usage
AWS Owned KeysAWS dùng cho nhiều accountsFree (không thấy trong console)
Customer Provided KeysBạ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

TypeMô tảCost
StringPlain textFree
StringListComma-separatedFree
SecureStringEncrypted with KMSFree (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

FeatureSecrets ManagerParameter Store
PurposeSecrets onlyConfig + Secrets
Rotation✅ Built-in❌ Manual
Pricing$0.40/secret/monthFree (standard)
Cross-account✅ Easy⚠️ Possible
Size limit64 KB8 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.