Lab práctico · Semana 7: Seguridad de datos: cifrado, secretos, identidad de aplicaciones y detección

Auditoría y detección: CloudTrail, AWS Config y GuardDuty

⏱ 90-120 minDificultad: mediaTask statements: 1.31.2

Qué vas a construir

El «kit mínimo» de auditoría y detección de una cuenta:

  1. Un trail multirregión de CloudTrail con validación de integridad que entrega los logs en un bucket S3 protegido por una política de recurso.
  2. AWS Config registrando solo buckets de S3, con una regla gestionada que comprueba el versionado, y verás cómo un recurso pasa de NON_COMPLIANT a COMPLIANT.
  3. Amazon GuardDuty activado, con hallazgos de ejemplo para ver qué detecta y cómo se consultan.
flowchart LR
    API["Llamadas a la API (tú, servicios)"] --> CT["CloudTrail trail multirregión"]
    CT -->|"logs + digest cada hora"| B["Bucket S3 de auditoría"]
    CFG["AWS Config (recorder: AWS::S3::Bucket)"] -->|"historial y snapshots"| B
    CFG --> R["Regla: s3-bucket-versioning-enabled"]
    GD["GuardDuty"] -->|"lee CloudTrail, Flow Logs, DNS"| F["Hallazgos"]

Antes de empezar

  • Usuario de IAM Identity Center o IAM con MFA y permisos de administrador del laboratorio; nunca root.
  • Región: eu-south-2 (España), en CloudShell. CloudTrail, AWS Config y GuardDuty están disponibles en esa región (lo puedes comprobar en la lista de endpoints de cada servicio).
  • Si tu cuenta está en el plan gratuito (Free account plan), algunos servicios de seguridad pueden no estar disponibles hasta pasar al plan de pago: revisa docs/investigacion.md (sección 5).
  • Costes (30/09/2026): la primera copia de los eventos de gestión que CloudTrail entrega a S3 es gratis; Config cobra 0,003 USD por configuration item y 0,001 USD por evaluación de regla (aquí, unos pocos); GuardDuty tiene 30 días de prueba gratuita al activarlo por primera vez. El almacenamiento S3 son céntimos.
  • Si tu cuenta pertenece a una organización con Control Tower, ya existe un organization trail y Config gestionado: haz el lab en una cuenta de pruebas propia para no interferir.
export AWS_REGION=eu-south-2
export CUENTA=$(aws sts get-caller-identity --query Account --output text)
export BUCKET=lab14-auditoria-$CUENTA-$AWS_REGION
export TRAIL=lab14-trail
mkdir -p ~/lab14 && cd ~/lab14

Paso 1: bucket de auditoría con su política de recurso

Crea el bucket (privado por defecto, con Block Public Access activado):

aws s3api create-bucket --bucket "$BUCKET" \
  --create-bucket-configuration LocationConstraint="$AWS_REGION"
aws s3api put-public-access-block --bucket "$BUCKET" \
  --public-access-block-configuration BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true

CloudTrail y Config escriben en el bucket como principales de servicio, así que necesitan permiso en la bucket policy. Esta política combina lo que exige cada servicio, limitado a tu cuenta y a tu trail (condiciones aws:SourceArn y AWS:SourceAccount, para evitar el problema del confused deputy):

cat > politica-bucket.json <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "CloudTrailAclCheck",
      "Effect": "Allow",
      "Principal": { "Service": "cloudtrail.amazonaws.com" },
      "Action": "s3:GetBucketAcl",
      "Resource": "arn:aws:s3:::$BUCKET",
      "Condition": { "StringEquals": { "aws:SourceArn": "arn:aws:cloudtrail:$AWS_REGION:$CUENTA:trail/$TRAIL" } }
    },
    {
      "Sid": "CloudTrailWrite",
      "Effect": "Allow",
      "Principal": { "Service": "cloudtrail.amazonaws.com" },
      "Action": "s3:PutObject",
      "Resource": "arn:aws:s3:::$BUCKET/AWSLogs/$CUENTA/*",
      "Condition": {
        "StringEquals": {
          "s3:x-amz-acl": "bucket-owner-full-control",
          "aws:SourceArn": "arn:aws:cloudtrail:$AWS_REGION:$CUENTA:trail/$TRAIL"
        }
      }
    },
    {
      "Sid": "ConfigBucketPermissionsCheck",
      "Effect": "Allow",
      "Principal": { "Service": "config.amazonaws.com" },
      "Action": "s3:GetBucketAcl",
      "Resource": "arn:aws:s3:::$BUCKET",
      "Condition": { "StringEquals": { "AWS:SourceAccount": "$CUENTA" } }
    },
    {
      "Sid": "ConfigBucketExistenceCheck",
      "Effect": "Allow",
      "Principal": { "Service": "config.amazonaws.com" },
      "Action": "s3:ListBucket",
      "Resource": "arn:aws:s3:::$BUCKET",
      "Condition": { "StringEquals": { "AWS:SourceAccount": "$CUENTA" } }
    },
    {
      "Sid": "ConfigBucketDelivery",
      "Effect": "Allow",
      "Principal": { "Service": "config.amazonaws.com" },
      "Action": "s3:PutObject",
      "Resource": "arn:aws:s3:::$BUCKET/AWSLogs/$CUENTA/Config/*",
      "Condition": {
        "StringEquals": {
          "s3:x-amz-acl": "bucket-owner-full-control",
          "AWS:SourceAccount": "$CUENTA"
        }
      }
    },
    {
      "Sid": "DenegarSinTLS",
      "Effect": "Deny",
      "Principal": "*",
      "Action": "s3:*",
      "Resource": ["arn:aws:s3:::$BUCKET", "arn:aws:s3:::$BUCKET/*"],
      "Condition": { "Bool": { "aws:SecureTransport": "false" } }
    }
  ]
}
EOF

aws s3api put-bucket-policy --bucket "$BUCKET" --policy file://politica-bucket.json

La última declaración obliga a usar HTTPS: cifrado en tránsito impuesto por política.

Paso 2: trail multirregión con validación de integridad

aws cloudtrail create-trail --name "$TRAIL" \
  --s3-bucket-name "$BUCKET" \
  --is-multi-region-trail \
  --enable-log-file-validation

aws cloudtrail start-logging --name "$TRAIL"
aws cloudtrail get-trail-status --name "$TRAIL" --query '{Registrando:IsLogging,UltimaEntrega:LatestDeliveryTime}'

Genera algo de actividad para que haya eventos (crear y borrar un bucket de prueba, por ejemplo):

aws s3api create-bucket --bucket "lab14-prueba-$CUENTA" \
  --create-bucket-configuration LocationConstraint="$AWS_REGION"

Los ficheros de log llegan al bucket en unos minutos, bajo AWSLogs/CUENTA/CloudTrail/REGION/AAAA/MM/DD/. Los ficheros digest de integridad llegan cada hora bajo AWSLogs/CUENTA/CloudTrail-Digest/.

aws s3 ls "s3://$BUCKET/AWSLogs/$CUENTA/" --recursive | head

Mientras tanto, consulta el event history (90 días, gratis, no depende del trail):

aws cloudtrail lookup-events \
  --lookup-attributes AttributeKey=EventName,AttributeValue=CreateBucket \
  --max-results 5 --query 'Events[].[EventTime,Username,EventName]' --output table

Paso 3: AWS Config registrando solo buckets de S3

Config usa un rol vinculado al servicio. Créalo (si ya existe, el comando da error y puedes seguir):

aws iam create-service-linked-role --aws-service-name config.amazonaws.com || true

Crea el configuration recorder limitado a AWS::S3::Bucket (así el coste es mínimo) y el delivery channel hacia tu bucket:

cat > grabacion.json <<'EOF'
{
  "allSupported": false,
  "includeGlobalResourceTypes": false,
  "resourceTypes": ["AWS::S3::Bucket"],
  "recordingStrategy": { "useOnly": "INCLUSION_BY_RESOURCE_TYPES" }
}
EOF

aws configservice put-configuration-recorder \
  --configuration-recorder name=default,roleARN=arn:aws:iam::$CUENTA:role/aws-service-role/config.amazonaws.com/AWSServiceRoleForConfig \
  --recording-group file://grabacion.json

aws configservice put-delivery-channel \
  --delivery-channel name=default,s3BucketName=$BUCKET

aws configservice start-configuration-recorder --configuration-recorder-name default
aws configservice describe-configuration-recorder-status

Añade la regla gestionada que comprueba si los buckets tienen versionado:

cat > regla.json <<'EOF'
{
  "ConfigRuleName": "lab14-s3-versionado",
  "Description": "Comprueba que los buckets de S3 tienen el versionado activado",
  "Scope": { "ComplianceResourceTypes": ["AWS::S3::Bucket"] },
  "Source": { "Owner": "AWS", "SourceIdentifier": "S3_BUCKET_VERSIONING_ENABLED" }
}
EOF

aws configservice put-config-rule --config-rule file://regla.json

Espera 2-5 minutos a que Config descubra los buckets y evalúe:

aws configservice start-config-rules-evaluation --config-rule-names lab14-s3-versionado
aws configservice get-compliance-details-by-config-rule \
  --config-rule-name lab14-s3-versionado \
  --query 'EvaluationResults[].[EvaluationResultIdentifier.EvaluationResultQualifier.ResourceId,ComplianceType]' \
  --output table

Los buckets lab14-prueba-… y el de auditoría aparecerán como NON_COMPLIANT.

Paso 4: corregir y volver a evaluar

Activa el versionado en el bucket de prueba (en un entorno real, esto lo haría una remediación automática con el documento de SSM Automation AWS-ConfigureS3BucketVersioning asociado a la regla):

aws s3api put-bucket-versioning --bucket "lab14-prueba-$CUENTA" \
  --versioning-configuration Status=Enabled

sleep 60
aws configservice start-config-rules-evaluation --config-rule-names lab14-s3-versionado
sleep 60
aws configservice get-compliance-details-by-config-rule \
  --config-rule-name lab14-s3-versionado \
  --query 'EvaluationResults[].[EvaluationResultIdentifier.EvaluationResultQualifier.ResourceId,ComplianceType]' \
  --output table

El bucket de prueba pasa a COMPLIANT. En la consola de Config abre el recurso y pulsa Resource timeline: verás el cambio de configuración y, enlazado, el evento de CloudTrail PutBucketVersioning con tu usuario. «Cómo estaba y cómo está» (Config) + «quién lo cambió» (CloudTrail).

Paso 5: GuardDuty y hallazgos de ejemplo

DETECTOR_ID=$(aws guardduty create-detector --enable \
  --finding-publishing-frequency FIFTEEN_MINUTES \
  --query DetectorId --output text)
echo "DETECTOR_ID=$DETECTOR_ID"

GuardDuty empieza a analizar solo, sin agentes: eventos de gestión de CloudTrail, VPC Flow Logs y logs DNS (no hace falta activarlos tú). Como en una cuenta de pruebas no habrá amenazas reales, genera hallazgos de ejemplo:

aws guardduty create-sample-findings --detector-id "$DETECTOR_ID" \
  --finding-types "CryptoCurrency:EC2/BitcoinTool.B!DNS" "UnauthorizedAccess:IAMUser/MaliciousIPCaller.Custom" "Recon:EC2/PortProbeUnprotectedPort"

sleep 20
aws guardduty list-findings --detector-id "$DETECTOR_ID" --query 'FindingIds' --output text > hallazgos.txt
aws guardduty get-findings --detector-id "$DETECTOR_ID" \
  --finding-ids $(head -c 2000 hallazgos.txt) \
  --query 'Findings[].[Type,Severity,Resource.ResourceType]' --output table

Revisa también la consola de GuardDuty: cada hallazgo indica el recurso afectado, la severidad y la acción recomendada. Observa el panel Protection plans: S3 Protection, EKS, Runtime Monitoring, Malware Protection, RDS y Lambda Protection.

Comprueba que funciona

  • aws cloudtrail get-trail-status --name lab14-trail devuelve IsLogging: true y una fecha de última entrega.
  • En el bucket hay objetos bajo AWSLogs/CUENTA/CloudTrail/ y AWSLogs/CUENTA/Config/.
  • La regla de Config muestra un bucket COMPLIANT y otro NON_COMPLIANT.
  • GuardDuty lista los hallazgos de ejemplo (marcados como sample).
  • Opcional (si ha pasado al menos una hora desde el paso 2): valida la integridad de los logs.
aws cloudtrail validate-logs \
  --trail-arn "arn:aws:cloudtrail:$AWS_REGION:$CUENTA:trail/$TRAIL" \
  --start-time "$(date -u -d '-2 hours' +%Y-%m-%dT%H:%M:%SZ)"

Limpieza

En orden de dependencias:

# 1) GuardDuty (borrar el detector detiene el servicio y la prueba gratuita en esta región)
aws guardduty delete-detector --detector-id "$DETECTOR_ID"

# 2) AWS Config: regla, grabación y canal de entrega
aws configservice delete-config-rule --config-rule-name lab14-s3-versionado
aws configservice stop-configuration-recorder --configuration-recorder-name default
aws configservice delete-delivery-channel --delivery-channel-name default
aws configservice delete-configuration-recorder --configuration-recorder-name default

# 3) CloudTrail
aws cloudtrail stop-logging --name "$TRAIL"
aws cloudtrail delete-trail --name "$TRAIL"

# 4) Buckets (el de prueba tiene versionado: hay que borrar también las versiones)
aws s3api delete-objects --bucket "lab14-prueba-$CUENTA" \
  --delete "$(aws s3api list-object-versions --bucket "lab14-prueba-$CUENTA" \
  --query '{Objects: Versions[].{Key:Key,VersionId:VersionId}}' --output json)" 2>/dev/null || true
aws s3api delete-bucket --bucket "lab14-prueba-$CUENTA"

aws s3 rm "s3://$BUCKET" --recursive
aws s3api delete-bucket --bucket "$BUCKET"

# 5) Ficheros locales
cd ~ && rm -rf ~/lab14

El rol vinculado a Config puede quedarse (no cuesta nada) o borrarse con aws iam delete-service-linked-role --role-name AWSServiceRoleForConfig. Revisa al día siguiente en Billing → Bills que no queda nada de Config ni de GuardDuty.

Preguntas para pensar como arquitecto

  1. Tu empresa tiene 40 cuentas en AWS Organizations y el auditor exige que toda la actividad de la API quede registrada de forma centralizada y que ninguna cuenta pueda desactivarlo. ¿Cómo lo diseñas?
Respuesta

Un organization trail multirregión creado desde la cuenta de administración (o un administrador delegado) que entrega en un bucket de una cuenta dedicada de log archive. Las cuentas miembro ven el trail pero no pueden modificarlo ni pararlo. El bucket con Object Lock, cifrado SSE-KMS, acceso muy restringido y validación de integridad activada. Una SCP puede además impedir cloudtrail:StopLogging y DeleteTrail. AWS Control Tower configura este patrón automáticamente.

  1. ¿Qué diferencia hay entre lo que te dio la regla de Config y lo que habría hecho una SCP que denegara crear buckets sin versionado?
Respuesta

Config es detectivo (y correctivo con remediación): el bucket se crea, Config lo marca como no conforme y, si configuras remediación, lo arregla después. Una SCP o una política IAM es preventiva: la acción no llega a ocurrir. En la práctica se combinan: guardarraíles preventivos donde se pueda expresar la condición y reglas de Config para todo lo demás y para demostrar cumplimiento continuo.

  1. GuardDuty genera un hallazgo de minería de criptomonedas en una instancia. ¿Cómo automatizarías la respuesta?
Respuesta

GuardDuty publica sus hallazgos en EventBridge. Una regla que filtre por tipo y severidad dispara una Lambda o un documento de SSM Automation que aísla la instancia (cambia su security group por uno sin reglas, toma una instantánea de EBS para análisis forense) y avisa por SNS. Para investigar el alcance, Amazon Detective; para verlo priorizado junto a otros hallazgos, Security Hub.

  1. ¿Por qué no basta con el event history de CloudTrail para una auditoría de dos años?
Respuesta

Porque el event history solo guarda 90 días de eventos de gestión y por región. Para retención larga hace falta un trail que entregue a S3 (con lifecycle hacia clases más baratas como Glacier y Object Lock para inmutabilidad). Tampoco incluye data events (lecturas de objetos S3, invocaciones de Lambda), que hay que activar explícitamente en el trail.

  1. Te piden descubrir si hay números de tarjeta o DNI en los buckets de la empresa. ¿GuardDuty, Inspector o Macie?
Respuesta

Amazon Macie: analiza objetos de S3 con identificadores gestionados (PII, datos financieros, credenciales) y personalizados (expresiones regulares, por ejemplo para el formato del DNI). GuardDuty detecta amenazas y comportamiento malicioso; Inspector, vulnerabilidades de software en EC2, ECR y Lambda.


Volver al módulo