Gestión de Accesos en Grafana: Integrando Keycloak vía OpenID Connect

Fecha publicación
Dificultad
Avanzado
#
11
3 more properties
Publicado
tags
IoT, Gateways, logs, métricas, plataforma, Grafana, Alloy, Docker, sistema, Mimir, Loki, MinIO, almacenamiento, AWS, S3, visualización, monitoreo, captura, servidores, integración, DB, BBDD, servers, integration, metrics, systems, logs, keycloak, oauth2, oauth2-client-credentials,OpenID Connect

vídeo local

Descripción

En este post exploraremos en profundidad cómo centralizar y simplificar la gestión de accesos en Grafana mediante la integración con Keycloak utilizando el protocolo OpenID Connect. A través de una arquitectura basada en contenedores, descrita en nuestro fichero docker-compose, se delega por completo el proceso de autenticación a sistemas especializados, lo que permite que Grafana “herede” la información de usuario y el role que debe tener en la sesión de Grafana gestionar internamente la base de datos de usuarios como source of trhuth; o sea, tenemos un S.S.O (Single Sign On) integrado nativamente con Grafana.

Visión General de la Arquitectura

El entorno que proponemos se apoya en una infraestructura compuesta por Grafana, configurado para desactivar su propio login interno, y autenticado a través del método Auth Proxy. El fichero docker-compose.yaml muestra cómo se definen las variables de entorno esenciales en Grafana, entre las cuales destacan:
Esta configuración permite que, en lugar de autenticar al usuario internamente, Grafana reciba la identidad del usuario ya validada y gestionada por componentes externos.

El Papel de Keycloak y OpenID Connect

Keycloak actúa como el centro neurálgico de gestión de identidades en este escenario. Gracias a OpenID Connect, Keycloak se encarga de:

Secuencia del proceso

A continuación se describe la secuencia de 14 pasos del flujo OAuth2 Authorization Code en el que Grafana actúa como cliente OpenID Connect que se comunica directamente con Keycloak:
OAuth2-OAuth2 Authorization Code with Application.drawio.svg
  1. Inicio del Acceso del Usuario: El usuario accede a la URL pública de Grafana a través de su navegador. Al llegar, Grafana detecta que no existe una sesión activa.
  2. Detección de la Falta de Autenticación: Al no contar con una sesión válida (por ejemplo, la cookie de sesión o token de acceso), Grafana inicia el proceso de autenticación mediante OpenID Connect.
  3. Redirección a Keycloak: Grafana genera una redirección hacia el endpoint de autorización de Keycloak. En esta URL se incluyen parámetros esenciales como el client_id, el scope (que puede incluir “openid”, “email”, “profile”, etc.) y la URL de redirección (redirect_uri) donde se espera recibir el código de autorización.
  4. Llegada a la Pantalla de Login de Keycloak: El navegador sigue la redirección y llega a Keycloak, donde se presenta la pantalla de login para que el usuario introduzca sus credenciales.
  5. Ingreso y Validación de Credenciales: El usuario introduce sus datos (nombre de usuario y contraseña) y Keycloak procede a validar las credenciales. En este paso también pueden activarse mecanismos adicionales de seguridad (como autenticación de dos factores).
  6. Emisión del Código de Autorización: Una vez autenticado correctamente, Keycloak genera un código de autorización temporal y redirige el navegador hacia la URL de redirección definida por Grafana, adjuntando dicho código como parámetro.
  7. Recepción del Código en Grafana: Grafana recibe la solicitud con el código de autorización en la URL. Este código es la prueba de que el usuario se ha autenticado correctamente en Keycloak.
  8. Intercambio del Código por Tokens: Con el código en mano, Grafana realiza una solicitud al endpoint de tokens de Keycloak. En esta solicitud se incluyen el client_id, client_secret y el código recibido, para intercambiarlo por un access token (y, opcionalmente, un refresh token).
  9. Validación y Recepción de Tokens: Keycloak verifica la validez del código de autorización y, de ser correcto, devuelve a Grafana el access token junto con otros tokens relevantes. Estos tokens contienen información (claims) sobre el usuario autenticado.
  10. Validación Interna en Grafana: Grafana valida el access token recibido, comprobando su firma y vigencia, y extrae los datos de usuario necesarios (correo, nombre, roles, etc.) según la configuración establecida.
  11. Mapeo de Roles: Utilizando la expresión definida en la variable de entorno GF_AUTH_GENERIC_OAUTH_ROLE_ATTRIBUTE_PATH, Grafana interpreta los roles contenidos en el token (por ejemplo, asignando “Admin”, “Editor” o “Viewer” según corresponda).
  12. Establecimiento de la Sesión de Usuario: Con los datos validados, Grafana crea una sesión interna para el usuario y establece las cookies correspondientes, de modo que las solicitudes futuras se consideren autenticadas sin necesidad de repetir el proceso.
  13. Redirección Final al Dashboard: Una vez establecida la sesión, Grafana redirige al usuario a su página de inicio o dashboard, permitiéndole acceder a las funcionalidades de forma segura y sin interrupciones.
  14. Mantenimiento de la Sesión y Acceso Continuado: Durante la navegación, Grafana utiliza la sesión establecida (y, en caso de expirar, puede utilizar el refresh token) para mantener la autenticación del usuario. Así, el usuario disfruta de un acceso continuo sin tener que reingresar sus credenciales en cada petición.
Esta secuencia de 14 pasos ilustra cómo Grafana, al utilizar su cliente OpenID Connect, se integra directamente con Keycloak para gestionar la autenticación, delegando en el proveedor de identidad toda la complejidad del proceso y estableciendo una experiencia de usuario fluida y segura.

Configuración

Keycloak

image.png

1. Creación del Client

image.png
image.png
image.png
image.png
Las credenciales de acceso: client-id lo hemos introducido al principio del proceso en nuestro caso: grafana y el client-password se puede encontrar aquí:
image.png

2. Creación de los roles

Hay que crear los tres roles que tiene Grafana: admin, editor y viewer.
image.png

3. Role mapper:

Queremos que cuando se devuelva la información del usuario durante la emisión del Access Token se envie también la información de los roles que hemos creado.
image.png
image.png
image.png

4. Creación de usuario y asignación del role

image.png
image.png
image.png
image.png
image.png
Hay que escoger alguno de los tres roles que pone grafana: admin, editor o viewer. el otro role (uma_protection) esta solución no nos aporta nada. Si se escoge más de un role es muy probable que no funcione.
image.png
Hasta aquí la configuración de Keycloak. Con esto debería ser suficiente para que podamos autenticarnos con el usuario username01 en el proceso de login al que nos desafiará Grafana cuando intentemos acceder a él.
Evidentemente es muy recomendable crear un usuario con permisos de administrador (admin) para que pueda gestionar los items de Grafana.

Grafana

En este fichoer de docker compose que se adjunta hay toda la configuración que le hace falta a Grafana para poder conectarse a Keycloak y delegar la autenticación de los usuarios y su role.

docker-compose.yaml

Bash
name: grafana_i40sys

x-logging:
  &default-logging
  driver: "json-file"
  options:
    max-size: "10m"
    max-file: "1"

services:
  grafana:
    image: grafana/grafana:latest
    restart: unless-stopped
    environment:
      - GF_LOG_LEVEL=warn
      # [server]
      - GF_SERVER_ROOT_URL=https://grafana.i40sys.com/
      
      # [dataproxy]
      - GF_DATAPROXY_SEND_USER_HEADER=true

      # [users]
      - GF_USERS_ALLOW_SIGN_UP=false
      - GF_USERS_AUTO_ASSIGN_ORG=true
      - GF_USERS_AUTO_ASSIGN_ORG_ID=1
      - GF_USERS_AUTO_ASSIGN_ORG_ROLE=Admin

      # [auth]
      - GF_AUTH_DISABLE_LOGIN_FORM=true
      - GF_AUTH_SIGNOUT_REDIRECT_URL=https://grafana.i40sys.com/oauth2/sign_out?rd=https://grafana.i40sys.com

      # [auth.anonymous]
      - GF_AUTH_ANONYMOUS_ENABLED=false

      # [auth.generic_oauth]
      - GF_AUTH_GENERIC_OAUTH_ENABLED=true
      - GF_AUTH_GENERIC_OAUTH_AUTO_LOGIN=true
      - GF_AUTH_GENERIC_OAUTH_NAME=Keycloak-OAuth
      - GF_AUTH_GENERIC_OAUTH_ALLOW_SIGN_UP=true
      - GF_AUTH_GENERIC_OAUTH_CLIENT_ID=grafana
      - GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET=UsaoxkLbC1h6tfzxdMrM6ZjfV24ueKtq
      - GF_AUTH_GENERIC_OAUTH_SCOPES="openid email profile offline_access roles"
      - GF_AUTH_GENERIC_OAUTH_EMAIL_ATTRIBUTE_PATH=email
      - GF_AUTH_GENERIC_OAUTH_LOGIN_ATTRIBUTE_PATH=username
      - GF_AUTH_GENERIC_OAUTH_NAME_ATTRIBUTE_PATH=full_name
      - GF_AUTH_GENERIC_OAUTH_AUTH_URL=https://iam.i40sys.com/realms/observability/protocol/openid-connect/auth
      - GF_AUTH_GENERIC_OAUTH_TOKEN_URL=https://iam.i40sys.com/realms/observability/protocol/openid-connect/token
      - GF_AUTH_GENERIC_OAUTH_API_URL=https://iam.i40sys.com/realms/observability/protocol/openid-connect/userinfo
      - GF_AUTH_GENERIC_OAUTH_ROLE_ATTRIBUTE_PATH=contains(resource_access.grafana.roles[*], 'admin') && 'Admin' || contains(resource_access.grafana.roles[*], 'editor') && 'Editor' || (contains(resource_access.grafana.roles[*], 'viewer') && 'Viewer')
      - GF_AUTH_GENERIC_OAUTH_SIGNOUT_REDIRECT_URL=https://iam.i40sys.com/realms/observability/protocol/openid-connect/logout?post_logout_redirect_uri=https%3A%2F%2Fgrafana.i40sys.com%2Flogin
      - GF_AUTH_GENERIC_OAUTH_ALLOW_ASSIGN_GRAFANA_ADMIN=true
      - GF_AUTH_GENERIC_OAUTH_USE_REFRESH_TOKEN=true
      - GF_AUTH_GENERIC_OAUTH_ROLE_ATTRIBUTE_STRICT=true
    volumes:
      - /etc/localtime:/etc/localtime:ro
    networks:
      - services
    logging: *default-logging

networks:
  services:
    external: true
    name: services

Referencias