La lección anterior terminó señalando el puente: la infraestructura inmutable y los contenedores son la mejor forma de aplicar una línea base, y sin embargo el hardening clásico no los cubre. Un contenedor no es una máquina virtual, no se parchea sino que se reconstruye, y la cuenta cloud que lo ejecuta tiene una superficie propia que ningún lynis mira. Esta lección cierra el plan técnico del curso donde hoy se produce la mayoría de las brechas: no en un exploit sofisticado, sino en una casilla mal marcada. Vamos a configurar de una vez por todas la cuenta A-05, el bucket de adjuntos A-02 y las copias A-03, y a construir imágenes que no regalen la partida.

Contenido

  1. Por qué la nube cambia el modelo de amenaza
  2. Responsabilidad compartida, en lo técnico
  3. La identidad es el nuevo perímetro
  4. El bucket A-02, bien configurado de una vez
  5. Red y datos: subredes privadas, cifrado y copias aisladas
  6. Registro y auditoría de la nube
  7. Postura automatizada: CSPM e infraestructura como código
  8. Contenedores: qué aísla y qué no
  9. Ejecución segura y firma de imágenes
  10. Orquestación y detección en tiempo de ejecución
  11. Qué debe hacer Nimbus primero, con su presupuesto

  1. Por qué la nube cambia el modelo de amenaza

En un centro de datos propio, atacar significaba encontrar un servicio vulnerable y explotarlo. En la nube, la causa dominante de brechas no es un exploit: es un error de configuración. Los grandes incidentes públicos de la última década en entornos cloud comparten un patrón que no requiere ninguna habilidad ofensiva: un bucket marcado como público, una clave de acceso publicada en un repositorio, un rol con permisos de administrador que solo necesitaba leer un fichero, una base de datos gestionada expuesta sin autenticación.

Tres cambios de fondo explican el desplazamiento:

  • Todo es una API. Crear una máquina, abrir un puerto o dar acceso a todos los datos son llamadas a una API, y una credencial con permisos amplios permite hacerlas todas. El plano de control importa más que el plano de datos: quien controla la cuenta no necesita entrar en ningún servidor.
  • La velocidad es la ventaja y el riesgo. Un terraform apply levanta una infraestructura completa en minutos, y también publica un bucket en segundos. La misma agilidad que hace competitiva a Nimbus multiplica la probabilidad del error.
  • Todo es público por defecto en cuanto a alcance de red. No hay una puerta física ni una red corporativa alrededor: lo que se despliega es alcanzable desde Internet salvo que se configure lo contrario.

La consecuencia práctica invierte la prioridad de trabajo: en la nube, revisar la configuración rinde más que buscar vulnerabilidades, y por eso el CSPM del apartado 7 es, para una PYME, más rentable que un escáner de vulnerabilidades adicional.


  1. Responsabilidad compartida, en lo técnico

En 04-04 vimos el modelo como asunto contractual. Aquí es una lista de tareas concretas, y la pregunta que la ordena es: si esto falla, ¿a quién llamas? Si la respuesta eres tú, es tuyo.

Servicio en Nimbus Responsabilidad del proveedor Responsabilidad de Nimbus
Máquinas virtuales Hipervisor, hardware, red física, disponibilidad de la zona Sistema operativo, parches, usuarios, servicios, cortafuegos local (todo 05-06)
Base de datos gestionada Motor, parches del motor, copias automáticas, réplicas Esquema, usuarios y roles, cifrado activado, red privada, retención de copias, RLS
Almacenamiento de objetos (A-02, A-03) Durabilidad, cifrado en tránsito, infraestructura Política de acceso, bloqueo público, cifrado en reposo, versionado, registro
Contenedores gestionados Nodos, orquestador, plano de control Imagen, usuario del proceso, secretos, capacidades, políticas de red
Identidad del proveedor Servicio de autenticación, disponibilidad Usuarios, roles, políticas, MFA, rotación, revisión de permisos
Todos Los datos, siempre. Sin excepción y en cualquier modelo de servicio

Dos conclusiones que conviene tener presentes. La primera: cuanto más gestionado es el servicio, menos tareas tienes, pero las que quedan son las más críticas —la configuración de acceso y los datos—. La segunda: el proveedor no te avisa cuando lo haces mal. Publicar un bucket no genera ninguna alerta desde su lado: la casilla existe, y usarla es una decisión legítima del cliente. Esa asimetría es exactamente el hueco donde vive el error de configuración.


  1. La identidad es el nuevo perímetro

Sin red que defender, el control de acceso a la API del proveedor es el perímetro real. Cinco reglas ordenadas por impacto:

  1. MFA obligatorio en la cuenta raíz, con la credencial custodiada y sin uso diario. La raíz solo se usa para lo que ninguna otra identidad puede hacer.
  2. Nada de claves de acceso de larga duración. Una clave estática vive en un fichero, viaja por chats, se copia a un portátil y aparece en un repositorio: es el riesgo R-06. En su lugar, roles asumibles con credenciales efímeras (minutos u horas) para las personas, e identidad de la propia instancia o del contenedor para los servicios, sin ningún secreto que gestionar.
  3. Federación con el proveedor de identidad. Nadie tiene usuario propio en la nube: se entra con la identidad corporativa, y la baja de un empleado revoca su acceso a todo de una vez. Es la corrección de un problema clásico: la cuenta cloud que sobrevive a quien la usaba.
  4. Mínimo privilegio real, que se consigue observando lo que cada rol usa de verdad y recortando lo demás.
  5. Revisión periódica de permisos excesivos, con el analizador de accesos del proveedor y con la recertificación semestral del control C-18.
// ANTES — la politica que "funciona seguro", y por eso es tan comun.
// Concede TODO sobre TODO: si esta credencial se filtra, se pierde la cuenta.
{"Version": "2012-10-17", "Statement": [
  {"Effect": "Allow", "Action": "s3:*", "Resource": "*"}
]}
// DESPUES — permiso minimo para el rol de la API de Nimbus.
{"Version": "2012-10-17", "Statement": [
  {
    "Sid": "LeerYEscribirAdjuntosDelTenant",
    "Effect": "Allow",
    "Action": ["s3:GetObject", "s3:PutObject"],   // ni Delete, ni List del bucket
    "Resource": "arn:aws:s3:::nimbus-adjuntos-prod/tenant/*",
    "Condition": {
      "Bool": {"aws:SecureTransport": "true"},    // solo por TLS
      "StringEquals": {"s3:x-amz-server-side-encryption": "aws:kms"}
    }
  },
  {
    "Sid": "DenegarSalidaDeLaVPC",
    "Effect": "Deny",                             // una DENEGACION explicita gana
    "Action": "s3:*",                             // siempre sobre cualquier Allow:
    "Resource": "*",                              // aunque otra politica conceda
    "Condition": {"StringNotEquals": {"aws:SourceVpce": "vpce-nimbus-s3"}}
  }
]}

Tres decisiones merecen justificarse. No se concede s3:DeleteObject: la API de Nimbus nunca necesita borrar adjuntos, y no tenerlo convierte un compromiso de la API en una fuga —grave— en lugar de en una destrucción —irreversible—. El Resource acota la ruta, no solo el bucket, así que un fallo de la aplicación no alcanza otros prefijos. Y la denegación explícita por punto de enlace de la VPC es la más potente: aunque otra política conceda acceso por error, si la petición no viene por la red privada de Nimbus, se deniega. Una credencial robada y usada desde fuera no sirve.


  1. El bucket A-02, bien configurado de una vez

Este es el activo que 02-06 vació durante siete días. Se cierra con siete controles que se aplican juntos.

B=nimbus-adjuntos-prod

# 1) BLOQUEO DE ACCESO PUBLICO a nivel de cuenta y de bucket. Es la casilla que
#    impide que una politica futura, escrita con prisa, lo haga publico.
aws s3api put-public-access-block --bucket $B --public-access-block-configuration \
  BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true

# 2) CIFRADO en reposo con clave gestionada por Nimbus (03-06: cifrado de sobre).
#    bucket-key-enabled reduce el coste de KMS sin perder el control de la clave.
aws s3api put-bucket-encryption --bucket $B --server-side-encryption-configuration \
  '{"Rules":[{"ApplyServerSideEncryptionByDefault":{"SSEAlgorithm":"aws:kms",
     "KMSMasterKeyID":"arn:aws:kms:eu-west-1:...:key/nimbus-adjuntos"},
     "BucketKeyEnabled":true}]}'

# 3) VERSIONADO: un borrado deja una version anterior recuperable.
aws s3api put-bucket-versioning --bucket $B --versioning-configuration Status=Enabled

# 4) REGISTRO DE ACCESOS: sin esto no existe la deteccion D-04 de 05-02.
aws s3api put-bucket-logging --bucket $B --bucket-logging-status \
  '{"LoggingEnabled":{"TargetBucket":"nimbus-logs-prod","TargetPrefix":"s3/adjuntos/"}}'

# 5) VERIFICAR que ya NO es publico. La configuracion no vale: vale la prueba.
curl -s -o /dev/null -w "%{http_code}\n" https://$B.s3.amazonaws.com/tenant/41/prueba.pdf
# Esperado: 403. Cualquier 200 significa que sigue expuesto.

Los dos controles restantes van sobre el bucket de copias A-03, y son los que 02-06 demostró imprescindibles:

# nimbus-backups-prod — en una CUENTA SEPARADA de produccion
bloqueo_de_objetos:
  modo: COMPLIANCE          # ni el administrador puede acortar la retencion:
  retencion_dias: 35        # es lo que convierte "inmutable" en algo real
politica:
  - efecto: Deny
    accion: [s3:DeleteObject, s3:DeleteObjectVersion, s3:PutBucketVersioning]
    principal: "*"          # NADIE borra, ni siquiera con credenciales validas
  - efecto: Allow
    accion: [s3:PutObject]
    principal: "arn:aws:iam::CUENTA-PRODUCCION:role/nimbus-backup"
    condicion: { StringEquals: { "s3:x-amz-server-side-encryption": "aws:kms" } }
replicacion: { destino: region-secundaria, cuenta: CUENTA-COPIAS }

El modo COMPLIANCE es la diferencia entre una copia y una esperanza. En modo governance, un administrador puede levantar la retención; en modo compliance, nadie puede, ni el propietario de la cuenta, hasta que expire. Es el cuarto dígito de la regla 3-2-1-1-0 de 04-06 y es, literalmente, lo que habría impedido el borrado del día 20 a las 02:10.

Y el séptimo control, ya conocido: las descargas se sirven con URL firmadas de 120 segundos (03-07), generadas tras comprobar la autorización en la aplicación (05-05). Nunca hay un enlace permanente ni un objeto legible sin credencial.


  1. Red y datos: subredes privadas, cifrado y copias aisladas

Retomando el diseño de 05-04 con la mirada puesta en los servicios gestionados:

  • La base de datos vive en una subred privada sin ruta a Internet, ni de entrada ni de salida. No es «un puerto cerrado»: es que no existe camino.
  • Puntos de enlace privados (VPC endpoints) para hablar con el almacenamiento de objetos y con el gestor de secretos. El tráfico no sale a Internet, y —lo más valioso— permite la condición aws:SourceVpce del apartado 3, que anula el valor de una credencial robada.
  • Cifrado en reposo y en tránsito por defecto en todos los servicios, con claves gestionadas por Nimbus y rotación con kid (03-06). El cifrado del proveedor protege frente al robo físico del disco; el cifrado con clave propia protege además frente a errores de acceso lógico.
  • Bloqueo del servicio de metadatos o exigencia de su versión con sesión (IMDSv2), que es la mitigación estructural del SSRF de 05-05: sin ella, un SSRF en la API entrega las credenciales del rol de la instancia.
  • Las copias viven en otra cuenta, con credenciales distintas. Es la lección más cara de 02-06: las copias de Nimbus estaban en la misma cuenta que producción, y quien comprometió la cuenta las borró junto con todo lo demás. Copias en otra cuenta significa que el atacante necesita comprometer dos cuentas independientes, con dos conjuntos de credenciales y dos proveedores de identidad. El coste añadido es de unos pocos euros al mes.

  1. Registro y auditoría de la nube

El registro de actividad del proveedor (CloudTrail o equivalente) responde a quién llamó a qué API, cuándo, desde dónde y con qué resultado. Es la fuente más importante de un entorno cloud y hay que configurarla bien:

  • Activo en todas las regiones, no solo en la que se usa. Una técnica habitual consiste en operar en una región que nadie mira.
  • Enviado a un bucket de otra cuenta, con las mismas propiedades del apartado 4: inmutable y solo escritura. Los logs se protegen igual que las copias.
  • Validación de integridad activada, para poder demostrar que no se manipularon.
  • Complementado con los registros de acceso al bucket, los del balanceador y los de flujo de la VPC (05-04).

Las alertas mínimas, que conectan directamente con el catálogo de 05-02:

Alerta Evento Severidad Por qué
Uso de la cuenta raíz Cualquier acción con la identidad raíz S1 No debería usarse nunca en el día a día
Cambio de política o de rol PutRolePolicy, AttachUserPolicy S1 (D-07) Es la escalada de privilegios en la nube
Cambio en la configuración del bucket PutBucketPolicy, PutPublicAccessBlock S1 (D-06) Es cómo un bucket se vuelve público
Desactivación de registro StopLogging, DeleteTrail S1 (D-08) Solo tiene un motivo: borrar el rastro
Borrado de copias o instantáneas DeleteBackup, DeleteDBSnapshot S1 (D-10) El día 20 de 02-06
Acceso desde región o país inusual Llamadas desde fuera de la lista esperada S2 (D-02) Credencial comprometida
MFA desactivado en una cuenta DeactivateMFADevice S2 Preparación de un acceso posterior

Las cinco primeras son S1 porque ninguna tiene explicación benigna frecuente. Son, además, baratísimas de montar: son consultas sobre un registro que ya existe.


  1. Postura automatizada: CSPM e infraestructura como código

CSPM (Cloud Security Posture Management) es evaluar de forma continua la configuración de la cuenta contra un conjunto de buenas prácticas. prowler y ScoutSuite son libres y suficientes.

prowler aws --compliance cis_2.0_aws --severity critical high \
        --output-formats html csv json --output-directory /var/log/prowler
Provider: aws   Account: 4711-nimbus-prod   Checks: 312   Duration: 6m
FAIL 14 · PASS 271 · MANUAL 27

CRITICAL  s3_bucket_public_access
  nimbus-web-assets  ->  Bucket accesible publicamente (ACL: AllUsers READ)
CRITICAL  iam_root_mfa_enabled
  La cuenta raiz NO tiene MFA habilitado
HIGH      iam_user_accesskey_unused_45_days
  usuario 'deploy-legacy': clave de acceso creada 2023-02-11, sin usar 402 dias
HIGH      rds_instance_backup_enabled
  nimbus-preprod-db: copias automaticas DESACTIVADAS
HIGH      cloudtrail_multi_region_enabled
  Trail 'principal' solo cubre eu-west-1
MEDIUM    s3_bucket_object_lock_enabled
  nimbus-backups-prod: sin bloqueo de objetos

Lectura comentada, con el criterio de priorización de 05-01. iam_root_mfa_enabled es el primero, pese a no ser el que más ruido hace: sin MFA en la raíz, todos los demás controles son opcionales para quien consiga esa credencial, y activarlo son cinco minutos. El bucket público va inmediatamente después, y la primera pregunta no es cómo cerrarlo sino qué contiene y desde cuándo está así: si tiene datos personales, esto ya no es un hallazgo de configuración, es una posible brecha (04-05, 06-03). La clave de acceso sin usar 402 días es el hallazgo más silencioso y uno de los más peligrosos: una credencial permanente, olvidada, que nadie echaría de menos si alguien la usara; se elimina, no se rota. Y las copias desactivadas en preproducción vuelven a señalar a A-22.

Desplazar el control a la izquierda. El CSPM detecta el error después de que llegue a producción. checkov y tfsec lo detectan en el pull request, sobre el código de infraestructura, antes de que exista:

# .github/workflows/iac.yml
- uses: bridgecrewio/checkov-action@master
  with:
    directory: infra/
    framework: terraform
    soft_fail: false          # BLOQUEA: un bucket publico nunca llega a produccion
    skip_check: CKV_AWS_18    # excepcion con justificacion y caducidad (04-02)
    output_format: sarif

La combinación correcta es checkov bloqueante en el CI y prowler semanal sobre la cuenta, porque no todo se despliega por código: siempre hay algo hecho a mano en la consola web, y eso solo lo ve el CSPM.


  1. Contenedores: qué aísla y qué no

Aísla razonablemente No aísla
Procesos, sistema de ficheros, red y nombres de host (por namespaces) El kernel: es el mismo que el del host. Una vulnerabilidad del kernel afecta a todo
Consumo de CPU y memoria (por cgroups) El contenedor privilegiado o con capacidades de más, que es equivalente a root en el host
Dependencias entre aplicaciones El socket de Docker montado dentro: es control total del host

La frase que hay que interiorizar: un contenedor es aislamiento de procesos, no una frontera de seguridad como la de una máquina virtual. Si necesitas separar cargas de confianza muy distinta, la frontera correcta es la máquina virtual o la cuenta separada.

# ANTES — el Dockerfile de la API en 01-04, con seis problemas graves
FROM python:3.11                      # imagen completa: ~1 GB y cientos de CVE
WORKDIR /app
COPY . .                              # copia TODO: .git, .env, claves, tests
RUN pip install -r requirements.txt   # sin hashes: se instala lo que haya hoy
ENV DATABASE_URL=postgresql://nimbus_api:Tr4mo...@db:5432/nimbus   # SECRETO
EXPOSE 8000
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0"]   # se ejecuta como ROOT
# DESPUES — multi-stage, minima, sin secretos y sin root
FROM python:3.11-slim@sha256:1c8f9a...  AS build
# Fijar por DIGEST y no por etiqueta: `3.11-slim` cambia de contenido sin avisar,
# asi que la etiqueta no garantiza que construyas dos veces lo mismo.
WORKDIR /app
COPY requirements.txt .
RUN pip install --require-hashes --prefix=/instalado -r requirements.txt
# --require-hashes: si una version fue republicada con otro contenido, FALLA
# la construccion en lugar de instalarla en silencio (04-04).

FROM gcr.io/distroless/python3-debian12@sha256:9d2e4b...
# Distroless: sin shell, sin gestor de paquetes, sin utilidades. Reduce las CVE
# en torno al 90 % y, sobre todo, deja al atacante sin herramientas si entra.
COPY --from=build /instalado /usr/local
COPY --chown=nonroot:nonroot app/ /app/app/
# Solo se copia el codigo de la aplicacion, gracias tambien a .dockerignore:
# .git, .env, tests y credenciales nunca entran en ninguna capa.
USER nonroot                          # el proceso NO es root dentro del contenedor
WORKDIR /app
EXPOSE 8000
ENTRYPOINT ["python", "-m", "uvicorn", "app.main:app", "--host", "0.0.0.0"]
# Sin ENV con secretos: la credencial se inyecta en el arranque desde el gestor
# (05-05). Un secreto en una capa permanece en la imagen aunque se borre despues.

El último comentario merece énfasis porque es el error más común y el menos evidente: borrar un fichero en una capa posterior no lo elimina de la imagen. Sigue estando en la capa anterior y se recupera con un comando. Por eso trivy --scanners secret (05-01) forma parte del pipeline.


  1. Ejecución segura y firma de imágenes

Construir bien la imagen es la mitad; la otra mitad es cómo se ejecuta.

# docker-compose.prod.yml / equivalente en el orquestador
services:
  api:
    image: registry.nimbus.example/api@sha256:6f1b...   # por DIGEST, no por :latest
    read_only: true               # sistema de ficheros de solo lectura: impide
    tmpfs: [/tmp]                 # escribir binarios o persistir; /tmp en memoria
    cap_drop: ["ALL"]             # se quitan TODAS las capacidades del kernel
    cap_add: []                   # y no se devuelve ninguna: la API no las necesita
    security_opt:
      - no-new-privileges:true    # ningun proceso hijo puede ganar privilegios
      - apparmor=docker-default   # perfil de control de acceso obligatorio (05-06)
    user: "10001:10001"           # usuario sin privilegios, explicito
    mem_limit: 512m               # limites: un contenedor no tumba al vecino
    pids_limit: 200               # ni agota la tabla de procesos del host

read_only más cap_drop: ALL más no-new-privileges es la combinación que más eleva el coste de un ataque, y no cuesta nada: la mayoría de las aplicaciones web funcionan tal cual, y las que no, solo necesitan declarar sus rutas de escritura como volúmenes.

Firma de artefactos con cosign (retomando 03-07), que responde a la pregunta «¿esta imagen es la que construimos nosotros?»:

# Firmar en el CI, sin claves que custodiar (identidad del propio pipeline)
cosign sign --yes registry.nimbus.example/api@sha256:6f1b...

# Verificar ANTES de desplegar; si falla, el despliegue se detiene
cosign verify registry.nimbus.example/api@sha256:6f1b... \
  --certificate-identity-regexp "https://github.com/nimbus/.*" \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com

Sin verificación en el despliegue, la firma es decorativa. La cadena completa queda así, y es la integridad de la cadena de suministro de 04-04 llevada a la práctica:

flowchart LR
    PR["Pull request\ncodigo + infra"] --> CK["checkov / tfsec\nBLOQUEA config insegura"]
    CK --> BU["Build multi-stage\ndistroless, sin root"]
    BU --> TR["trivy\nvuln + secretos\nBLOQUEA sobre umbral"]
    TR --> CS["cosign sign\nidentidad del pipeline"]
    CS --> RG["Registro\nimagen por digest"]
    RG --> VE["cosign verify\nen el despliegue"]
    VE -->|"firma valida"| PROD["Produccion\nread_only, cap_drop ALL"]
    VE -->|"sin firma"| STOP["Despliegue detenido"]
    PROD --> FA["Falco\ndeteccion en ejecucion"]

  1. Orquestación y detección en tiempo de ejecución

Nimbus todavía no usa Kubernetes, y conviene decir con claridad que Kubernetes es un ámbito de seguridad propio, no un detalle de despliegue. Si llega, estas son las seis piezas mínimas: espacios de nombres por entorno y por criticidad, con cuotas; RBAC con cuentas de servicio propias por aplicación y sin usar la cuenta por defecto; políticas de red, porque por defecto todos los pods hablan con todos —es el error más frecuente y el equivalente a la red plana de 05-04—; estándares de seguridad de pods en nivel restricted, que impone lo del apartado 9; gestión de secretos con un proveedor externo, porque los secretos nativos van codificados en base64 y no cifrados; y kube-bench, que evalúa el clúster contra el benchmark CIS igual que lynis evalúa un servidor.

Detección en tiempo de ejecución con Falco. El escaneo de imágenes mira lo que hay; Falco mira lo que ocurre, observando las llamadas al sistema:

- rule: Shell abierta dentro de un contenedor de produccion
  desc: >
    Un contenedor de la API no deberia ejecutar NUNCA una shell. Como la imagen
    es distroless, esto solo puede significar un compromiso o un despliegue mal
    construido: en ambos casos hay que enterarse.
  condition: >
    spawned_process and container
    and container.image.repository = "registry.nimbus.example/api"
    and proc.name in (bash, sh, dash, zsh)
  output: "Shell en contenedor (usuario=%user.name proceso=%proc.cmdline
           contenedor=%container.name imagen=%container.image.repository)"
  priority: CRITICAL
  tags: [container, mitre_execution]

Qué merece la pena detectar en tiempo de ejecución, sin generar ruido: shell dentro de un contenedor, escritura en rutas del sistema (/etc, /usr/bin) cuando el sistema de ficheros debería ser de solo lectura, conexión saliente a un destino inesperado, lectura de ficheros sensibles como /etc/shadow o las credenciales de la instancia, y montaje del socket de Docker. Todas van al mismo canal de 05-02 y comparten sus reglas de calidad de alerta.


  1. Qué debe hacer Nimbus primero, con su presupuesto

Prioridad Acción Coste Esfuerzo Riesgo que reduce
1 MFA en la raíz y federación con el proveedor de identidad 0 € 2 h Compromiso total de A-05
2 Bloqueo de acceso público en todos los buckets, y verificado 0 € 2 h R-04, la fuga de 02-06
3 Copias en cuenta separada con bloqueo de objetos en modo compliance ~15 €/mes 6 h R-02, el día 20
4 Eliminar claves de acceso de larga duración; roles efímeros 0 € 8 h R-06
5 Registro de actividad multirregión a cuenta separada + las 5 alertas S1 ~10 €/mes 8 h Los 20 días de ceguera
6 prowler semanal y revisión de los hallazgos 0 € 4 h + 1 h/mes Deriva de configuración
7 checkov bloqueante en el CI de infraestructura 0 € 4 h Que el error llegue a producción
8 Dockerfile distroless, sin root y sin secretos, con trivy 0 € 12 h Superficie de la imagen
9 Puntos de enlace privados y condición SourceVpce ~20 €/mes 6 h Valor de una credencial robada
10 Falco con cinco reglas y cosign en el despliegue 0 € 16 h Compromiso en ejecución

El total suma unos 540 €/año y unas 70 horas. Es menos del 3 % del presupuesto de 18.000 €/año y el 16 % de las 440 horas de Lucía, y cubre los tres riesgos peor puntuados del registro de 04-01. Es, con diferencia, la mejor relación coste/impacto de todo el módulo 5, y la razón es la misma con la que empezó la lección: en la nube, casi todo lo que falla es una casilla, y las casillas son gratis.


Errores Comunes y Consejos

  • Creer que el proveedor te protege de tus propias configuraciones. No te avisa cuando publicas un bucket: es una acción legítima del cliente.
  • Usar claves de acceso de larga duración. Acaban en un repositorio, en un chat o en un portátil. Roles efímeros e identidad de instancia.
  • Conceder Action: "*" «temporalmente». No hay nada más permanente que un permiso temporal que funciona.
  • Dejar las copias en la misma cuenta que producción. Es lo que convirtió el incidente de 02-06 en una catástrofe.
  • Confiar en que un contenedor aísla como una máquina virtual. Comparte kernel; con capacidades de más o el socket de Docker montado, es el host entero.
  • Etiquetas en lugar de digests. :latest no es reproducible: escaneas una imagen y despliegas otra.
  • Secretos en ENV del Dockerfile. Quedan en la capa aunque los borres después.
  • Consejo: empieza por las dos primeras filas de la tabla del §11. Cuatro horas, coste cero, y cubren el peor escenario posible.
  • Consejo: verifica, no configures. Un curl que devuelva 403 vale más que una captura de la consola web, y es la evidencia que pide 04-03.
  • Consejo: checkov en el CI antes que prowler en la cuenta. Es más barato impedir el error que descubrirlo, aunque necesitarás los dos.

Ejercicios

Ejercicio 1 — Interpretar un informe de CSPM

Sobre la salida de prowler del §7, y sabiendo que nimbus-web-assets sirve las imágenes públicas de la web de marketing:

  1. Ordena los seis hallazgos por prioridad real, justificando cada posición.
  2. ¿Cambia tu evaluación del bucket público al saber para qué se usa? ¿Qué comprobarías antes de decidir?
  3. Escribe la corrección concreta de los tres primeros e indica cómo verificarías cada una.

Ejercicio 2 — Revisar un despliegue de contenedor

Iván propone este servicio para producción. Identifica todos los problemas y reescríbelo.

services:
  api:
    image: nimbus/api:latest
    privileged: true
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - /:/host
    environment:
      DATABASE_URL: "postgresql://nimbus_api:Tr4mo...@db:5432/nimbus"
    ports: ["8000:8000"]

Ejercicio 3 — Diseñar el aislamiento de las copias

Marta pregunta, tras leer el post mortem de 02-06: «¿cómo garantizamos que esto no puede volver a pasar?». Diseña el esquema de copias de Nimbus de modo que un atacante con control total de la cuenta de producción no pueda destruirlas, e indica cómo lo demostrarías.


Soluciones

Ejercicio 1

(1) Orden por prioridad real:

Orden Hallazgo Justificación
1 Raíz sin MFA Es la llave maestra de A-05, del que dependen casi todos los activos (01-04). Una sola contraseña separa a un atacante del control total. Se arregla en 5 minutos
2 Bucket público Exposición actual, con impacto que depende del contenido (ver punto 2)
3 Clave de acceso sin usar 402 días El hallazgo más silencioso: credencial permanente, olvidada y sin dueño. Si alguien la usa, nadie la echaría de menos. No se rota: se elimina
4 CloudTrail solo en una región No es exposición, es ceguera parcial, y precisamente en las regiones que nadie mira, que es donde opera un atacante informado
5 nimbus-backups-prod sin bloqueo de objetos Es el fallo del día 20 de 02-06 sin corregir del todo. Sube al puesto 1 si se confirma que las copias siguen en la cuenta de producción
6 Copias desactivadas en nimbus-preprod-db Preproducción tolera pérdida... salvo que contenga datos reales, que es la duda abierta de A-22. Resolver esa clasificación es más urgente que activar la copia

(2) Sí cambia, pero menos de lo que parece. Que un bucket de imágenes de marketing sea legible públicamente puede ser exactamente lo que se pretende, y en ese caso el hallazgo es un falso positivo que se documenta como excepción (04-02). Antes de decidir hay que comprobar cuatro cosas: qué objetos contiene realmente —los buckets de «recursos web» acumulan con enorme frecuencia exportaciones, copias de seguridad puntuales y PDF de clientes que alguien dejó ahí—; si además de lectura pública tiene escritura pública, que sería crítico porque permitiría a cualquiera alterar el contenido servido bajo el dominio de Nimbus; desde cuándo está así y qué dicen los registros de acceso; y si hay un modo mejor de conseguir lo mismo, que lo hay: CDN delante con el bucket privado, lo que da control, caché y registro sin exposición directa. La regla general: un bucket público es aceptable solo si es un bucket dedicado, cuyo contenido está íntegramente destinado a ser público y así consta por escrito.

(3) Correcciones y verificación:

  • Raíz sin MFA: activar MFA con una llave física, guardar la credencial y el segundo factor de respaldo en custodia física con acceso de dos personas, y crear una alerta S1 ante cualquier uso de la raíz (§6). Verificación: prowler vuelve a ejecutar iam_root_mfa_enabled con resultado PASS, y una prueba de acceso pide el segundo factor.
  • Bucket público: si el contenido debe ser público, moverlo a un bucket dedicado detrás de CDN y dejar el original privado; si no, aplicar el bloqueo de acceso público del §4. En ambos casos, inventariar el contenido antes. Verificación: curl sobre un objeto conocido devolviendo 403 y put-public-access-block confirmado.
  • Clave sin usar: eliminarla, no desactivarla —una clave desactivada se reactiva—; comprobar antes en el registro de actividad que ningún proceso la usó en 402 días, y buscar si aparece en el historial del repositorio con gitleaks (05-01), porque una clave olvidada suele estarlo por haber sido incrustada en algún sitio. Verificación: la clave no aparece en el listado y no hay errores de autenticación nuevos en 48 horas.

Ejercicio 2

Problemas, de mayor a menor gravedad:

# Problema Consecuencia
1 /var/run/docker.sock montado Es control total del host. Quien entre en el contenedor puede crear otro contenedor privilegiado y salir. Equivale a dar root en la máquina
2 privileged: true Desactiva prácticamente todo el aislamiento: todas las capacidades y acceso a los dispositivos
3 /:/host El sistema de ficheros completo del anfitrión, montado y escribible desde el contenedor
4 Credencial en environment Secreto en claro, visible con docker inspect, en los logs del orquestador y en el historial de despliegue. Es R-06
5 image: nimbus/api:latest No reproducible: se escanea una imagen y se despliega otra. Y no se verifica firma
6 ports: ["8000:8000"] Publicado en todas las interfaces del host, saltándose el balanceador; es el patrón del panel de colas de 05-03
7 Ausencias: sin read_only, sin cap_drop, sin no-new-privileges, sin user, sin límites de recursos El contenedor puede escribir binarios, escalar privilegios y agotar el host
services:
  api:
    image: registry.nimbus.example/api@sha256:6f1b...   # digest + firma verificada
    read_only: true
    tmpfs: [/tmp]
    cap_drop: ["ALL"]
    security_opt: [no-new-privileges:true, apparmor=docker-default]
    user: "10001:10001"
    mem_limit: 512m
    pids_limit: 200
    ports: ["127.0.0.1:8000:8000"]     # solo local; el balanceador hace de frente
    secrets: [db_url]                  # inyectado en arranque desde el gestor
    # sin docker.sock, sin volumenes del host, sin privileged

Ejercicio 3

El requisito reformulado: que la destrucción de las copias exija credenciales que la cuenta de producción no posee y una acción que ninguna credencial puede realizar. Cuatro capas, cada una resolviendo un fallo distinto:

  1. Cuenta separada, con su propio proveedor de identidad y su propio MFA. Comprometer producción no da ningún acceso a la cuenta de copias. Es la capa que faltaba en 02-06, donde todo vivía junto.
  2. Flujo de escritura en un solo sentido y de mínimo privilegio. El rol nimbus-backup de producción tiene únicamente PutObject sobre el bucket de copias: no puede listar, no puede leer y no puede borrar. Que no pueda leer es importante y suele olvidarse: impide que un atacante en producción se lleve también el histórico completo.
  3. Bloqueo de objetos en modo COMPLIANCE con 35 días de retención. Aquí está el núcleo de la respuesta a Marta: ni siquiera un administrador de la cuenta de copias con MFA puede acortar la retención ni borrar un objeto antes de que expire. No es una cuestión de permisos, que siempre se pueden cambiar; es una propiedad del almacenamiento.
  4. Replicación a una segunda región y, para el conjunto crítico, una copia adicional en almacenamiento frío con credenciales distintas de todo lo anterior.

Se complementa con detección: la alerta D-10 de 05-02 ante cualquier intento de borrado o de cambio de retención, y la alerta de silencio —si el proceso de copia deja de escribir durante 24 horas, alguien debe enterarse—, porque la forma sigilosa de destruir copias no es borrarlas, sino impedir que se creen y esperar a que el histórico caduque.

Cómo se demuestra, que es lo que Marta debe exigir y no una explicación: (a) intento real de borrado desde la cuenta de copias con credenciales de administrador, que debe fallar con AccessDenied, con la salida guardada como evidencia; (b) prueba de restauración cronometrada de 04-06, ejecutada desde la cuenta de copias, que verifica hash, compara filas y mide el RTO real; (c) simulacro de compromiso: revocar todas las credenciales de producción y comprobar que las copias siguen siendo accesibles y restaurables; y (d) revisión trimestral de que el bloqueo de objetos sigue activo, incluida en el --check --diff de la infraestructura como código (05-06). La respuesta correcta a la pregunta de Marta no es «hemos configurado esto»: es «lo hemos intentado destruir y no hemos podido, y aquí está la salida de consola con fecha».


Conclusión

Has cerrado el plan técnico del curso donde hoy se rompen la mayoría de las empresas. Sabes por qué la nube cambia el modelo de amenaza —el error de configuración sustituye al exploit como causa dominante— y las tres razones de fondo: todo es una API, la velocidad multiplica el error y el alcance es público por defecto. De ahí la inversión de prioridades: revisar la configuración rinde más que buscar vulnerabilidades. Aplicas el modelo de responsabilidad compartida servicio a servicio con la pregunta que lo ordena —«si esto falla, ¿a quién llamas?»— y con las dos conclusiones que importan: cuanto más gestionado el servicio, menos tareas y más críticas; y el proveedor nunca te avisa cuando lo haces mal.

Dominas la identidad como nuevo perímetro: MFA en la raíz, fin de las claves de larga duración, roles asumibles con credenciales efímeras, federación con el proveedor de identidad y revisión de permisos excesivos; con la política mínima que no concede Delete, acota el prefijo y añade una denegación explícita por punto de enlace de la VPC que anula el valor de una credencial robada. Has configurado el bucket A-02 de una vez: bloqueo de acceso público, cifrado con clave propia, versionado, registro de accesos y verificación con un curl que debe devolver 403; y las copias A-03 en cuenta separada con bloqueo de objetos en modo COMPLIANCE, que es lo que convierte «inmutable» en algo real y lo que habría impedido el borrado del día 20. Sabes montar subredes privadas, puntos de enlace privados, cifrado por defecto, bloqueo del servicio de metadatos como mitigación estructural del SSRF, y copias que exigen comprometer dos cuentas independientes.

Sabes configurar el registro de actividad multirregión, inmutable, en otra cuenta y con validación de integridad, y las siete alertas mínimas de las que cinco son S1 porque ninguna tiene explicación benigna. Manejas CSPM con prowler —leyendo su informe con el criterio de 05-01, empezando por el MFA de la raíz y preguntando qué contiene el bucket público antes de cerrarlo— y checkov bloqueante en el CI para que el error no llegue a producción, con la conclusión de que hacen falta los dos. Entiendes qué aísla y qué no un contenedor, y tienes el Dockerfile antes y después: multi-stage, distroless, fijado por digest, --require-hashes, sin secretos en capas y sin root; con la advertencia de que borrar un fichero en una capa posterior no lo elimina de la imagen. Ejecutas con read_only, cap_drop: ALL, no-new-privileges y límites, firmas con cosign y verificas antes de desplegar, porque sin verificación la firma es decorativa. Y conoces el encuadre de Kubernetes como ámbito propio y Falco para detectar lo que ocurre en ejecución. Todo ello priorizado en una tabla que suma unos 540 € y 70 horas y cubre los tres riesgos peor puntuados del registro: en la nube, casi todo lo que falla es una casilla, y las casillas son gratis.

Con esto, el Módulo 5 está completo. Nimbus ya sabe encontrar lo que tiene expuesto y priorizarlo (05-01), ver al atacante moverse con doce detecciones que habrían cortado el incidente de 02-06 en el día 0 (05-02), probarse con un pentest autorizado y aprovechar su informe (05-03), segmentar su red y verificar que la segmentación funciona de verdad (05-04), defender su propio código con autorización centralizada y un CI que dice que no (05-05), endurecer sus sistemas con una línea base automatizada y verificada semanalmente (05-06) y cerrar su nube y sus contenedores (05-07). Han caído, uno a uno, todos los hallazgos que arrastrábamos desde 01-04: el PostgreSQL expuesto, el Redis sin autenticación, el SSH abierto, el python -m http.server y el acceso permanente de la consultora.

Y aquí aparece el límite de todo lo anterior. Nada de esto se mantiene solo. Las herramientas encuentran y corrigen, pero el escaneo mensual deja de ejecutarse cuando Lucía tiene una semana mala; la línea base se degrada si nadie mira el --check --diff; la CSP se rellena de excepciones; el security.txt caduca; y el próximo empleado que entre no sabrá nada de todo esto salvo que alguien se lo enseñe. Además, haber hecho el trabajo no basta: hay que poder demostrarlo —ante un cliente que exige garantías, ante una auditoría, ante la Agencia Española de Protección de Datos si un día hay que notificar una brecha—. En el Módulo 6: Buenas Prácticas y Normativas pasamos de la ejecución a lo que la sostiene en el tiempo: buenas prácticas que convierten decisiones puntuales en hábitos (06-01), normativas y estándares como ISO 27001, NIS2 y ENS (06-02), la protección de datos y el RGPD aplicados de verdad a un SaaS que trata datos de salud (06-03), cumplimiento y auditoría con la evidencia que este módulo ha ido generando (06-04), formación y concienciación, porque las personas siguen siendo el vector principal (06-05), y la ética y la divulgación responsable que 05-03 dejó pendientes (06-06). Dejamos de preguntar si está protegido y empezamos a preguntar si puede demostrarse y si durará.

Curso de Fundamentos de Seguridad Informática

Módulo 1: Introducción a la Seguridad Informática

Módulo 2: Ciberseguridad

Módulo 3: Criptografía

Módulo 4: Gestión de Riesgos y Medidas de Protección

Módulo 5: Herramientas y Técnicas de Seguridad

Módulo 6: Buenas Prácticas y Normativas

Módulo 7: Proyecto Final

© Copyright 2026. Todos los derechos reservados