Cosa fa#
Costruisce la topologia screened subnet (Gibson Cap 3) usando Linux network namespace: due firewall reali con iptables FORWARD, DMZ isolata tra i due, VLAN 802.1Q sulle connessioni inter-zona, WAF (nginx + ModSecurity) nella DMZ davanti al backend.
Questo lab richiede nginx + libmodsecurity3 installati su Ubuntu e il ruleset OWASP CRS scaricato. Vedi Fase 0 prima di tutto.
TL;DR#
INTERNET (Kali)
|
[eth0 Ubuntu root 192.168.64.3]
| veth pair 10.0.0.0/30
[ns-fw1] FW1 — iptables FORWARD: solo 80/443 verso DMZ
| VLAN 10 / 10.10.10.0/24
[ns-dmz] WAF — nginx + ModSecurity OWASP CRS su 10.10.10.2:80
| VLAN 20 / 10.20.20.0/24
[ns-fw2] FW2 — iptables FORWARD: solo IP WAF verso LAN:8080
| LAN / 10.30.30.0/24
[ns-lan] BACKEND — Python HTTP server su 10.30.30.2:8080Obiettivo: SQLi da Kali → WAF risponde 403; accesso diretto al backend da Kali → timeout.
Architettura#
┌─────────────────────────────────────────┐
│ INTERNET ZONE │
│ Kali 192.168.64.200 │
│ Mac 192.168.64.1 │
└───────────────────┬─────────────────────┘
│ host-only 192.168.64.0/24
┌───────────────────▼─────────────────────┐
│ UBUNTU ROOT NAMESPACE │
│ eth0: 192.168.64.3 │
│ veth-root: 10.0.0.1/30 │
└───────────────────┬─────────────────────┘
│ veth pair 10.0.0.0/30
┌───────────────────▼─────────────────────┐
│ ns-fw1 — FIREWALL 1 (Edge) │
│ veth-ext: 10.0.0.2/30 │
│ │
│ FORWARD: ACCEPT tcp 80,443 │
│ destinazione 10.10.10.2 │
│ DROP tutto il resto │
│ │
│ veth-fw1.10: 10.10.10.1/24 │
└───────────────────┬─────────────────────┘
│ 802.1Q VLAN id=10
│ subnet 10.10.10.0/24
┌───────────────────▼─────────────────────┐
│ ns-dmz — DMZ ZONE │
│ veth-dmz.10: 10.10.10.2/24 │
│ │
│ nginx + ModSecurity OWASP CRS │
│ listen: 10.10.10.2:80 │
│ proxy: → 10.30.30.2:8080 │
│ blocks: SQLi / XSS / LFI / RFI │
│ │
│ veth-d20.20: 10.20.20.1/24 │
└───────────────────┬─────────────────────┘
│ 802.1Q VLAN id=20
│ subnet 10.20.20.0/24
┌───────────────────▼─────────────────────┐
│ ns-fw2 — FIREWALL 2 (Internal) │
│ veth-fw2.20: 10.20.20.2/24 │
│ │
│ FORWARD: ACCEPT tcp 8080 │
│ src=10.10.10.2 (WAF) only │
│ DROP tutto il resto │
│ │
│ veth-f-lan: 10.30.30.1/24 │
└───────────────────┬─────────────────────┘
│ subnet 10.30.30.0/24
┌───────────────────▼─────────────────────┐
│ ns-lan — LAN / BACKEND │
│ veth-lan: 10.30.30.2/24 │
│ │
│ Python HTTP server :8080 │
│ (crown jewel simulato) │
└─────────────────────────────────────────┘Flussi permessi e bloccati:
Kali → WAF porta 80 FW1: ACCEPT → WAF proxya al backend
Kali → backend porta 8080 FW1: DROP → timeout (non passa FW1)
SQLi via WAF WAF: 403 → mai arriva al backend
WAF → backend porta 8080 FW2: ACCEPT → src 10.10.10.2 autorizzato
Kali → backend via salto FW1 FW2: DROP → src non e' il WAF IPPerche' due firewall e non uno?
Un solo firewall che permette 80/443 verso la DMZ non protegge il backend se il WAF viene compromesso. Il secondo firewall garantisce che anche in caso di compromissione della DMZ, il traffico verso il backend richieda comunque una policy esplicita basata sul source IP del WAF. Questa e' la defense in depth del modello screened subnet.
Fase 0 — Prerequisiti#
Tutto gira su Ubuntu (192.168.64.3). Prima di iniziare verifica che siano installati:
Task 0.1 — Installa nginx + ModSecurity
nginx,libnginx-mod-security2(olibmodsecurity3+libnginx-mod-http-modsecurity)- Come sai che hai finito:
nginx -V 2>&1 | grep modsecuritymostra il modulo
Task 0.2 — Scarica OWASP CRS
- Rilascio ufficiale: https://github.com/coreruleset/coreruleset/releases
- Estrai in
/etc/nginx/modsecurity-crs/ - Copia
crs-setup.conf.example→crs-setup.conf - Come sai che hai finito:
ls /etc/nginx/modsecurity-crs/rules/*.conf | wc -lmostra > 40 file
Task 0.3 — Configura ModSecurity in Detection Mode
- Crea
/etc/nginx/modsecurity.confche attivi ModSecurity conSecRuleEngine DetectionOnly - Includi
modsecurity-crs/crs-setup.confemodsecurity-crs/rules/*.conf - Come sai che hai finito:
nginx -tnon da errori
- Crea
Parti in DetectionOnly (solo log, nessun blocco) per verificare che il traffico passi correttamente. Passa a On (enforcement) solo nella Fase 4.
Fase 1 — Topologia namespace e veth#
Costruisci la rete virtuale: 4 namespace, 4 veth pair, VLAN 802.1Q su due link.
ROOT ── [veth-root:10.0.0.1] ↔ [veth-ext:10.0.0.2] ── ns-fw1
ns-fw1 ─ [veth-fw1.10:10.10.10.1] ↔ [veth-dmz.10:10.10.10.2] ─ ns-dmz (VLAN 10)
ns-dmz ─ [veth-d20:10.20.20.1] ↔ [veth-fw2:10.20.20.2] ─ ns-fw2 (VLAN 20)
ns-fw2 ─ [veth-f-lan:10.30.30.1] ↔ [veth-lan:10.30.30.2] ─ ns-lanTask 1.1 — Crea i 4 namespace
sudo ip netns add ns-fw1 sudo ip netns add ns-dmz sudo ip netns add ns-fw2 sudo ip netns add ns-lan- Come sai che hai finito:
ip netns listmostra tutti e quattro
- Come sai che hai finito:
Task 1.2 — Veth pair ROOT ↔ ns-fw1
sudo ip link add veth-root type veth peer name veth-ext sudo ip link set veth-ext netns ns-fw1 sudo ip addr add 10.0.0.1/30 dev veth-root sudo ip netns exec ns-fw1 ip addr add 10.0.0.2/30 dev veth-ext sudo ip link set veth-root up sudo ip netns exec ns-fw1 ip link set veth-ext up sudo ip netns exec ns-fw1 ip link set lo up- Come sai che hai finito:
ping -c1 10.0.0.2dal root funziona
- Come sai che hai finito:
Task 1.3 — Veth pair ns-fw1 ↔ ns-dmz con VLAN 10
Crea il veth grezzo tra ns-fw1 e ns-dmz, poi aggiungi subinterface VLAN 10 su entrambe le estremita'.
# veth grezzo sudo ip link add veth-fw1-raw type veth peer name veth-dmz-raw sudo ip link set veth-fw1-raw netns ns-fw1 sudo ip link set veth-dmz-raw netns ns-dmz # VLAN 10 su ns-fw1 sudo ip netns exec ns-fw1 ip link set veth-fw1-raw up sudo ip netns exec ns-fw1 ip link add link veth-fw1-raw name veth-fw1.10 type vlan id 10 sudo ip netns exec ns-fw1 ip addr add 10.10.10.1/24 dev veth-fw1.10 sudo ip netns exec ns-fw1 ip link set veth-fw1.10 up # VLAN 10 su ns-dmz sudo ip netns exec ns-dmz ip link set veth-dmz-raw up sudo ip netns exec ns-dmz ip link add link veth-dmz-raw name veth-dmz.10 type vlan id 10 sudo ip netns exec ns-dmz ip addr add 10.10.10.2/24 dev veth-dmz.10 sudo ip netns exec ns-dmz ip link set veth-dmz.10 up sudo ip netns exec ns-dmz ip link set lo up- Come sai che hai finito:
sudo ip netns exec ns-fw1 ping -c1 10.10.10.2funziona - Verifica VLAN tag:
sudo ip netns exec ns-fw1 tcpdump -i veth-fw1-raw -e -c5 &poi ping → vedivlan 10nell'output
- Come sai che hai finito:
Task 1.4 — Veth pair ns-dmz ↔ ns-fw2 con VLAN 20
Stessa procedura del task 1.3 ma con VLAN ID 20 e subnet 10.20.20.0/24.
sudo ip link add veth-d20-raw type veth peer name veth-fw2-raw sudo ip link set veth-d20-raw netns ns-dmz sudo ip link set veth-fw2-raw netns ns-fw2 # VLAN 20 su ns-dmz sudo ip netns exec ns-dmz ip link set veth-d20-raw up sudo ip netns exec ns-dmz ip link add link veth-d20-raw name veth-d20.20 type vlan id 20 sudo ip netns exec ns-dmz ip addr add 10.20.20.1/24 dev veth-d20.20 sudo ip netns exec ns-dmz ip link set veth-d20.20 up # VLAN 20 su ns-fw2 sudo ip netns exec ns-fw2 ip link set veth-fw2-raw up sudo ip netns exec ns-fw2 ip link add link veth-fw2-raw name veth-fw2.20 type vlan id 20 sudo ip netns exec ns-fw2 ip addr add 10.20.20.2/24 dev veth-fw2.20 sudo ip netns exec ns-fw2 ip link set veth-fw2.20 up sudo ip netns exec ns-fw2 ip link set lo up- Come sai che hai finito:
sudo ip netns exec ns-dmz ping -c1 10.20.20.2funziona
- Come sai che hai finito:
Task 1.5 — Veth pair ns-fw2 ↔ ns-lan
sudo ip link add veth-f-lan type veth peer name veth-lan sudo ip link set veth-f-lan netns ns-fw2 sudo ip link set veth-lan netns ns-lan sudo ip netns exec ns-fw2 ip addr add 10.30.30.1/24 dev veth-f-lan sudo ip netns exec ns-fw2 ip link set veth-f-lan up sudo ip netns exec ns-lan ip addr add 10.30.30.2/24 dev veth-lan sudo ip netns exec ns-lan ip link set veth-lan up sudo ip netns exec ns-lan ip link set lo up- Come sai che hai finito:
sudo ip netns exec ns-fw2 ping -c1 10.30.30.2funziona
- Come sai che hai finito:
Fase 2 — Routing e IP forwarding#
Ogni namespace "firewall" deve instradare pacchetti tra zone diverse.
Task 2.1 — Abilita IP forwarding nei namespace router
sudo ip netns exec ns-fw1 sysctl -w net.ipv4.ip_forward=1 sudo ip netns exec ns-fw2 sysctl -w net.ipv4.ip_forward=1 # Root namespace (gia' abilitato di solito — verifica) sudo sysctl -w net.ipv4.ip_forward=1Task 2.2 — Route in ns-fw1
# ns-fw1 deve raggiungere ns-dmz (gia' diretto) e sapere dove tornare verso root sudo ip netns exec ns-fw1 ip route add default via 10.0.0.1Task 2.3 — Route in ns-dmz
# ns-dmz raggiunge FW1 (default upstream) e FW2 (diretto VLAN 20) sudo ip netns exec ns-dmz ip route add default via 10.10.10.1 # il route verso 10.20.20.0/24 e' gia' diretto via veth-d20.20Task 2.4 — Route in ns-fw2
sudo ip netns exec ns-fw2 ip route add default via 10.20.20.1Task 2.5 — Route in ns-lan
sudo ip netns exec ns-lan ip route add default via 10.30.30.1Task 2.6 — Route nel root namespace verso le zone interne
# Root deve sapere che 10.x.x.x si raggiunge via ns-fw1 sudo ip route add 10.10.10.0/24 via 10.0.0.2 sudo ip route add 10.20.20.0/24 via 10.0.0.2 sudo ip route add 10.30.30.0/24 via 10.0.0.2- Come sai che hai finito:
ping -c1 10.30.30.2dalla root funziona (prima di attivare i firewall)
- Come sai che hai finito:
Task 2.7 — Aggiungi route su Kali verso le zone interne
# Da Kali — per simulare traffico "internet" verso la DMZ sudo ip route add 10.10.10.0/24 via 192.168.64.3 sudo ip route add 10.20.20.0/24 via 192.168.64.3 sudo ip route add 10.30.30.0/24 via 192.168.64.3
Fase 3 — Firewall 1 (ns-fw1): regole di accesso alla DMZ#
ns-fw1 e' il firewall perimetrale. Permette solo HTTP/HTTPS verso la DMZ.
Prima di applicare le regole verifica che il ping root→10.30.30.2 funzioni (Fase 2). Dopo le regole, dovrebbe smettere di funzionare (ns-fw1 droppa ICMP).
Task 3.1 — Regole FORWARD su ns-fw1
Obiettivo: Firewall 1 permette solo TCP porta 80 e 443 verso ns-dmz (10.10.10.2). Tutto il resto viene droppato.
# Policy default DROP su FORWARD sudo ip netns exec ns-fw1 iptables -P FORWARD DROP # Permetti connessioni nuove verso WAF su 80/443 sudo ip netns exec ns-fw1 iptables -A FORWARD \ -p tcp -d 10.10.10.2 --dport 80 -m state --state NEW,ESTABLISHED -j ACCEPT sudo ip netns exec ns-fw1 iptables -A FORWARD \ -p tcp -d 10.10.10.2 --dport 443 -m state --state NEW,ESTABLISHED -j ACCEPT # Permetti return traffic (ESTABLISHED) in entrambe le direzioni sudo ip netns exec ns-fw1 iptables -A FORWARD \ -m state --state ESTABLISHED,RELATED -j ACCEPT- Come sai che hai finito:
# Questo deve funzionare (porta 80 verso WAF) curl -s --max-time 3 http://10.10.10.2/ --connect-to 10.10.10.2:80:10.10.10.2:80 # Questo deve dare timeout (porta 8080 verso backend diretto) curl -s --max-time 3 http://10.30.30.2:8080/
- Come sai che hai finito:
Task 3.2 — Verifica da Kali
# Da Kali — la DMZ deve essere raggiungibile su 80 curl -s --max-time 3 http://10.10.10.2/ # Da Kali — il backend LAN deve essere irraggiungibile curl -s --max-time 3 http://10.30.30.2:8080/
Fase 4 — WAF nella DMZ (nginx + ModSecurity)#
nginx gira nel namespace ns-dmz. Ascolta su 10.10.10.2:80, proxya il backend in ns-lan (10.30.30.2:8080), applica OWASP CRS.
Task 4.1 — Avvia il backend in ns-lan
# Backend simulato: Python HTTP server sudo ip netns exec ns-lan python3 -m http.server 8080 \ --bind 10.30.30.2 --directory /var/www/html &- Come sai che hai finito:
sudo ip netns exec ns-dmz curl -s http://10.30.30.2:8080/risponde
- Come sai che hai finito:
Task 4.2 — Crea configurazione nginx WAF per ns-dmz
Crea
/etc/nginx/nginx-waf.confcon:pid /run/nginx-waf.pid;(pid separato dal nginx principale)modsecurity on;+modsecurity_rules_file /etc/nginx/modsecurity.conf;listen 10.10.10.2:80;proxy_pass http://10.30.30.2:8080;nel location block
Schema configurazione:
# /etc/nginx/nginx-waf.conf pid /run/nginx-waf.pid; load_module modules/ngx_http_modsecurity_module.so; events { worker_connections 1024; } http { server { listen 10.10.10.2:80; server_name mustache-dmz; modsecurity on; modsecurity_rules_file /etc/nginx/modsecurity.conf; location / { proxy_pass http://10.30.30.2:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } } }Task 4.3 — Avvia nginx nel namespace ns-dmz
sudo ip netns exec ns-dmz nginx -c /etc/nginx/nginx-waf.conf- Come sai che hai finito:
sudo ip netns exec ns-dmz curl -s http://10.10.10.2/restituisce la pagina del backend
- Come sai che hai finito:
Task 4.4 — Passa ModSecurity in enforcement mode
Nel file
/etc/nginx/modsecurity.confcambiaSecRuleEngine DetectionOnlyinSecRuleEngine On, poi ricarica:sudo ip netns exec ns-dmz nginx -c /etc/nginx/nginx-waf.conf -s reload
Fase 5 — Firewall 2 (ns-fw2): protegge il backend LAN#
ns-fw2 e' il firewall interno. Permette traffico verso ns-lan SOLO dall'IP del WAF (10.10.10.2).
Se ns-fw2 e' troppo restrittivo nginx non puo' nemmeno raggiungere il backend. Il source IP del proxy e' 10.10.10.2 (interfaccia ns-dmz lato FW1) — verifica con tcpdump prima di applicare le regole.
Task 5.1 — Cattura il source IP reale in ns-fw2
# In ns-fw2, osserva il source IP quando nginx fa proxy verso il backend sudo ip netns exec ns-fw2 tcpdump -i any port 8080 -nn -c10 # (poi fai una richiesta via curl sul WAF e osserva)- Come sai che hai finito: identifichi il source IP che ns-fw2 vede per le richieste WAF
Task 5.2 — Regole FORWARD su ns-fw2
sudo ip netns exec ns-fw2 iptables -P FORWARD DROP # Permetti solo WAF → backend (usa l'IP source identificato al task 5.1) sudo ip netns exec ns-fw2 iptables -A FORWARD \ -p tcp -s 10.10.10.2 -d 10.30.30.2 --dport 8080 \ -m state --state NEW,ESTABLISHED -j ACCEPT sudo ip netns exec ns-fw2 iptables -A FORWARD \ -m state --state ESTABLISHED,RELATED -j ACCEPT- Come sai che hai finito: traffico via WAF funziona ancora;
curldiretto da root a 10.30.30.2:8080 → timeout
- Come sai che hai finito: traffico via WAF funziona ancora;
Fase 6 — Test di verifica completa#
Esegui questa sequenza. Ogni test deve dare il risultato atteso.
Test 6.1 — Accesso normale al WAF (deve funzionare)
# Da Kali o da root namespace curl -v http://10.10.10.2/ # Atteso: 200 OK con contenuto del backendTest 6.2 — SQL Injection bloccata dal WAF
curl -v "http://10.10.10.2/?id=1'+OR+'1'='1" # Atteso: 403 Forbidden (ModSecurity CRS regola 942100)Test 6.3 — XSS bloccato dal WAF
curl -v "http://10.10.10.2/?q=<script>alert(1)</script>" # Atteso: 403 Forbidden (ModSecurity CRS regola 941100)Test 6.4 — Accesso diretto al backend bypassando WAF (deve fallire)
curl --max-time 3 http://10.30.30.2:8080/ # Atteso: timeout — FW1 non permette traffico diretto a 10.30.30.xTest 6.5 — Accesso diretto dalla DMZ al backend senza passare per FW2 (deve fallire)
sudo ip netns exec ns-dmz curl --max-time 3 http://10.30.30.2:8080/ # Atteso: timeout — FW2 droppa traffico che non viene dal WAF IPTest 6.6 — Verifica VLAN tag con tcpdump
# In un terminale: cattura sul link grezzo tra FW1 e DMZ sudo ip netns exec ns-fw1 tcpdump -i veth-fw1-raw -e -c20 & # In un altro: manda una richiesta HTTP curl http://10.10.10.2/ # Atteso: vedi "vlan 10" nei pacchetti catturatiTest 6.7 — Leggi i log ModSecurity
sudo tail -f /var/log/nginx/error.log | grep ModSecurity # Ripeti test 6.2 — vedi la regola che ha triggerato (es: "id 942100")
Cleanup#
Al termine del lab, rimuovi namespace e interfacce:
sudo ip netns del ns-fw1
sudo ip netns del ns-dmz
sudo ip netns del ns-fw2
sudo ip netns del ns-lan
# Le veth pair vengono eliminate automaticamente con i namespace
# Ferma nginx WAF
sudo kill $(cat /run/nginx-waf.pid 2>/dev/null)Scenario Reale#
Questa e' la topologia usata in ambienti enterprise per proteggere server web pubblici. Il WAF (spesso in cloud: AWS WAF, Azure Front Door, Cloudflare) sta nella DMZ. I database e i microservizi interni stanno nella LAN, protetti dal firewall interno. Un attaccante che bypassa il WAF si trova comunque bloccato davanti al secondo firewall prima di raggiungere dati sensibili. Il Gibson Cap 3 chiama questa zona "screened subnet". Il termine VLAN qui e' usato per segmentare il traffico a livello 2 anche su infrastruttura condivisa — in cloud questo corrisponde a subnets separate all'interno di una VPC.
Risorse#
man ip-netns— documentazione network namespacesman iptables— regole FORWARD e state matching- https://coreruleset.org/ — OWASP CRS regole ModSecurity
ip netns exec <ns> <cmd>— pattern base per eseguire comandi in un namespace
Collegato a#
- cap-03-security-architecture — screened subnet, firewall types, DMZ
- [[[TODO] lab-iptables-stateful|lab-iptables-stateful]] — conntrack e regole ESTABLISHED
- [[[TODO] lab-router-acl-iptables|lab-router-acl-iptables]] — ACL e FORWARD chain


