Files
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

104 lines
3.6 KiB
Markdown

# 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](PROMPT.md).
---
## Quickstart
### Avec docker-compose (dev local)
```bash
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
```bash
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
```bash
./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
```bash
./scripts/lint.sh
```
Lance `shellcheck` sur tous les scripts puis `nginx -t` dans un conteneur jetable.
---
## Architecture
Voir [PROMPT.md](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
```