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>
This commit is contained in:
commit
099584c95c
42 files changed
+4260
No files matched your search
@@ -0,0 +1,2 @@
|
||||
---
|
||||
apps_dir: "{{ poc_base_dir }}/apps"
|
||||
@@ -0,0 +1,13 @@
|
||||
[Unit]
|
||||
Description=PoC BunkerWeb - filtrage des ports applis (DOCKER-USER)
|
||||
After=docker.service
|
||||
Requires=docker.service
|
||||
PartOf=docker.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
ExecStart=/usr/local/sbin/bwpoc-firewall.sh
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target docker.service
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
- name: Recharger le filtrage applis
|
||||
ansible.builtin.systemd_service:
|
||||
name: bwpoc-firewall.service
|
||||
state: restarted
|
||||
daemon_reload: true
|
||||
@@ -0,0 +1,68 @@
|
||||
---
|
||||
- name: Créer l'arborescence des applis
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
mode: "0755"
|
||||
loop:
|
||||
- "{{ apps_dir }}"
|
||||
- "{{ apps_dir }}/www"
|
||||
|
||||
- name: Déposer la vitrine statique
|
||||
ansible.builtin.template:
|
||||
src: "www/{{ item }}.j2"
|
||||
dest: "{{ apps_dir }}/www/{{ item }}"
|
||||
mode: "0644"
|
||||
loop:
|
||||
- index.html
|
||||
- contact.html
|
||||
|
||||
- name: Créer le réseau partagé bw-services (mode single)
|
||||
ansible.builtin.shell: docker network inspect bw-services >/dev/null 2>&1 || { docker network create bw-services && echo created; }
|
||||
register: apps_net
|
||||
changed_when: "'created' in apps_net.stdout"
|
||||
when: poc_mode == 'single'
|
||||
|
||||
- name: Générer le compose des applis
|
||||
ansible.builtin.template:
|
||||
src: compose.yml.j2
|
||||
dest: "{{ apps_dir }}/compose.yml"
|
||||
mode: "0644"
|
||||
validate: docker compose -f %s config -q
|
||||
|
||||
- name: Démarrer les applis
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ apps_dir }}"
|
||||
project_name: apps
|
||||
pull: missing
|
||||
remove_orphans: true
|
||||
state: present
|
||||
|
||||
- name: Filtrage des ports publiés (mode split)
|
||||
when: poc_mode == 'split'
|
||||
block:
|
||||
- name: Installer iptables
|
||||
ansible.builtin.apt:
|
||||
name: iptables
|
||||
state: present
|
||||
|
||||
- name: Déposer le script de filtrage
|
||||
ansible.builtin.template:
|
||||
src: bwpoc-firewall.sh.j2
|
||||
dest: /usr/local/sbin/bwpoc-firewall.sh
|
||||
mode: "0755"
|
||||
notify: Recharger le filtrage applis
|
||||
|
||||
- name: Déposer l'unité systemd
|
||||
ansible.builtin.copy:
|
||||
src: bwpoc-firewall.service
|
||||
dest: /etc/systemd/system/bwpoc-firewall.service
|
||||
mode: "0644"
|
||||
notify: Recharger le filtrage applis
|
||||
|
||||
- name: Activer le filtrage au démarrage
|
||||
ansible.builtin.systemd_service:
|
||||
name: bwpoc-firewall.service
|
||||
enabled: true
|
||||
state: started
|
||||
daemon_reload: true
|
||||
@@ -0,0 +1,18 @@
|
||||
#!/bin/sh
|
||||
# {{ ansible_managed }}
|
||||
# N'autorise que le WAF (et les sources listées) à joindre les ports publiés
|
||||
# des applis. Les règles vont dans DOCKER-USER, consultée par Docker avant
|
||||
# ses propres règles ; --ctorigdstport filtre sur le port publié (avant DNAT).
|
||||
set -eu
|
||||
IFACE="{{ ansible_default_ipv4.interface }}"
|
||||
PORTS="{{ apps_ports.values() | min }}:{{ apps_ports.values() | max }}"
|
||||
|
||||
iptables -N BWPOC 2>/dev/null || iptables -F BWPOC
|
||||
{% for src in [waf_ip] + apps_direct_access_sources %}
|
||||
iptables -A BWPOC -s {{ src }} -j RETURN
|
||||
{% endfor %}
|
||||
iptables -A BWPOC -j DROP
|
||||
|
||||
RULE="-i $IFACE -p tcp -m conntrack --ctstate NEW --ctorigdstport $PORTS -j BWPOC"
|
||||
# shellcheck disable=SC2086
|
||||
iptables -C DOCKER-USER $RULE 2>/dev/null || iptables -I DOCKER-USER $RULE
|
||||
@@ -0,0 +1,56 @@
|
||||
# {{ ansible_managed }}
|
||||
# Sites de test du PoC BunkerWeb, mode {{ poc_mode }}
|
||||
{% if poc_mode == 'split' %}
|
||||
# Ports publiés sur {{ ansible_host }}, filtrés par la chaîne iptables BWPOC :
|
||||
# seul le WAF ({{ waf_ip }}) peut les joindre.
|
||||
{% else %}
|
||||
# Aucun port publié : BunkerWeb joint les conteneurs par le réseau bw-services.
|
||||
{% endif %}
|
||||
|
||||
services:
|
||||
www:
|
||||
image: {{ nginx_image }}
|
||||
container_name: poc-www
|
||||
restart: unless-stopped
|
||||
read_only: true
|
||||
tmpfs:
|
||||
- /var/cache/nginx
|
||||
- /run
|
||||
- /tmp
|
||||
volumes:
|
||||
- ./www:/usr/share/nginx/html:ro
|
||||
{% if poc_mode == 'split' %}
|
||||
ports:
|
||||
- "{{ ansible_host }}:{{ apps_ports.www }}:80"
|
||||
{% endif %}
|
||||
networks: [apps]
|
||||
|
||||
shop:
|
||||
image: {{ juiceshop_image }}
|
||||
container_name: poc-shop
|
||||
restart: unless-stopped
|
||||
{% if poc_mode == 'split' %}
|
||||
ports:
|
||||
- "{{ ansible_host }}:{{ apps_ports.shop }}:3000"
|
||||
{% endif %}
|
||||
networks: [apps]
|
||||
|
||||
api:
|
||||
image: {{ httpbin_image }}
|
||||
container_name: poc-api
|
||||
restart: unless-stopped
|
||||
read_only: true
|
||||
{% if poc_mode == 'split' %}
|
||||
ports:
|
||||
- "{{ ansible_host }}:{{ apps_ports.api }}:8080"
|
||||
{% endif %}
|
||||
networks: [apps]
|
||||
|
||||
networks:
|
||||
apps:
|
||||
{% if poc_mode == 'single' %}
|
||||
name: bw-services
|
||||
external: true
|
||||
{% else %}
|
||||
name: poc-apps
|
||||
{% endif %}
|
||||
@@ -0,0 +1,26 @@
|
||||
<!doctype html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>PoC BunkerWeb - contact</title>
|
||||
<style>
|
||||
body { font-family: system-ui, sans-serif; max-width: 40rem; margin: 2rem auto; padding: 0 1rem; }
|
||||
label, input, textarea, button { display: block; width: 100%; margin-bottom: .8rem; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Contact</h1>
|
||||
<p>Formulaire factice envoyé en GET : la vitrine est statique, c'est BunkerWeb qui
|
||||
inspecte les paramètres. Essayez un message contenant
|
||||
<code><script>alert(1)</script></code> ou <code>' OR 1=1 --</code>.</p>
|
||||
<form method="get" action="contact.html">
|
||||
<label for="nom">Nom</label>
|
||||
<input id="nom" name="nom">
|
||||
<label for="message">Message</label>
|
||||
<textarea id="message" name="message" rows="5"></textarea>
|
||||
<button type="submit">Envoyer</button>
|
||||
</form>
|
||||
<p><a href="index.html">Retour</a></p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,36 @@
|
||||
<!doctype html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>PoC BunkerWeb - vitrine</title>
|
||||
<style>
|
||||
body { font-family: system-ui, sans-serif; max-width: 52rem; margin: 2rem auto; padding: 0 1rem; line-height: 1.5; color: #222; }
|
||||
h1 { color: #0b7a75; }
|
||||
code { background: #f2f2f2; padding: .1rem .3rem; border-radius: 3px; }
|
||||
table { border-collapse: collapse; width: 100%; }
|
||||
td, th { border: 1px solid #ddd; padding: .4rem .6rem; text-align: left; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>PoC BunkerWeb</h1>
|
||||
<p>Vous voyez cette page : vous avez passé le <strong>challenge antibot JavaScript</strong>
|
||||
de BunkerWeb. Un <code>curl</code> sur cette même URL reste bloqué sur <code>/challenge</code>.</p>
|
||||
|
||||
<h2>Sites du PoC</h2>
|
||||
<table>
|
||||
<tr><th>Site</th><th>Application</th><th>Fonctionnalités mises en avant</th></tr>
|
||||
<tr><td><a href="https://www.{{ poc_domain }}/">www</a></td><td>nginx statique</td><td>antibot, en-têtes de sécurité, HTTPS forcé</td></tr>
|
||||
<tr><td><a href="https://shop.{{ poc_domain }}/">shop</a></td><td>OWASP Juice Shop</td><td>ModSecurity + OWASP CRS 4, websockets, User-Agents malveillants</td></tr>
|
||||
<tr><td><a href="https://api.{{ poc_domain }}/get">api</a></td><td>go-httpbin</td><td>limitation de débit, méthodes, taille max, bad behavior, règle ModSecurity maison</td></tr>
|
||||
<tr><td><a href="https://bwui.{{ poc_domain }}/">bwui</a></td><td>UI BunkerWeb</td><td>rapports, bannissements, réglages</td></tr>
|
||||
</table>
|
||||
|
||||
<h2>Essais rapides dans le navigateur</h2>
|
||||
<ul>
|
||||
<li><a href="contact.html">Formulaire de contact</a> : saisissez <code><script>alert(1)</script></code> et envoyez.</li>
|
||||
<li><a href="/?file=../../../../etc/passwd">Traversée de répertoire</a> : réponse 403 attendue.</li>
|
||||
</ul>
|
||||
<p><small>Servi par {{ nginx_image }} derrière BunkerWeb {{ bunkerweb_version }}.</small></p>
|
||||
</body>
|
||||
</html>
|
||||
Reference in new issue
Block a user