Saltar a contenido

Key Rotation

La rotación de claves actualiza los secretos y claves criptográficas de la plataforma sin causar downtime. Keycloak soporta rotación de claves de firma JWT sin interrumpir las sesiones activas.

Claves a rotar y frecuencia

Clave / Secreto Frecuencia recomendada Impacto si comprometida
Claves de firma JWT (Keycloak RSA) Cada 90 días Tokens falsificables hasta la rotación
Client secrets (clientes confidenciales) Cada 90 días Acceso no autorizado como ese cliente
Contraseña admin de Keycloak Cada 90 días o tras salida de personal Acceso total a todos los realms
Contraseña de PostgreSQL Cada 6 meses Acceso directo a todos los datos
Claves de cifrado de backups (GPG) Cada año Backups descifrables por terceros

Rotación de claves de firma JWT (sin downtime)

Este es el procedimiento más crítico. Keycloak permite tener múltiples claves activas simultáneamente, lo que permite rotar sin invalidar tokens existentes.

Estrategia

Fase 1: Añadir nueva clave como ACTIVE (emitir nuevos tokens con ella)
Fase 2: Degradar la clave anterior a PASSIVE o VALID (seguir verificando tokens antiguos)
Fase 3: Esperar el TTL del access token (5 minutos) → todos los tokens viejos expirados
Fase 4: Eliminar la clave antigua

Procedimiento via Admin Console

Realm Settings → Keys → Providers

1. Clic en "Add provider" → rsa-generated
   Priority: 200 (mayor que la existente, que tiene 100)
   Key Size: 2048
   Enabled: ✅
   Active: ✅
   → Save

2. Verificar que la nueva clave aparece con status "Active"
   y la anterior pasa a "Passive"

3. Verificar JWKS — debe incluir ambas claves:
   curl https://auth.example.com/realms/realm-internal/protocol/openid-connect/certs | jq '.keys | length'
   # Debe ser ≥ 2

4. Esperar el TTL del access token (5 minutos en producción)

5. Eliminar la clave antigua (la de menor priority)
   → Actions → Delete en la clave antigua

6. Verificar que Spring WebFlux sigue funcionando sin reinicio

Procedimiento via Admin API

REALM="realm-internal"
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')

# 1. Listar providers de claves actuales
curl -s -H "Authorization: Bearer ${ADMIN_TOKEN}" \
  "http://localhost:8080/admin/realms/${REALM}/components?type=org.keycloak.keys.KeyProvider" | \
  jq '.[] | {id, name, providerId, config: .config.priority}'

# 2. Añadir nueva clave RSA con mayor prioridad
curl -s -X POST \
  -H "Authorization: Bearer ${ADMIN_TOKEN}" \
  -H "Content-Type: application/json" \
  "http://localhost:8080/admin/realms/${REALM}/components" \
  -d '{
    "name": "rsa-generated-2026Q2",
    "providerId": "rsa-generated",
    "providerType": "org.keycloak.keys.KeyProvider",
    "config": {
      "priority": ["200"],
      "enabled": ["true"],
      "active": ["true"],
      "keySize": ["2048"]
    }
  }'

# 3. Verificar JWKS actualizado
curl -s \
  "http://localhost:8080/realms/${REALM}/protocol/openid-connect/certs" | \
  jq '.keys | length'

# 4. Después del TTL del access token, eliminar la clave antigua
OLD_KEY_ID="<id-de-la-clave-antigua>"
curl -s -X DELETE \
  -H "Authorization: Bearer ${ADMIN_TOKEN}" \
  "http://localhost:8080/admin/realms/${REALM}/components/${OLD_KEY_ID}"

Rotación de client secrets

REALM="realm-partners"
CLIENT_ID_KC="<uuid-del-cliente>"  # No el clientId, sino el ID interno de Keycloak

# Obtener ID interno del cliente
CLIENT_ID_KC=$(curl -s -H "Authorization: Bearer ${ADMIN_TOKEN}" \
  "http://localhost:8080/admin/realms/${REALM}/clients?clientId=partner-service-m2m" | \
  jq -r '.[0].id')

# Regenerar el secret
NEW_SECRET=$(curl -s -X POST \
  -H "Authorization: Bearer ${ADMIN_TOKEN}" \
  "http://localhost:8080/admin/realms/${REALM}/clients/${CLIENT_ID_KC}/client-secret" | \
  jq -r '.value')

echo "Nuevo secret: ${NEW_SECRET}"

# Actualizar el secret en el servicio M2M (sin downtime si el servicio implementa retry)
# 1. Actualizar la variable de entorno / secreto en el gestor de secretos
# 2. Recargar la configuración del servicio o reiniciarlo

Rotación de contraseña de PostgreSQL

# 1. Generar nueva contraseña
NEW_DB_PASS=$(openssl rand -base64 32)

# 2. Cambiar en PostgreSQL (con Keycloak temporalmente en modo solo lectura no es necesario)
docker compose exec postgres \
  psql -U postgres -c "ALTER USER ${KC_DB_USER} PASSWORD '${NEW_DB_PASS}';"

# 3. Actualizar .env
sed -i "s/KC_DB_PASSWORD=.*/KC_DB_PASSWORD=${NEW_DB_PASS}/" /opt/identity-platform/.env
sed -i "s/POSTGRES_PASSWORD=.*/POSTGRES_PASSWORD=${NEW_DB_PASS}/" /opt/identity-platform/.env

# 4. Reiniciar Keycloak para que use la nueva contraseña
docker compose restart keycloak

# 5. Verificar que Keycloak arranca correctamente
curl -sf http://localhost:8080/health/ready | jq .

Registro de rotaciones

Cada rotación debe registrarse:

Fecha: 2026-04-15
Tipo: JWT signing keys - realm-internal
Realizado por: ops-engineer
Método: Admin Console
Verificación: JWKS actualizado, logins funcionando, no errores 401 en 30 min post-rotación
Próxima rotación: 2026-07-15

Automatizar la rotación de claves JWT

Keycloak puede configurarse para rotar claves automáticamente. En Realm Settings → Keys → Providers, el provider rsa-generated tiene una opción de Key Rotation Period que crea nuevas claves automáticamente. Verifica que la configuración de PASSIVE para claves antiguas deje suficiente tiempo para que expiren los tokens existentes.