Skip to main content
  1. Lab-blue/

[DONE] Lab - Screened Subnet: Doppio Firewall, DMZ, VLAN 802.1Q, WAF

·13 mins
Alessio Barnini
Author
Alessio Barnini
Table of Contents

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.

Important

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:8080

Obiettivo: 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 8080src=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 IP

Perche' 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 (o libmodsecurity3 + libnginx-mod-http-modsecurity)
    • Come sai che hai finito: nginx -V 2>&1 | grep modsecurity mostra il modulo
  • Task 0.2 — Scarica OWASP CRS

  • Task 0.3 — Configura ModSecurity in Detection Mode

    • Crea /etc/nginx/modsecurity.conf che attivi ModSecurity con SecRuleEngine DetectionOnly
    • Includi modsecurity-crs/crs-setup.conf e modsecurity-crs/rules/*.conf
    • Come sai che hai finito: nginx -t non da errori
Tip

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-lan
  • Task 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 list mostra tutti e quattro
  • 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.2 dal root funziona
  • 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.2 funziona
    • Verifica VLAN tag: sudo ip netns exec ns-fw1 tcpdump -i veth-fw1-raw -e -c5 & poi ping → vedi vlan 10 nell'output
  • 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.2 funziona
  • 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.2 funziona

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=1
  • Task 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.1
  • Task 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.20
  • Task 2.4 — Route in ns-fw2

    sudo ip netns exec ns-fw2 ip route add default via 10.20.20.1
  • Task 2.5 — Route in ns-lan

    sudo ip netns exec ns-lan ip route add default via 10.30.30.1
  • Task 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.2 dalla root funziona (prima di attivare i firewall)
  • 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.

Important

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/
  • 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
  • Task 4.2 — Crea configurazione nginx WAF per ns-dmz

    Crea /etc/nginx/nginx-waf.conf con:

    • 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
  • Task 4.4 — Passa ModSecurity in enforcement mode

    Nel file /etc/nginx/modsecurity.conf cambia SecRuleEngine DetectionOnly in SecRuleEngine 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).

Warning

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; curl diretto da root a 10.30.30.2:8080 → timeout

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 backend
  • Test 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.x
  • Test 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 IP
  • Test 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 catturati
  • Test 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 namespaces
  • man 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

Related