fzarifian b968ec8aa5 feat: implémentation initiale du reverse proxy de maintenance
Reverse proxy Nginx (image stable-alpine) qui sert l'app upstream ou une
page de maintenance 503 selon l'IP du client.

- Modes whitelist/blacklist commutables via MAINTENANCE_MODE
- Liste IPv4 via MAINTENANCE_IP_LIST (séparée par virgules, validée au boot)
- Logique en directives Nginx natives (geo + map), zéro Lua/njs
- Page statique sobre HTML+CSS inline, zéro JS, zéro réseau sortant
- Log dédié /var/log/nginx/maintenance.log pour les requêtes bloquées
- Validation des env vars dans /docker-entrypoint.d/10-init.sh
- Stack Docker + docker-compose pour dev local et tests
- 6 cas de tests d'intégration (whitelist/blacklist x autorisée/bloquée + log + nginx -t)
- Lint shellcheck propre sur tous les scripts shell
2026-05-05 13:52:13 +02:00

Maintenance Proxy

Reverse proxy Nginx qui, selon l'adresse IP du client, redirige les requêtes vers une application locale (proxy_pass) ou affiche une page de maintenance statique avec un code HTTP 503 Service Unavailable.

Cas d'usage typique : exposer une application en pré-production à une équipe interne uniquement, ou bloquer ponctuellement quelques IP indésirables pendant un correctif.

Pour la spécification complète et les règles de contribution, voir PROMPT.md.


Quickstart

Avec docker-compose (dev local)

docker compose up --build
# → http://localhost:8080

Par défaut, la stack démarre en mode whitelist avec 127.0.0.1 autorisée et un upstream factice qui sert une page « UPSTREAM_OK ». Édite docker-compose.yml pour brancher ton vraie app.

Avec docker run

docker build -t maintenance-proxy .

docker run --rm -p 8080:8080 \
    -e MAINTENANCE_MODE=whitelist \
    -e MAINTENANCE_IP_LIST="81.92.47.8,10.0.0.42" \
    -e UPSTREAM_HOST="host.docker.internal:3000" \
    maintenance-proxy

Variables d'environnement

Variable Obligatoire Défaut Description
MAINTENANCE_MODE whitelist (seules les IP listées passent) ou blacklist (les IP listées sont bloquées).
MAINTENANCE_IP_LIST Liste d'IPv4 individuelles séparées par des virgules. Espaces tolérés. Ex : 81.92.47.8, 10.0.0.42.
UPSTREAM_HOST Cible du proxy_pass au format host:port. Ex : 127.0.0.1:3000.
LISTEN_PORT 8080 Port d'écoute du conteneur.
SERVER_NAME _ server_name Nginx.

Le conteneur refuse de démarrer si une variable obligatoire est absente, si MAINTENANCE_MODE n'est pas whitelist ou blacklist, ou si une IP est syntaxiquement invalide.


Comportement

  • Réponse 503 Service Unavailable + en-tête Retry-After: 3600 quand l'IP doit voir la maintenance.
  • Toute le domaine est concerné, sans exclusion (ni assets, ni healthcheck).
  • Un log dédié /var/log/nginx/maintenance.log (à l'intérieur du conteneur) ne contient que les requêtes ayant abouti sur la page de maintenance.
  • Pas de bypass token, pas de cookie magique : seul critère = IP source ($remote_addr).
  • Nginx est censé être directement exposé au client. Aucune confiance accordée à X-Forwarded-For reçu de l'extérieur.

Ajouter ou retirer une IP

  1. Modifier MAINTENANCE_IP_LIST (env var ou docker-compose.yml).
  2. Redémarrer le conteneur : docker compose up -d --force-recreate proxy.

Pas de hot-reload : les changements de liste exigent un restart (validation et regen du snippet geo au boot).


Tests

./tests/run.sh

6 cas couverts :

  • whitelist + IP autorisée → 200 (proxy)
  • whitelist + IP non autorisée → 503 (maintenance)
  • blacklist + IP listée → 503 (maintenance)
  • blacklist + IP non listée → 200 (proxy)
  • log dédié maintenance.log n'est rempli que sur les 503
  • nginx -t valide la conf templatée

Lint

./scripts/lint.sh

Lance shellcheck sur tous les scripts puis nginx -t dans un conteneur jetable.


Architecture

Voir PROMPT.md §3 pour le détail. En bref :

nginx/templates/default.conf.template  # template envsubst
nginx/snippets/maintenance-log.conf    # log_format dédié
public/maintenance.html                # page statique sobre, zéro JS
scripts/build-ip-list.sh               # MAINTENANCE_IP_LIST → snippet `geo`
scripts/entrypoint.sh                  # validation env + génération snippet
Dockerfile                             # nginx:stable-alpine + assemblage
S
Description
No description provided
Readme 52 KiB
Languages
Shell 77.7%
HTML 13.4%
Dockerfile 8.9%