Files
bunkerwebpoc/README.md
T
claude BotandClaude Opus 5.5 099584c95c PoC BunkerWeb 1.6.17 : docker-compose sur Proxmox, déployé par Ansible
Stack BunkerWeb (scheduler, UI, MariaDB, Redis), sites de test (nginx,
Juice Shop, go-httpbin), filtrage DOCKER-USER, script de tests, README
et schéma d'architecture.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-27 11:11:07 +01:00

12 KiB
Raw Blame History

PoC BunkerWeb sur Proxmox

Un environnement de test complet pour BunkerWeb, le reverse proxy WAF open source basé sur nginx, déployé en docker-compose sur des VM Proxmox, de bout en bout par Ansible.

L'objectif : voir concrètement ce que BunkerWeb bloque, laisse passer et journalise, sur des applis qui s'y prêtent, puis rejouer les mêmes attaques sans WAF pour comparer.

Architecture du PoC

Ce que contient le PoC

VM Rôle Conteneurs
bw-waf Point d'entrée unique, ports 80/443 (TCP + UDP pour HTTP/3) bunkerweb, bw-scheduler, bw-ui, bw-db (MariaDB 11), bw-redis
bw-apps Sites de test, joignables uniquement depuis le WAF (iptables DOCKER-USER) poc-www (nginx), poc-shop (OWASP Juice Shop), poc-api (go-httpbin)
bw-client Poste "attaquant" avec les outils et le script de tests curl, sqlmap, nmap, ab, bwpoc-tests

Versions épinglées dans inventory/group_vars/all.yml : BunkerWeb 1.6.17, Juice Shop v20.3.0, go-httpbin 2.26.0, nginx 1.30, Debian 13 (trixie) genericcloud.

Une variante tout-en-un (une seule VM) est fournie : inventory/single.yml. Le mode est détecté automatiquement : si WAF et applis sont le même hôte, BunkerWeb joint les applis par le réseau Docker bw-services et aucun port applicatif n'est publié.

Les sites de test

Chaque site met en avant un groupe de fonctionnalités :

Site Appli Ce qu'on y teste
www.<domaine> vitrine nginx statique (page d'accueil + formulaire de contact) antibot JavaScript, en-têtes de sécurité, redirection HTTPS, XSS via formulaire dans le navigateur
shop.<domaine> OWASP Juice Shop, appli volontairement vulnérable ModSecurity + OWASP CRS 4 (SQLi, XSS, traversée), websockets, User-Agents malveillants
api.<domaine> go-httpbin, API qui renvoie ce qu'elle reçoit limitation de débit, méthodes autorisées, taille max du corps, bad behavior et bannissement, règle ModSecurity maison, compression, X-Forwarded-For
bwui.<domaine> interface web de BunkerWeb rapports d'attaques, bannissements, réglages, journaux

Le domaine par défaut est bwpoc.internal (TLD réservé par l'ICANN pour l'usage privé). Les certificats sont auto-signés par BunkerWeb, site par site.

Prérequis

  • Un nœud Proxmox VE 8 joignable en SSH root depuis le poste Ansible, avec accès Internet (image Debian, images Docker, listes noires communautaires).
  • 3 IP libres sur le réseau des VM (1 en mode tout-en-un). Le playbook vérifie par ping qu'elles sont libres avant de créer les VM.
  • Sur le poste Ansible : ansible-core 2.16 ou plus, une paire de clés SSH.
git clone git@gitea.vlab.bzh:vLab-BZH/bunkerwebpoc.git && cd bunkerwebpoc
make deps   # installe community.docker et ansible.posix dans ./collections

Configuration

Tout ce qui dépend de votre infra est marqué A ADAPTER :

  1. inventory/hosts.yml : IP du nœud Proxmox, IP et VMID des 3 VM, CPU, RAM, disque.
  2. inventory/group_vars/all.yml : domaine, passerelle, DNS, stockage Proxmox, pont, VLAN, clé SSH publique.
  3. inventory/group_vars/bunkerweb.yml : les réglages BunkerWeb, globaux et par site. C'est le fichier à modifier pour jouer avec les fonctionnalités.

Les mots de passe (MariaDB, Redis, admin de l'UI) sont générés au premier lancement dans credentials/ côté poste Ansible (ignoré par git). Pour les mettre sous ansible-vault, remplacez les lookup('password') de bunkerweb.yml par vos variables chiffrées.

Déploiement

make deploy        # = ansible-playbook playbooks/site.yml
Étape Playbook Ce qu'elle fait
1 00-proxmox.yml télécharge l'image Debian 13 genericcloud, crée le modèle cloud-init (VMID 9100), clone les VM, injecte IP et clé SSH, agrandit les disques, démarre
2 10-base.yml paquets de base, agent QEMU, résolution locale des noms du PoC vers le WAF, Docker CE depuis le dépôt officiel
3 20-apps.yml stack des applis, filtrage DOCKER-USER (mode split)
4 30-bunkerweb.yml stack BunkerWeb, attente qu'api réponde au travers du WAF, écriture de credentials/ACCES.txt
5 40-client.yml outils d'attaque et script de tests sur bw-client

Vous avez déjà vos VM (Debian 12 ou 13, accès SSH avec sudo) ? Renseignez-les dans l'inventaire et sautez l'étape 1 : make base apps waf client.

Pour relire les fichiers compose générés sans rien déployer :

ansible-playbook playbooks/render.yml   # écrit build/split/... ou build/single/...

Un exemple de rendu (secrets remplacés par CHANGEME) est versionné dans examples/split/ (bunkerweb.compose.yml, apps.compose.yml, bwpoc-firewall.sh).

Accéder aux sites depuis votre poste

Les VM du PoC résolvent déjà les noms (bloc dans /etc/hosts). Sur votre poste, ajoutez la ligne suivante à /etc/hosts, ou déclarez les enregistrements dans votre DNS de labo :

192.168.200.61  www.bwpoc.internal shop.bwpoc.internal api.bwpoc.internal bwui.bwpoc.internal

Puis ouvrez https://bwui.bwpoc.internal/ (certificat auto-signé à accepter). Identifiants : make creds.

Comment BunkerWeb est configuré ici

Intégration Docker officielle (pas l'autoconf) : toute la configuration est portée par les variables d'environnement du bw-scheduler, qui génère la configuration nginx et la pousse à l'instance bunkerweb par son API (réseau bw-universe 10.20.30.0/24, seul autorisé par API_WHITELIST_IP). Ce choix permet de protéger des backends situés sur une autre VM, ce que les labels de l'autoconf ne savent pas faire.

MULTISITE=yes : chaque réglage peut être préfixé par le nom du site (api.bwpoc.internal_LIMIT_REQ_RATE=5r/s). Dans Ansible, il suffit de remplir le dictionnaire bw_sites :

bw_sites:
  api:
    upstream: api                # clé de apps_ports, ou URL complète
    settings:
      ALLOWED_METHODS: "GET|POST|HEAD"
      MAX_CLIENT_SIZE: "1m"
      LIMIT_REQ_RATE: "5r/s"
    custom_confs:                # devient api.<domaine>_CUSTOM_CONF_MODSEC_poc-rule
      MODSEC_poc-rule: >-
        SecRule ARGS:poc "@streq attack" "id:10001,phase:2,deny,status:403,log,msg:'PoC'"

Les réglages posés par variables d'environnement apparaissent dans l'UI en lecture seule (méthode scheduler). Les modifications faites dans l'UI sont stockées en base et s'ajoutent par-dessus.

Redis est activé (USE_REDIS=yes) : bans, sessions et compteurs sont partagés, ce qui prépare l'ajout d'une seconde instance bunkerweb derrière un VIP (keepalived) sans changer le reste.

Tester les fonctionnalités

Depuis bw-client (ou n'importe quel poste avec curl et jq, le script utilise --resolve et n'a pas besoin de DNS) :

make tests                              # depuis le poste Ansible
ssh debian@192.168.200.63 bwpoc-tests   # ou directement sur le client

Le script enchaîne une vingtaine de contrôles et affiche OK, KO ou INFO :

Fonctionnalité Réglage Requête Attendu
HTTPS forcé AUTO_REDIRECT_HTTP_TO_HTTPS http://api/get 301 vers https
Serveur par défaut fermé DISABLE_DEFAULT_SERVER Host inconnu connexion fermée (444)
En-têtes de sécurité STRICT_TRANSPORT_SECURITY, CONTENT_SECURITY_POLICY... api/get en-têtes présents, Server masqué
Proxy transparent USE_REVERSE_PROXY api/headers le backend voit X-Forwarded-For
Compression USE_GZIP, USE_BROTLI api/html Content-Encoding: br ou gzip
Antibot USE_ANTIBOT=javascript www/ en curl redirection vers /challenge
WAF SQLi ModSecurity + CRS 4 shop/rest/products/search?q=')) OR 1=1-- 403
WAF XSS idem shop/?q=<script>alert(1)</script> 403
WAF traversée idem shop/?file=../../../../etc/passwd 403
WAF injection de commande idem api/get?cmd=;cat /etc/passwd 403
Règle maison CUSTOM_CONF_MODSEC api/get?poc=attack 403
User-Agent maison BLACKLIST_USER_AGENT -A PoC-EvilBot 403
User-Agent communautaire BLACKLIST_COMMUNITY_LISTS -A sqlmap/1.8 403 (info : dépend du téléchargement de la liste)
Méthodes ALLOWED_METHODS PUT api/put 405
Taille du corps MAX_CLIENT_SIZE=1m POST de 2 Mo 413
Limitation de débit LIMIT_REQ_RATE=5r/s 40 requêtes en parallèle une partie en 429
Bad behavior BAD_BEHAVIOR_THRESHOLD=10 en 1 min 12 × /status/404 IP bannie (403) sur api seulement

Les derniers tests bannissent volontairement votre IP du site api pendant 5 minutes (bw_ban_time). Pour lever le ban : menu Bans de l'UI, ou sur bw-waf :

docker exec bw-scheduler bwcli unban 192.168.200.63

Avec et sans WAF

Pour mesurer l'apport du WAF, autorisez le client à joindre les applis en direct, puis rejouez les attaques :

# inventory/group_vars/all.yml
apps_direct_access_sources: [192.168.200.63]
make apps
ssh debian@192.168.200.63 bwpoc-tests --direct

La SQLi de Juice Shop renvoie alors la liste complète des produits (200) au lieu d'un 403, et le PUT passe.

Aller plus loin à la main

  • sqlmap contre Juice Shop, d'abord avec son User-Agent par défaut (bloqué par la liste communautaire), puis avec --random-agent pour voir le CRS prendre le relais : sqlmap -u "https://shop.bwpoc.internal/rest/products/search?q=test" --random-agent --batch
  • Antibot dans le navigateur : passez www en captcha ou cookie dans bunkerweb.yml, relancez make waf.
  • Faux positifs : naviguez dans Juice Shop (connexion, panier, avis). Ce que le CRS bloque à tort apparaît dans l'UI (Reports) avec l'ID de règle ; on l'exclut par un custom_confs de type MODSEC_CRS (exemple : SecRuleRemoveById 942100).
  • Mode détection seule : MODSECURITY_SEC_RULE_ENGINE: "DetectionOnly" sur un site, pour observer avant de bloquer, comme on le ferait en production.
  • Journaux : docker logs -f bunkerweb sur bw-waf, et l'audit ModSecurity dans /var/log/bunkerweb/modsec_audit.log du conteneur.

Exploitation

make waf           # réapplique la conf BunkerWeb après modification de bunkerweb.yml
make destroy       # supprime les VM (confirmation demandée), modèle conservé
ansible-playbook playbooks/99-destroy.yml -e destroy_template=true   # supprime aussi le modèle

Sur bw-waf, la stack vit dans /opt/bwpoc/bunkerweb (docker compose ps, logs, pull...). Sur bw-apps, dans /opt/bwpoc/apps.

Limites assumées du PoC

  • Certificats auto-signés : pour du Let's Encrypt en labo fermé, passer par le challenge DNS (AUTO_LETS_ENCRYPT, LETS_ENCRYPT_CHALLENGE=dns) avec votre fournisseur DNS.
  • Une seule instance BunkerWeb : pas de haute disponibilité, Redis est là pour la suite.
  • Les fonctions PRO de BunkerWeb (anti DDoS, monitoring Prometheus, cache, rapports) ne sont pas couvertes.
  • Le géoblocage (BLACKLIST_COUNTRY) et les DNSBL ne se testent pas depuis des IP privées.

Arborescence

inventory/            hosts.yml (3 VM), single.yml (tout-en-un), group_vars/
playbooks/            00 à 50 + site.yml, render.yml, 99-destroy.yml
roles/
  proxmox_template/   modèle Debian 13 cloud-init via qm
  proxmox_vm/         clonage, cloud-init, démarrage
  common/             paquets, agent QEMU, /etc/hosts
  docker/             Docker CE + compose (dépôt officiel)
  apps/               compose des applis, vitrine, filtrage DOCKER-USER
  bunkerweb/          compose BunkerWeb (template Jinja)
  client/             outils et script de tests
tests/bwpoc-tests.sh  batterie de tests fonctionnels
examples/split/       compose générés, pour lecture
docs/                 schéma d'architecture (.excalidraw et .png)

Références