Saltar al contenido principal

Rotación de secretos

Por qué rotar los secretos

RiesgoCómo lo mitiga la rotación
Fuga de credencialesUna contraseña filtrada deja de ser válida tras el siguiente ciclo de rotación
Tokens de larga duraciónLos tiempos de vida más cortos reducen la ventana de oportunidad de un atacante
Acceso de exempleadosLas credenciales rotadas tras la baja son inútiles aunque se hayan memorizado
Requisitos de cumplimientoPCI-DSS, SOC 2 e ISO 27001 exigen cambios periódicos de credenciales

Flujo de trabajo de rotación

Orden de las operaciones

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 secretoFrecuencia
Contraseñas de bases de datos de producción30–90 días
Claves de API externas90 días o según la política del proveedor
Tokens de API de cuentas de servicio (Passwork)30 días
Claves SSH6–12 meses
Certificados TLSAntes 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.