En este post, vamos a hablar de cómo añadir una capa de seguridad basada en el protocolo de autenticación OIDC, para los servicios que se sirvan con Traefik, utilizando Keycloak como servicio de autenticación. Con esto, nos evitamos los famosos basic_auths
. Que muchas veces son ingestionables.
Keycloak es un servicio de autenticación de RedHat que tiene capacidad de gestionar varios identity providers e incluso federar servicios como Kerberos o LDAP. Además de esto, permite una conexión bastante sencilla mediante varios protocolos como OpenID o SAML entre otros. Además de Keycloak, usaremos Traefik (en la versión 1.7) y un proxy desarrollado en Go.
Arquitectura
Vamos a explicar un poco la arquitectura. Antes de nada, explicar que la magia de esto, reside en el parámetro auth forward
de Traefik. Esto hace que cuando una petición llegua a un servicio, la reenvíe al servicio de autenticación indicado. En este caso, será el Auth Proxy.
Como ejemplo, vamos a utilizar un servicio web y la petición se hará contra www.example.com
. Esto entrará en el servicio de Traefik como no autenticado la primera vez. Al llegar al servicio web
, lo mandará al Auth Forward
definido, que será el Auth Proxy
. Este, al ver que no está autenticado, forzará al usuario a autenticarse contra Keycloak. Una vez autenticado, Keycloak reenviará la petición al dominio www.example.com
volviendo a iniciar el flujo, pero esta vez autenticado pasándole las cookies de sesión al Auth Proxy
gracias al parámetro X-Forwarder
. De este modo la petición se validará y se permitirá.
Deploy
Una vez explicado el flujo, vamos a ver como sería el deploy. No voy a explicar como se desplegará Keycloak. Para esto, dejo el docker-compose.yaml. Los pasos están en el README. Recordad que hay que fijar las variables antes de levantar el compose.
Keycloak
En Keycloak, tenemos que definir la configuración que queramos para esta aplicación. Lo primero, es conectarse al panel de administración. La URL es la que hemos definido en la variable KEYCLOAK_URL
a la hora de desplegarlo. Al igual que las credenciales.
Una vez autenticados, podemos utilizar el REALM master
o crearnos uno nuevo. En este caso, crearemos el REALM test
.
A continuación, vamos a crear el cliente que necesitemos, para esto, vamos a la parte de client
y le damos a create
Ahora hay que definir el cliente con el protocolo openid-connect
En la pestaña de Settings
, configurar el Access Type
a confidential
y añadir en Valid Redirect URIs
las URLs que vayamos a utilizar. Una vez hecho esto, darle a Save
.
Al darle a Save
, se creará una pestaña nueva que es Credentials
. Aquí estará el secreto que vamos a utilizar luego para autenticar el Auth Proxy contra Keycloak.
Auth Proxy
Como hemos dicho antes, el proxy que vamos a desplegar va a ser uno desarrollado en Go. Para el despliegue, también nos vamos a apoyar en un docker-compose.yaml
.
--- version: '3.7' services: auth-proxy: container_name: auth-proxy image: funkypenguin/traefik-forward-auth environment: - CLIENT_ID=${KEYCLOAK_CLIENT_ID} - CLIENT_SECRET=${KEYCLOAK_CLIENT_SECRET} - OIDC_ISSUER=https://${KEYCLOAK_URL}/auth/realms/${KEYCLOAK_REALM} - SECRET=${OID_SESSION_SECRET} - AUTH_HOST=${AUTH_URL} - COOKIE_DOMAIN=${COOKIE_DOMAIN} - COOKIE_SECURE=${OID_SESSION_CHECK_SSI:-false} networks: - traefik labels: - "traefik.enable=true" - "traefik.port=4181" - "traefik.frontend.rule=Host:${AUTH_URL}" - "traefik.frontend.auth.forward.address=http://auth-proxy:4181" - "traefik.frontend.auth.forward.trustForwardHeader=true" networks: traefik: external: name: traefik
Una vez creado el docker-compose.yaml
vamos a generar el fichero con las variables. Este se llamará .env
.
Para generar el OID_SESSION_SECRET
se puede utilizar el siguiente comando pwgen -n 32
KEYCLOAK_CLIENT_ID=auth KEYCLOAK_CLIENT_SECRET=d557c398-b08e-4024-8fe4-4ca46cd9ed56 KEYCLOAK_URL=keycloak.example.com KEYCLOAK_REALM=test OID_SESSION_SECRET=ju4eekimah0tuena4chaid0hiethu5Ie COOKIE_DOMAIN=example.com AUTH_URL=auth.example.com
Servicio Web
Por último, vamos a crear el docker-compose.yaml
del servicio que vamos a desplegar. En este caso será un Nginx.
--- version: '3.7' services: nginx container_name: web-service image: nginx restart: unless-stopped environment: - TZ=${TZ:-Europe/Madrid} networks: - traefik labels: - "traefik.enable=true" - "traefik.backend=web-service" - "traefik.frontend.rule=Host:${WEB_SERVICE_URL}" - "traefik.port=80" - "traefik.frontend.auth.forward.address=http://auth-proxy:4181" - "traefik.frontend.auth.forward.authResponseHeaders=X-Forwarded-User" - "traefik.frontend.auth.forward.trustForwardHeader=true" networks: traefik: external: name: traefik
Para levantar el Docker-compose, al igual que antes, hay que definir las variables en el .env
. En este caso solo hay que definir una.
WEB_SERVICE_URL=www.example.com
Pruebas
Con todo esto levantado, ya debería de funcionar. Para probarlo, vamos al navegador y metemos la URL https://www.example.com
. Y nos debería de saltar la ventana de login de Keycloak. Para los usuarios, se pueden usar o los que se creen localmente en Keycloak o si se ha configurado previamente algún Identity Provider
en el REALM.
Y una vez autenticados, nos redirigirá otra vez a https://www.example.com
de forma automática.
¿No ha sido tan difícil verdad? Ahora cada vez que añadamos un servicio con las siguientes etiquetas, aplicará este flujo. Hay que acordarse también en darlo de alta en Redirect URIs
labels: - "traefik.frontend.auth.forward.address=http://auth-proxy:4181" - "traefik.frontend.auth.forward.authResponseHeaders=X-Forwarded-User" - "traefik.frontend.auth.forward.trustForwardHeader=true"
Mas info:
https://geek-cookbook.funkypenguin.co.nz/ha-docker-swarm/traefik-forward-auth/keycloak/
https://github.com/ibuetler/docker-keycloak-traefik-workshop
Also published on Medium.