El viaje de una petición¶
Sigue una petición de kubectl desde que sale de tu máquina hasta que vuelve la respuesta. Así entiendes qué comprueba kubelatch, qué ve el cluster y qué queda en la auditoría.
De un vistazo¶
sequenceDiagram
participant K as kubectl
participant P as Proxy de kubelatch
participant DB as Postgres
participant A as API server
K->>P: GET /clusters/prod/api/v1/namespaces/apps/pods<br/>Authorization: Bearer klt_…
P->>P: Ruta y cabeceras Impersonate-*
P->>DB: Busca el hash del token
P->>DB: Sujeto, cluster y permisos activos
P->>DB: Inserta la fila de auditoría (inicio)
P->>A: Misma petición con Impersonate-User,<br/>Impersonate-Group, Impersonate-Uid, Audit-ID
A-->>P: Respuesta (RBAC nativo del cluster)
P-->>K: Respuesta + Audit-ID
P->>DB: Completa la fila (estado, duración)
1. La petición llega al proxy¶
Tu kubeconfig apunta a https://<kubelatch>/clusters/<id> y lleva un token klt_… como Bearer. Todo lo que cuelga de /clusters/ va al proxy antes que a cualquier otra ruta.
Toda respuesta del proxy lleva una cabecera Audit-ID, también los errores. Es lo que buscas en los logs o en la auditoría para localizar una petición concreta.
2. Primer filtro: ruta y cabeceras¶
Antes de mirar el token, el proxy rechaza con 400:
- Rutas con segmentos vacíos,
.o.., caracteres escapados o un%. - Un identificador de cluster que no puede existir.
- Cualquier cabecera
Impersonate-*que venga del cliente. Por esokubectl --asno funciona a través de kubelatch: el proxy ya usa la impersonación, y dejar pasar una impersonación anidada permitiría pedir ser otra persona.
Estos rechazos no dejan fila de auditoría: la petición no llega a identificar a nadie.
3. ¿Quién eres? El token¶
El proxy solo acepta Authorization: Bearer klt_… con el formato exacto de un token de kubelatch. Ignora las cookies. Después:
- Calcula el SHA-256 del token y busca la credencial por ese hash. No hay caché: cada petición consulta Postgres, así que una revocación vale en la petición siguiente.
- Comprueba que la credencial no esté revocada ni caducada. Si falla algo de esto, o el token no existe, responde
401. - Carga el sujeto dueño de la credencial. Si la cuenta está deshabilitada, responde
403. - Si la credencial está restringida a un cluster y no es este, responde
403.
4. ¿Qué puedes hacer aquí? Cluster y permisos¶
El proxy carga el cluster. Si no existe responde 404; si todavía no tiene tokens configurados, 503.
Luego busca los permisos activos del sujeto en ese cluster: no revocados y sin caducar en este instante. Si no hay ninguno responde 403 con un mensaje claro. Cada permiso se traduce en un grupo:
| Ámbito del permiso | Grupo que se impersona |
|---|---|
| Un namespace | kubelatch:ns:<namespace>:<nivel> |
Todo el cluster (*) |
kubelatch:cluster:<nivel> |
Una caducidad no necesita que nadie haga nada: en cuanto un permiso caduca, el proxy deja de enviar su grupo.
5. La fila de auditoría, antes de reenviar¶
Con la identidad y los grupos resueltos, el proxy clasifica la petición como lo haría el API server: verbo, grupo de API, recurso, subrecurso, namespace y nombre. Inserta la fila de auditoría antes de reenviar. Si no puede escribirla, responde 503 y no reenvía nada: ninguna petición pasa sin su registro.
También actualiza el «último uso» de la credencial, como mucho una vez por minuto.
6. Lo que recibe el API server¶
El proxy reescribe la petición antes de reenviarla:
| Cabecera | Qué hace el proxy |
|---|---|
Authorization |
La sustituye por el token de la ServiceAccount kubelatch-proxy de ese cluster. Tu token nunca llega al cluster. |
Impersonate-User |
user:<login> para personas, bot:<nombre> para bots. |
Impersonate-Uid |
El identificador interno del sujeto. |
Impersonate-Group |
Una por cada grupo del paso 4. |
Audit-ID |
Un UUIDv7 generado por kubelatch. Nunca respeta el del cliente. |
X-Forwarded-For |
La IP real del cliente (ver proxies de confianza). |
Cookie, X-Real-IP, Forwarded, X-Forwarded-* |
Las elimina. |
El API server autentica a la ServiceAccount del proxy, comprueba que puede impersonar a ese usuario y a esos grupos, y aplica su RBAC nativo a la identidad impersonada. kubelatch no añade system:authenticated: lo pone el propio API server.
El API server usa la cabecera Audit-ID como auditID de su propio registro. Así una fila de kubelatch y el evento de la auditoría nativa del cluster se correlacionan por el mismo identificador. En la auditoría nativa, user es la ServiceAccount del proxy e impersonatedUser es user:<login>.
7. La respuesta vuelve¶
El proxy devuelve la respuesta del API server tal cual, con su propio Audit-ID. Si no consigue hablar con el API server, responde 502 con un resumen del problema (tiempo agotado, certificado que no coincide, API server inalcanzable), sin filtrar direcciones internas.
Al terminar, completa la fila de auditoría con el código de estado, la duración, la hora de fin y el error si lo hubo. Una fila solo se puede completar una vez.
Streams largos: exec, port-forward, watch y logs¶
kubectl exec, attach, port-forward y cp abren una conexión que se actualiza a WebSocket o SPDY. El proxy las envía por un transporte HTTP/1.1 dedicado, porque el API server no hace esa actualización sobre HTTP/2. El resto de peticiones van por HTTP/2. watch y logs -f se reenvían sin búfer, byte a byte.
La fila de auditoría de un stream se abre al empezar (101 para las conexiones actualizadas) y se completa al cerrarse, con su duración real.
Revocar corta lo que ya está abierto¶
Revocar una credencial rechaza la siguiente petición con 401. Pero un exec o un watch abiertos no hacen peticiones nuevas. Por eso cada réplica lleva un registro de sus peticiones en curso y cada 10 s revalida cada una:
- ¿Sigue la credencial sin revocar y sin caducar?
- ¿Sigue la cuenta habilitada?
- ¿Siguen siendo exactamente los mismos grupos? Añadir o quitar un permiso en ese cluster también corta el stream, y el cliente se reconecta con los grupos nuevos.
Lo que ya no vale se corta en 10 s como máximo. Si el corte llega antes de que el API server responda, el cliente recibe un 403; si el stream ya estaba abierto, se cierra la conexión. La fila de auditoría queda con el error «stream cortado: la credencial ya no es válida».
Si Postgres falla durante esa revalidación, los streams abiertos siguen abiertos. Las peticiones nuevas sí fallan cerradas.
Qué queda registrado cuando algo falla¶
| Rechazo | ¿Deja fila de auditoría? |
|---|---|
400 (ruta, cluster no válido, Impersonate-*) |
No |
401 (token ausente, desconocido, revocado o caducado) |
Sí, sin sujeto, con el prefijo visible del token |
403 (cuenta deshabilitada, otro cluster, sin permisos), 404 (cluster desconocido), 503 (cluster sin tokens) |
Sí, con credencial y sujeto |
503 porque kubelatch no puede usar Postgres |
No: no hay dónde escribirla |
Las filas de rechazo se limitan a una por IP cada 10 s, para que nadie llene la tabla probando tokens. La respuesta al cliente no cambia por ese límite. La lista completa de códigos está en Errores.