Região: us-east-1
Custo estimado: ~$1–3/mês (KMS CMK) + tempo de EC2 se usado
Pré-requisito: AWS CLI configurado, Terraform >= 1.5
- Criar uma Customer-Managed CMK no KMS com key policy customizada
- Criar uma role cross-account com condição de MFA e PrincipalOrgID
- Armazenar e rotacionar uma credencial no Secrets Manager
- Usar Parameter Store com SecureString encriptado com CMK própria
# Variáveis
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
REGION="us-east-1"
# Criar CMK com descrição
KEY_ID=$(aws kms create-key \
--description "CMK para lab IAM e Segurança" \
--key-usage ENCRYPT_DECRYPT \
--origin AWS_KMS \
--region $REGION \
--query KeyMetadata.KeyId --output text)
echo "CMK criada: $KEY_ID"
# Criar alias amigável
aws kms create-alias \
--alias-name alias/lab-iam-security \
--target-key-id $KEY_ID \
--region $REGION
# Habilitar rotação anual automática
aws kms enable-key-rotation \
--key-id $KEY_ID \
--region $REGION
# Testar encriptação
echo "dado sensível" | base64 | aws kms encrypt \
--key-id alias/lab-iam-security \
--plaintext fileb:///dev/stdin \
--output text \
--query CiphertextBlob \
--region $REGION > /tmp/encrypted.b64
echo "Dado encriptado: $(cat /tmp/encrypted.b64)"
# Decriptar
aws kms decrypt \
--ciphertext-blob fileb://<(base64 -d /tmp/encrypted.b64) \
--output text \
--query Plaintext \
--region $REGION | base64 -d# main.tf
terraform {
required_version = ">= 1.5"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = "us-east-1"
}
data "aws_caller_identity" "current" {}
data "aws_region" "current" {}
# ──────────────────────────────────────────────────────
# 1. Customer-Managed KMS Key
# ──────────────────────────────────────────────────────
data "aws_iam_policy_document" "kms_key_policy" {
# Root account: administração total (recover always)
statement {
sid = "EnableRootAdmin"
effect = "Allow"
principals {
type = "AWS"
identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"]
}
actions = ["kms:*"]
resources = ["*"]
}
# Serviços AWS (Secrets Manager, SSM) podem usar a chave
statement {
sid = "AllowAWSServices"
effect = "Allow"
principals {
type = "Service"
identifiers = ["secretsmanager.amazonaws.com", "ssm.amazonaws.com"]
}
actions = [
"kms:GenerateDataKey*",
"kms:Decrypt",
"kms:Encrypt",
]
resources = ["*"]
}
# App role: apenas encrypt/decrypt, sem administração de chave
statement {
sid = "AllowAppRole"
effect = "Allow"
principals {
type = "AWS"
identifiers = [aws_iam_role.app_role.arn]
}
actions = [
"kms:Decrypt",
"kms:GenerateDataKey",
"kms:DescribeKey",
]
resources = ["*"]
}
}
resource "aws_kms_key" "lab_cmk" {
description = "CMK para lab IAM e Segurança SAA-C03"
deletion_window_in_days = 7
enable_key_rotation = true
policy = data.aws_iam_policy_document.kms_key_policy.json
}
resource "aws_kms_alias" "lab_cmk_alias" {
name = "alias/saa-lab-iam-security"
target_key_id = aws_kms_key.lab_cmk.key_id
}
# ──────────────────────────────────────────────────────
# 2. IAM Role para Aplicação (com permission boundary)
# ──────────────────────────────────────────────────────
resource "aws_iam_policy" "app_boundary" {
name = "AppPermissionBoundary"
description = "Limite máximo de permissões para roles de aplicação"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = ["s3:Get*", "s3:Put*", "s3:List*"]
Resource = "*"
},
{
Effect = "Allow"
Action = ["secretsmanager:GetSecretValue"]
Resource = "arn:aws:secretsmanager:us-east-1:${data.aws_caller_identity.current.account_id}:secret:/app/*"
},
{
Effect = "Allow"
Action = ["kms:Decrypt", "kms:GenerateDataKey"]
Resource = aws_kms_key.lab_cmk.arn
}
]
})
}
resource "aws_iam_role" "app_role" {
name = "saa-lab-app-role"
permissions_boundary = aws_iam_policy.app_boundary.arn
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = { Service = "ec2.amazonaws.com" }
Action = "sts:AssumeRole"
}]
})
}
resource "aws_iam_instance_profile" "app_profile" {
name = "saa-lab-app-profile"
role = aws_iam_role.app_role.name
}
# ──────────────────────────────────────────────────────
# 3. Secrets Manager — Secret encriptado com CMK
# ──────────────────────────────────────────────────────
resource "aws_secretsmanager_secret" "db_secret" {
name = "/app/prod/db-credentials"
description = "Credenciais do banco de dados de produção"
kms_key_id = aws_kms_key.lab_cmk.arn
recovery_window_in_days = 7
tags = {
Environment = "lab"
Module = "02-IAM-e-Seguranca"
}
}
resource "aws_secretsmanager_secret_version" "db_secret_v1" {
secret_id = aws_secretsmanager_secret.db_secret.id
secret_string = jsonencode({
username = "app_user"
password = "SenhaTemporaria123!"
host = "rds-endpoint.us-east-1.rds.amazonaws.com"
port = 5432
dbname = "appdb"
})
}
# ──────────────────────────────────────────────────────
# 4. Parameter Store — SecureString com CMK própria
# ──────────────────────────────────────────────────────
resource "aws_ssm_parameter" "app_config" {
name = "/app/prod/api-key"
type = "SecureString"
value = "minha-api-key-temporaria-para-lab"
key_id = aws_kms_key.lab_cmk.arn
tags = {
Environment = "lab"
Module = "02-IAM-e-Seguranca"
}
}
# ──────────────────────────────────────────────────────
# Outputs
# ──────────────────────────────────────────────────────
output "cmk_key_id" {
value = aws_kms_key.lab_cmk.key_id
description = "ID da CMK criada"
}
output "cmk_arn" {
value = aws_kms_key.lab_cmk.arn
description = "ARN da CMK"
}
output "app_role_arn" {
value = aws_iam_role.app_role.arn
description = "ARN da role da aplicação"
}
output "secret_arn" {
value = aws_secretsmanager_secret.db_secret.arn
description = "ARN do secret no Secrets Manager"
}# Verificar key rotation status
aws kms get-key-rotation-status \
--key-id alias/saa-lab-iam-security \
--region us-east-1
# Listar versões do secret
aws secretsmanager describe-secret \
--secret-id /app/prod/db-credentials \
--region us-east-1
# Obter o secret (simula o que a aplicação faz)
aws secretsmanager get-secret-value \
--secret-id /app/prod/db-credentials \
--region us-east-1 \
--query SecretString --output text | python3 -m json.tool
# Obter parâmetro do SSM
aws ssm get-parameter \
--name /app/prod/api-key \
--with-decryption \
--region us-east-1 \
--query Parameter.Value --output text
# Ver quem usou a CMK (últimas 24h via CloudTrail)
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=EventName,AttributeValue=Decrypt \
--start-time $(date -u -v-1d +%Y-%m-%dT%H:%M:%SZ) \
--region us-east-1 \
--query 'Events[].{User:Username,Time:EventTime,Source:EventSource}' \
--output table# Terraform destroy
terraform destroy -auto-approve
# Ou manualmente via CLI:
# Agenda deleção da CMK (mínimo 7 dias, não é imediato)
aws kms schedule-key-deletion \
--key-id $(aws kms describe-key --key-id alias/saa-lab-iam-security \
--query KeyMetadata.KeyId --output text) \
--pending-window-in-days 7 \
--region us-east-1
# Deletar secret (recovery_window_in_days=0 para deleção imediata)
aws secretsmanager delete-secret \
--secret-id /app/prod/db-credentials \
--force-delete-without-recovery \
--region us-east-1
# Deletar parâmetro SSM
aws ssm delete-parameter \
--name /app/prod/api-key \
--region us-east-1
echo "Cleanup concluído. CMK será deletada em 7 dias."- Por que a key policy do KMS inclui o root account como administrador?
- O que acontece se você deletar a key policy e o root account não estiver listado?
- Qual é a diferença entre
kms:Encryptekms:GenerateDataKey*? - Por que o Permission Boundary não é suficiente sozinho? O que mais é necessário?
- Em qual cenário
aws:PrincipalOrgIDé mais eficiente do que listar account IDs?
Credito autoral: Thiago Cardoso - LinkedIn