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>
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], jamaisadmin: 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 denftables_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.