Skip to main content
  1. Blog/

Lab 05: Linux come Firewall e iptables

·17 mins
Alessio Barnini
Author
Alessio Barnini
Table of Contents
Lab Cisco Packet Tracer - This article is part of a series.
Part 5: This Article

Il lab precedente
#

Nel Lab 04 abbiamo creato la topologia di rete. Ora la mettiamo in sicurezza.

iptables default DROP - il security-level
#

Cisco: cisco-packet-tracer-dmz#FW1 - configurazione|FW1 - configurazione — su un ASA Cisco il traffico da zona a security-level piu' basso verso zona a security-level piu' alto e' bloccato per default. Nessuna ACL = nessun transito. iptables -P FORWARD DROP e' la stessa policy: la chain e' vuota, tutto cade nella policy di default.

Stato attualeObiettivoCome
Kali → Sofia :qualsiasi✓ PASS✗ DROP-P FORWARD DROP su FW1
Sofia → Giulia :qualsiasi✓ PASS✗ DROP-P FORWARD DROP su FW2
Kali → Giulia :qualsiasi✓ PASS✗ DROPDROP su FW1 + FW2 in cascata
# ns-fw1: blocca tutto il traffico in transito.
# equivalente di: access-group outside_access_in in interface outside (deny-all implicito)
sudo ip netns exec ns-fw1 iptables -P FORWARD DROP

# ns-fw2: LAN isolata per default.
# nessun pacchetto dalla DMZ raggiunge ns-giulia senza una regola esplicita
sudo ip netns exec ns-fw2 iptables -P FORWARD DROP

Verifica immediata dopo il DROP — Kali non raggiunge piu' Sofia:

# da Kali VM
ping -c3 10.10.10.2
# 3 packets transmitted, 0 received, 100% packet loss

Verifica su ns-fw1 — i pacchetti droppati sono contati dalla policy:

sudo ip netns exec ns-fw1 iptables -L FORWARD -v -n
# Chain FORWARD (policy DROP 3 packets, 252 bytes)

La chain e' vuota — nessuna regola esplicita. I 3 pacchetti sono esattamente i 3 ping di Kali, droppati dalla policy di default. Non da una regola, dalla policy. Differenza importante: le regole esplicite (-A FORWARD) compaiono nella lista sopra la policy. La policy e' il catch-all finale per tutto quello che non matcha nessuna regola.

flowchart LR
    classDef ext fill:#4a0d0d,stroke:#e05555,color:#eee
    classDef fw fill:#3d0d0d,stroke:#ff4444,color:#eee
    classDef dmz fill:#2d2010,stroke:#886633,color:#aaa
    classDef lan fill:#0d1a0d,stroke:#336633,color:#aaa

    KALI["Kali"]:::ext
    FW1["ns-fw1
FORWARD DROP
chain vuota"]:::fw SOFIA["ns-sofia
irraggiungibile"]:::dmz FW2["ns-fw2
FORWARD DROP
chain vuota"]:::fw GIULIA[("ns-giulia
irraggiungibile")]:::lan KALI -. "qualsiasi traffico" .-> FW1 FW1 -. "✗ DROP — nessuna regola" .-> SOFIA SOFIA -. "qualsiasi traffico" .-> FW2 FW2 -. "✗ DROP — nessuna regola" .-> GIULIA

Isolamento namespace - il test equivalente alle VLAN
#

Cisco: su un ASA con security-level configurati, un PC nella LAN non puo' raggiungere la DMZ senza una access-list esplicita — anche se conosce il percorso. Stesso principio: la policy DROP blocca il transito indipendentemente dal routing.

Tre test dopo iptables -P FORWARD DROP su ns-fw1 e ns-fw2:

ns-giulia → ns-sofia (10.10.10.2)
#

sudo ip netns exec ns-giulia ping -c3 10.10.10.2
# 3 packets transmitted, 0 received, 100% packet loss

Il pacchetto parte da ns-giulia verso il default gateway ns-fw2 (10.30.30.1). ns-fw2 dovrebbe instradarlo verso la DMZ — ma la FORWARD chain di ns-fw2 ha policy DROP. Il pacchetto viene silenziosamente scartato.

mindmap
  root((ns-giulia a ns-sofia))
    Percorso tentato
      ns-giulia a ns-fw2 default gw
      ns-fw2 dovrebbe forwardare a DMZ
    Risultato DROP
      FORWARD chain ns-fw2 policy DROP
      Scartato silenziosamente

ns-sofia → ns-giulia (10.30.30.2)
#

sudo ip netns exec ns-sofia ping -c3 10.30.30.2
# From 10.10.10.1: icmp_seq=1 Redirect Host(New nexthop: 10.10.10.3)
# From 10.10.10.1: icmp_seq=2 Redirect Host(New nexthop: 10.10.10.3)
# From 10.10.10.1: icmp_seq=3 Redirect Host(New nexthop: 10.10.10.3)
# 3 packets transmitted, 0 received, 100% packet loss

ns-fw1 manda ancora l'ICMP Redirect (vedi Linux-namespaces-dmz#Test 7 - ns-sofia → ns-giulia|Test 7) suggerendo a Sofia di andare direttamente a ns-fw2. Sofia ci prova — ma ns-fw2 riceve il pacchetto sulla sua interfaccia DMZ e deve instradarlo verso la LAN: FORWARD chain → DROP. Il redirect e' inutile se il firewall successivo blocca comunque.

mindmap
  root((ns-sofia a ns-giulia))
    ICMP Redirect da ns-fw1
      ns-fw1 suggerisce nexthop ns-fw2
    Risultato DROP
      ns-fw2 FORWARD chain DROP
      Redirect inutile se FW blocca

ns-fw1 → ns-sofia (10.10.10.2)
#

sudo ip netns exec ns-fw1 ping -c3 10.10.10.2
# 64 bytes from 10.10.10.2: icmp_seq=1 ttl=64 time=0.081 ms
# 3 packets transmitted, 3 received, 0% packet loss

Funziona — TTL=64, nessun drop. ns-fw1 e' il mittente del pacchetto, non un router di transito. La FORWARD chain si applica solo ai pacchetti che attraversano il namespace da un'interfaccia all'altra. Un pacchetto originato dal namespace stesso passa per la chain OUTPUT, non FORWARD.

TestPercorsoChain coinvoltaRisultato
ns-giulia → ns-sofians-fw2 deve forwardareFORWARD ns-fw2DROP
ns-sofia → ns-giulians-fw2 deve forwardareFORWARD ns-fw2DROP
ns-fw1 → ns-sofians-fw1 e' il mittenteOUTPUT ns-fw1PASS

La regola e' semplice: FORWARD = traffico in transito. OUTPUT = traffico originato localmente. iptables -P FORWARD DROP non isola il firewall da se stesso — isola le zone tra loro.

flowchart TB
    classDef ext fill:#4a0d0d,stroke:#e05555,color:#eee
    classDef fw fill:#0d1a3d,stroke:#4a9eff,color:#eee
    classDef dmz fill:#3d2a0d,stroke:#ffaa4a,color:#eee
    classDef drop fill:#3d0d0d,stroke:#ff4444,color:#eee
    classDef pass fill:#0d2d15,stroke:#4aff7e,color:#eee

    subgraph TRANSIT["Pacchetto in TRANSITO — chain FORWARD"]
        K["Kali"]:::ext -. "SYN verso Sofia" .-> F1T["ns-fw1
FORWARD chain
policy DROP"]:::fw -. "✗ DROP" .-> D["scartato"]:::drop end subgraph LOCAL["Pacchetto ORIGINATO da ns-fw1 — chain OUTPUT"] F1L["ns-fw1
OUTPUT chain
nessun DROP"]:::fw -->|"✓ PASS"| S["ns-sofia
risponde TTL=64"]:::dmz end
mindmap
  root((ns-fw1 a ns-sofia))
    Risultato PASS
      TTL 64 nessun drop
    Motivo
      ns-fw1 e mittente non transito
      Chain OUTPUT non FORWARD

Security-level equivalente: chi puo' parlare con chi
#

Cisco: cisco-packet-tracer-dmz#Security-level: chi puo' parlare con chi|Security-level ASA — su un ASA ogni interfaccia ha un security-level da 0 a 100. Il traffico fluisce liberamente da livello alto a livello basso (inside → DMZ → outside), il contrario richiede una access-list esplicita. In Linux non esiste il concetto di security-level — tutto e' simmetrico e tutto e' bloccato. Ogni direzione richiede una regola esplicita.

ZonaCisco security-leveliptables equivalente
Outside (Kali)0nessuna interfaccia privilegiata — tutto bloccato
DMZ (ns-sofia)50FORWARD chain ns-fw1: regole esplicite per HTTP/HTTPS
Inside (ns-giulia)100FORWARD chain ns-fw2: regola esplicita per MySQL

La differenza fondamentale:

In Cisco, inside (100) puo' raggiungere DMZ (50) e outside (0) senza ACL — il traffico fluisce naturalmente verso security-level piu' basso. Il firewall e' asimmetrico per design.

In Linux con iptables, la simmetria e' totale: ns-giulia non puo' raggiungere ns-sofia, e ns-sofia non puo' raggiungere ns-giulia, finche' non esiste una regola esplicita in entrambe le direzioni. Non c'e' gerarchia — c'e' solo permit o deny.

DirezioneCisco (default)iptables (dopo FORWARD DROP)
Kali → SofiaDENY (0→50)DENY
Kali → GiuliaDENY (0→100)DENY
Sofia → KaliPERMIT (50→0)DENY
Sofia → GiuliaDENY (50→100)DENY
Giulia → SofiaPERMIT (100→50)DENY
Giulia → KaliPERMIT (100→0)DENY

In produzione con iptables si replicano le stesse policy Cisco con regole esplicite — piu' verboso, ma piu' controllabile. Nessun comportamento implicito da ricordare.

Connessioni permesse — stato finale
#

flowchart LR
    classDef ext fill:#4a0d0d,stroke:#e05555,color:#eee
    classDef fw fill:#0d1a3d,stroke:#4a9eff,color:#eee
    classDef dmz fill:#3d2a0d,stroke:#ffaa4a,color:#eee
    classDef lan fill:#0d2d15,stroke:#4aff7e,color:#eee

    KALI["Kali
192.168.64.200"]:::ext FW1["ns-fw1
FORWARD DROP
ACL: 80, 443"]:::fw SOFIA["ns-sofia
nginx + WAF
10.10.10.2"]:::dmz FW2["ns-fw2
FORWARD DROP
ACL: 3306 da Sofia"]:::fw GIULIA[("ns-giulia
MySQL
10.30.30.2")]:::lan KALI -->|"TCP 80"| FW1 KALI -->|"TCP 443"| FW1 FW1 -->|"PASS"| SOFIA SOFIA -->|"TCP 3306"| FW2 FW2 -->|"PASS"| GIULIA

Dove si interrompe e perche'
#

flowchart LR
    classDef ext fill:#4a0d0d,stroke:#e05555,color:#eee
    classDef fw fill:#0d1a3d,stroke:#4a9eff,color:#eee
    classDef dmz fill:#3d2a0d,stroke:#ffaa4a,color:#eee
    classDef lan fill:#0d2d15,stroke:#4aff7e,color:#eee
    classDef drop fill:#1a1a1a,stroke:#666,color:#888

    KALI["Kali"]:::ext
    FW1["ns-fw1
FORWARD DROP"]:::fw FW2["ns-fw2
FORWARD DROP"]:::fw SOFIA["ns-sofia"]:::dmz GIULIA[("ns-giulia")]:::lan DROP1["✗ DROP
nessuna ACL per porta 22"]:::drop DROP2["✗ DROP
nessuna ACL verso LAN"]:::drop DROP3["✗ DROP
non e' 10.10.10.2"]:::drop DROP4["✗ DROP
nessuna ACL outbound"]:::drop DROP5["✗ DROP
nessuna ACL su porta 22"]:::drop KALI -. "TCP 22 SSH" .-> FW1 -. " " .-> DROP1 KALI -. "TCP 3306 verso LAN" .-> FW1 -. " " .-> DROP2 GIULIA -. "TCP 80 inizia connessione" .-> FW2 -. " " .-> DROP4 SOFIA -. "TCP 3306 altro host DMZ" .-> FW2 -. " " .-> DROP3 SOFIA -. "TCP 22 verso Giulia" .-> FW2 -. " " .-> DROP5

Il ciclo richiesta-risposta — chi inizia e chi risponde
#

sequenceDiagram
    participant Kali
    participant FW1
    participant Sofia
    participant FW2
    participant Giulia

    Note over Kali,FW1: ACL: TCP 80 in entrata
    Kali->>FW1: SYN TCP 80 → PASS
    FW1->>Sofia: SYN forwarded

    Note over Sofia,FW1: ACL: ESTABLISHED in uscita
    Sofia->>FW1: SYN-ACK → PASS
    FW1->>Kali: SYN-ACK forwarded

    Note over Sofia,FW2: ACL: TCP 3306 da 10.10.10.2
    Sofia->>FW2: SYN TCP 3306 → PASS
    FW2->>Giulia: SYN forwarded

    Note over Giulia,FW2: ACL: ESTABLISHED in uscita
    Giulia->>FW2: SYN-ACK → PASS
    FW2->>Sofia: SYN-ACK forwarded

    Note over Giulia,FW2: Giulia NON puo' iniziare
    Giulia--xFW2: SYN TCP 80 → DROP

iptables ACL su ns-fw1 - HTTP e HTTPS verso ns-sofia
#

Cisco: cisco-packet-tracer-dmz#FW1 - configurazione|FW1 ACL — su un ASA le access-list aprono porte specifiche dopo il default deny. access-list outside_in permit tcp any host 10.10.10.2 eq 80 diventa in Linux una regola FORWARD esplicita su ns-fw1.

Stato attualeObiettivoCome
Kali → Sofia :80✗ DROP✓ PASSACCEPT tcp:80 in entrata su FW1
Kali → Sofia :443✗ DROP✗ rimane bloccatonessuna regola per ora
Sofia → Kali (risposta)✗ DROP✓ PASSACCEPT ESTABLISHED su FW1

Con iptables -P FORWARD DROP attivo, aggiungiamo due regole su ns-fw1: una per i pacchetti in ingresso verso Sofia, una per le risposte.

# apri TCP porta 80 da outside verso DMZ (Kali → ns-sofia)
sudo ip netns exec ns-fw1 iptables -A FORWARD \
  -i veth-fw1-out -o veth-fw1-dmz \
  -p tcp --dport 80 -j ACCEPT

# permetti il traffico di ritorno: SYN-ACK, ACK, dati in risposta (ns-sofia → Kali)
# ESTABLISHED = connessione gia' aperta, RELATED = traffico correlato (es. ICMP error)
sudo ip netns exec ns-fw1 iptables -A FORWARD \
  -i veth-fw1-dmz -o veth-fw1-out \
  -m state --state ESTABLISHED,RELATED -j ACCEPT

Verifica regole aggiunte:

sudo ip netns exec ns-fw1 iptables -L FORWARD -v -n
# Chain FORWARD (policy DROP 6 packets, 504 bytes)
#  pkts bytes target  prot opt in             out            source    destination
#     0     0 ACCEPT  6    --  veth-fw1-out   veth-fw1-dmz   0.0.0.0/0 0.0.0.0/0  tcp dpt:80
#     0     0 ACCEPT  0    --  veth-fw1-dmz   veth-fw1-out   0.0.0.0/0 0.0.0.0/0  state RELATED,ESTABLISHED

I contatori sono a 0 — le regole ci sono ma nessun pacchetto ha ancora attraversato la chain.

Primo test: 0 pacchetti — diagnosi
#

Listener su ns-sofia, connessione da Kali:

# Ubuntu - terminale 1
sudo ip netns exec ns-sofia nc -l -p 80

# Kali - terminale 2
echo "hello world" | nc 10.10.10.2 80

Niente. Pacchetti ancora 0. Il testo non arriva su ns-sofia.

tcpdump su ns-fw1 per vedere se il SYN di Kali arriva sull'interfaccia outside:

sudo ip netns exec ns-fw1 tcpdump -i veth-fw1-out -n
# nessun output

Il pacchetto non arriva nemmeno a ns-fw1. Il problema e' prima — su Ubuntu host.

tcpdump su enp0s1 — l'interfaccia dove Ubuntu riceve i pacchetti di Kali:

sudo tcpdump -i enp0s1 -n host 192.168.64.200
# 20:22:02 IP 192.168.64.200.50882 > 10.10.10.2.80: Flags [S], seq 2068371333 ...
# 20:22:03 IP 192.168.64.200.50882 > 10.10.10.2.80: Flags [S], seq 2068371333 ...
# 20:22:04 IP 192.168.64.200.50882 > 10.10.10.2.80: Flags [S], seq 2068371333 ...

Il SYN arriva su Ubuntu — ma non viene inoltrato a ns-fw1. Ubuntu riceve e droppa.

Root cause — FORWARD chain di Ubuntu host:

sudo iptables -L FORWARD -v -n
# Chain FORWARD (policy DROP 68 packets, 4080 bytes)
#  pkts bytes target        prot opt in   out   source    destination
# 38265  417M DOCKER-USER   0    --  *    *     0.0.0.0/0 0.0.0.0/0
# 38265  417M DOCKER-FORWARD 0   --  *    *     0.0.0.0/0 0.0.0.0/0
#    95  6348 ufw-before-forward  0  --  *    *  0.0.0.0/0 0.0.0.0/0
#    68  4080 ufw-reject-forward  0  --  *    *  0.0.0.0/0 0.0.0.0/0
# ...

Lo stesso problema di ip_forward: Docker e ufw hanno la loro FORWARD chain sul namespace di default di Ubuntu. Nessuna regola permette il traffico tra enp0s1 e veth-host — tutto cade nella policy DROP del host.

Docker/ufw e la FORWARD chain dell'host

Docker imposta iptables -P FORWARD DROP sull'host e aggiunge le sue catene (DOCKER-USER, DOCKER-FORWARD) per gestire il traffico dei container. ufw aggiunge le sue (ufw-before-forward, ufw-reject-forward). Il traffico del nostro lab non e' Docker e non e' noto a ufw — viene droppato silenziosamente.

In un ambiente senza Docker/ufw la FORWARD chain dell'host avrebbe policy ACCEPT di default e questo problema non si presenterebbe.

Fix — aggiungi regole FORWARD sull'host Ubuntu:

# permetti forwarding da Kali (enp0s1) verso ns-fw1 (veth-host)
# -I inserisce in cima alla chain, prima delle regole Docker/ufw
sudo iptables -I FORWARD -i enp0s1 -o veth-host -j ACCEPT
sudo iptables -I FORWARD -i veth-host -o enp0s1 -j ACCEPT
mindmap
  root((Primo test diagnosi))
    Sintomo
      nc non riceve niente
      Contatori ns-fw1 a zero
    Diagnosi
      tcpdump ns-fw1 nessun SYN
      tcpdump enp0s1 SYN arriva
      Ubuntu droppa prima del namespace
    Root cause
      FORWARD chain Ubuntu host
      Docker e ufw policy DROP
      Traffico non e Docker non e noto

Secondo test: funziona
#

# Ubuntu
sudo ip netns exec ns-sofia nc -l -p 80

# Kali
echo "hello world" | nc 10.10.10.2 80

Su ns-sofia compare:

hello world

Verifica contatori ns-fw1 dopo il test:

sudo ip netns exec ns-fw1 iptables -L FORWARD -v -n
# Chain FORWARD (policy DROP 6 packets, 504 bytes)
#  pkts bytes target  prot opt in             out
#     4   228 ACCEPT  6    --  veth-fw1-out   veth-fw1-dmz   tcp dpt:80
#     4   216 ACCEPT  0    --  veth-fw1-dmz   veth-fw1-out   state RELATED,ESTABLISHED

4 pacchetti per direzione: SYN, SYN-ACK, ACK, dato. La regola funziona. Il testo e' passato attraverso ns-fw1 come previsto.

flowchart LR
    classDef ext fill:#4a0d0d,stroke:#e05555,color:#eee
    classDef fw fill:#0d1a3d,stroke:#4a9eff,color:#eee
    classDef fwblock fill:#3d0d0d,stroke:#ff4444,color:#eee
    classDef dmz fill:#3d2a0d,stroke:#ffaa4a,color:#eee
    classDef lan fill:#0d2d15,stroke:#4aff7e,color:#eee
    classDef drop fill:#1a1a1a,stroke:#555,color:#888

    KALI["Kali"]:::ext
    FW1["ns-fw1
ACL: TCP 80 in
ESTABLISHED out"]:::fw SOFIA["ns-sofia
nc listener :80"]:::dmz FW2["ns-fw2
FORWARD DROP
nessuna ACL"]:::fwblock GIULIA[("ns-giulia")]:::lan DROP1["✗ DROP
porta non in ACL"]:::drop DROP2["✗ DROP
FW2 no ACL"]:::drop KALI -->|"TCP 80 ✓
hello world"| FW1 -->|"PASS"| SOFIA SOFIA -->|"ESTABLISHED ✓
ritorno"| FW1 -->|"PASS"| KALI KALI -. "TCP 443, 22..." .-> DROP1 SOFIA -. "verso Giulia" .-> FW2 -. " " .-> DROP2
mindmap
  root((Secondo test funziona))
    Fix applicato
      iptables -I FORWARD enp0s1 a veth-host
      iptables -I FORWARD veth-host a enp0s1
    Risultato
      hello world arriva su ns-sofia
      4 pacchetti per direzione
      SYN SYN-ACK ACK dato

iptables ACL su ns-fw2 - MySQL verso ns-giulia
#

Cisco: cisco-packet-tracer-dmz#ACL su FW2 - MySQL verso Giulia/MySQL|FW2 ACL — access-list DMZ_IN permit tcp host 10.10.10.2 host 10.30.30.2 eq 3306. La stessa logica: la sorgente e' un IP specifico, non any. Solo ns-sofia puo' aprire connessioni verso il database.

Stato attualeObiettivoCome
Kali → Giulia :3306✗ DROP a FW1✗ rimane bloccatonessuna regola su FW1 verso LAN
Sofia → Giulia :3306✗ DROP a FW2 (no ACL)✓ PASSACCEPT tcp:3306 src 10.10.10.2 su FW2
Sofia → Giulia :altra porta✗ DROP a FW2✗ rimane bloccatonessuna regola su FW2
# Sofia → Giulia: bloccato a FW2 con ICMP Redirect da FW1
sudo ip netns exec ns-sofia ping -c3 10.30.30.2
# From 10.10.10.1: icmp_seq=1 Redirect Host(New nexthop: 10.10.10.3)
# From 10.10.10.1: icmp_seq=2 Redirect Host(New nexthop: 10.10.10.3)
# From 10.10.10.1: icmp_seq=3 Redirect Host(New nexthop: 10.10.10.3)
# 3 packets transmitted, 0 received, 100% packet loss

# Kali → Giulia: bloccato a FW1 (nessuna ACL verso LAN)
ping -c3 10.30.30.2
# 3 packets transmitted, 0 received, 100% packet loss

FW2 ha policy DROP. ns-sofia non puo' raggiungere ns-giulia. Apriamo solo MySQL (3306) e solo dall'IP specifico del web server — non da qualsiasi host della DMZ.

# apri TCP porta 3306 solo da ns-sofia (10.10.10.2) verso ns-giulia (10.30.30.2).
# -s 10.10.10.2: solo il web server puo' aprire connessioni al database.
# se un attaccante compromette un altro host in DMZ, il suo IP non matcha
# questa regola e viene droppato — lateral movement bloccato a livello L3/L4.
#
# -i veth-fw2-dmz -o veth-fw2-lan: il traffico entra dal lato DMZ ed esce
# verso la LAN. Se arrivasse da un'altra interfaccia la regola non matcha.
sudo ip netns exec ns-fw2 iptables -A FORWARD \
  -i veth-fw2-dmz -o veth-fw2-lan \
  -p tcp --dport 3306 \
  -s 10.10.10.2 -d 10.30.30.2 \
  -j ACCEPT

# traffico di ritorno: risposte MySQL da ns-giulia verso ns-sofia.
# senza questa regola il SYN-ACK verrebbe droppato — la connessione non si apre.
# ESTABLISHED,RELATED: solo risposte a connessioni gia' aperte da Sofia.
# ns-giulia non puo' avviare connessioni spontanee verso la DMZ.
sudo ip netns exec ns-fw2 iptables -A FORWARD \
  -i veth-fw2-lan -o veth-fw2-dmz \
  -m state --state ESTABLISHED,RELATED -j ACCEPT

Verifica regole — pkts deve essere 0, nessun traffico e' ancora passato:

sudo ip netns exec ns-fw2 iptables -L FORWARD -v -n
# Chain FORWARD (policy DROP 3 packets, 252 bytes)
#  pkts bytes target     prot opt in           out
#     0     0 ACCEPT     6    --  veth-fw2-dmz veth-fw2-lan  10.10.10.2  10.30.30.2  tcp dpt:3306
#     0     0 ACCEPT     0    --  veth-fw2-lan veth-fw2-dmz  0.0.0.0/0   0.0.0.0/0   state RELATED,ESTABLISHED

Problema: il routing di Sofia non passa per FW2

Prima di testare, controllo dove Sofia manda effettivamente il traffico verso 10.30.30.2:

# ip route get mostra il next-hop esatto che il kernel userebbe per raggiungere l'IP
sudo ip netns exec ns-sofia ip route get 10.30.30.2
# 10.30.30.2 via 10.10.10.1 dev veth-sofia src 10.10.10.2 uid 0
#     cache

Il next-hop e' 10.10.10.1 — ns-fw1. Questo e' il default gateway di Sofia, e ogni traffico verso subnet sconosciute ci passa. ns-fw1 riceve il pacchetto, non ha nessuna regola per Sofia→LAN, FORWARD DROP: il pacchetto sparisce. ns-fw2 non lo vede mai — i contatori rimangono a 0.

Su Cisco il problema non esiste perche' Sofia e' sulla DMZ e il default gateway del PC e' FW2. Qui la topologia e' diversa: Sofia ha solo FW1 come gateway. Va aggiunta una rotta statica per dire a Sofia di raggiungere la LAN passando direttamente per FW2:

# rotta statica: il traffico verso la LAN (10.30.30.0/30) passa per ns-fw2 (10.10.10.3),
# non per il default gateway ns-fw1 (10.10.10.1).
# senza questa rotta il SYN arriva a FW1 che lo droppa — FW2 non vede niente.
sudo ip netns exec ns-sofia ip route add 10.30.30.0/30 via 10.10.10.3

Verifica che ora il next-hop sia corretto:

sudo ip netns exec ns-sofia ip route get 10.30.30.2
# 10.30.30.2 via 10.10.10.3 dev veth-sofia src 10.10.10.2 uid 0
#     cache

Test — netcat su 3306:

# Ubuntu - terminale 1: simula MySQL in ascolto su ns-giulia
sudo ip netns exec ns-giulia nc -l -p 3306

# Ubuntu - terminale 2: connessione da ns-sofia (il web server)
# bash -c serve per eseguire la pipe dentro il namespace corretto
sudo ip netns exec ns-sofia bash -c "echo 'SELECT * FROM lezioni' | nc 10.30.30.2 3306"

Su ns-giulia compare:

SELECT * FROM lezioni

Contatori dopo il test — pkts in crescita su entrambe le regole:

sudo ip netns exec ns-fw2 iptables -L FORWARD -v -n
# Chain FORWARD (policy DROP 3 packets, 252 bytes)
#  pkts bytes target     prot opt in           out
#     4   228 ACCEPT     6    --  veth-fw2-dmz veth-fw2-lan  10.10.10.2  10.30.30.2  tcp dpt:3306
#     4   216 ACCEPT     0    --  veth-fw2-lan veth-fw2-dmz  0.0.0.0/0   0.0.0.0/0   state RELATED,ESTABLISHED
# 4 pacchetti in entrambe le direzioni: SYN, SYN-ACK, dati, FIN

Test negativo — Kali non raggiunge il database:

# da Kali VM: nessuna rotta verso la LAN, FW1 blocca tutto
nc -w3 10.30.30.2 3306
# (UNKNOWN) [10.30.30.2] 3306 (mysql) : Connection timed out
flowchart LR
    KALI["🖥 Kali
192.168.64.200"]:::ext FW1["🛡 ns-fw1
ACL: tcp :80 ✓
ACL: tcp :3306 ✗
ACL: ESTABLISHED ✓"]:::fw SOFIA["🌐 ns-sofia
10.10.10.2"]:::dmz FW2["🛡 ns-fw2
ACL: tcp :3306 ✓
solo src 10.10.10.2
ACL: ESTABLISHED ✓"]:::fw GIULIA[("🗄 ns-giulia
10.30.30.2")]:::lan KALI -- "tcp :80" --> FW1 FW1 -- "✓" --> SOFIA SOFIA -- "tcp :3306" --> FW2 FW2 -- "✓" --> GIULIA KALI -. "tcp :3306 ✗" .-> FW1 classDef ext fill:#4a1942,color:#fff,stroke:#c084fc classDef fw fill:#1e3a5f,color:#fff,stroke:#60a5fa classDef dmz fill:#1a3a2a,color:#fff,stroke:#4ade80 classDef lan fill:#3a2a1a,color:#fff,stroke:#fb923c

Sofia non parla mai direttamente con Giulia — il traffico attraversa sempre ns-fw2. ns-fw2 e' il confine tra le due zone: controlla porta, sorgente, e direzione. Kali non raggiungera' mai ns-giulia direttamente — FW1 non ha nessuna regola che permette il transito verso la LAN.


Porta 443 su ns-fw1
#

Stato attualeObiettivoCome
Kali → Sofia :80✓ PASS✓ PASSgia' aperta
Kali → Sofia :443✗ DROP✓ PASSACCEPT tcp:443 su FW1

La porta 80 e' aperta, la 443 no. Aggiungiamo HTTPS — stesso pattern della 80:

# aggiungi TCP 443 (HTTPS) alla chain FORWARD di ns-fw1.
# la regola ESTABLISHED,RELATED gia' configurata copre il traffico di ritorno
# per entrambe le porte — non serve una seconda regola.
sudo ip netns exec ns-fw1 iptables -A FORWARD \
  -i veth-fw1-out -o veth-fw1-dmz \
  -p tcp --dport 443 -j ACCEPT

Verifica — ns-fw1 ha ora tre regole:

sudo ip netns exec ns-fw1 iptables -L FORWARD -v -n
# Chain FORWARD (policy DROP X packets, Y bytes)
#  pkts bytes target  prot opt in             out
#     4   228 ACCEPT  6    --  veth-fw1-out   veth-fw1-dmz   tcp dpt:80
#     0     0 ACCEPT  6    --  veth-fw1-out   veth-fw1-dmz   tcp dpt:443
#     4   216 ACCEPT  0    --  veth-fw1-dmz   veth-fw1-out   state RELATED,ESTABLISHED

Test TCP 443 con netcat — verifica che il SYN arriva a ns-sofia prima di installare nginx con TLS:

# Ubuntu - terminale 1
sudo ip netns exec ns-sofia nc -l -p 443

# Kali - terminale 2
echo "test https" | nc 10.10.10.2 443

Il testo arriva su ns-sofia. La porta 443 attraversa FW1.

mindmap
  root((Porta 443 su ns-fw1))
    Stato prima
      80 aperta
      443 DROP
    Regola aggiunta
      ACCEPT tcp 443 in entrata
      ESTABLISHED gia copre il ritorno
    Verifica
      nc listener su ns-sofia 443
      Testo arriva da Kali
    Stato finale ns-fw1
      tcp 80 ACCEPT
      tcp 443 ACCEPT
      ESTABLISHED RELATED ACCEPT

Topologia completa — stato prima del WAF
#

Tutte le regole iptables sono in place. Prima di passare al layer applicativo (nginx + WAF), la rete e' questa:

flowchart LR
    KALI["🖥 Kali
192.168.64.200"]:::ext UBUNTU["Ubuntu host
192.168.64.1 / 10.0.0.1"]:::host FW1["🛡 ns-fw1
10.0.0.2 / 10.10.10.1
ACCEPT tcp:80
ACCEPT tcp:443
ACCEPT ESTABLISHED"]:::fw BRIDGE["br-dmz
switch L2"]:::bridge SOFIA["🌐 ns-sofia
10.10.10.2"]:::dmz FW2["🛡 ns-fw2
10.10.10.3 / 10.30.30.1
ACCEPT tcp:3306 src .10.2
ACCEPT ESTABLISHED"]:::fw GIULIA[("🗄 ns-giulia
10.30.30.2")]:::lan KALI -->|"enp0s1 / veth-host"| UBUNTU UBUNTU -->|"veth-host / veth-fw1-out"| FW1 FW1 -->|"veth-fw1-dmz / veth-fw1-dmz-br"| BRIDGE BRIDGE -->|"veth-sofia-br / veth-sofia"| SOFIA BRIDGE -->|"veth-fw2-dmz-br / veth-fw2-dmz"| FW2 FW2 -->|"veth-fw2-lan / veth-giulia"| GIULIA classDef ext fill:#4a1942,color:#fff,stroke:#c084fc classDef host fill:#2a2a2a,color:#fff,stroke:#9ca3af classDef fw fill:#1e3a5f,color:#fff,stroke:#60a5fa classDef bridge fill:#1a2a3a,color:#fff,stroke:#38bdf8 classDef dmz fill:#1a3a2a,color:#fff,stroke:#4ade80 classDef lan fill:#3a2a1a,color:#fff,stroke:#fb923c

Ogni label sugli edge mostra i due capi del cavo virtuale:

  • l'interfaccia di chi manda
  • e quella di chi riceve.

FW1 e FW2 hanno le ACL attive — tutto il resto e' DROP.



Codice

Script e file di configurazione del lab: github.com/Barno/u-random-dev

Lab Cisco Packet Tracer - This article is part of a series.
Part 5: This Article

Related