Sincronizzazione automatica dei gruppi authentik verso Grafana OSS
Repo
https://gitlab.eagleprojects.cloud/devsecops/sync_team_from_authentik_to_grafana
Scopo
Questo servizio sincronizza automaticamente i gruppi presenti in authentik con i Team di Grafana OSS.
La regola di sincronizzazione Γ¨ diretta:
Gruppo authentik β Team Grafana con lo stesso nome
Per ogni gruppo authentik:
- viene creato il Team Grafana corrispondente, se non esiste;
- vengono aggiunti al Team gli utenti appartenenti al gruppo;
- vengono rimossi dal Team gli utenti che non appartengono piΓΉ al gruppo;
- gli utenti non ancora presenti in Grafana vengono ignorati fino al loro primo accesso;
- i Team presenti solo in Grafana vengono eliminati esclusivamente quando
DELETE_STALE_TEAMS=true.
La sincronizzazione Γ¨ unidirezionale:
authentik ββββββββββββββββ> Grafana
fonte autorevole destinazione
Le modifiche effettuate manualmente sui Team Grafana possono essere sovrascritte alla sincronizzazione successiva.
Architettura
+--------------------------+
| authentik |
|--------------------------|
| Utenti |
| Gruppi |
| Membership |
| API token |
+------------+-------------+
|
| HTTPS / REST API
| Authorization: Bearer <token>
v
+--------------------------+
| authentik-grafana-sync |
|--------------------------|
| Container Python 3 |
| Lettura gruppi e utenti |
| Confronto membership |
| Creazione Team |
| Aggiornamento membri |
| Eventuale cancellazione |
+------------+-------------+
|
| HTTPS / Grafana HTTP API
| Authorization: Bearer <token>
v
+--------------------------+
| Grafana OSS |
|--------------------------|
| Utenti OAuth esistenti |
| Team |
| Membership dei Team |
| Service Account token |
+--------------------------+
Flusso di autenticazione degli utenti
La sincronizzazione dei Team Γ¨ separata dall'autenticazione interattiva degli utenti.
Utente
|
| 1. Accesso a Grafana
v
Grafana
|
| 2. Redirect OIDC/OAuth
v
authentik
|
| 3. Autenticazione completata
v
Grafana
|
| 4. Creazione/aggiornamento utente Grafana
v
Utente disponibile per la sincronizzazione dei Team
Lo script non crea utenti Grafana.
Un utente authentik puΓ² essere aggiunto a un Team soltanto dopo avere effettuato almeno un accesso a Grafana tramite authentik.
Flusso di sincronizzazione
+----------------------------------------------------+
| 1. Lettura di tutti i gruppi da authentik |
+------------------------------+---------------------+
|
v
+----------------------------------------------------+
| 2. Lettura di tutti gli utenti da authentik |
+------------------------------+---------------------+
|
v
+----------------------------------------------------+
| 3. Lettura organizzazione, utenti e Team Grafana |
+------------------------------+---------------------+
|
v
+----------------------------------------------------+
| 4. Per ogni gruppo authentik |
| |
| Il Team con lo stesso nome esiste? |
| |
| no ββ> creazione Team |
| sΓ¬ ββ> utilizzo Team esistente |
+------------------------------+---------------------+
|
v
+----------------------------------------------------+
| 5. Confronto membership |
| |
| utenti desiderati - utenti presenti = aggiunte |
| utenti presenti - utenti desiderati = rimozioni |
+------------------------------+---------------------+
|
v
+----------------------------------------------------+
| 6. DELETE_STALE_TEAMS=true? |
| |
| no ββ> conserva Team solo Grafana |
| sΓ¬ ββ> elimina Team assenti in authentik |
+------------------------------+---------------------+
|
v
+----------------------------------------------------+
| 7. Scrittura riepilogo nei log |
+----------------------------------------------------+
Regole di sincronizzazione
Nomi dei gruppi
Il nome del Team Grafana coincide con il nome del gruppo authentik.
authentik Grafana
------------------------------------------------
Administrators β Administrators
Developers β Developers
Monitoring β Monitoring
Network Operations β Network Operations
Non vengono applicati:
- prefissi;
- suffissi;
- mapping;
- filtri;
- rinominazioni.
I nomi vengono confrontati senza distinguere maiuscole e minuscole. Due gruppi authentik i cui nomi differiscono soltanto per il case producono un errore, perchΓ© colliderebbero nello stesso Team Grafana.
Esempio non valido:
Developers
developers
Membership
La membership dei Team viene sincronizzata esattamente.
authentik group "Developers"
βββ alice@example.com
βββ bob@example.com
βββ carol@example.com
Grafana team "Developers"
βββ alice@example.com
βββ bob@example.com
βββ carol@example.com
Alla sincronizzazione successiva:
authentik group "Developers"
βββ alice@example.com
βββ carol@example.com
lo script rimuove bob@example.com dal Team Grafana.
Corrispondenza degli utenti
Lo script cerca l'utente Grafana usando:
- indirizzo e-mail;
- username/login come fallback.
Utente authentik
|
+--> e-mail presente in Grafana? ---- sì ---> utente trovato
|
+--> no
|
+--> username presente? ------ sì ---> utente trovato
|
+--> no -----------------------------> utente ignorato
Se l'utente non Γ¨ ancora presente in Grafana, viene prodotto un warning:
User not yet present in Grafana; skipping for team 'Developers'
L'utente verrΓ incluso automaticamente dopo:
- il primo accesso a Grafana tramite authentik;
- la successiva esecuzione dello script.
Utenti esclusi
Non vengono sincronizzati:
- utenti authentik disabilitati;
- service account authentik.
Gruppi annidati
La versione corrente calcola la membership effettiva dei gruppi includendo anche gli utenti appartenenti ai gruppi discendenti.
Esempio:
Engineering
βββ Backend
β βββ Alice
β βββ Bob
βββ Frontend
βββ Carol
βββ David
Risultato:
Team Grafana "Engineering"
βββ Alice
βββ Bob
βββ Carol
βββ David
I Team Backend e Frontend vengono comunque creati e sincronizzati separatamente.
Requisiti
Infrastruttura
- Docker Engine;
- Docker Compose v2;
- connettivitΓ HTTPS dal container verso authentik;
- connettivitΓ HTTPS dal container verso Grafana;
- risoluzione DNS funzionante;
- orario del sistema sincronizzato.
authentik
Sono necessari:
- URL dell'istanza authentik;
- API token;
- permessi di lettura su utenti e gruppi.
Endpoint utilizzati:
GET /api/v3/core/groups/
GET /api/v3/core/users/
Grafana
Sono necessari:
- Grafana OSS;
- autenticazione authentik giΓ funzionante tramite Generic OAuth/OIDC;
- Service Account Grafana;
- Service Account token con ruolo
Adminnell'organizzazione interessata.
Endpoint principali utilizzati:
GET /api/org/
GET /api/org/users
GET /api/teams/search
POST /api/teams
GET /api/teams/{teamId}/members
POST /api/teams/{teamId}/members
DELETE /api/teams/{teamId}/members/{userId}
DELETE /api/teams/{teamId}
Struttura del progetto
authentik-grafana-sync/
βββ authentik_grafana_sync.py
βββ Dockerfile
βββ compose.yaml
βββ docker-entrypoint.sh
βββ requirements.txt
βββ .env.example
βββ .dockerignore
βββ README.md
Configurazione
Creare il file .env:
cp .env.example .env
chmod 600 .env
Configurazione di esempio:
# authentik
AUTHENTIK_URL=https://authentik.example.com
AUTHENTIK_TOKEN=replace-with-authentik-api-token
AUTHENTIK_VERIFY_TLS=true
# Grafana
GRAFANA_URL=https://grafana.example.com
GRAFANA_TOKEN=replace-with-grafana-service-account-token
GRAFANA_VERIFY_TLS=true
# Opzionale: organizzazione Grafana attesa
GRAFANA_ORG_ID=1
# Conserva o elimina i Team presenti solo in Grafana
DELETE_STALE_TEAMS=false
# Timeout HTTP
HTTP_TIMEOUT_SECONDS=30
# ModalitΓ simulazione
DRY_RUN=false
# Esecuzione container
SYNC_INTERVAL_SECONDS=300
RUN_ONCE=false
Variabili di ambiente
| Variabile | Obbligatoria | Default | Descrizione |
|---|---|---|---|
AUTHENTIK_URL |
sΓ¬ | β | URL base di authentik |
AUTHENTIK_TOKEN |
sΓ¬ | β | API token authentik |
AUTHENTIK_VERIFY_TLS |
no | true |
Verifica il certificato TLS di authentik |
GRAFANA_URL |
sΓ¬ | β | URL base di Grafana |
GRAFANA_TOKEN |
sΓ¬ | β | Service Account token Grafana |
GRAFANA_VERIFY_TLS |
no | true |
Verifica il certificato TLS di Grafana |
GRAFANA_ORG_ID |
no | β | Verifica che il token operi sull'organizzazione prevista |
DELETE_STALE_TEAMS |
no | false |
Elimina i Team Grafana assenti in authentik |
HTTP_TIMEOUT_SECONDS |
no | 30 |
Timeout delle richieste HTTP |
DRY_RUN |
no | false |
Simula le modifiche senza scrivere in Grafana |
SYNC_INTERVAL_SECONDS |
no | 300 |
Intervallo tra due sincronizzazioni |
RUN_ONCE |
no | false |
Esegue una sola sincronizzazione e termina |
Creazione del token authentik
Creare un service account o un utente tecnico in authentik con privilegi di lettura su utenti e gruppi.
Generare un API token e inserirlo nel file .env:
AUTHENTIK_TOKEN=ak_xxxxxxxxxxxxxxxxx
Il valore deve contenere soltanto il token, senza il prefisso Bearer.
Corretto:
AUTHENTIK_TOKEN=ak_xxxxxxxxxxxxxxxxx
Errato:
AUTHENTIK_TOKEN=Bearer ak_xxxxxxxxxxxxxxxxx
Creazione del Service Account Grafana
In Grafana:
Administration
βββ Users and access
βββ Service accounts
Creare un Service Account, ad esempio:
authentik-grafana-sync
Assegnare il ruolo:
Admin
Generare un token e salvarlo nel file .env:
GRAFANA_TOKEN=glsa_xxxxxxxxxxxxxxxxx
Anche in questo caso il valore deve contenere soltanto il token.
Deployment con Docker Compose
Build
docker compose build
Avvio
docker compose up -d
Verifica dello stato
docker compose ps
Visualizzazione dei log
docker compose logs -f sync
Arresto
docker compose down
Aggiornamento dello script
Quando viene modificato authentik_grafana_sync.py, occorre ricostruire l'immagine:
docker compose down
docker compose build --no-cache sync
docker compose up -d --force-recreate
Modificare soltanto .env non richiede una nuova build, ma richiede la ricreazione del container:
docker compose up -d --force-recreate
Prima esecuzione
Eseguire sempre inizialmente un dry-run.
docker compose run --rm \
-e RUN_ONCE=true \
-e DRY_RUN=true \
sync
Il dry-run:
- legge authentik;
- legge Grafana;
- calcola le modifiche;
- mostra le operazioni nei log;
- non crea Team;
- non aggiunge o rimuove utenti;
- non elimina Team.
Esempio:
[dry-run] Would create Grafana team 'Developers'
[dry-run] Would add Grafana user ID 42 to team 'Developers'
[dry-run] Would remove old.user from team 'Monitoring'
Dopo avere verificato il risultato:
docker compose run --rm \
-e RUN_ONCE=true \
-e DRY_RUN=false \
sync
Esecuzione periodica
Il container esegue immediatamente una sincronizzazione e poi attende il numero di secondi configurato.
Avvio container
|
v
Sincronizzazione
|
v
Attesa SYNC_INTERVAL_SECONDS
|
v
Sincronizzazione
|
v
...
Intervallo predefinito:
SYNC_INTERVAL_SECONDS=300
Corrisponde a una sincronizzazione ogni cinque minuti.
Esempio ogni quindici minuti:
SYNC_INTERVAL_SECONDS=900
Applicare la modifica:
docker compose up -d --force-recreate
Cancellazione dei Team obsoleti
ModalitΓ conservativa
DELETE_STALE_TEAMS=false
Gruppo authentik: assente
Team Grafana: presente
Risultato: Team conservato
Questa Γ¨ la configurazione consigliata durante l'installazione iniziale.
ModalitΓ autoritativa
DELETE_STALE_TEAMS=true
Gruppo authentik: assente
Team Grafana: presente
Risultato: Team eliminato
Schema decisionale:
Team Grafana
|
v
Esiste un gruppo authentik con lo stesso nome?
|
+---- sì ----> conserva e sincronizza
|
+---- no ----> DELETE_STALE_TEAMS?
|
+---- false ----> conserva
|
+---- true -----> elimina
Attenzione: con
DELETE_STALE_TEAMS=true, authentik diventa la fonte autoritativa per tutti i Team dell'organizzazione Grafana. Anche i Team creati manualmente in Grafana vengono eliminati se non esiste un gruppo authentik con lo stesso nome.
Sicurezza del container
Il container Γ¨ configurato con:
- utente non root;
- filesystem in sola lettura;
-
/tmpmontato cometmpfs; - rimozione di tutte le Linux capabilities;
-
no-new-privileges; - token forniti tramite variabili di ambiente;
- nessun token salvato nell'immagine.
Schema:
Container
βββ USER non-root
βββ read_only: true
βββ cap_drop: ALL
βββ no-new-privileges
βββ /tmp in RAM
Proteggere il file .env:
chmod 600 .env
Non includere .env nel repository Git.
Log operativi
Avvio normale
Starting periodic synchronization every 300 seconds.
2026-07-20T12:57:53Z Starting synchronization.
INFO Reading authentik groups and users
INFO Loaded 8 authentik groups and 501 users
INFO Using Grafana organization 'Main Org.' (ID 1)
Riepilogo finale
Synchronization completed:
teams_created=2
teams_deleted=0
members_added=18
members_removed=3
users_missing=4
| Contatore | Descrizione |
|---|---|
teams_created |
Team Grafana creati |
teams_deleted |
Team Grafana eliminati |
members_added |
Utenti aggiunti ai Team |
members_removed |
Utenti rimossi dai Team |
users_missing |
Utenti authentik non ancora presenti in Grafana |
Troubleshooting
401 Unauthorized da Grafana
Errore:
GET https://grafana.example.com/api/org/ returned HTTP 401
Possibili cause:
- token Grafana errato;
- token scaduto o eliminato;
- valore configurato come
Bearer glsa_...; - container non ricreato dopo la modifica di
.env; - reverse proxy che rimuove l'header
Authorization; - token appartenente a un'altra istanza Grafana.
Verificare il token dal container:
docker compose exec -T sync python3 - <<'PY'
import os
import requests
url = os.environ["GRAFANA_URL"].rstrip("/")
token = os.environ["GRAFANA_TOKEN"].strip()
response = requests.get(
url + "/api/org/",
headers={"Authorization": "Bearer " + token},
timeout=30,
)
print("Status:", response.status_code)
print("Body:", response.text)
PY
Risultato atteso:
Status: 200
Body: {"id":1,"name":"Main Org."}
403 Forbidden da Grafana
Il token Γ¨ valido ma il Service Account non dispone dei permessi necessari.
Assegnare al Service Account il ruolo organizzativo:
Admin
Utente ignorato perchΓ© non presente in Grafana
Warning:
User not yet present in Grafana; skipping for team 'Developers'
Procedura:
- fare accedere l'utente a Grafana tramite authentik;
- attendere la sincronizzazione successiva;
- verificare la membership del Team.
Il container continua a usare la vecchia versione
Verificare il file incluso nell'immagine:
docker compose exec sync \
sha256sum /app/authentik_grafana_sync.py
Ricostruire senza cache:
docker compose down --remove-orphans
docker compose build --no-cache sync
docker compose up -d --force-recreate
Il dry-run rimane attivo
Controllare:
DRY_RUN=false
Verificare le variabili caricate:
docker compose exec sync env | grep DRY_RUN
Ricreare il container:
docker compose up -d --force-recreate
Certificato TLS non valido
Errore tipico:
certificate verify failed
Correggere preferibilmente la catena del certificato.
Solo per test temporanei:
AUTHENTIK_VERIFY_TLS=false
GRAFANA_VERIFY_TLS=false
Questa configurazione riduce la sicurezza e non Γ¨ consigliata in produzione.
Grafana dietro reverse proxy
Il proxy deve inoltrare l'header:
Authorization: Bearer <token>
Testare, quando possibile, l'accesso diretto a Grafana:
GRAFANA_URL=http://grafana:3000
oppure:
GRAFANA_URL=http://host.docker.internal:3000
Se l'accesso diretto funziona e quello tramite proxy restituisce 401, il problema Γ¨ nella configurazione del reverse proxy.
Organizzazione Grafana errata
Errore:
Grafana API context returned org ID 2, expected 1
Correggere:
GRAFANA_ORG_ID=2
oppure rimuovere GRAFANA_ORG_ID se non serve una verifica esplicita.
Verifiche operative
Verifica quotidiana
docker compose ps
docker compose logs --since=24h sync
Controllare che siano presenti messaggi:
Synchronization completed
e che non siano presenti errori HTTP ripetuti.
Verifica manuale di una membership
- aggiungere un utente a un gruppo authentik;
- attendere il successivo ciclo;
- aprire Grafana;
- verificare il Team con lo stesso nome;
- controllare la presenza dell'utente.
Verifica della rimozione
- rimuovere l'utente dal gruppo authentik;
- attendere il successivo ciclo;
- verificare che sia stato rimosso dal Team Grafana.
Verifica della creazione Team
- creare un nuovo gruppo authentik;
- attendere il successivo ciclo;
- verificare la creazione del Team omonimo in Grafana.
Verifica della cancellazione Team
Da eseguire soltanto con:
DELETE_STALE_TEAMS=true
- creare un Team di test in Grafana;
- non creare il gruppo omonimo in authentik;
- eseguire un dry-run;
- verificare il messaggio:
[dry-run] Would delete Grafana team 'Team di test'
Aggiornamento e rollback
Aggiornamento
docker compose down
docker compose build --no-cache sync
docker compose up -d --force-recreate
docker compose logs -f sync
Rollback
Conservare una copia della versione precedente:
authentik-grafana-sync/
βββ releases/
β βββ 2.0.0/
β βββ 2.0.1/
βββ current/
Per il rollback:
cd releases/2.0.0
docker compose build --no-cache sync
docker compose up -d --force-recreate
Prima di ogni aggiornamento Γ¨ consigliato eseguire la nuova immagine in dry-run.
Limiti noti
- La sincronizzazione Γ¨ associata a una singola organizzazione Grafana.
- Gli utenti Grafana non vengono creati dallo script.
- Un utente deve avere effettuato almeno un accesso OAuth a Grafana.
- I Team Grafana non hanno un identificatore esterno authentik: la corrispondenza avviene tramite il nome.
- Rinominare un gruppo authentik equivale a creare un nuovo Team.
- Con
DELETE_STALE_TEAMS=true, il Team con il vecchio nome viene eliminato. - I ruoli Grafana
Viewer,EditoreAdminnon vengono gestiti da questo script. - La sincronizzazione riguarda esclusivamente Team e membership.
- La modifica manuale delle membership in Grafana viene sovrascritta.
Esempio completo
Stato iniziale authentik
Groups
βββ Administrators
β βββ alice@example.com
βββ Developers
β βββ bob@example.com
β βββ carol@example.com
βββ Monitoring
βββ david@example.com
Stato iniziale Grafana
Teams
βββ Developers
β βββ old.user@example.com
βββ Legacy Team
Sincronizzazione con DELETE_STALE_TEAMS=false
Teams
βββ Administrators
β βββ alice@example.com
βββ Developers
β βββ bob@example.com
β βββ carol@example.com
βββ Monitoring
β βββ david@example.com
βββ Legacy Team
Sincronizzazione con DELETE_STALE_TEAMS=true
Teams
βββ Administrators
β βββ alice@example.com
βββ Developers
β βββ bob@example.com
β βββ carol@example.com
βββ Monitoring
βββ david@example.com
Comandi rapidi
# Build
docker compose build
# Avvio
docker compose up -d
# Log
docker compose logs -f sync
# Dry-run singolo
docker compose run --rm \
-e RUN_ONCE=true \
-e DRY_RUN=true \
sync
# Esecuzione singola reale
docker compose run --rm \
-e RUN_ONCE=true \
-e DRY_RUN=false \
sync
# Ricreazione dopo modifica .env
docker compose up -d --force-recreate
# Rebuild completo dopo modifica dello script
docker compose build --no-cache sync
docker compose up -d --force-recreate
# Arresto
docker compose down
No Comments