Keycloak: Autenticación por protocolo OIDC para los servicios bajo Traefik

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.

Leave a Reply

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *