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
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êteRetry-After: 3600quand 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-Forreçu de l'extérieur.
Ajouter ou retirer une IP
- Modifier
MAINTENANCE_IP_LIST(env var oudocker-compose.yml). - 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.logn'est rempli que sur les 503 nginx -tvalide 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