Set-OPS-Public/docs/autorisation.md

240 lines
11 KiB
Markdown
Raw Normal View History

# Accès et habilitations : Set-OPS amorce, le sysadmin gouverne
> **Directive d'architecture du 2026-08-07.** L'authentification dit *qui tu es*
> (`authentification.md`). Ce document dit *ce que tu peux faire* — et surtout **qui en
> décide**. La réponse n'est pas « le dépôt ».
## 1. Le constat, mesuré
`ou=groups` est créé par `serveur_openldap` depuis le début. Au 2026-08-07, c'est un
**conteneur vide qu'aucun service ne lit** : pas un `memberOf`, pas un filtre de groupe
dans les 29 rôles du catalogue.
Toute personne qui s'authentifie obtient donc le défaut du service — tout, ou rien. Et
aucun humain n'a de compte : `ou=people` est vide aussi. L'écosystème est déployé et
personne ne peut y entrer autrement que par les comptes de secours en voûte.
## 2. Deux régimes, et la frontière entre eux
C'est la décision principale, et elle **contredit délibérément la doctrine du dépôt**.
Partout ailleurs, un écart entre le déclaré et le réel est un défaut à corriger : les devis
réconcilient, les applicateurs retirent ce qui n'est plus demandé. **Ici, l'écart est
légitime** — c'est le sysadmin qui fait son travail.
| | Qui décide | Régime |
|---|---|---|
| **Infrastructure** — quel groupe accorde quoi dans un service | Set-OPS | réconcilié à chaque déploiement, comme le reste |
| **Habilitations** — qui appartient à quel groupe | **une personne** | **amorcé une fois, jamais réconcilié** |
Le mapping « le groupe `sysadmin` vaut `Admin` dans Grafana » est de la configuration
d'infrastructure : il se déclare, se dérive et se corrige. Savoir **qui** est dans
`sysadmin` ne l'est pas — c'est une décision d'exploitation, prise par un humain, souvent
en urgence, et le dépôt n'a pas à la défaire au prochain `make deployer`.
Un dépôt qui réconcilierait les appartenances effacerait le compte créé la veille pour un
nouvel employé. C'est exactement le genre de « correction » qu'il ne faut pas faire.
## 3. L'amorçage est un one-shot
Set-OPS crée **un** accès : celui du sysadmin, pour qu'il puisse prendre la main. Rien de
plus.
**Idempotence par non-intervention.** La condition n'est pas la conformité, c'est
l'**existence** : si le compte est là, on n'y touche pas — ni son mot de passe, ni ses
groupes, ni ses attributs. Il a pu être renommé, promu, déplacé ; c'est le droit de celui
qui exploite.
```
compte sysadmin absent → créé, mot de passe généré en voûte, changement forcé
compte sysadmin présent → aucune action, quel que soit son état
```
C'est la différence entre `state: present` et une réconciliation. Le second serait un
défaut ici.
**Le premier mot de passe n'est pas *le* mot de passe de quelqu'un.** Généré, déposé en
voûte, **changement forcé à la première ouverture** : c'est un jeton d'amorçage à usage
unique. Sans ce changement, l'auteur du déploiement connaîtrait le mot de passe du
sysadmin — ce qui contredit « une identité, une personne ».
## 4. Où vivent les habilitations : LDAP, pas Keycloak
**Les groupes LDAP sont la source unique.** Keycloak les *projette* en rôles pour le web ;
les services non-OIDC les lisent directement.
L'argument est mécanique, pas doctrinal : **Dovecot et Postfix ne savent pas lire un rôle
Keycloak.** L'y loger rendrait la moitié courriel du système aveugle, et imposerait au
sysadmin de tenir deux modèles de permissions — donc de les voir diverger.
Conséquence pratique pour l'exploitant : **un seul endroit à administrer**. Ajouter
quelqu'un au groupe `sysadmin` dans LDAP lui ouvre Grafana, Forgejo, Icinga et le courriel,
sans toucher à un seul service.
## 5. Un service nomme un groupe, jamais une personne
C'est la règle qui rend la reprise possible. Chaque rôle déclare le **groupe** qu'il
reconnaît, dans son `meta/acces.yml` :
```yaml
acces:
- groupe: sysadmin # groupe LDAP
accorde: Admin # ce que ça vaut DANS ce service
raison: >-
Administration : sources de données, tableaux de bord, utilisateurs.
```
Le champ `accorde` est **le vocabulaire du service**, pas un niveau abstrait : `Admin` chez
Grafana, `owner` chez Forgejo. Inventer une échelle commune obligerait à la traduire
partout, et la traduction est exactement l'endroit où une habilitation se perd.
Un service qui nommerait une personne créerait une dette qu'on découvre le jour du
départ, service par service — et il faudrait un déploiement pour révoquer quelqu'un.
## 6. Prendre la main — le runbook
Ce qui suit est destiné au sysadmin le jour de la livraison. **C'est la partie utile de ce
document.**
### 6.1 Récupérer le mot de passe d'amorçage
```
ansible-vault view instance/inventories/principal/group_vars/all/vault.yml \
| grep vault_sysadmin_amorcage
```
**Tu ne pourras rien faire d'autre avant de l'avoir changé** : l'annuaire refuse toute
opération sauf le changement lui-même (§3). C'est le premier geste de la reprise, et il
n'est pas optionnel.
> Le mot de passe de la voûte est lu depuis `ANSIBLE_VAULT_PASSWORD_FILE`
> (`~/.config/setops-vault-pass` par défaut). **Sans ce fichier, rien de ce qui suit n'est
> possible** — c'est la clé de voûte au sens propre, et la première chose à sauvegarder
> hors de la machine.
### 6.2 Deux consoles Keycloak, et la racine mène à la mauvaise
**C'est le piège de la première connexion.** `https://auth.<domaine>/` redirige vers `/admin/`
— la console d'administration du realm **`master`**. Le compte `sysadmin` n'y existe pas : il
vit dans le realm applicatif. Keycloak répond alors *« invalid username or password »*, ce qui
est exact et parfaitement trompeur — le mot de passe est bon, c'est la porte qui ne l'est pas.
| Ce que tu veux faire | URL | Compte |
|---|---|---|
| **ton compte, tes accès** | `https://auth.<domaine>/realms/<realm>/account/` | `sysadmin` + jeton d'amorçage |
| **administrer ton realm** | `https://auth.<domaine>/admin/<realm>/console/` | `sysadmin` — par le groupe |
| Keycloak lui-même (secours) | `https://auth.<domaine>/admin/` | `admin` + `vault_keycloak_admin` |
La première ligne est celle de la reprise. C'est là que le changement de mot de passe te sera
imposé, et c'est cette connexion qui **importe ton groupe dans le realm** — sans elle, les
habilitations sur Grafana, Forgejo et Nextcloud restent câblées mais jamais exercées.
**Administrer ton realm passe désormais par le groupe** : `realm-admin` (rôle du client
`realm-management`) est attaché à `sysadmin`. La console est celle du realm —
`/admin/<realm>/console/` — pas la racine `/admin/`, qui est celle de `master`.
La dernière ligne reste le **compte de secours** (D-40). Sa portée est `master`, hors d'atteinte
du groupe : c'est voulu. Un accès de secours qui dépendrait des habilitations qu'il doit pouvoir
réparer ne serait pas un accès de secours.
### 6.3 Faire confiance à l'AC interne
Les interfaces web portent des certificats de l'autorité interne, qu'aucun navigateur ne
connaît. Récupérer la racine et **son empreinte** :
```
make ca-racine # écrit ./root_ca.crt, affiche sujet, validité, empreinte
make ca-empreinte # la même empreinte, lue SUR l'AC — le témoin de comparaison
```
**Compare les deux avant d'installer.** Ce n'est pas une formalité : installer une AC, c'est
lui donner le droit de signer *n'importe quel nom* pour ton navigateur. La comparaison est ce
qui distingue ta racine d'une racine interceptée.
```
sudo cp root_ca.crt /usr/local/share/ca-certificates/setops-root.crt
sudo update-ca-certificates
```
Firefox a son propre magasin : *Paramètres → Certificats → Autorités → Importer*.
La racine est un certificat **public** — elle n'a rien à faire dans la voûte, et tout à faire
dans le magasin de confiance de qui administre.
### 6.4 Où administrer quoi
| Ce que tu veux faire | Où | Comment |
|---|---|---|
| créer/désactiver une personne | **LDAP** (`ou=people`) | via Keycloak, ou `ldapmodify` |
| donner ou retirer un accès | **LDAP** (`ou=groups`) | ajouter/retirer du groupe |
| changer ce qu'un groupe *vaut* dans un service | **le plan** (`meta/acces.yml`) | c'est de l'infrastructure : éditer, redéployer |
La troisième ligne est la seule qui passe par Set-OPS. Les deux premières t'appartiennent
et **le dépôt ne les touchera plus jamais**.
### 6.5 Si tu te fermes dehors
Les comptes locaux de chaque service existent, sont en voûte, et leur formulaire n'est pas
annoncé (`authentification.md` §3). L'accès de secours passe par `sudo` sur l'hôte :
```
grafana-cli admin reset-admin-password
forgejo admin user change-password
occ user:resetpassword
```
Et si LDAP lui-même est en panne, ces trois commandes fonctionnent quand même : elles ne
dépendent ni de Keycloak, ni de l'annuaire. C'est le sens de D-40.
### 6.6 Ce que tu dois changer en priorité
1. **Le mot de passe d'amorçage** — imposé dès la première connexion, tu n'as pas le
choix (§3).
2. **Les comptes de secours** générés au déploiement : ils sont en voûte, et l'auteur du
déploiement y a eu accès. Les régénérer transfère réellement le contrôle.
3. **Le mot de passe de la voûte** elle-même, si le dépôt change de mains.
### 6.7 Faire tourner un secret : l'ordre n'est pas indifférent
**Les comptes de secours des services** (`vault_grafana_admin`, `vault_forgejo_admin`,
`vault_nextcloud_admin`) se régénèrent en voûte, puis un redéploiement les applique. Les rôles
savent désormais *changer* un mot de passe existant, pas seulement le créer.
**Le compte d'administration de Keycloak est différent : il est le moyen de se changer
lui-même.** Régénérer la voûte d'abord le rendrait inapplicable — plus rien ne pourrait
s'authentifier pour poser la nouvelle valeur. L'ordre est donc inversé :
```
1. s'authentifier avec la valeur ACTUELLE
2. poser la nouvelle valeur dans Keycloak
3. vérifier que la nouvelle fonctionne
4. seulement alors, écrire la voûte
```
C'est une **procédure**, pas un redéploiement. La même contrainte vaut pour tout secret qui
est aussi la clé de son propre changement — `vault_openldap_admin` en est un.
**Et une vérification n'est pas un message de succès.** `grafana-cli` annonçait
« Admin password changed successfully » en écrivant dans une base qui n'était pas celle du
serveur. Ce qui l'a démasqué est l'**état** — le champ `updated` du compte, inchangé. Après
toute rotation, s'authentifier réellement avec la nouvelle valeur.
## 7. Ce que ce document ne couvre pas
**La gestion courante des comptes.** Créer, suspendre, réaffecter : c'est le travail du
sysadmin, pas du dépôt. Set-OPS ne fournit ni registre de personnes, ni synchronisation —
volontairement. Un registre versionné mettrait des données personnelles dans l'historique
git, de façon permanente et difficile à retirer.
**L'autorisation machine-à-machine.** Les comptes de service (PostgreSQL, Redis, jetons
d'API) ne sont pas des humains et ne passent pas par LDAP — ils vivent en voûte, liés à un
service et non à une personne.
**Les permissions internes à un service.** Qui peut écrire dans quel dépôt Forgejo, qui voit
quel dossier Nextcloud : ça se règle dans le service, à partir du groupe qu'il a reçu.
Set-OPS accorde l'entrée et le niveau ; il ne réimplémente pas le modèle de chaque
application.
**Rien n'est construit.** Ce document fixe la direction ; le rôle d'amorçage, les
`meta/acces.yml` et la preuve restent à écrire.