---
title: "Rotación de claves maestras y de firma, y acceso de emergencia"
description: "Rota la clave maestra que protege las credenciales guardadas y usa el acceso de emergencia."
image: "https://docs.quirelms.com/og.png"
---

> Documentation Index
> Fetch the complete documentation index at: https://docs.quirelms.com/es-419/llms.txt
> Use this file to discover all available pages before exploring further.

# Rotación de claves maestras y de firma, y acceso de emergencia

<span id="master-key-and-signing-key-rotation-and-break-glass-access"></span>

Los controles de la sección 14 de 21-compliance.md que los auditores solicitan por nombre. Esta página describe el procedimiento; los registros que genera son la evidencia.

## Las claves <!--quire:the-keys-->

Cada credencial guardada se cifra con una clave nueva de cifrado de datos (DEK). La DEK se envuelve con la clave maestra (KEK) y su referencia queda guardada junto a ella (`key_ref` o la referencia dentro de un valor empaquetado). Rotar la clave maestra vuelve a envolver las DEK. No descifra ni vuelve a cifrar ninguna credencial.

| Configuración | Significado |
| --- | --- |
| `QUIRE_MASTER_KEY` | Clave maestra actual: 32 bytes en base64. Todos los secretos nuevos se envuelven con ella |
| `QUIRE_MASTER_KEY_VERSION` | Etiqueta de versión. `v1` si no se configura. Auméntala cada vez que cambies la clave |
| `QUIRE_MASTER_KEY_RETIRED` | Claves anteriores que todavía pueden proteger secretos, en el formato `v1=<base64>,v0=<base64>`. Se leen, nunca se escriben |

La capa web, el worker y el comando `bun run kek:rotate` leen estas mismas tres configuraciones. Deben tener los mismos valores; de lo contrario, un proceso no podrá abrir lo que otro cifró.

Sin `QUIRE_MASTER_KEY`, cada subsistema conserva la clave que deriva de `QUIRE_SECRET_KEY`. Así funciona, aunque la página Estado del sistema indica una condición degradada. El contenido sigue siendo legible después de configurar una clave maestra; por eso la primera rotación permite dejar de usar la clave derivada. Cualquier persona que pueda leer el entorno del proceso puede descifrar todas las credenciales guardadas; por eso, una instalación de producción debe tener una clave maestra en un almacén de secretos, separada del respaldo de la base de datos.

## Rotar la clave <!--quire:rotating-->

La antigüedad de la clave aparece en Consola de la plataforma, Seguridad, Clave maestra, y en la métrica `quire.secrets.master_key.age` (días). La programación diaria `platform.key_age` (03:41 UTC) escribe un recordatorio en la cadena de auditoría de la plataforma cuando la clave cumple 365 días y repite el aviso cada 30 días hasta que la rotes. Rótala cuando recibas el aviso y siempre que exista la posibilidad de que se haya expuesto.

1. Genera una clave nueva: `openssl rand -base64 32`.
2. Configura `QUIRE_MASTER_KEY` con ella y `QUIRE_MASTER_KEY_VERSION` con la siguiente etiqueta (`v2`). Mueve la clave anterior a `QUIRE_MASTER_KEY_RETIRED` como `v1=<old base64>`. Guarda una copia de ambas fuera de este host.
3. Despliega la capa web y el worker con la nueva configuración. Los secretos nuevos se envuelven con `env:QUIRE_MASTER_KEY:v2`; los anteriores se siguen abriendo con la clave retirada.
4. Solicita la rotación e indica el motivo que se guardará en el historial de auditoría:
   - en la consola: Seguridad, Clave maestra, Rotar la clave maestra; o
   - en una terminal con el mismo entorno: `bun run kek:rotate request --reason "Annual rotation, ticket SEC-114"`.
5. El worker vuelve a envolver un lote por minuto (la programación `platform.key_rotation` del scheduler) y retoma el trabajo al reiniciarse. Para terminarlo de una vez: `bun run kek:rotate run`. Revisa el avance con `bun run kek:rotate status`.
6. Cuando el registro indique que la rotación terminó con **cero pendientes sin resolver y cero fallas**, quita la clave retirada de `QUIRE_MASTER_KEY_RETIRED` y vuelve a desplegar. Hasta entonces, consérvala: cualquier valor que no pudo migrar todavía está envuelto con la clave anterior.

### Qué recorre la tarea <!--quire:what-the-job-walks-->

Cada almacén que contiene una DEK envuelta: los que enumera `SEALED_STORES` (`apps/worker/src/key-rotation.ts`). Los almacenes de la base de control se recorren allí; los de cada organización se recorren de una en una, con seguridad a nivel de fila y en la base donde vive la organización, así que un tenant fijado a una base dedicada rota en esa base. Una prueba falla si el esquema agrega una columna con clave envuelta que la lista no identifica; otra, si la revisión de credenciales clasifica una columna cifrada que la lista omite.

### El registro <!--quire:the-record-->

- `ops.key_rotation`: una fila por rotación, con el motivo, quién la solicitó, su estado y sus totales (vueltas a envolver, ya actualizadas, pendientes sin resolver y fallidas).
- `ops.key_rotation_progress`: una fila por almacén y alcance después de recorrerlo, con las referencias de claves que no se pudieron leer y cuántos valores correspondían a cada una. Una rotación reanudada omite esas filas.
- Cadena de auditoría de la plataforma: `platform/key_rotation_request` (con el motivo), una entrada `platform/key_rotation_store` por almacén con sus recuentos y `platform/key_rotation_complete` o `platform/key_rotation_fail`; `platform/key_age_reminder` para los recordatorios.
- Métricas: `quire.secrets.master_key.age` y `quire.secrets.rewrap.outstanding` (valores que la última rotación no pudo migrar).

### Si quedan valores sin resolver <!--quire:when-values-are-unresolved-->

Un valor queda sin resolver si está envuelto con una referencia de clave que esta instalación no tiene o si no tiene el formato que indica su columna. El registro de avance muestra la referencia (por ejemplo, `env:QUIRE_MASTER_KEY:v0 (unreadable)`). Vuelve a agregar esa clave a `QUIRE_MASTER_KEY_RETIRED` y ejecuta otra rotación. Si se perdió definitivamente, pide al administrador de la organización que ingrese otra vez la credencial; entonces se cifra con la clave actual. El registro muestra el error de las rotaciones fallidas; corrige la causa y vuelve a solicitarlas.

## Claves de firma <!--quire:signing-keys-->

Por separado de la clave maestra, cada organización firma sus tokens OpenID Connect y mensajes LTI con su propia clave RSA, publicada en `/.well-known/jwks.json`. Esto no requiere intervención del operador. La programación horaria `platform.signing_keys` publica una clave sucesora siete días antes de que la actual cumpla 90 días. Una semana después, la sucesora empieza a firmar y la anterior pasa a retirarse; noventa días más tarde, la clave anterior se elimina del conjunto. Cada paso agrega una entrada `platform/signing_key_advance` a la cadena de auditoría de la plataforma.

Para reemplazar antes una clave de la organización, por ejemplo, si se expuso:

- en la consola: Seguridad, Clave maestra, Publicar una clave de firma nueva (requiere `platform/keys_manage`); o
- en una terminal con el entorno del worker: `bun run kek:rotate signing-keys rotate --tenant <slug or id> --reason "Key exposed, INC-3310"`. `bun run kek:rotate signing-keys status` muestra las claves de cada organización por etapa.

La clave nueva se publica de inmediato y empieza a firmar después de siete días, cuando se retira la actual. El plazo es intencional: los sistemas que confían en la clave guardan en caché el conjunto, y un periodo de superposición más corto haría que todas las herramientas fallaran al mismo tiempo. La clave retirada permanece en el conjunto durante otros 90 días para seguir verificando los tokens que ya firmó. Si la exposición exige dejar de confiar en ella antes, se elimina su fila con el acceso propio del operador a la base de datos y bajo un registro de cambios (el acceso de emergencia es de solo lectura); a partir de ese momento, no se verifican los tokens que firmó. La rotación forzada queda en la cadena de auditoría como `platform/signing_key_rotate`, junto con el motivo. El worker necesita la misma configuración `QUIRE_MASTER_KEY` que la capa web para envolver la clave nueva; `bun run kek:rotate` de la clave maestra vuelve a envolver también las claves de firma (`oauth_signing_key` está en `SEALED_STORES`).

## Acceso de emergencia a producción <!--quire:break-glass-production-access-->

Nadie tiene acceso permanente a producción. Si algo no puede esperar, una persona propietaria concede un acceso de emergencia: Consola de la plataforma, Seguridad, Acceso de emergencia.

- El acceso define un alcance (una organización o el registro de la plataforma), un motivo de al menos 20 caracteres que identifique el incidente o ticket y un periodo de 5 a 240 minutos. Vence por sí solo: se comprueba la hora en cada instrucción.
- Puede concederse a la misma persona propietaria que lo solicita o a otra (modalidad de dos personas). Solo puede usarlo la persona indicada. Para concederlo se requiere `platform/break_glass_issue`, y para usarlo, `platform/break_glass_use`; de forma predeterminada, ambos permisos son exclusivos de propietarios.
- Las instrucciones pasan por la puerta de enlace, no por un acceso a la base de datos: son de solo lectura, se ejecutan de una en una, se limitan a la organización o al registro de control, tienen un tiempo de espera de cinco segundos y devuelven como máximo 500 filas. Los valores binarios se muestran por tamaño.
- La cadena de auditoría de la plataforma registra la concesión (junto con el motivo), su revocación, cada instrucción antes de ejecutarla (`platform/break_glass_statement`; las rechazadas quedan con resultado `denied`) y cada resultado (`platform/break_glass_result`). `ops.break_glass_statement` guarda los ID de entradas de auditoría, así la concesión se puede vincular con ellas.
- No se ofrecen escrituras. Un cambio que no pueda esperar a una versión se hace fuera del producto, con el acceso propio del operador a la base de datos y bajo su propio registro de cambios; ese registro debe citar la referencia del incidente indicada aquí.

¿Por qué no entregar credenciales de base de datos? Un acceso a Postgres sigue vigente después de que termine la sesión que lo solicitó, evita la seguridad a nivel de fila que usa la aplicación y no puede escribir en la cadena de auditoría del producto. Así, las instrucciones solo quedarían auditadas hasta donde alguien hubiera enviado los registros del servidor. La puerta de enlace convierte el historial de auditoría en una propiedad del acceso, en lugar de una práctica que lo rodea.

Para responder a una auditoría: consulta las concesiones del periodo (Acceso de emergencia), abre el historial de una concesión para ver sus instrucciones y los ID de entradas de auditoría, y revisa esas entradas en la cadena de auditoría de la plataforma (`bun run audit:verify --platform` comprueba que la cadena siga intacta).

Source: https://docs.quirelms.com/es-419/ops/key-rotation/index.mdx
