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:
2026-05-05 13:52:13 +02:00
commit b968ec8aa5
24 changed files with 1147 additions and 0 deletions
+15
View File
@@ -0,0 +1,15 @@
#!/usr/bin/env bash
# Mode blacklist + IP du client présente → page de maintenance (503).
set -euo pipefail
# shellcheck source-path=SCRIPTDIR
# shellcheck source=../lib.sh
source "$(dirname "$0")/../lib.sh"
restart_proxy blacklist "172.28.5.50"
status="$(curl_status)"
assert_eq "503" "$status" "code HTTP"
body="$(curl_body)"
assert_contains "Site en maintenance" "$body" "corps de la page de maintenance"
+15
View File
@@ -0,0 +1,15 @@
#!/usr/bin/env bash
# Mode blacklist + IP du client absente → l'app upstream est servie (200).
set -euo pipefail
# shellcheck source-path=SCRIPTDIR
# shellcheck source=../lib.sh
source "$(dirname "$0")/../lib.sh"
restart_proxy blacklist "10.99.99.99"
status="$(curl_status)"
assert_eq "200" "$status" "code HTTP"
body="$(curl_body)"
assert_contains "UPSTREAM_OK" "$body" "corps de la réponse"
+37
View File
@@ -0,0 +1,37 @@
#!/usr/bin/env bash
# Une requête bloquée doit apparaître dans /var/log/nginx/maintenance.log
# avec l'IP du client, et NE DOIT PAS y apparaître en mode passant.
set -euo pipefail
# shellcheck source-path=SCRIPTDIR
# shellcheck source=../lib.sh
source "$(dirname "$0")/../lib.sh"
# Scénario bloqué (whitelist sans l'IP du client)
restart_proxy whitelist "10.99.99.99"
# Vide le log avant le test pour partir d'un état connu
proxy_exec sh -c ': > /var/log/nginx/maintenance.log'
# Génère une requête bloquée
status="$(curl_status)"
assert_eq "503" "$status" "code HTTP attendu"
# Petit délai pour laisser Nginx flusher le log
sleep 1
log_content="$(proxy_exec cat /var/log/nginx/maintenance.log)"
assert_contains "172.28.5.50" "$log_content" "IP du client dans maintenance.log"
# Vérifie qu'une requête passante (mode blacklist sans IP du client) ne logge PAS
restart_proxy blacklist "10.99.99.99"
proxy_exec sh -c ': > /var/log/nginx/maintenance.log'
status="$(curl_status)"
assert_eq "200" "$status" "code HTTP en mode passant"
sleep 1
log_after_pass="$(proxy_exec cat /var/log/nginx/maintenance.log)"
if [[ -n "$log_after_pass" ]]; then
t_fail "maintenance.log devrait être vide après une requête non bloquée, contient : $log_after_pass"
fi
+20
View File
@@ -0,0 +1,20 @@
#!/usr/bin/env bash
# Vérifie que la configuration Nginx complète passe `nginx -t` une fois
# templatée et le snippet geo généré. Ne dépend pas du proxy "live" :
# lance un conteneur jetable.
set -euo pipefail
# shellcheck source-path=SCRIPTDIR
# shellcheck source=../lib.sh
source "$(dirname "$0")/../lib.sh"
docker run --rm \
-e MAINTENANCE_MODE=whitelist \
-e MAINTENANCE_IP_LIST="172.28.5.50,10.0.0.42" \
-e UPSTREAM_HOST="upstream:80" \
-e LISTEN_PORT=8080 \
-e SERVER_NAME=_ \
--entrypoint /bin/sh \
maintenance-proxy:test \
-c '/docker-entrypoint.sh nginx -t' >/dev/null 2>&1 \
|| t_fail "nginx -t a échoué sur la configuration générée"
+15
View File
@@ -0,0 +1,15 @@
#!/usr/bin/env bash
# Mode whitelist + IP du client présente → l'app upstream est servie (200).
set -euo pipefail
# shellcheck source-path=SCRIPTDIR
# shellcheck source=../lib.sh
source "$(dirname "$0")/../lib.sh"
restart_proxy whitelist "172.28.5.50"
status="$(curl_status)"
assert_eq "200" "$status" "code HTTP"
body="$(curl_body)"
assert_contains "UPSTREAM_OK" "$body" "corps de la réponse"
+15
View File
@@ -0,0 +1,15 @@
#!/usr/bin/env bash
# Mode whitelist + IP du client absente → page de maintenance (503).
set -euo pipefail
# shellcheck source-path=SCRIPTDIR
# shellcheck source=../lib.sh
source "$(dirname "$0")/../lib.sh"
restart_proxy whitelist "10.99.99.99"
status="$(curl_status)"
assert_eq "503" "$status" "code HTTP"
body="$(curl_body)"
assert_contains "Site en maintenance" "$body" "corps de la page de maintenance"