Jak zalogować się do Grafany przez Authelię jako admin?
Ostatnio hobbystycznie wracam do zabawy w selfhosting — jeden z tych okresowych nawrotów, gdzie na nowo można rozwiązać stare problemy, testując różne podejścia do rzeczy, które kiedyś już “rozwiązałem”1. Tym razem przyszła kolej na monitoring.
Zdziwiło mnie, że zaraz po skonfigurowaniu OIDC nie mogłem dodać żadnego dashboardu w Grafanie — logowanie działało, a mimo to brakowało uprawnień.
Setup
Grafana stała już za Authelią (self-hosted SSO, jeden użytkownik, w grupie admins) przez natywne OIDC2 (auth.generic_oauth). Zadanie wydawało się trywialne: każdy z grupy admins ma dostawać rolę GrafanaAdmin w Grafanie automatycznie przy logowaniu, zamiast nadać ręcznie w UI — i tak zostałoby nadpisane przy każdym logowaniu, bo allow_sign_up: true re-provisionuje usera za każdym razem.
Grafana
Standardowa, udokumentowana ścieżka: scope groups na kliencie OIDC + role_attribute_path (wyrażenie JMESPath3) po stronie Grafany.
Grafanę stawiam przez Ansible, rolą grafana.grafana.grafana. Najważniejszy fragment configu to zmienna grafana_ini_oauth:
grafana_ini_oauth:
"auth.generic_oauth":
enabled: true
name: Authelia
client_id: grafana
client_secret: "{{ grafana_authelia_oidc_secret | default('') }}"
scopes: "openid profile email groups"
auth_url: "https://authelia.example.com/api/oidc/authorization"
token_url: "https://authelia.example.com/api/oidc/token"
api_url: "https://authelia.example.com/api/oidc/userinfo"
use_pkce: true
allow_sign_up: true
role_attribute_path: "contains(groups[*], 'admins') && 'GrafanaAdmin' || 'Viewer'"
Admin user jest jedynym członkiem grupy admins w Authelii — stąd contains(groups[*], 'admins') w wyrażeniu wyżej. Warunek konieczny, żeby to zadziałało: scope groups musi być dodany do klienta OIDC Grafany, tak jak w configu powyżej.
Co miało się wydarzyć
Dokumentacja role_attribute_path w Grafanie opisuje trójstopniowy fallback: najpierw id_token, jeśli nic nie znajdzie — userinfo (api_url), jeśli nadal nic — access_token. Brzmi jak solidna redundancja.
Co się wydarzyło w praktyce
W Grafanie 13.2.1 fallback do userinfo jest zepsuty — potwierdzone przez grafana/grafana#106686. Jeśli claima nie ma w id_token, Grafana nie sprawdza userinfo tak, jak deklaruje dokumentacja.
Tymczasem Authelia od wersji 4.39 przestała domyślnie wrzucać claimy ze scope’ów (np. groups) do id_token — trafiają tylko do userinfo. To sensowne zaostrzenie bezpieczeństwa samo w sobie (mniej PII w tokenie, który trafia do różnych stron), ale w zderzeniu ze zepsutym fallbackiem Grafany daje cichy blackout: dokładnie ten claim, który ma znaczenie dla roli, nigdzie nie dociera.4
Fix po stronie Authelii
identity_providers.oidc.claims_policies po stronie Authelii — jawnie wymusza konkretne claimy z powrotem do id_token per-klient, referencjonowane z klienta przez claims_policy: <nazwa>.
identity_providers:
oidc:
# ...
claims_policies:
grafana: # nazwa polityki, dowolna, referencjonowana niżej
id_token:
- 'groups' # wymuś ten claim do id_tokenu
clients:
- client_id: 'inny'
# brak claims_policy — nie dostaje tego traktowania
- client_id: 'grafana'
client_secret: '...'
claims_policy: 'grafana' # podłączenie polityki do klienta
redirect_uris:
- 'https://grafana.example.com/login/generic_oauth'
scopes:
- 'openid'
- 'profile'
- 'email'
- 'groups' # musi być zażądany, inaczej nic do wymuszenia
Z tymi ustawieniami ponownie można logować się do Grafany jako admin — kolejny stary problem odhaczony, na nowo i inaczej niż ostatnim razem.
I czy, mając możliwość dodania dashboardu, dodałem go?

Przypisy
-
RPi-Monitor, pamiętamy ↩
-
OIDC (OpenID Connect) — protokół logowania (SSO) zbudowany na OAuth2. ↩
-
JMESPath — język zapytań do wyciągania i przekształcania danych z dokumentów JSON, tu używany do wyciągnięcia grup z claimów tokenu. ↩
-
Kto się naszukał, jak dodać nowy dashboard, żeby po dłuższej chwili zorientować się, że ma tylko Viewera? Autor. ↩
