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
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
# 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
|
||||
```
|
||||
Reference in New Issue
Block a user