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

215 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# PoC BunkerWeb sur Proxmox
Un environnement de test complet pour [BunkerWeb](https://www.bunkerweb.io/), 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](docs/architecture.png)
## 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`](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`](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](https://owasp.org/www-project-juice-shop/), appli volontairement vulnérable | ModSecurity + OWASP CRS 4 (SQLi, XSS, traversée), websockets, User-Agents malveillants |
| `api.<domaine>` | [go-httpbin](https://github.com/mccutchen/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.
```bash
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`](inventory/hosts.yml) : IP du nœud Proxmox, IP et VMID des 3 VM, CPU, RAM, disque.
2. [`inventory/group_vars/all.yml`](inventory/group_vars/all.yml) : domaine, passerelle, DNS, stockage Proxmox, pont, VLAN, clé SSH publique.
3. [`inventory/group_vars/bunkerweb.yml`](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
```bash
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 :
```bash
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/`](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` :
```yaml
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) :
```bash
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` :
```bash
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 :
```yaml
# inventory/group_vars/all.yml
apps_direct_access_sources: [192.168.200.63]
```
```bash
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
```bash
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
- Documentation BunkerWeb : <https://docs.bunkerweb.io/latest/>
- Fichiers d'intégration officiels (docker, MariaDB, UI) : <https://github.com/bunkerity/bunkerweb/tree/v1.6.17/misc/integrations>
- Liste des réglages, extraite des `plugin.json` : <https://github.com/bunkerity/bunkerweb/tree/v1.6.17/src/common/core>
- OWASP Core Rule Set : <https://coreruleset.org/>