Truke KF no incorpora código propio de SAML, LDAP, Kerberos ni MFA. Cuando tu organización se autentica con alguno de esos protocolos, colocas un proxy inverso delante de KF: el proxy termina el protocolo corporativo, verifica al usuario y pasa la identidad verificada a KF como un pequeño conjunto de cabeceras HTTP. Este es el canal de cabeceras que se presenta en la página de autenticación; esta página es la referencia para operaciones —configuración lista para copiar— de los tres proxies que suelen anteponerse a KF: nginx, Caddy y Apache httpd.
En cada petición hacia KF, el proxy debe hacer cuatro cosas:
X-Auth-Secret).La cabecera de secreto es la frontera de seguridad, no la eliminación. KF
rechaza toda petición cuyo X-Auth-Secret falte o sea incorrecto (devolviendo
403), de modo que un cliente que falsifique X-Authenticated-User sigue sin poder
entrar: no conoce el secreto. La eliminación del paso 4 es defensa en profundidad.
Esa separación importa porque la eliminación es fácil de hacer mal. El
forward_auth + copy_headers de Caddy, por ejemplo, no elimina una cabecera
de identidad suministrada por el cliente —la cabecera solo se sobrescribe cuando el
servicio de autenticación la devuelve, así que un claim ausente deja pasar el valor
del cliente (CVE-2026-30851, versiones estables desde Caddy v2.10.0). Las
configuraciones de abajo eliminan explícitamente y nunca confían en el flujo de
autenticación para hacerlo.
127.0.0.1:8080) —o a una interfaz privada
protegida por cortafuegos de modo que el proxy sea la única vía de acceso. Si KF
es alcanzable directamente, el canal de cabeceras es falsificable por muy bien que
esté configurado el proxy.openssl rand -hex 32 # expórtalo como KF_HEADER_SECRET en ambos lados
KF lo lee mediante shared_secret_env KF_HEADER_SECRET en su sección [auth]; el
proxy inyecta ese valor como X-Auth-Secret.
El proxy inyecta cinco cabeceras. Cuatro llevan la identidad verificada; la quinta es el secreto compartido:
| Cabecera inyectada | Claim de origen (ejemplo OIDC) | Se convierte en KF en |
|---|---|---|
X-Authenticated-User | preferred_username | el uid |
X-Authenticated-Email | email | el correo |
X-Authenticated-Name | name | el nombre visible |
X-Authenticated-Groups | groups | etiquetas ACL (solo si el mapa de grupos está activo) |
X-Auth-Secret | el secreto compartido (constante) | — |
X-Authenticated-User lleva el valor del que KF deriva el uid —mantenlo estable y
sin espacios. Los nombres de cabecera de arriba son los predeterminados; se
configuran en el bloque header de la sección [auth] de KF (user_header,
email_header, name_header, groups_header, secret_header).
Se combina con oauth2-proxy (OIDC / OAuth2) en 127.0.0.1:4180. Para SAML,
antepón a nginx una pasarela con soporte SAML (Authelia / Keycloak) mediante el
mismo patrón auth_request, o usa Apache en su lugar —nginx open-source y
oauth2-proxy no hablan SAML.
Mecanismo de eliminación: en nginx, proxy_set_header sobrescribe la cabecera
del cliente hacia el upstream, así que fijar explícitamente cada cabecera de
confianza es en sí mismo la eliminación. Una cabecera con valor vacío se omite,
nunca se toma del cliente.
# /etc/nginx/conf.d/kf.conf
# KF está en 127.0.0.1:8080 y protegido por cortafuegos; nginx es la única vía.
server {
listen 443 ssl;
http2 on;
server_name kf.example.com;
ssl_certificate /etc/ssl/kf/fullchain.pem;
ssl_certificate_key /etc/ssl/kf/privkey.pem;
# Endpoints de oauth2-proxy
location /oauth2/ {
proxy_pass http://127.0.0.1:4180;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
}
location = /oauth2/auth {
internal;
proxy_pass http://127.0.0.1:4180;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
}
location / {
auth_request /oauth2/auth;
error_page 401 = @signin;
# Captura la identidad verificada de oauth2-proxy
# (ejecuta oauth2-proxy con --set-xauthrequest).
auth_request_set $kf_user $upstream_http_x_auth_request_preferred_username;
auth_request_set $kf_email $upstream_http_x_auth_request_email;
auth_request_set $kf_name $upstream_http_x_auth_request_user;
auth_request_set $kf_groups $upstream_http_x_auth_request_groups;
# Secreto. Mantenlo fuera de config legible por todos; restringe permisos.
set $kf_secret "REPLACE_WITH_SECRET";
# Inyecta las cabeceras de confianza. Cada set SOBRESCRIBE la copia del cliente.
proxy_set_header X-Authenticated-User $kf_user;
proxy_set_header X-Authenticated-Email $kf_email;
proxy_set_header X-Authenticated-Name $kf_name;
proxy_set_header X-Authenticated-Groups $kf_groups;
proxy_set_header X-Auth-Secret $kf_secret;
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
}
location @signin {
return 302 /oauth2/start?rd=$scheme://$host$request_uri;
}
}
Se combina con oauth2-proxy mediante forward_auth. Para SAML, apunta
forward_auth a una pasarela con soporte SAML (Authelia / Keycloak) y ajusta los
nombres de las cabeceras copiadas.
Mecanismo de eliminación: como no se puede confiar en copy_headers para
eliminar (véase la nota del CVE más arriba), las cabeceras del cliente se borran
primero con request_header -…, dentro de un bloque route que fija el orden
de ejecución (eliminar → autenticar → proxy).
# Caddyfile
# KF está en 127.0.0.1:8080 y protegido por cortafuegos; Caddy es la única vía.
# Ejecuta Caddy con KF_HEADER_SECRET definido en el entorno.
kf.example.com {
route {
# 1. Elimina incondicionalmente las cabeceras de confianza del cliente.
request_header -X-Authenticated-User
request_header -X-Authenticated-Email
request_header -X-Authenticated-Name
request_header -X-Authenticated-Groups
request_header -X-Auth-Secret
# 2. Autentica y copia la identidad verificada sobre la petición limpia.
forward_auth 127.0.0.1:4180 {
uri /oauth2/auth
copy_headers X-Auth-Request-Preferred-Username>X-Authenticated-User \
X-Auth-Request-Email>X-Authenticated-Email \
X-Auth-Request-User>X-Authenticated-Name \
X-Auth-Request-Groups>X-Authenticated-Groups
}
# 3. Inyecta el secreto (sobrescribe) y reenvía a KF.
reverse_proxy 127.0.0.1:8080 {
header_up X-Auth-Secret {env.KF_HEADER_SECRET}
}
}
}
Ejecuta oauth2-proxy con --set-xauthrequest para que las cabeceras de identidad
estén siempre presentes en las peticiones autenticadas. Como la eliminación va
primero y detrás está la puerta del secreto, un valor de relleno o un claim ausente
falla de forma cerrada en KF en vez de filtrar un valor del cliente.
Se combina con mod_auth_openidc (OIDC). Para SAML, cambia a
mod_auth_mellon —sus atributos aparecen como variables de entorno MELLON_,
así que solo cambian las líneas RequestHeader set … %{MELLON_}e.
Mecanismo de eliminación: RequestHeader unset borra la cabecera del cliente;
RequestHeader set la sobrescribe. Los claims se exponen como variables de entorno
y se leen con %{VAR}e.
# KF está en 127.0.0.1:8080 y protegido por cortafuegos; Apache es la única vía. # Módulos: mod_proxy mod_proxy_http mod_headers mod_auth_openidc # Haz visible el secreto del sistema para Apache: PassEnv KF_HEADER_SECRETServerName kf.example.com SSLEngine on SSLCertificateFile /etc/ssl/kf/fullchain.pem SSLCertificateKeyFile /etc/ssl/kf/privkey.pem OIDCProviderMetadataURL https://idp.example.com/.well-known/openid-configuration OIDCClientID kf-app OIDCClientSecret "REPLACE" OIDCRedirectURI https://kf.example.com/oauth2callback OIDCCryptoPassphrase "REPLACE" OIDCPassClaimsAs environment # claims como variables OIDC_CLAIM_* AuthType openid-connect Require valid-user # 1. Elimina las cabeceras de confianza del cliente. RequestHeader unset X-Authenticated-User RequestHeader unset X-Authenticated-Email RequestHeader unset X-Authenticated-Name RequestHeader unset X-Authenticated-Groups RequestHeader unset X-Auth-Secret # 2. Inyecta la identidad verificada (set = sobrescribir). RequestHeader set X-Authenticated-User "%{OIDC_CLAIM_preferred_username}e" RequestHeader set X-Authenticated-Email "%{OIDC_CLAIM_email}e" RequestHeader set X-Authenticated-Name "%{OIDC_CLAIM_name}e" RequestHeader set X-Authenticated-Groups "%{OIDC_CLAIM_groups}e" # 3. Inyecta el secreto. RequestHeader set X-Auth-Secret "%{KF_HEADER_SECRET}e" ProxyPass http://127.0.0.1:8080/ ProxyPassReverse http://127.0.0.1:8080/
Si una variable de entorno de claim no está definida cuando se ejecuta
RequestHeader, usa OIDCPassClaimsAs both y confirma que la cabecera llega; la
lectura de la variable de entorno es la vía más limpia cuando funciona.
Ejecuta estas comprobaciones contra un despliegue en vivo —prueban la frontera, no el caso feliz:
# A. Identidad falsificada, sin secreto -> nunca autenticado como el usuario falso. # Se espera una redirección a login o 403; nunca una sesión para 'attacker'. curl -is https://kf.example.com/ -H 'X-Authenticated-User: attacker' | head -n1 # B. Identidad falsificada Y un secreto incorrecto -> 403. curl -is https://kf.example.com/ \ -H 'X-Authenticated-User: attacker' \ -H 'X-Auth-Secret: wrong' | head -n1 # C. Acceso directo saltándose el proxy -> rechazado (KF enlaza a loopback). # Desde cualquier host que no sea el de KF: curl -is http://KF_HOST:8080/ -H 'X-Authenticated-User: attacker'
Criterios de aprobado: A y B nunca producen una sesión para attacker, B
devuelve 403 y C no puede conectar. Si C conecta desde otro host, KF no está
enlazado a loopback y el canal es inseguro —corrige el enlace antes que nada.
127.0.0.1 (o privado + cortafuegos); no alcanzable directamente.openssl rand -hex 32, idéntico en ambos lados y
almacenado fuera de config legible por todos.X-Auth-Secret se eliminan y luego se fijan.X-Authenticated-User lleva un valor estable y sin espacios.--set-xauthrequest para oauth2-proxy; OIDCPassClaimsAs
para mod_auth_openidc).header de [auth] en KF: enabled true, secret_header X-Auth-Secret,
shared_secret_env KF_HEADER_SECRET, trusted_proxy definido y una política
jit elegida.Esta página cubre solo el canal de cabeceras de confianza. El inicio de sesión único nativo (OIDC sin proxy) y el acceso por token para la API REST y el endpoint MCP no necesitan nada de esto —véase autenticación y acceso.