Set-OPS-Public/docs/acces-administration.md
Daniel Allaire be8c3917e5 acces d'administration : un tunnel par locataire, declare par lui, borne a lui
plan/acces.yml chez le locataire (cles publiques), reseau/port/instance derives de l'index.
La garde du devis refuse qu'un tunnel de locataire vise autre chose que son supernet : sans
elle, un ecosysteme s'ouvrirait un acces chez un voisin depuis son propre plan.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 16:01:54 -04:00

6.5 KiB

L'accès d'administration — un tunnel nominatif

Pour qui : celui qui administre le site et ses écosystèmes, et celui qui se demande par où un humain entre, avec quelle clé, et ce qu'il peut atteindre une fois entré.

Décidé le 2026-09-17, en réponse à une question : le runner ne devrait-il pas être le rebond SSH des admins ?

Pourquoi pas le runner

Le runner du site détient la clé de la voûte du site et les clés SSH qui configurent toutes les machines. En faire la porte des humains réunirait deux pouvoirs que tout le reste du dépôt sépare :

  • une session humaine compromise (un agent SSH transféré de trop) deviendrait le plan de contrôle — pas seulement un rebond ;
  • il ne doit jamais entrer chez un locataire (charte des responsabilités) ; en faire le passage obligé des admins créerait ce chemin en fait ;
  • il est reconstructible par le code, et c'est sa vertu. Un point d'entrée doit survivre à la reconstruction de ce qu'il sert ;
  • « qui est entré » et « qu'est-ce qui a été déployé » cesseraient de se raconter séparément.

Ce qui a été construit

Un second tunnel WireGuard sur la frontière — l'instance admins, port 51821 — à côté du tunnel site-à-site vers le site pair (instance chezlePro, port 51820), jamais mêlé à lui.

déclaré SITE-<nom>/plan/10-intrants.yml, clé acces_admin_vpn
réconcilié scripts/vpn_admin.py (make vpn-admin-plan, make vpn-admin-appliquer)
réseau 10.37.29.0/24, la frontière en .1
un pair une personne ET un appareil — révoquer l'appareil perdu ne coupe pas les autres
clés seule la clé publique est au plan ; la privée ne quitte jamais l'appareil

Le réseau du tunnel est un réseau d'administration, et tout en dérive : les pare-feux des machines du site (resoudre_flux), le contrat vers les locataires (site_intrants, donc les pare-feux de leurs machines) et les règles de la frontière (devis_opnsense). Rien à recopier — une liste recopiée prend toujours du retard sur celle qu'elle suit.

Ajouter un appareil

python3 scripts/vpn_admin.py pair-nouveau --nom prenom-appareil   # tire la paire de clés
# coller le bloc `pairs:` rendu dans SITE-<nom>/plan/10-intrants.yml
make vpn-admin-plan                                                # lire ce qui changerait
CONFIRMER=true make vpn-admin-appliquer                            # poser le pair
python3 scripts/vpn_admin.py config --nom prenom-appareil          # la config de l'appareil
make flux && make deployer-groupe GROUPE=serveur_durci             # les pare-feux des machines

La clé privée ne s'affiche qu'une fois, au moment où elle est tirée : elle appartient à l'appareil. Perdue, on en tire une autre ; l'ancienne se révoque par etat: absent.

Retirer un accès

etat: absent sur le pair, puis CONFIRMER=true make vpn-admin-appliquer. Le script retire aussi tout pair attaché à notre instance que le plan ne déclare plus : un accès qui survivrait à la décision de le retirer est exactement ce qu'on ne veut pas.

Deux trous que seul l'usage a montrés (2026-09-17, une heure après la pose)

  • Le SSH des machines du site. Le socle déclare son 22 en pair: [flotte, externe], jamais admin : la source dérivée est le site lui-même. L'exploitant y arrivait par rebond sur la frontière — la connexion partait alors du boîtier, que pf laisse sortir sans règle. Par le tunnel, il arrive comme une source extérieure, et plus rien ne l'autorisait. Les locataires, eux, marchaient : leur règle SSH naît de nftables_admin_ssh, qui contient déjà le tunnel.
  • La frontière elle-même. Aucun flux ne la désignait comme destination d'administration : monter le tunnel faisait perdre sa console et son API — donc le moyen même de réparer la règle manquante. La panne se referme sur celui qui la répare : il a fallu remettre l'adresse d'avant pour poser le correctif.

Les deux règles sont désormais émises par devis_opnsense dès que acces_admin_vpn est déclaré (ports_frontiere, par défaut 22 et 443). Une règle par port : OPNsense refuse « 22,443 » dans un champ de port, et aucun flux jusque-là n'en portait deux.

Un tunnel par locataire (2026-09-17)

Chaque écosystème a le sien, et c'est lui qui décide qui entre chez lui. Le site porte la route, jamais la liste des gens — même partage que les zones DNS publiques.

déclaré par le locataire, dans plan/acces.yml (registre acces_admin_vpn)
réseau 10.<index>.29.0/24 — dérivé de son index, hors de ses six zones (.16 à .21)
port 52000 + index — dérivé, donc sans collision : P21 garde déjà l'unicité des index
instance wg<index> — un rang (2, 3, 4…) renommerait les interfaces le jour où un écosystème part
ce qu'il atteint son supernet, et rien d'autre : SSH et consoles web

L'isolement est gardé, pas promis. devis_opnsense.verifier refuse toute règle dont la source est le tunnel d'un locataire et la destination autre chose que son supernet — sinon, un écosystème obtiendrait un accès chez un voisin en ajoutant un pair dans son propre plan. La garde est exécutée par P24, avant toute écriture. Mise en défaut vérifiée.

Le pare-feu de ses machines suit tout seul : resoudre_flux ajoute le réseau du tunnel aux sources d'administration dérivées de son plan, et seulement s'il déclare au moins un pair actif — déclarer un réseau que personne n'emprunte ouvrirait le SSH à un tunnel qui n'existe pas.

python3 scripts/vpn_admin.py pair-nouveau --nom prenom-appareil --locataire OPS-<X>
# coller le bloc rendu dans OPS-<X>/plan/acces.yml
make vpn-admin-plan && CONFIRMER=true make vpn-admin-appliquer
python3 scripts/vpn_admin.py config --nom prenom-appareil --locataire OPS-<X>

Ce que le tunnel donne, et ce qu'il ne donne pas

AllowedIPs est dérivé de la carte : zones du site, fabric, lien de transit et supernets des locataires. Ce que la frontière laisse ensuite passer reste décidé par les flux déclarés — SSH partout, et les consoles d'administration (Grafana, Icinga Web, la forge, le runner).

Le seul port ouvert sur l'Internet par cette voie est le 51821/udp, vers l'adresse publique de la frontière. Tout le reste voyage dans le tunnel.

Ce qui n'est pas fait : l'enregistrement des sessions (un rebond dédié le permettrait, pas un tunnel), et la double authentification — la possession de l'appareil fait foi.