Identidad y enrollment
El WhyStack Agent se autentica por mTLS. Esta página trata de la credencial que usa para eso: cómo la obtiene, cómo esa credencial se mantiene viva y (la pregunta que sale cara cuando nadie la hace primero) qué pasa cuando se reemplaza el contenedor.
Conviene separar las dos cosas desde ya, porque se confunden:
- Cómo nace la identidad: tres caminos, y eliges uno.
- Cómo sobrevive: renovación (dentro del proceso) y persistencia (a través del reemplazo del contenedor).
La segunda mitad es la que decide si un deploy necesita a alguien frente a un navegador.
Cómo nace la identidad
Sección titulada «Cómo nace la identidad»| Camino | Cuándo usarlo | Qué configuras |
|---|---|---|
| Atestación de nube | Donde la plataforma prueba la identidad por sí sola: GKE, EC2/EKS | RUNNER_ATTESTATION_MODE + RUNNER_ENROLL_URL |
| Bootstrap token | Donde no hay identidad de plataforma: VPS, tu propio host, un primer piloto | RUNNER_BOOTSTRAP_TOKEN + RUNNER_ENROLL_URL |
| Certificado aprovisionado | Un puente: nosotros lo emitimos, tú lo montas | RUNNER_CERT_PEM + RUNNER_KEY_PEM |
La atestación de nube es la mejor dondequiera que exista, y la razón es concreta: no hay ningún secreto que generar, entregar ni rotar. Los modos disponibles y lo que exige cada uno están en Atestación.
El flujo de enrollment
Sección titulada «El flujo de enrollment»El boot es una máquina de estados: liveness → config → secrets → enroll → connect → ready. El paso
enroll hace esto:
- Genera un par de claves EC P-256. La clave privada existe solo en memoria, como PKCS#8.
- Genera un nonce y arma un CSR con él en el campo
challengePassword, que es la prueba de posesión. - Recopila la atestación de la plataforma, según
RUNNER_ATTESTATION_MODE. - Hace un
POSTa/api/enrollcon{ version, attestation, csr, challengeNonce }. - El control-plane valida la atestación, firma el CSR y devuelve un certificado con el SPIFFE id del
tenant adentro:
spiffe://rootpilot/tenant/<slug>/runner. El trust domain pasa awhystackmás adelante; el Agent acepta los dos, y los dos nombran a la misma organización. - El Agent abre el túnel WSS con mTLS usando ese certificado.
El tenant sale del certificado, no de la configuración
Sección titulada «El tenant sale del certificado, no de la configuración»Vale la pena insistir en esto, porque es la garantía que sostiene todo lo demás: el material de atestación determina el tenant, no una variable de entorno. El enrollment devuelve un certificado con el SPIFFE id adentro, y eso es lo que el control-plane lee en cada llamada.
RUNNER_TENANT, que aparece en los scripts de la flota, no elige ningún tenant: elige la carpeta de la
bóveda de la que se leen las credenciales. Equivocarte en ese slug no te va a conectar a la organización
equivocada, y si el slug declarado no coincide con aquel con el que la credencial hizo el enrollment, el
Agent se niega a arrancar en lugar de servir a la organización equivocada.
Cómo se mantiene viva: renovación
Sección titulada «Cómo se mantiene viva: renovación»El certificado es de vida corta, 15 minutos por defecto. Es deliberado: un certificado que vale poco tiempo vale poco para quien lo robe.
RUNNER_CERT_RENEWAL=on es el valor por defecto, y la renovación se dispara a los 2/3 del tiempo
restante, no cerca del vencimiento. La razón es operativa: renovar justo en notAfter convertiría
cualquier inestabilidad transitoria de la red en pérdida de identidad. Renovar a los 2/3 deja un tercio de
la vida útil como ventana de reintentos.
Los reintentos hacen backoff con un techo de 5 minutos y jitter de ±25%. Sin el jitter, toda la flota
golpearía el control-plane en el mismo segundo. Si el servidor responde Retry-After, eso prevalece sobre
el cálculo local.
La renovación requiere RUNNER_ENROLL_URL: es la dirección con la que habla.
El reloj que consulta la renovación
Sección titulada «El reloj que consulta la renovación»Cada 30 segundos el Agent verifica si ya es hora de renovar, comparando el reloj del sistema con el vencimiento del certificado. No pone una alarma para dentro de 40 minutos.
La diferencia se nota cuando el host deja de contar el tiempo: una máquina suspendida, una VM restaurada desde un snapshot, un contenedor sin CPU por una cuota. Una alarma larga cuenta tiempo de proceso y no avanza mientras el host duerme. Despertaría creyendo que todavía quedan minutos para renovar un certificado que murió horas antes. Al preguntarle al reloj del sistema cada 30 segundos, el Agent descubre la situación real justo después de volver:
- Si vuelve antes del vencimiento: renueva de inmediato. El certificado se salva.
- Si vuelve después: declara la pérdida y sale, para que el supervisor levante un reemplazo (consulta Solución de problemas), en lugar de pasar media hora intentando reconectarse con una identidad muerta.
Cómo prueba la identidad la renovación
Sección titulada «Cómo prueba la identidad la renovación»La renovación no es un RUNNER_ATTESTATION_MODE. Renovar no es una opción de configuración: es lo que
hace el Agent cuando ya tiene un certificado.
Firma el nonce del nuevo CSR con la clave privada actual y envía el certificado actual junto con él. El control-plane valida la cadena contra la CA, verifica la firma con la clave pública del certificado y extrae el tenant del certificado, nunca del cuerpo de la solicitud. Leerlo del cuerpo convertiría la renovación en una forma de cambiar de tenant.
Cómo sobrevive a un reinicio: persistencia
Sección titulada «Cómo sobrevive a un reinicio: persistencia»Esta es la diferencia que más cuesta si nadie la configura.
La renovación vive dentro del proceso. Reemplaza el contenedor y la identidad muere con él. La alternativa es el enrollment desde cero. Donde la identidad nació de un bootstrap token, eso significa un token nuevo, generado a mano, en cada deploy (el token es de un solo uso).
RUNNER_IDENTITY_DIR resuelve eso: apunta a un directorio donde el Agent escribe la identidad
(identity.json, modo 600). En el siguiente boot renueva a partir de ella en lugar de hacer el
enrollment desde cero.
volumes: - whystack-identity:/identity # named volume: inherits the right ownership from the imageenvironment: RUNNER_IDENTITY_DIR: /identityEso es todo: la variable alcanza por sí sola. La primera vez que el Agent usa el directorio, escribe
ahí un id de dueño (instance-id, modo 600), así que la identidad se sigue reconociendo como suya
después de que se recrea el contenedor, que es justamente el objetivo de la funcionalidad.
RUNNER_INSTANCE_ID sigue valiendo la pena, pero por otro motivo: identifica la instancia en los logs y
en la Flota. Ya no tiene nada que ver con la persistencia.
Tres cosas que conviene saber antes de activarlo:
- El directorio tiene que tener permiso de escritura para el uid del Agent (la imagen corre como
no-root, uid 1000). Un volumen con nombre lo resuelve solo: la imagen trae
/identitycon la propiedad correcta, y el volumen la hereda al crearse. Un bind mount no hereda: la propiedad es la del host. Si no se puede escribir, el boot no falla: avisa y sigue, y solo te enteras en el siguiente deploy, concould not persist identity — next boot will re-enroll from scratch. - Un directorio por Agent. Dos contenedores en el mismo volumen compartirían una identidad.
- La ventana tiene un límite. Lo que sobrevive es la renovación, y vale hasta 6 minutos después del vencimiento del certificado (5 de gracia + 1 de margen de reloj). Un deploy normal cabe con holgura; un host apagado toda la noche, no. Ese caso es enrollment desde cero.
Cuando se pierde la identidad
Sección titulada «Cuando se pierde la identidad»Pasados esos 6 minutos, el control-plane responde presented cert expired beyond the renewal grace window,
y lo va a responder para siempre: la atestación de renovación prueba la posesión con el certificado que
murió.
El comportamiento entonces es salir del proceso, para que el supervisor levante un Agent nuevo que haga el enrollment desde cero. Sin esa salida, el proceso se volvería un zombi: arriba, sin servir nada, invisible para cualquier probe.
identity expired beyond the renewal grace window — runner needs re-enrollmentEl certificado aprovisionado
Sección titulada «El certificado aprovisionado»WhyStack emite un certificado para tu tenant y tú lo montas en /certs (o lo pasas inline en
RUNNER_CERT_PEM/RUNNER_KEY_PEM/RUNNER_CA_PEM). No requiere atestación de plataforma ni egress hacia
el endpoint de enrollment, y es el puente para entornos donde todavía no aplica ninguno de los otros dos.
El costo se declara de entrada: no se renueva. Cuando vence, la flota deja de conectarse, y el Agent ni
sale del proceso ni avisa. Sigue intentando reconectarse indefinidamente, con un error de TLS que nunca
dice que eso es lo que pasó. La fecha está en el notAfter de la línea de boot; anótala en el calendario
el día en que recibas el certificado.
La validación de configuración cubre esto
Sección titulada «La validación de configuración cubre esto»El esquema exige al menos un camino de identidad, y falla rápido cuando no hay ninguno:
either provide RUNNER_CERT_PEM+RUNNER_KEY_PEM (provisioned cert) or RUNNER_ENROLL_URL (enrollment)