Región: us-east-1
Duración estimada: 35–55 minutos
Costo-riesgo: Bajo–Medio
Certificación: AWS Certified Security - Specialty (SCS-C03)
Dominio: Data Protection
Tarea 5.2: Design and implement controls for data at rest
Caso de uso
Camilo viene de una clase de AWS donde hablaron de Data Protection, pero se quedó con una duda que no se atrevió a preguntar al instructor:
“Ok, cifré… ¿cómo se ve eso en la realidad?”
En Digital Café Luna ya están guardando facturas y reportes en S3. La Sra. Blanca quiere que nadie lea esos documentos y que Camilo pueda demostrarle, con evidencia, que los archivos están cifrados y bajo control.
¿Qué vamos a construir?
Este laboratorio busca cerrar la brecha entre teoría y operación, vas a crear:
- Un bucket S3 para documentos de Café Luna.
- Una KMS key administrada por el cliente.
- Cifrado por defecto en S3 usando SSE-KMS.
- Una bucket policy que obligue HTTPS y uploads con la KMS key del lab.
- Evidencia por CLI usando
head-object,ServerSideEncryptionySSEKMSKeyId. - Cleanup completo.
Figura 1 — La Sra. Blanca quiere proteger facturas y reportes; Camilo configura S3 privado, KMS, políticas del bucket y valida la evidencia por CLI.
Nota de alcance
Este lab se enfoca en data at rest para objetos en S3 usando SSE-KMS. No vamos a cubrir IAM Rol, Secrets Manager, rotación de llaves ni acceso entre aplicaciones, eso lo dejamos para próximos labs.
Convención de nombres
| Recurso | Nombre |
|---|---|
| KMS Key Alias | alias/scs-lab2-data-key |
| S3 Bucket | scs-lab2-data-<tu-sufijo> |
| Prefix data | data/ |
| Prefix evidencia | evidence/ |
| Bucket policy file | scs-lab2-bucket-guardrails.json |
Nota práctica: el nombre del bucket debe ser único globalmente. Sugerencia puedes usar 4 números aleatorios como sufijo.
Requisitos previos
- Cuenta AWS con permisos para crear recursos en S3 y KMS.
- Región
us-east-1. - AWS CLI configurado. Valida con
aws sts get-caller-identity. - Recomendado: usar CloudShell para evitar problemas locales.
Ojo: KMS puede generar costos por key y por requests. En este lab lo mantenemos pequeño y cerramos con cleanup para evitar sustos.
Paso 1 — Preparar variables y crear la KMS key
Este lab lo haremos por CLI, es viable, limpio y nos ayuda a ganar habilidad en el CLI.
REGION="us-east-1"
LAB_ID="scs-lab2"
ACCOUNT_ID="$(aws sts get-caller-identity --query Account --output text)"
KMS_ALIAS="alias/${LAB_ID}-data-key"
KMS_DESC="SCS Lab2 - Data Protection baseline with S3 and KMS"
echo "REGION=$REGION"
echo "ACCOUNT_ID=$ACCOUNT_ID"
echo "KMS_ALIAS=$KMS_ALIAS"
Crear la KMS key:
KMS_KEY_ID="$(aws kms create-key \
--region "$REGION" \
--description "$KMS_DESC" \
--key-usage ENCRYPT_DECRYPT \
--origin AWS_KMS \
--tags TagKey=Name,TagValue="${LAB_ID}-data-key" TagKey=Lab,TagValue="$LAB_ID" \
--query 'KeyMetadata.KeyId' \
--output text)"
echo "KMS_KEY_ID=$KMS_KEY_ID"
Crear el alias:
aws kms create-alias \
--region "$REGION" \
--alias-name "$KMS_ALIAS" \
--target-key-id "$KMS_KEY_ID"
Obtener el ARN de la key:
KMS_KEY_ARN="$(aws kms describe-key \
--region "$REGION" \
--key-id "$KMS_KEY_ID" \
--query 'KeyMetadata.Arn' \
--output text)"
echo "KMS_KEY_ARN=$KMS_KEY_ARN"
Validar:
aws kms describe-key \
--region "$REGION" \
--key-id "$KMS_KEY_ID" \
--query 'KeyMetadata.{KeyId:KeyId,Arn:Arn,State:KeyState,Manager:KeyManager}' \
--output table
aws kms list-aliases \
--region "$REGION" \
--query "Aliases[?AliasName=='$KMS_ALIAS'].{AliasName:AliasName,TargetKeyId:TargetKeyId}" \
--output table
Checkpoint
Valida que:
- La key aparece en estado
Enabled. - El alias
alias/scs-lab2-data-keyapunta a tuKMS_KEY_ID. - La key quedó taggeada con
Lab=scs-lab2.
Paso 2 — Crear el bucket S3 y habilitar cifrado por defecto
Ahora crearemos un bucket privado y dejaremos cifrado por defecto con la KMS key del lab.
SUFFIX="$(printf "%04d" $((RANDOM%10000)))"
BUCKET="${LAB_ID}-data-camilo-${SUFFIX}"
echo "BUCKET=$BUCKET"
Crear bucket:
aws s3api create-bucket \
--region "$REGION" \
--bucket "$BUCKET"
Bloquear acceso público:
aws s3api put-public-access-block \
--region "$REGION" \
--bucket "$BUCKET" \
--public-access-block-configuration \
BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true
Agregar tags:
aws s3api put-bucket-tagging \
--region "$REGION" \
--bucket "$BUCKET" \
--tagging "TagSet=[{Key=Name,Value=${LAB_ID}-data-bucket},{Key=Lab,Value=$LAB_ID}]"
Configurar cifrado por defecto con SSE-KMS:
aws s3api put-bucket-encryption \
--region "$REGION" \
--bucket "$BUCKET" \
--server-side-encryption-configuration "{
\"Rules\": [
{
\"ApplyServerSideEncryptionByDefault\": {
\"SSEAlgorithm\": \"aws:kms\",
\"KMSMasterKeyID\": \"$KMS_KEY_ARN\"
},
\"BucketKeyEnabled\": true
}
]
}"
Validar:
aws s3api head-bucket \
--region "$REGION" \
--bucket "$BUCKET"
aws s3api get-public-access-block \
--region "$REGION" \
--bucket "$BUCKET"
aws s3api get-bucket-encryption \
--region "$REGION" \
--bucket "$BUCKET"
Checkpoint
Valida que:
- El bucket existe.
- Public Access Block está activo.
- Default encryption está en
aws:kms. - La KMS key corresponde a la key del lab.
Paso 3 — Subir un objeto cifrado con KMS y validarlo
Ahora vamos a subir una factura de prueba y comprobar con CLI cómo quedó guardada.
echo "Factura demo - $(date -u)" > factura-demo.txt
Subir el objeto usando SSE-KMS con la key del lab:
aws s3 cp factura-demo.txt "s3://$BUCKET/data/factura-demo.txt" \
--region "$REGION" \
--sse aws:kms \
--sse-kms-key-id "$KMS_KEY_ARN"
Validar el objeto:
aws s3api head-object \
--region "$REGION" \
--bucket "$BUCKET" \
--key "data/factura-demo.txt" \
--query "{SSE:ServerSideEncryption,KMSKey:SSEKMSKeyId}" \
--output table
Comparar alias contra la key real:
KEY_ID_FROM_ALIAS="$(aws kms list-aliases \
--region "$REGION" \
--query "Aliases[?AliasName=='$KMS_ALIAS'].TargetKeyId" \
--output text)"
echo "Alias apunta a KeyId: $KEY_ID_FROM_ALIAS"
echo "Key creada en el lab: $KMS_KEY_ID"
Checkpoint
Valida que:
-
head-objectmuestraSSE = aws:kms. -
SSEKMSKeyIdapunta a la KMS key del lab. - El alias apunta al mismo
KMS_KEY_ID.
Paso 4 — Aplicar guardrails: HTTPS y KMS key correcta
Ya tenemos cifrado por defecto, pero ahora vamos a poner guardrails para reducir errores humanos:
- bloquear tráfico sin HTTPS;
- bloquear uploads sin SSE-KMS explícito;
- bloquear uploads con una KMS key diferente a la del lab.
Ojo: este guardrail exige intención explícita en el upload. Aunque el bucket tenga default encryption, vamos a pedir que se envíe los headers de SSE-KMS y la key correcta.
Crear la bucket policy:
cat > scs-lab2-bucket-guardrails.json <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyInsecureTransport",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::$BUCKET",
"arn:aws:s3:::$BUCKET/*"
],
"Condition": {
"Bool": {
"aws:SecureTransport": "false"
}
}
},
{
"Sid": "DenyPutWithoutKMSEncryption",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::$BUCKET/*",
"Condition": {
"StringNotEquals": {
"s3:x-amz-server-side-encryption": "aws:kms"
}
}
},
{
"Sid": "DenyPutWithWrongKMSKey",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::$BUCKET/*",
"Condition": {
"StringNotEquals": {
"s3:x-amz-server-side-encryption-aws-kms-key-id": "$KMS_KEY_ARN"
}
}
}
]
}
EOF
Aplicar la policy:
aws s3api put-bucket-policy \
--region "$REGION" \
--bucket "$BUCKET" \
--policy file://scs-lab2-bucket-guardrails.json
Checkpoint
Valida que get-bucket-policy devuelve la policy sin error.
aws s3api get-bucket-policy \
--region "$REGION" \
--bucket "$BUCKET" \
--query Policy \
--output text
Paso 5 — Probar el guardrail
Aquí hacemos la prueba màs importante, demostrar que el bucket bloquea uploads inseguros y acepta uploads con aws:kms y la key correcta.
Intento incorrecto: debe fallar
echo "prueba sin SSE-KMS - $(date -u)" > sin-kms.txt
aws s3 cp sin-kms.txt "s3://$BUCKET/data/sin-kms.txt" \
--region "$REGION"
La salida esperada es un error AccessDenied.
Intento correcto: debe funcionar
aws s3 cp sin-kms.txt "s3://$BUCKET/data/con-kms.txt" \
--region "$REGION" \
--sse aws:kms \
--sse-kms-key-id "$KMS_KEY_ARN"
Evidencia final
aws s3api head-object \
--region "$REGION" \
--bucket "$BUCKET" \
--key "data/con-kms.txt" \
--query "{SSE:ServerSideEncryption,KMSKey:SSEKMSKeyId}" \
--output table
Checkpoint
Valida que:
- El upload sin SSE-KMS falla.
- El upload con
--sse aws:kmsfunciona. -
head-objectmuestraSSE = aws:kms. -
SSEKMSKeyIdapunta a la key del lab.
Clean up completo
Este lab deja S3 y KMS configurados. Para no dejar costos ni recursos colgados, eliminamos en este orden:
- objetos;
- bucket policy;
- bucket;
- alias;
- KMS key con borrado programado.
Vaciar bucket:
aws s3 rm "s3://$BUCKET" \
--region "$REGION" \
--recursive
Quitar bucket policy:
aws s3api delete-bucket-policy \
--region "$REGION" \
--bucket "$BUCKET"
Borrar bucket:
aws s3api delete-bucket \
--region "$REGION" \
--bucket "$BUCKET"
Borrar alias:
aws kms delete-alias \
--region "$REGION" \
--alias-name "$KMS_ALIAS"
Programar borrado de la KMS key:
aws kms schedule-key-deletion \
--region "$REGION" \
--key-id "$KMS_KEY_ID" \
--pending-window-in-days 7
Validar cleanup:
aws s3api head-bucket \
--region "$REGION" \
--bucket "$BUCKET" 2>&1 | tail -n 2
aws kms list-aliases \
--region "$REGION" \
--query "Aliases[?AliasName=='$KMS_ALIAS']" \
--output table
aws kms describe-key \
--region "$REGION" \
--key-id "$KMS_KEY_ID" \
--query "KeyMetadata.KeyState" \
--output text
Ojo:
404 Not Founden el bucket es esperado si ya fue eliminado. En KMS, la key debe quedar enPendingDeletion.
Troubleshooting
-
AccessDenied al subir a S3: revisa si falta
--sse aws:kmso si estás usando una KMS key distinta a la del lab. -
KMS alias no existe: valida con
aws kms list-aliasesy confirmaalias/scs-lab2-data-key. -
head-objectno muestraaws:kms: el objeto pudo subirse sin SSE-KMS. Vuelve a subirlo forzando--sse aws:kms. - No puedes borrar el bucket: elimina primero objetos, luego bucket policy y vuelve a intentar.
- No puedes borrar la key de inmediato: KMS requiere borrado programado, mínimo 7 días.
Well-Architected lens: ¿Qué aplicamos aquí?
- Security: cifrado en reposo con SSE-KMS y guardrails explícitos para reducir errores humanos.
- Operational Excellence: evidencia por CLI para auditar sin depender de “fe en la consola”.
- Cost Optimization: lab corto y cleanup completo para controlar S3 y KMS.
- Reliability: guardrails ayudan a evitar que datos entren mal configurados.
Resultado esperado
Al finalizar este laboratorio, Camilo puede demostrar con evidencia:
- que un objeto en S3 quedó cifrado con
aws:kms; - qué KMS key lo cifró usando
SSEKMSKeyId; - que el bucket bloquea prácticas inseguras, como uploads sin SSE-KMS o con una key distinta.
La idea no era solo “activar cifrado”, sino entender cómo se ve, cómo se valida y cómo se controla.
Referencias oficiales
- Protecting data using server-side encryption
- Using server-side encryption with AWS KMS keys
- AWS KMS keys
- Using aliases in AWS KMS
- Bucket policy examples
- Amazon S3 policy keys
¿Qué viene en el próximo lab?
Camilo ya vio cómo funciona KMS, cómo se cifra un objeto, cómo se valida por CLI y cómo se aplican guardrails (básicos) en S3.
En el próximo lab subimos el nivel: trabajaremos con IAM Roles y políticas de acceso para que solo los componentes correctos puedan leer o escribir datos.
Nada de credenciales sueltas por ahí.
Nos vemos en el próximo laboratorio de Digital Café Luna.













Top comments (0)