Incident Response¶
Este runbook define el proceso de respuesta a incidentes de la plataforma de identidad. Un incidente es cualquier evento que afecte la disponibilidad, integridad o seguridad de la plataforma.
Severidades¶
| Severidad | Descripción | Ejemplos | Tiempo de respuesta |
|---|---|---|---|
| P1 — Crítico | Plataforma completamente inaccesible o compromiso de seguridad activo | Keycloak caído, DB corrompida, credenciales comprometidas | 15 minutos |
| P2 — Alto | Funcionalidad parcial degradada, sin workaround | Logins fallando intermitentemente, latencia >5s | 1 hora |
| P3 — Medio | Funcionalidad reducida con workaround disponible | Admin Console inaccesible, backup fallido | 4 horas (horario laboral) |
| P4 — Bajo | Impacto mínimo, cosmético o de rendimiento | Slow queries, dashboard sin datos | Próximo sprint |
Checklist de primera respuesta (P1/P2)¶
1. [ ] Confirmar el incidente (no es una alerta falsa)
2. [ ] Notificar al equipo (Slack #incidents + responsable de guardia)
3. [ ] Abrir un ticket con: hora inicio, síntomas, impacto estimado
4. [ ] Comenzar diagnóstico (ver árbol de decisión)
5. [ ] Actualizar el ticket cada 15 minutos con el estado
6. [ ] Resolver o escalar según el árbol de decisión
7. [ ] Confirmar resolución con smoke tests
8. [ ] Notificar resolución al equipo
9. [ ] Programar post-mortem (P1) o registrar lecciones (P2)
Árbol de diagnóstico¶
flowchart TD
A[Incidente detectado] --> B{Health checks}
B -->|Keycloak /health/ready falla| C[Problema en Keycloak]
B -->|PostgreSQL no responde| D[Problema en DB]
B -->|Traefik no responde| E[Problema de red/edge]
B -->|Todo OK pero logins fallan| F[Problema de configuración]
C --> C1{Logs de Keycloak}
C1 -->|OOM / OutOfMemory| C2[Aumentar memory limits\n ver compose.yml]
C1 -->|DB connection failed| D
C1 -->|Startup crash| C3[Ver error en logs\n docker logs keycloak]
D --> D1{Estado del contenedor}
D1 -->|Running pero no acepta conexiones| D2[pg_isready falla\n Revisar logs postgres]
D1 -->|Crashed / Restarting| D3[Restaurar desde backup\n ver restore.md]
E --> E1{Certificados TLS}
E1 -->|Expirado| E2[Renovar certificado\n docker restart traefik]
E1 -->|OK| E3[Revisar reglas UFW\n Revisar Traefik logs]
F --> F1[Revisar eventos de Keycloak\n Admin Console → Events]
Comandos de diagnóstico rápido¶
# Estado general de todos los contenedores
docker compose ps
# Logs de Keycloak (últimas 100 líneas)
docker compose logs --tail=100 keycloak
# Logs de PostgreSQL
docker compose logs --tail=50 postgres
# Logs de Traefik
docker compose logs --tail=50 traefik
# Health checks
curl -sf http://localhost:8080/health/ready | jq .
curl -sf http://localhost:8080/health/live | jq .
# Verificar conectividad DB desde Keycloak
docker compose exec keycloak \
/bin/sh -c "nc -zv postgres 5432 && echo 'DB alcanzable'"
# Uso de recursos
docker stats --no-stream
# Espacio en disco
df -h /var/lib/docker
Escenarios comunes y solución¶
Keycloak no arranca¶
# Ver el error específico
docker compose logs keycloak | grep -E "ERROR|FATAL|Exception"
# Problema frecuente: DB no disponible al arrancar
docker compose up -d postgres
sleep 10
docker compose up -d keycloak
# Problema: puerto 8080 ocupado
netstat -tlnp | grep 8080
Logins fallan — error de certificado¶
# Verificar fecha de expiración del certificado
echo | openssl s_client -connect auth.example.com:443 2>/dev/null | \
openssl x509 -noout -dates
# Forzar renovación de Let's Encrypt
docker compose restart traefik
# Traefik renueva automáticamente si el cert expira en < 30 días
Logins fallan — error de credenciales de usuario¶
# Verificar en Admin Console → Events → LOGIN_ERROR
# O via API:
ADMIN_TOKEN=$(curl -s -X POST "http://localhost:8080/realms/master/protocol/openid-connect/token" \
-d "grant_type=password&client_id=admin-cli&username=${KC_ADMIN}&password=${KC_ADMIN_PASSWORD}" | \
jq -r '.access_token')
curl -s -H "Authorization: Bearer ${ADMIN_TOKEN}" \
"http://localhost:8080/admin/realms/realm-internal/events?type=LOGIN_ERROR&max=10" | \
jq '.[] | {time: .time, error: .error, username: .details.username}'
Base de datos llena¶
# Verificar espacio
df -h /var/lib/docker/volumes/
# Tamaño de la DB de Keycloak
docker compose exec postgres \
psql -U "${KC_DB_USER}" -c "\l+"
# Limpiar sesiones expiradas (Keycloak lo hace automáticamente, pero se puede forzar)
docker compose exec postgres \
psql -U "${KC_DB_USER}" -d "${KC_DB_NAME}" \
-c "DELETE FROM offline_client_session WHERE expiration < EXTRACT(EPOCH FROM NOW())::bigint;"
Post-mortem (para P1)¶
El post-mortem se realiza dentro de las 48 horas siguientes a la resolución de un P1.
Estructura: - Fecha y duración del incidente - Impacto (usuarios afectados, servicios degradados) - Timeline (cuándo se detectó, cuándo se notificó, cuándo se resolvió) - Causa raíz (Root Cause Analysis — 5 Whys) - Acciones correctivas (qué se hará para prevenir la recurrencia) - Acciones de mejora (qué se hará para detectar/responder más rápido)
Sin culpables en el post-mortem
El post-mortem es un ejercicio de aprendizaje, no de culpabilización. El objetivo es mejorar los sistemas y procesos, no señalar a personas.