Publication auto des conteneurs #41

Open
opened 2026-09-24 05:03:23 +00:00 by romain · 2 comments
Owner

Autopublication des conteneurs à la demande à la mode Traefik

"Cette partie va être très dense"

Comme le Nginx Control acceder au socket Docker on devrait pouvoir le faire.

comme toujours on garde le mecanisme de test avant recharge de nginx

Partie 1 : depuis le meme hote que Nginx Control

A la manière de Traefik on se base sur les labels Docker pour générer de façon dynamique les fichiers de virtualhost.

  • nginx-control.auto_config=enable
  • nginx-control.network=
  • nginx-control.vhost.server_name=example.com
  • nginx-control.vhost.listen=<port_du_listen>
  • nginx-control.vhost.ssl_certificate=<auto = a charge du Dashboard de trouve si un cert match avec server_name | cerbot_dns | certbot_http | snippet>
  • nginx-control.vhost.ssl_certificate.snippet=

Snippet au niveau server si qui donne en config : include snippets/<filename.conf>

  • nginx-control.vhost.server.snippet01=
  • nginx-control.vhost.server.snippet02=
  • nginx-control.vhost.server.snippetXX=

Declaration des locations XX est un increment 01 / 02 / 03 ...

  • nginx-control.vhost.server.locationXX=<cible : /, /api, =/login, …>
  • nginx-control.vhost.server.locationXX.proxy_pass=<http(s)://$backendXX:port>
  • nginx-control.vhost.server.locationXX.snippetXX=

Auto HTTP vers HTTPS devrait créer un bloc server automatique de redirection 301 de HTTP vers HTTPS

  • nginx-control.vhost.server.http_to_https_auto=<true | false | si pas ce label on considere false par defaut>

Pour les $backendXX certainement generer ce block avec la liste des localtions car on est docker donc proxpass peut poser des problèmes :

resolver 127.0.0.11 valid=30s;
set $backendXX http(s)://$backendXX:port;
...

Prévoir des labels aussi pour activer le monitoring global, la désactivation si niveau location si on le souhaite des desactiver le monitoring à ce niveau
Prévoir des labels pour désactiver le diagnostic au niveau vhost
Voir d apres toi ce qui manque

Prendre en compte le deploiement Git, comme les vhosts sont généré à la voler pas Nginx Control sur les … peut etre créer un modele du type docker_<server_name>.conf

Partie 2 : gestion des hôtes docker distant

Pour cette partie l'idée va être de faire la même chose pour les hôtes distants, ce qui va nécessite un agent qui doit etre déployer sur les hôtes Docker et va communiquer avec Nginx Control.

Reprendre la partie avec l'agent
Dans le Dashboard une gestion des agents et aussi des métriques (cpu, ram, traffic …)

Depuis les hôtes distants devrait pouvoir être de 3 types les deux premiers ne devrait pas posser de probleme :

  • Le conteneur utilise un bind port du type 80:80 1234:80 … il suffit d'avoir un proxy_pass http(s)://ip-server-hote-agent:port
  • Un frontal traefik est en place ou autre reverse proxy (revient au meme cas que le 1)
  • le trafic est envoyé à l'agent et va agir comme un reverse proxy cette solution permet de ne pas avoir de bind de port au autre reverse

L agent doit etre leger (Go ?)

Securité, chaque doit etre approuver dans le Dashboard , trafic chiffré (TLS, SSL ?) et commnication Nginx Control proterger (cle api, hash …)

En fonction du mode 1,2,3 le proxy_pass Backend doit etre adapter et je suppose que le resolver …, ici n'est pas necessaire. dans le fichier vhost en entete avoir en commentaire

Generer auto

Agent XX

…

idéalement dans le top, serait qu'un agent puisse communiquer avec plusieurs Nginx Control, car j'ai régulièrement plusieurs reverse proxy (DMZ pour exterieure et LAN pour interne) mais certain sites/application sont publié sur les deux reverse

Autopublication des conteneurs à la demande à la mode Traefik "Cette partie va être très dense" Comme le Nginx Control acceder au socket Docker on devrait pouvoir le faire. comme toujours on garde le mecanisme de test avant recharge de nginx Partie 1 : depuis le meme hote que Nginx Control A la manière de Traefik on se base sur les labels Docker pour générer de façon dynamique les fichiers de virtualhost. - nginx-control.auto_config=enable - nginx-control.network=<nom-du-reseau-de-com-avec-nginx> - nginx-control.vhost.server_name=example.com - nginx-control.vhost.listen=<port_du_listen> - nginx-control.vhost.ssl_certificate=<auto = a charge du Dashboard de trouve si un cert match avec server_name | cerbot_dns | certbot_http | snippet> - nginx-control.vhost.ssl_certificate.snippet=<nom-du-fichier-snippet-si-snippet> # Snippet au niveau server si qui donne en config : include snippets/<filename.conf> - nginx-control.vhost.server.snippet01=<nom-du-fichier-snippet> - nginx-control.vhost.server.snippet02=<nom-du-fichier-snippet> - nginx-control.vhost.server.snippetXX=<nom-du-fichier-snippet> # Declaration des locations XX est un increment 01 / 02 / 03 ... - nginx-control.vhost.server.locationXX=<cible : /, /api, =/login, …> - nginx-control.vhost.server.locationXX.proxy_pass=<http(s)://$backendXX:port> - nginx-control.vhost.server.locationXX.snippetXX=<nom-du-fichier-snippet> # Auto HTTP vers HTTPS devrait créer un bloc server automatique de redirection 301 de HTTP vers HTTPS - nginx-control.vhost.server.http_to_https_auto=<true | false | si pas ce label on considere false par defaut> Pour les $backendXX certainement generer ce block avec la liste des localtions car on est docker donc proxpass peut poser des problèmes : resolver 127.0.0.11 valid=30s; set $backendXX http(s)://$backendXX:port; ... Prévoir des labels aussi pour activer le monitoring global, la désactivation si niveau location si on le souhaite des desactiver le monitoring à ce niveau Prévoir des labels pour désactiver le diagnostic au niveau vhost Voir d apres toi ce qui manque Prendre en compte le deploiement Git, comme les vhosts sont généré à la voler pas Nginx Control sur les … peut etre créer un modele du type docker_<server_name>.conf Partie 2 : gestion des hôtes docker distant Pour cette partie l'idée va être de faire la même chose pour les hôtes distants, ce qui va nécessite un agent qui doit etre déployer sur les hôtes Docker et va communiquer avec Nginx Control. Reprendre la partie avec l'agent Dans le Dashboard une gestion des agents et aussi des métriques (cpu, ram, traffic …) Depuis les hôtes distants devrait pouvoir être de 3 types les deux premiers ne devrait pas posser de probleme : - Le conteneur utilise un bind port du type 80:80 1234:80 … il suffit d'avoir un proxy_pass http(s)://ip-server-hote-agent:port - Un frontal traefik est en place ou autre reverse proxy (revient au meme cas que le 1) - le trafic est envoyé à l'agent et va agir comme un reverse proxy cette solution permet de ne pas avoir de bind de port au autre reverse L agent doit etre leger (Go ?) Securité, chaque doit etre approuver dans le Dashboard , trafic chiffré (TLS, SSL ?) et commnication Nginx Control proterger (cle api, hash …) En fonction du mode 1,2,3 le proxy_pass Backend doit etre adapter et je suppose que le resolver …, ici n'est pas necessaire. dans le fichier vhost en entete avoir en commentaire # Generer auto # Agent XX # … idéalement dans le top, serait qu'un agent puisse communiquer avec plusieurs Nginx Control, car j'ai régulièrement plusieurs reverse proxy (DMZ pour exterieure et LAN pour interne) mais certain sites/application sont publié sur les deux reverse
Author
Owner

Autopublication des conteneurs par labels Docker (façon Traefik) — Design

Statut : proposition à valider avant implémentation. Ce document couvre
les deux parties demandées, complète le schéma de labels proposé, et liste
explicitement ce qu'il manque à mon avis. Rien n'est codé — c'est la base de
discussion avant de démarrer.


Principe général

Nginx Control a déjà un accès (lecture) au socket Docker (health-checks,
listing de conteneurs pour /api/backends). L'extension proposée est de
même nature : un watcher qui scanne les conteneurs, en extrait un état
désiré, génère un/des fichiers vhost, et applique le même pipeline de
sécurité que tout le reste du projet
: écrire → nginx -t sur le résultat
→ rollback si échec → reload seulement si un changement réel → jamais
committer ces fichiers dans Git → skip proprement au premier boot.

C'est exactement le mécanisme déjà construit pour features/blocklists.js
(cache-on-failure, test-before-reload, unchanged-detection, protection
contre le déploiement Git). Il sera généralisé plutôt que dupliqué.


Partie 1 — Auto-config locale (même hôte que Nginx Control)

Schéma de labels (révisé)

Je garde votre proposition, avec quelques ajustements pour la cohérence et
la sécurité :

Label Rôle Remarque
nginx-control.enable=true Active l'auto-config pour ce conteneur Renommé depuis auto_config=enable pour un booléen cohérent avec le reste du projet
nginx-control.network=<network> Réseau Docker partagé avec nginx Vérifié : doit être un réseau réellement attaché au conteneur nginx — sinon échec explicite, jamais un vhost silencieusement cassé
nginx-control.vhost.server_name=example.com server_name Accepte une liste espace/virgule (alias), comme nginx
nginx-control.vhost.listen=443 Port HTTPS principal Port 80 seul = site HTTP sans TLS
nginx-control.vhost.ssl_certificate=auto|certbot_dns|certbot_http|snippet|none Mode de résolution du certificat Détail ci-dessous
nginx-control.vhost.ssl_certificate.snippet=<file> Nom du snippet SSL si mode snippet Validé contre la liste réelle des snippets existants — jamais un chemin arbitraire
nginx-control.vhost.server.snippet01, 02, ... Snippets inclus au niveau server{} include /nginx/snippets/<file>;
nginx-control.vhost.location01=/, location02=/api, ... Cibles de location
nginx-control.vhost.location01.proxy_pass=http://backend:8080 Backend de la location Voir génération du bloc resolver ci-dessous
nginx-control.vhost.location01.snippet01, 02, ... Snippets par location Numérotation propre à chaque location (pas de collision avec server.snippetXX)
nginx-control.vhost.http_to_https_auto=true|false Redirection 301 auto Défaut false si absent
nginx-control.monitor.enable=true|false Monitoring global du vhost Ajoute/retire automatiquement la cible dans le monitoring continu existant
nginx-control.vhost.location01.monitor.enable=false Désactivation du monitoring par location Override, comme demandé
nginx-control.diagnostic.enable=false Désactive le diagnostic du fichier Écrit # nginx-control-diagnostic: off en tête — réutilise le mécanisme déjà existant, pas un nouveau
nginx-control.vhost.analyze.enable=false Désactive l'analyse du bloc server Écrit # nginx-control-analyze: off — idem, mécanisme existant réutilisé
nginx-control.vhost.analyze.ignore_rules=1,2,4 Ignore certaines règles d'analyse Écrit # nginx-control-analyze-ignore-rules: 1, 2, 4

Point important : les deux derniers ne sont pas de nouveaux mécanismes —
ce sont les commentaires magiques déjà supportés par le parseur de vhost
existant (parseVhostFile(), testé dans votre suite actuelle). Le
générateur auto n'a qu'à les écrire lui-même en tête de fichier/bloc.

Résolution du backend (le point technique le plus sensible)

Votre intuition est correcte et c'est la bonne pratique nginx+Docker : sans
variable, nginx résout le nom une seule fois au chargement de la config et
ne revoit jamais un conteneur backend qui redémarre avec une autre IP.
Pour chaque location, le générateur produira :

location /api {
    resolver 127.0.0.11 valid=30s;   # résolveur DNS interne Docker
    set $backend01 "http://backend:8080";
    proxy_pass $backend01;
    include /nginx/snippets/logging.conf;   # ou autre snippet listé
}

127.0.0.11 est le résolveur DNS embarqué de Docker — pas une valeur à
laisser configurable, elle est fixe par construction Docker.

Mode ssl_certificate=auto

Le projet a déjà une logique de résolution SSL (GET /api/vhost/ssl-sources,
utilisée par la génération manuelle de vhost). Le mode auto la réutilise
telle quelle plutôt que d'être réinventée : chercher un certificat
existant qui matche server_name, sinon un snippet SSL existant, sinon
échec explicite (pas de vhost HTTPS cassé silencieusement — on retombe sur
HTTP le temps qu'un certificat soit obtenu).

Modes certbot_dns / certbot_http

Ce sont les seuls modes asynchrones : obtenir un certificat prend du
temps (délai DNS-01 en particulier). Proposition :

  1. Premier passage : vhost généré en HTTP seul (port 80), demande de
    certificat déclenchée en tâche de fond via les features Certbot
    existantes.
  2. Dès le certificat obtenu : régénération automatique du vhost en HTTPS +
    redirection, si http_to_https_auto est actif.
  3. Statut visible dans l'UI dédiée (voir plus bas) — "en attente de
    certificat" est un état normal, pas une erreur.

Nommage et protection Git

Fichier généré : sites/docker_<server_name>.conf, comme vous le proposez.
Le préfixe docker_ sert à trois choses :

  • reconnaissable visuellement comme "généré, ne pas éditer à la main" ;
  • jamais committé dans le dépôt Git de configuration (ces fichiers
    reflètent un état runtime Docker, pas une intention versionnée) ;
  • protégé contre la suppression/écrasement par un déploiement Git complet,
    exactement comme les fichiers générés par blocklists.js.

Changement nécessaire au mécanisme existant : aujourd'hui,
features/deploy.js's GENERATED_FILES est une liste statique de
chemins fixes (deux ou trois fichiers connus au boot). Avec l'auto-config
Docker, le nombre de fichiers est dynamique (un par conteneur labellisé,
apparaît/disparaît à la volée). Je généraliserai setDeps({ generatedFiles })
pour accepter soit un tableau (rétrocompatible avec blocklists.js), soit une
fonction rappelée à chaque vérification (() => [...fichiers courants]) —
et j'ajouterai un second critère : tout fichier de sites/ dont le nom
commence par docker_ est protégé par préfixe, pas seulement par liste
exhaustive. Ceinture et bretelles.

Détection : événements Docker + filet de sécurité

Deux mécanismes combinés, comme partout ailleurs dans ce projet (précédent :
blocklists.js a un scheduler + un bouton de rafraîchissement manuel) :

  • Docker Events (/events en streaming sur le socket) pour réagir
    immédiatement à un start/stop/die/update d'un conteneur labellisé
    — la réactivité "à la Traefik".
  • Poll de sécurité (ex. toutes les 5 min) en filet — au cas où un
    événement serait manqué (redémarrage du dashboard pendant qu'un conteneur
    démarre).
  • Debounce : plusieurs conteneurs démarrant en rafale (ex. docker compose up d'une stack de 10 services) ne doivent jamais déclencher 10
    reloads nginx à la suite. Un court délai de regroupement (quelques
    secondes) avant d'appliquer un lot de changements en un seul test+reload.

Sécurité — le point le plus important de toute cette fonctionnalité

Un label Docker peut être posé par quiconque a le droit de lancer un
conteneur sur l'hôte
. C'est une source de données à traiter avec la même
méfiance qu'une liste IP externe (cf. lib/blocklist-parse.js) :

  • Validation stricte de chaque valeur avant écriture dans un fichier
    nginx : server_name validé par regex, ports numériques bornés, noms de
    snippet validés contre la liste réelle des fichiers existants sur le
    disque (jamais un chemin arbitraire, jamais de traversée de répertoire).
  • Conflit de server_name entre deux conteneurs : jamais un écrasement
    silencieux — détection explicite, log clair, notification, aucun des deux
    n'est appliqué tant que le conflit n'est pas résolu (sauf usage voulu du
    upstream_group, voir "ce qui manque" ci-dessous).
  • Option d'approbation : sur un hôte partagé, un conteneur pourrait
    légitimement déclarer un vhost pour lui-même, mais rien n'empêche un
    conteneur compromis de déclarer server_name=admin.example.com pour
    détourner un vhost existant. Je propose un réglage
    (docker-autoconfig.yml) avec deux options combinables :
    • allowed_server_name_patterns — domaines/wildcards pré-autorisés,
      appliqués automatiquement sans intervention ;
    • require_approval: true — tout server_name hors de cette liste
      apparaît "en attente" dans l'UI jusqu'à validation manuelle.
      Traefik lui-même n'a pas cette granularité (confiance totale à quiconque
      peut lancer un conteneur) ; je pense que ce filet est cohérent avec les
      instructions du projet ("bonnes pratiques de sécurité").

Ce qu'il manque à mon avis (au-delà de votre liste)

  • upstream_group — plusieurs conteneurs (répliques) contribuant au
    même service : nginx-control.vhost.location01.upstream_group=api génère
    un bloc upstream api { server ...; server ...; } agrégeant toutes les
    instances portant ce même nom de groupe, plutôt qu'un conflit de
    server_name.
  • Nettoyage automatique : quand un conteneur labellisé s'arrête/est
    supprimé, son vhost doit être retiré (avec test avant reload, comme
    toujours) — sinon accumulation de vhosts fantômes pointant vers des
    backends morts.
  • Idempotence stricte : ne comparer et ne reload que ce qui a réellement
    changé, comme l'unchanged-detection de blocklists.js — jamais un reload
    sur un no-op.
  • Audit : chaque création/modification/suppression de vhost auto tracée
    dans le log d'événements existant, avec sa source (Docker local vs quel
    agent en Partie 2).
  • UI dédiée : page "Auto-config Docker" listant les conteneurs
    labellisés, leur statut (actif / en attente d'approbation / en attente de
    certificat / erreur), un aperçu en lecture seule du vhost généré, et un
    bouton "Forcer un rescan".

Partie 2 — Hôtes Docker distants (agent)

Principe

Un agent léger (Go — accord avec votre proposition : binaire statique,
faible empreinte, cross-compilation facile) tourne sur chaque hôte distant.
Il lit les mêmes labels sur son Docker local, construit un manifest de
vhosts désirés, et le pousse à un ou plusieurs Nginx Control. Nginx Control
génère alors le vhost exactement comme en Partie 1, avec un proxy_pass
adapté au mode de connectivité.

Workflow d'approbation (jamais de confiance implicite)

  1. L'agent démarre avec juste l'URL du/des Nginx Control cibles.
  2. Premier contact : demande d'enrôlement (nom d'hôte proposé, empreinte).
  3. Dans le Dashboard, nouvelle page Agents : liste des demandes en
    attente, bouton Approuver → génère un jeton scoped dédié (même
    famille que les jetons de déploiement CI/CD déjà livrés : rôle propre,
    permissions limitées aux seules routes agent, révocable à tout moment
    sans fenêtre de cache).
  4. Toute communication ensuite : TLS + jeton (voir sécurité ci-dessous).

Sécurité des communications

  • TLS obligatoire, jamais de mode non chiffré pour un agent distant.
  • V1 : jeton API scoped (Bearer) par agent + TLS serveur classique —
    cohérent avec le mécanisme des jetons de déploiement déjà en place, rapide
    à livrer, révocable instantanément.
  • V2 (option avancée) : mTLS (certificat client par agent) pour les
    déploiements à plus haute exigence — plus robuste (résiste à un vol du
    seul jeton) mais nécessite une PKI et une révocation de certificat, plus
    lourd. Je recommande de démarrer par V1.
  • Rotation de jeton supportée (bouton "Régénérer").

Les 3 modes — analyse

Modes 1 et 2 (bind port hôte, ou frontal Traefik/reverse-proxy déjà en
place)
sont en réalité le même mode côté Nginx Control : un simple
proxy_pass direct vers une IP:port joignable, sans bloc resolver (qui
n'a de sens que pour la résolution DNS interne Docker sur le même hôte que
nginx). Je les fusionne en un seul "mode direct" dans l'implémentation ;
la distinction 1 vs 2 n'existe que côté hôte distant (bind natif vs délégué
à un autre reverse proxy), pas dans le code de Nginx Control.

Mode 3 (l'agent fait lui-même reverse proxy, aucun port bindé) :

  • Nginx Control fait un proxy_pass vers l'agent (pas vers le conteneur
    backend directement).
  • L'agent expose un port dédié par vhost (alloué dynamiquement,
    communiqué dans le manifest) plutôt qu'un routing par Host/SNI partagé —
    plus simple, plus robuste, pas de dépendance à la préservation du Host
    header bout en bout.
  • Terminaison TLS chez Nginx Control (recommandé, cohérent avec tous les
    autres vhosts, gestion de certificat centralisée) — la liaison Nginx
    Control ↔ agent est un canal interne déjà chiffré (voir tunnel ci-dessous).
  • Raffinement que je propose d'ajouter : un tunnel sortant initié par
    l'agent
    (à la Cloudflare Tunnel/ngrok) plutôt qu'une connexion entrante
    Nginx Control → agent. Ça résout un vrai problème réseau que Traefik ne
    résout pas nativement : un hôte distant derrière NAT, sans IP publique
    ni port entrant ouvert
    , peut quand même publier ses services — l'agent
    ouvre la connexion vers Nginx Control, jamais l'inverse. Gros avantage
    pratique.

En-tête des vhosts générés pour un agent distant

# Genere automatiquement par nginx-control — NE PAS EDITER A LA MAIN
# Agent: <nom-agent> (<host-id>)
# Mode: direct (ip:port) | mode3-tunnel
# Genere le: <timestamp>

Multi-master (un agent, plusieurs Nginx Control — DMZ + LAN)

Le fichier de config de l'agent contient une liste de cibles Nginx
Control, chacune avec sa propre approbation/jeton (deux Nginx Control
indépendants ne se connaissent pas entre eux, donc deux identités
distinctes). Nouveau label pour contrôler la diffusion :

nginx-control.publish=dmz,lan   # ou "all" par defaut

dmz/lan sont des noms logiques définis dans la config locale de l'agent,
qui les fait correspondre à ses cibles réelles. Un conteneur publié
uniquement en LAN n'apparaît jamais dans le manifest envoyé au Nginx Control
DMZ.

Métriques

Nouvelle page Agents : statut (en ligne/hors ligne, dernière
communication), CPU/RAM de l'hôte, trafic réseau, liste des conteneurs
exposés par agent. Récupération en push (heartbeat périodique initié
par l'agent) plutôt qu'en pull — cohérent avec le modèle de connexion
sortante du mode 3/tunnel, et plus simple côté authentification (toujours
l'agent qui initie).

Ce qu'il manque à mon avis

  • Agent hors ligne ≠ suppression immédiate des vhosts : même principe
    que le cache blocklists — garder la dernière configuration connue,
    afficher "agent hors ligne depuis Xmin" dans l'UI, jamais de suppression
    automatique sur un simple flap réseau. Une suppression ne doit intervenir
    que sur une action explicite (agent désapprouvé/supprimé) ou un délai de
    grâce long et configurable.
  • Versionnement du protocole (protocol_version dans le manifest) pour
    faire évoluer le format sans casser des agents plus anciens déployés sur
    des hôtes qu'on ne met pas à jour au même rythme que le dashboard.
  • Anti-abus : un agent compromis ne doit pas pouvoir déclencher un déni
    de service (spam de vhosts, reload en boucle) — même limitation de
    fréquence/debounce qu'en Partie 1, plus un plafond du nombre de vhosts
    acceptés par agent.
  • Documentation/exemple : un docker-compose.yml annoté avec les
    labels, à livrer comme les pipelines CI de la dernière fonctionnalité.

Plan de mise en œuvre proposé (phasage)

Vu la densité assumée du sujet, je propose quatre livraisons distinctes
plutôt qu'un unique gros chantier :

  1. Partie 1 — socle : détection par poll, génération d'un vhost simple
    (server_name + listen + une location + proxy_pass + resolver), test +
    reload, fichier docker_<name>.conf, généralisation de la protection
    Git (liste dynamique + protection par préfixe).
  2. Partie 1 — complet : modes SSL, snippets multiples (server +
    location), monitoring/diagnostic par label, http_to_https_auto,
    upstream_group, Docker Events + debounce, UI dédiée.
  3. Partie 2 — fondations : protocole agent ↔ Nginx Control (schéma du
    manifest), page Agents (liste/approbation/jetons scoped), mode direct
    uniquement (fusion 1+2).
  4. Partie 2 — avancé : mode 3 (+ option tunnel NAT), métriques,
    multi-master (publish=), agent Go complet.

Je recommande de démarrer par l'étape 1, livrée et testée, avant de figer le
protocole de la Partie 2 (qui a plus d'inconnues réseau/sécurité à valider).

# Autopublication des conteneurs par labels Docker (façon Traefik) — Design Statut : **proposition à valider avant implémentation**. Ce document couvre les deux parties demandées, complète le schéma de labels proposé, et liste explicitement ce qu'il manque à mon avis. Rien n'est codé — c'est la base de discussion avant de démarrer. --- ## Principe général Nginx Control a déjà un accès (lecture) au socket Docker (health-checks, listing de conteneurs pour `/api/backends`). L'extension proposée est de même nature : un watcher qui scanne les conteneurs, en extrait un état désiré, génère un/des fichiers vhost, et applique **le même pipeline de sécurité que tout le reste du projet** : écrire → `nginx -t` sur le résultat → rollback si échec → reload seulement si un changement réel → jamais committer ces fichiers dans Git → skip proprement au premier boot. C'est exactement le mécanisme déjà construit pour `features/blocklists.js` (cache-on-failure, test-before-reload, unchanged-detection, protection contre le déploiement Git). Il sera généralisé plutôt que dupliqué. --- ## Partie 1 — Auto-config locale (même hôte que Nginx Control) ### Schéma de labels (révisé) Je garde votre proposition, avec quelques ajustements pour la cohérence et la sécurité : | Label | Rôle | Remarque | |---|---|---| | `nginx-control.enable=true` | Active l'auto-config pour ce conteneur | Renommé depuis `auto_config=enable` pour un booléen cohérent avec le reste du projet | | `nginx-control.network=<network>` | Réseau Docker partagé avec nginx | Vérifié : doit être un réseau **réellement attaché au conteneur nginx** — sinon échec explicite, jamais un vhost silencieusement cassé | | `nginx-control.vhost.server_name=example.com` | `server_name` | Accepte une liste espace/virgule (alias), comme nginx | | `nginx-control.vhost.listen=443` | Port HTTPS principal | Port 80 seul = site HTTP sans TLS | | `nginx-control.vhost.ssl_certificate=auto\|certbot_dns\|certbot_http\|snippet\|none` | Mode de résolution du certificat | Détail ci-dessous | | `nginx-control.vhost.ssl_certificate.snippet=<file>` | Nom du snippet SSL si mode `snippet` | Validé contre la liste réelle des snippets existants — jamais un chemin arbitraire | | `nginx-control.vhost.server.snippet01`, `02`, ... | Snippets inclus au niveau `server{}` | `include /nginx/snippets/<file>;` | | `nginx-control.vhost.location01=/`, `location02=/api`, ... | Cibles de location | | | `nginx-control.vhost.location01.proxy_pass=http://backend:8080` | Backend de la location | Voir génération du bloc resolver ci-dessous | | `nginx-control.vhost.location01.snippet01`, `02`, ... | Snippets par location | Numérotation propre à chaque location (pas de collision avec `server.snippetXX`) | | `nginx-control.vhost.http_to_https_auto=true\|false` | Redirection 301 auto | Défaut `false` si absent | | `nginx-control.monitor.enable=true\|false` | Monitoring global du vhost | Ajoute/retire automatiquement la cible dans le monitoring continu existant | | `nginx-control.vhost.location01.monitor.enable=false` | Désactivation du monitoring **par location** | Override, comme demandé | | `nginx-control.diagnostic.enable=false` | Désactive le diagnostic du **fichier** | Écrit `# nginx-control-diagnostic: off` en tête — réutilise le mécanisme déjà existant, pas un nouveau | | `nginx-control.vhost.analyze.enable=false` | Désactive l'analyse du **bloc server** | Écrit `# nginx-control-analyze: off` — idem, mécanisme existant réutilisé | | `nginx-control.vhost.analyze.ignore_rules=1,2,4` | Ignore certaines règles d'analyse | Écrit `# nginx-control-analyze-ignore-rules: 1, 2, 4` | Point important : les deux derniers ne sont **pas de nouveaux mécanismes** — ce sont les commentaires magiques déjà supportés par le parseur de vhost existant (`parseVhostFile()`, testé dans votre suite actuelle). Le générateur auto n'a qu'à les écrire lui-même en tête de fichier/bloc. ### Résolution du backend (le point technique le plus sensible) Votre intuition est correcte et c'est la bonne pratique nginx+Docker : sans variable, nginx résout le nom une seule fois au chargement de la config et ne revoit jamais un conteneur backend qui redémarre avec une autre IP. Pour **chaque location**, le générateur produira : ```nginx location /api { resolver 127.0.0.11 valid=30s; # résolveur DNS interne Docker set $backend01 "http://backend:8080"; proxy_pass $backend01; include /nginx/snippets/logging.conf; # ou autre snippet listé } ``` `127.0.0.11` est le résolveur DNS embarqué de Docker — pas une valeur à laisser configurable, elle est fixe par construction Docker. ### Mode `ssl_certificate=auto` Le projet a déjà une logique de résolution SSL (`GET /api/vhost/ssl-sources`, utilisée par la génération manuelle de vhost). Le mode `auto` la réutilise **telle quelle** plutôt que d'être réinventée : chercher un certificat existant qui matche `server_name`, sinon un snippet SSL existant, sinon échec explicite (pas de vhost HTTPS cassé silencieusement — on retombe sur HTTP le temps qu'un certificat soit obtenu). ### Modes `certbot_dns` / `certbot_http` Ce sont les seuls modes **asynchrones** : obtenir un certificat prend du temps (délai DNS-01 en particulier). Proposition : 1. Premier passage : vhost généré en HTTP seul (port 80), demande de certificat déclenchée en tâche de fond via les features Certbot existantes. 2. Dès le certificat obtenu : régénération automatique du vhost en HTTPS + redirection, si `http_to_https_auto` est actif. 3. Statut visible dans l'UI dédiée (voir plus bas) — "en attente de certificat" est un état normal, pas une erreur. ### Nommage et protection Git Fichier généré : `sites/docker_<server_name>.conf`, comme vous le proposez. Le préfixe `docker_` sert à trois choses : - reconnaissable visuellement comme "généré, ne pas éditer à la main" ; - **jamais committé** dans le dépôt Git de configuration (ces fichiers reflètent un état runtime Docker, pas une intention versionnée) ; - protégé contre la suppression/écrasement par un déploiement Git complet, exactement comme les fichiers générés par `blocklists.js`. **Changement nécessaire au mécanisme existant** : aujourd'hui, `features/deploy.js`'s `GENERATED_FILES` est une **liste statique** de chemins fixes (deux ou trois fichiers connus au boot). Avec l'auto-config Docker, le nombre de fichiers est **dynamique** (un par conteneur labellisé, apparaît/disparaît à la volée). Je généraliserai `setDeps({ generatedFiles })` pour accepter soit un tableau (rétrocompatible avec blocklists.js), soit une fonction rappelée à chaque vérification (`() => [...fichiers courants]`) — et j'ajouterai un second critère : tout fichier de `sites/` dont le nom commence par `docker_` est protégé par préfixe, pas seulement par liste exhaustive. Ceinture et bretelles. ### Détection : événements Docker + filet de sécurité Deux mécanismes combinés, comme partout ailleurs dans ce projet (précédent : `blocklists.js` a un scheduler + un bouton de rafraîchissement manuel) : - **Docker Events** (`/events` en streaming sur le socket) pour réagir immédiatement à un `start`/`stop`/`die`/`update` d'un conteneur labellisé — la réactivité "à la Traefik". - **Poll de sécurité** (ex. toutes les 5 min) en filet — au cas où un événement serait manqué (redémarrage du dashboard pendant qu'un conteneur démarre). - **Debounce** : plusieurs conteneurs démarrant en rafale (ex. `docker compose up` d'une stack de 10 services) ne doivent jamais déclencher 10 reloads nginx à la suite. Un court délai de regroupement (quelques secondes) avant d'appliquer un lot de changements en un seul test+reload. ### Sécurité — le point le plus important de toute cette fonctionnalité Un label Docker peut être posé par **quiconque a le droit de lancer un conteneur sur l'hôte**. C'est une source de données à traiter avec la même méfiance qu'une liste IP externe (cf. `lib/blocklist-parse.js`) : - **Validation stricte de chaque valeur** avant écriture dans un fichier nginx : `server_name` validé par regex, ports numériques bornés, noms de snippet validés contre la liste réelle des fichiers existants sur le disque (jamais un chemin arbitraire, jamais de traversée de répertoire). - **Conflit de `server_name`** entre deux conteneurs : jamais un écrasement silencieux — détection explicite, log clair, notification, aucun des deux n'est appliqué tant que le conflit n'est pas résolu (sauf usage voulu du `upstream_group`, voir "ce qui manque" ci-dessous). - **Option d'approbation** : sur un hôte partagé, un conteneur pourrait légitimement déclarer un vhost pour lui-même, mais rien n'empêche un conteneur compromis de déclarer `server_name=admin.example.com` pour détourner un vhost existant. Je propose un réglage (`docker-autoconfig.yml`) avec deux options combinables : - `allowed_server_name_patterns` — domaines/wildcards pré-autorisés, appliqués automatiquement sans intervention ; - `require_approval: true` — tout `server_name` hors de cette liste apparaît "en attente" dans l'UI jusqu'à validation manuelle. Traefik lui-même n'a pas cette granularité (confiance totale à quiconque peut lancer un conteneur) ; je pense que ce filet est cohérent avec les instructions du projet ("bonnes pratiques de sécurité"). ### Ce qu'il manque à mon avis (au-delà de votre liste) - **`upstream_group`** — plusieurs conteneurs (répliques) contribuant au même service : `nginx-control.vhost.location01.upstream_group=api` génère un bloc `upstream api { server ...; server ...; }` agrégeant toutes les instances portant ce même nom de groupe, plutôt qu'un conflit de `server_name`. - **Nettoyage automatique** : quand un conteneur labellisé s'arrête/est supprimé, son vhost doit être retiré (avec test avant reload, comme toujours) — sinon accumulation de vhosts fantômes pointant vers des backends morts. - **Idempotence stricte** : ne comparer et ne reload que ce qui a réellement changé, comme l'unchanged-detection de blocklists.js — jamais un reload sur un no-op. - **Audit** : chaque création/modification/suppression de vhost auto tracée dans le log d'événements existant, avec sa source (Docker local vs quel agent en Partie 2). - **UI dédiée** : page "Auto-config Docker" listant les conteneurs labellisés, leur statut (actif / en attente d'approbation / en attente de certificat / erreur), un aperçu en lecture seule du vhost généré, et un bouton "Forcer un rescan". --- ## Partie 2 — Hôtes Docker distants (agent) ### Principe Un agent léger (Go — accord avec votre proposition : binaire statique, faible empreinte, cross-compilation facile) tourne sur chaque hôte distant. Il lit les mêmes labels sur son Docker local, construit un **manifest** de vhosts désirés, et le pousse à un ou plusieurs Nginx Control. Nginx Control génère alors le vhost exactement comme en Partie 1, avec un `proxy_pass` adapté au mode de connectivité. ### Workflow d'approbation (jamais de confiance implicite) 1. L'agent démarre avec juste l'URL du/des Nginx Control cibles. 2. Premier contact : demande d'enrôlement (nom d'hôte proposé, empreinte). 3. Dans le Dashboard, nouvelle page **Agents** : liste des demandes en attente, bouton **Approuver** → génère un jeton scoped dédié (même famille que les jetons de déploiement CI/CD déjà livrés : rôle propre, permissions limitées aux seules routes agent, révocable à tout moment sans fenêtre de cache). 4. Toute communication ensuite : TLS + jeton (voir sécurité ci-dessous). ### Sécurité des communications - **TLS obligatoire**, jamais de mode non chiffré pour un agent distant. - **V1** : jeton API scoped (Bearer) par agent + TLS serveur classique — cohérent avec le mécanisme des jetons de déploiement déjà en place, rapide à livrer, révocable instantanément. - **V2 (option avancée)** : mTLS (certificat client par agent) pour les déploiements à plus haute exigence — plus robuste (résiste à un vol du seul jeton) mais nécessite une PKI et une révocation de certificat, plus lourd. Je recommande de démarrer par V1. - Rotation de jeton supportée (bouton "Régénérer"). ### Les 3 modes — analyse **Modes 1 et 2 (bind port hôte, ou frontal Traefik/reverse-proxy déjà en place)** sont en réalité **le même mode côté Nginx Control** : un simple `proxy_pass` direct vers une IP:port joignable, sans bloc `resolver` (qui n'a de sens que pour la résolution DNS interne Docker sur le *même* hôte que nginx). Je les fusionne en un seul **"mode direct"** dans l'implémentation ; la distinction 1 vs 2 n'existe que côté hôte distant (bind natif vs délégué à un autre reverse proxy), pas dans le code de Nginx Control. **Mode 3 (l'agent fait lui-même reverse proxy, aucun port bindé)** : - Nginx Control fait un `proxy_pass` vers l'agent (pas vers le conteneur backend directement). - L'agent expose **un port dédié par vhost** (alloué dynamiquement, communiqué dans le manifest) plutôt qu'un routing par `Host`/SNI partagé — plus simple, plus robuste, pas de dépendance à la préservation du `Host` header bout en bout. - **Terminaison TLS chez Nginx Control** (recommandé, cohérent avec tous les autres vhosts, gestion de certificat centralisée) — la liaison Nginx Control ↔ agent est un canal interne déjà chiffré (voir tunnel ci-dessous). - **Raffinement que je propose d'ajouter** : un **tunnel sortant initié par l'agent** (à la Cloudflare Tunnel/ngrok) plutôt qu'une connexion entrante Nginx Control → agent. Ça résout un vrai problème réseau que Traefik ne résout pas nativement : un hôte distant **derrière NAT, sans IP publique ni port entrant ouvert**, peut quand même publier ses services — l'agent ouvre la connexion vers Nginx Control, jamais l'inverse. Gros avantage pratique. ### En-tête des vhosts générés pour un agent distant ```nginx # Genere automatiquement par nginx-control — NE PAS EDITER A LA MAIN # Agent: <nom-agent> (<host-id>) # Mode: direct (ip:port) | mode3-tunnel # Genere le: <timestamp> ``` ### Multi-master (un agent, plusieurs Nginx Control — DMZ + LAN) Le fichier de config de l'agent contient une **liste de cibles** Nginx Control, chacune avec sa propre approbation/jeton (deux Nginx Control indépendants ne se connaissent pas entre eux, donc deux identités distinctes). Nouveau label pour contrôler la diffusion : ``` nginx-control.publish=dmz,lan # ou "all" par defaut ``` `dmz`/`lan` sont des noms logiques définis dans la config locale de l'agent, qui les fait correspondre à ses cibles réelles. Un conteneur publié uniquement en LAN n'apparaît jamais dans le manifest envoyé au Nginx Control DMZ. ### Métriques Nouvelle page **Agents** : statut (en ligne/hors ligne, dernière communication), CPU/RAM de l'hôte, trafic réseau, liste des conteneurs exposés par agent. Récupération en **push** (heartbeat périodique initié par l'agent) plutôt qu'en pull — cohérent avec le modèle de connexion sortante du mode 3/tunnel, et plus simple côté authentification (toujours l'agent qui initie). ### Ce qu'il manque à mon avis - **Agent hors ligne ≠ suppression immédiate des vhosts** : même principe que le cache blocklists — garder la dernière configuration connue, afficher "agent hors ligne depuis Xmin" dans l'UI, jamais de suppression automatique sur un simple flap réseau. Une suppression ne doit intervenir que sur une action explicite (agent désapprouvé/supprimé) ou un délai de grâce long et configurable. - **Versionnement du protocole** (`protocol_version` dans le manifest) pour faire évoluer le format sans casser des agents plus anciens déployés sur des hôtes qu'on ne met pas à jour au même rythme que le dashboard. - **Anti-abus** : un agent compromis ne doit pas pouvoir déclencher un déni de service (spam de vhosts, reload en boucle) — même limitation de fréquence/debounce qu'en Partie 1, plus un plafond du nombre de vhosts acceptés par agent. - **Documentation/exemple** : un `docker-compose.yml` annoté avec les labels, à livrer comme les pipelines CI de la dernière fonctionnalité. --- ## Plan de mise en œuvre proposé (phasage) Vu la densité assumée du sujet, je propose quatre livraisons distinctes plutôt qu'un unique gros chantier : 1. **Partie 1 — socle** : détection par poll, génération d'un vhost simple (server_name + listen + une location + proxy_pass + resolver), test + reload, fichier `docker_<name>.conf`, généralisation de la protection Git (liste dynamique + protection par préfixe). 2. **Partie 1 — complet** : modes SSL, snippets multiples (server + location), monitoring/diagnostic par label, `http_to_https_auto`, `upstream_group`, Docker Events + debounce, UI dédiée. 3. **Partie 2 — fondations** : protocole agent ↔ Nginx Control (schéma du manifest), page Agents (liste/approbation/jetons scoped), mode direct uniquement (fusion 1+2). 4. **Partie 2 — avancé** : mode 3 (+ option tunnel NAT), métriques, multi-master (`publish=`), agent Go complet. Je recommande de démarrer par l'étape 1, livrée et testée, avant de figer le protocole de la Partie 2 (qui a plus d'inconnues réseau/sécurité à valider).
romain referenced this issue from a commit 2026-09-24 06:53:07 +00:00
romain referenced this issue from a commit 2026-09-24 07:34:03 +00:00
romain referenced this issue from a commit 2026-09-24 08:53:29 +00:00
romain referenced this issue from a commit 2026-09-24 09:50:47 +00:00
Author
Owner

fonctionnalité totalement implémenter, test à faire les tests

fonctionnalité totalement implémenter, test à faire les tests
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Dockerfiles/nginx-reverse-proxy-dashboard#41
No description provided.