Rotación de secretos
Por qué rotar los secretos
| Riesgo | Cómo lo mitiga la rotación |
|---|---|
| Fuga de credenciales | Una contraseña filtrada deja de ser válida tras el siguiente ciclo de rotación |
| Tokens de larga duración | Los tiempos de vida más cortos reducen la ventana de oportunidad de un atacante |
| Acceso de exempleados | Las credenciales rotadas tras la baja son inútiles aunque se hayan memorizado |
| Requisitos de cumplimiento | PCI-DSS, SOC 2 e ISO 27001 exigen cambios periódicos de credenciales |
Flujo de trabajo de rotación
Actualice siempre el sistema de destino primero y luego guarde en Passwork. Si guarda primero en Passwork y la actualización del sistema de destino falla, Passwork tendrá la nueva contraseña pero el sistema seguirá usando la antigua: sus sistemas quedarán desincronizados.
1. Generar el nuevo secreto
↓
2. Aplicarlo al sistema de destino (base de datos, proveedor de API, etc.)
↓
3. Verificar que la nueva credencial funciona
↓
4. Actualizar Passwork (passwork-cli update / SDK de Python)
↓
5. Notificar / actualizar los consumidores posteriores
Rotación mediante la CLI
PostgreSQL
#!/bin/bash
set -euo pipefail
PASSWORK_ITEM_ID="<postgres-item-id>"
DB_USER="order_service"
DB_HOST="pg.prod.internal"
# 1. Generate a new password
NEW_PASS=$(openssl rand -base64 32 | tr -d '=+/' | cut -c1-32)
# 2. Apply to PostgreSQL
psql -h "$DB_HOST" -U postgres -d postgres \
-c "ALTER ROLE ${DB_USER} WITH PASSWORD '${NEW_PASS}';"
# 3. Update in Passwork
passwork-cli update --password-id "$PASSWORK_ITEM_ID" --password "$NEW_PASS"
echo "Rotated password for ${DB_USER}"
MySQL
#!/bin/bash
set -euo pipefail
PASSWORK_ITEM_ID="<mysql-item-id>"
DB_USER="inventory_svc"
NEW_PASS=$(openssl rand -base64 32 | tr -d '=+/' | cut -c1-32)
mysql -h mysql.prod.internal -u root \
-p"$(passwork-cli get --password-id '<mysql-root-item-id>')" \
-e "ALTER USER '${DB_USER}'@'%' IDENTIFIED BY '${NEW_PASS}';"
passwork-cli update --password-id "$PASSWORK_ITEM_ID" --password "$NEW_PASS"
echo "Rotated password for ${DB_USER}"
Clave de API externa
#!/bin/bash
set -euo pipefail
PASSWORK_ITEM_ID="<stripe-item-id>"
OLD_KEY=$(passwork-cli get --password-id "$PASSWORK_ITEM_ID")
# 1. Request new key from external service (provider-specific)
NEW_KEY=$(curl -s -X POST "https://api.service.example.com/keys/rotate" \
-H "Authorization: Bearer ${OLD_KEY}" \
-H "Content-Type: application/json" | jq -r '.new_key')
# 2. Verify the new key works
curl -s "https://api.service.example.com/verify" \
-H "Authorization: Bearer ${NEW_KEY}" | grep -q '"status":"ok"'
# 3. Save to Passwork
passwork-cli update --password-id "$PASSWORK_ITEM_ID" --password "$NEW_KEY"
echo "API key rotated"
Rotación mediante el SDK de Python
El conector de Python le ofrece un control total sobre el flujo de trabajo de rotación: gestión de errores, registro, semántica transaccional y notificaciones a los consumidores posteriores.
#!/usr/bin/env python3
"""PostgreSQL password rotation with full error handling."""
import os
import secrets
import sys
import psycopg2
from passwork import Client
from passwork.exceptions import PassworkResponseError
def rotate_postgres_password(item_id: str, db_user: str) -> bool:
client = Client(
url=os.environ["PASSWORK_HOST"],
token=os.environ["PASSWORK_TOKEN"],
refresh_token=os.environ.get("PASSWORK_REFRESH_TOKEN"),
)
# Fetch admin credentials from Passwork
admin_item = client.get_password(item_id=os.environ["PG_ADMIN_ITEM_ID"])
new_password = secrets.token_urlsafe(32)
try:
# Apply to PostgreSQL first
conn = psycopg2.connect(
host=admin_item.fields["PG_HOST"],
dbname="postgres",
user=admin_item.login,
password=admin_item.password,
)
conn.autocommit = True
with conn.cursor() as cur:
cur.execute("ALTER ROLE %s WITH PASSWORD %s", (db_user, new_password))
conn.close()
except psycopg2.Error as e:
print(f"Database error — password NOT changed: {e}", file=sys.stderr)
return False
try:
# Only update Passwork after the target system is confirmed updated
item = client.get_password(item_id=item_id)
item.password = new_password
client.update_password(item)
except PassworkResponseError as e:
# Critical: DB password changed but Passwork not updated
# Alert on-call immediately
print(
f"CRITICAL: DB password rotated but Passwork update failed! "
f"API error {e.code}: {e.message}",
file=sys.stderr,
)
return False
print(f"Password rotated for {db_user}")
return True
if __name__ == "__main__":
success = rotate_postgres_password(
item_id=os.environ["TARGET_ITEM_ID"],
db_user=os.environ["PG_DB_USER"],
)
sys.exit(0 if success else 1)
Programación de la rotación
Cron (Linux)
# /etc/cron.d/passwork-rotation
# Rotate production DB passwords every Sunday at 03:00
0 3 * * 0 deploy /opt/scripts/rotate-postgres.sh \
>> /var/log/passwork-rotation.log 2>&1
Canalización programada de GitLab CI
# .gitlab-ci.yml
rotate_secrets:
stage: maintenance
image: passwork/passwork-cli:latest
script:
- pip install passwork-python
- python /scripts/rotate-all.py
rules:
- if: '$CI_PIPELINE_SOURCE == "schedule"'
Configure la programación en CI/CD → Schedules (p. ej., todos los domingos a las 03:00 UTC).
Frecuencias de rotación recomendadas
| Tipo de secreto | Frecuencia |
|---|---|
| Contraseñas de bases de datos de producción | 30–90 días |
| Claves de API externas | 90 días o según la política del proveedor |
| Tokens de API de cuentas de servicio (Passwork) | 30 días |
| Claves SSH | 6–12 meses |
| Certificados TLS | Antes de la caducidad (automatizar mediante certbot o ACME) |
Gestión de los consumidores posteriores
Cuando un secreto rota, todos los servicios que lo utilizan necesitan recargar el nuevo valor. Patrones habituales:
Inyección de variables de entorno al inicio: si los servicios cargan los secretos al inicio mediante passwork-cli exec, adoptan el nuevo valor en el siguiente reinicio. Combínelo con un reinicio continuo (rolling restart) tras la rotación.
Actualización con sidecar: en Kubernetes, el patrón sidecar (consulte Patrones de CI/CD) actualiza periódicamente el archivo .env. La aplicación vigila los cambios en el archivo y lo recarga sin reiniciarse.
Notificación tras la rotación: en el script de rotación, después de actualizar Passwork, envíe una notificación al canal de Slack correspondiente o active una canalización de despliegue que reinicie los servicios afectados.
Para consultar la referencia completa de rotación, consulte Prácticas de rotación de secretos.