- IPsec Suite: Una suite di protocolli di rete sicuri (IKE + ESP + AH) implementata a livello IP per garantire autenticazione, integrità e riservatezza.
- IKE (Internet Key Exchange): Negozia gli algoritmi di sicurezza e stabilisce le Security Association (SA) scambiando chiavi tramite Diffie-Hellman (UDP 500/4500).
- ESP (Encapsulating Security Payload): Cifra il payload dei pacchetti (ad es. con AES-256) garantendo riservatezza ed autenticazione. Supporta il NAT tramite incapsulamento NAT-T.
- AH (Authentication Header): Firma crittograficamente i pacchetti per garantirne l'integrità, ma non cifra il payload, lasciando i dati in chiaro ed esposti allo sniffing.
- Tunnel vs Transport: Tunnel mode cifra l'intero pacchetto originale aggiungendo un nuovo header IP (ideale per VPN Site-to-Site); Transport mode cifra solo il payload (ideale per host-to-host).
▶ $ history
- apt install strongswan -y
- ipsec version
- ipsec restart
- ipsec up mustache
- ipsec statusall
- tcpdump -i enp0s1 udp port 500
- tcpdump -i enp0s1 proto 50
- tcpdump -i enp0s1 proto 51 -v
- ping 192.168.64.3
Configurare IPsec host-to-host con StrongSwan e vedere con tcpdump la differenza tra ESP e AH. ESP cifra il payload - AH no. Questa distinzione è una domanda classica Security+ e fondamentale per la sicurezza di rete.

Obiettivo del laboratorio#
In questo laboratorio configureremo un collegamento IPsec host-to-host sicuro tra due macchine virtuali e ne analizzeremo in profondità i pacchetti di rete sul canale fisico:
Ubuntu (192.168.64.3) → StrongSwan concentrator (responder)
Kali (192.168.64.200) → StrongSwan initiator (client)VPN Concentrator#
In questo lab Ubuntu è il concentrator - il punto di terminazione del tunnel.
Un concentrator è il dispositivo che:
- sta in ascolto su UDP 500 (IKE)
- accetta connessioni da uno o più client
- termina il tunnel: riceve ESP cifrato, decapsula, passa il payload alla rete interna
In produzione sono appliance dedicati: Cisco ASA, Palo Alto, Fortinet. Qui usiamo StrongSwan su Ubuntu - funzione identica, hardware diverso.
Il client (Kali) non è un concentrator: apre una connessione, non ne accetta. È asimmetrico per design - il concentrator gestisce N client simultanei, ogni client gestisce 1 tunnel.
Installazione#
IPsec è uno standard - una suite di protocolli (IKE + ESP + AH). Non è software, è una specifica RFC. Il kernel Linux capisce ESP e AH nativamente, ma qualcuno deve gestire la negoziazione IKE: scambiare chiavi, creare le SA, autenticarsi.
Prima di installare, i tre protocolli che compongono IPsec:
IPsec
├── IKE - Internet Key Exchange UDP 500 / 4500
│ │ Negozia algoritmi, scambia chiavi, crea le SA.
│ │ Sparisce dopo il setup. Non trasporta dati.
│ │
│ └── NAT-T - NAT Traversal
│ Se rileva NAT, incapsula IKE in UDP 4500.
│ Necessario perché ESP non ha porte TCP/UDP
│ e i NAT non saprebbero a chi girare i pacchetti.
│
├── SA - Security Association
│ L'accordo tra due host: quale algoritmo usare,
│ quale chiave, per quanto tempo. Unidirezionale:
│ una SA per ogni direzione (inbound + outbound).
│ Identificata dall'SPI (Security Parameter Index).
│
├── ESP - Encapsulating Security Payload IP proto 50
│ Cifra il payload (AES-256) + autentica (HMAC).
│ È quello che vedi sul cavo dopo il tunnel up.
│ Funziona con NAT (NAT-T lo incapsula in UDP 4500).
│
└── AH - Authentication Header IP proto 51
Autentica il pacchetto ma NON cifra nulla.
Payload visibile in tcpdump.
Non funziona con NAT (firma anche gli IP header).
Praticamente mai usato in produzione.StrongSwan è il daemon che fa quella parte. Quando fai sudo ipsec up mustache, stai dicendo a StrongSwan di avviare una negoziazione IKE verso l'altro host. StrongSwan poi parla con il kernel per installare le Security Association, e da quel momento il kernel gestisce ESP direttamente senza che StrongSwan sia in mezzo.
Il comando ipsec che usi è il CLI di StrongSwan - è lui che lo installa.
┌─────────────────────────────────────────┐
│ sudo ipsec up mustache │
│ │ │
│ StrongSwan daemon │ ← installa strongswan
│ (gestisce IKE su UDP 500/4500) │
│ │ │
│ kernel Linux │ ← già capisce ESP/AH
│ (gestisce ESP/AH sul cavo) │
└─────────────────────────────────────────┘Esistono altre implementazioni dello stesso standard: Libreswan (RHEL/Fedora), OpenSwan (predecessore), Cisco IOS (enterprise). Tutti implementano IPsec e si parlano tra loro perché seguono lo stesso RFC - come nginx e Apache che implementano entrambi HTTP.
sudo apt install strongswan -y
ipsec versionSu Kali il primo tentativo dava 404 su strongswan-starter - repo in aggiornamento. Soluzione: sudo apt update e riprovare.
Configurazione Ubuntu - responder#
/etc/ipsec.conf:
config setup
conn mustache
left=192.168.64.3
right=192.168.64.200
type=tunnel
esp=aes256-sha256
ikelifetime=1h
keylife=30m
authby=secret
auto=add/etc/ipsec.secrets - shared secret PSK:
192.168.64.3 192.168.64.200 : PSK "mustache2026"sudo ipsec restart
# Stopping strongSwan IPsec...
# Starting strongSwan 5.9.13 IPsec [starter]...Configurazione Kali - initiator#
Stessa struttura di Ubuntu ma left e right invertiti.
/etc/ipsec.conf:
config setup
conn mustache
left=192.168.64.200
right=192.168.64.3
type=tunnel
esp=aes256-sha256
ikelifetime=1h
keylife=30m
authby=secret
auto=add/etc/ipsec.secrets:
192.168.64.200 192.168.64.3 : PSK "mustache2026"sudo ipsec restartLeft e right in IPsec:
leftè sempre "io",rightè sempre "l'altro". Ubuntu si mette a sinistra e vede Kali a destra. Kali si mette a sinistra e vede Ubuntu a destra. StrongSwan capisce da solo chi è il responder e chi l'initiator in base a chi apre la connessione.
Initiator e Responder#
Concentrator vs Responder: il termine "concentrator" è quello che trovi su Security+ - "responder" è quello che usa StrongSwan nei log. Stessa funzione, vocabolario diverso. Ubuntu qui fa entrambe le cose.
In IKE qualcuno deve fare la prima mossa:
- Initiator (Kali): manda il primo pacchetto IKE_SA_INIT. È lui che vuole aprire il tunnel.
- Responder (Ubuntu): sta in ascolto, risponde. Non sa che Kali esiste finché non arriva quel primo pacchetto.
INITIATOR RESPONDER
Kali (192.168.64.200) Ubuntu (192.168.64.3)
[ ipsec up mustache ] [ in ascolto... ]
│ │
│── IKE_SA_INIT ──────────────►│ "qualcuno vuole parlare"
│◄─────────────── response ────│ "rispondo"
│ │
│── IKE_AUTH ────────────────►│ "ecco il mio PSK firmato"
│◄─────────────── response ────│ "confermato, tunnel su"
│ │
[ tunnel stabilito ] [ tunnel stabilito ]
│ │
│══ ESP (proto 50) ════════════►│
│◄════ ESP (proto 50) ══════════│
(da qui: simmetrico)Una volta stabilito il tunnel la distinzione sparisce - il traffico ESP va in entrambe le direzioni.
conn mustache - conn sta per connection, il nome del blocco di configurazione. mustache è il nome che abbiamo dato noi a questa connessione - poteva essere vpn-lab o qualsiasi cosa. StrongSwan usa il nome per riferirsi alla connessione nei comandi (ipsec up mustache, ipsec down mustache).
PSK "mustache2026" - PSK è Pre-Shared Key, la chiave condivisa in anticipo. È letteralmente una password che entrambi gli host devono conoscere prima che IKE inizi. "Pre-shared" significa che non viene negoziata sul cavo - la metti a mano su entrambe le macchine.
Il PSK è usato solo per autenticazione - dimostrare "sono io, conosco il segreto". Non viene usato per cifrare il traffico ESP.
Le chiavi di cifratura vengono da ECDH (lo stesso meccanismo di WireGuard) - entrambi gli host generano coppie di chiavi temporanee, si scambiano le pubbliche, e derivano la stessa chiave condivisa senza mai trasmetterla sul cavo.
IKE_SA_INIT:
Kali e Ubuntu si scambiano chiavi pubbliche DH
→ entrambi calcolano la stessa chiave condivisa (mai sul cavo)
→ da questa derivano le chiavi di cifratura per IKE
IKE_AUTH:
Kali firma un messaggio con il PSK → "sono Kali"
Ubuntu verifica → "confermato"
→ PSK ha finito il suo lavoro, non serve più
ESP:
Chiavi di sessione derivate da ECDH, non dal PSK
→ il PSK non cifra nullaÈ come la parola "Fidelio" in Eyes Wide Shut - ti apre la porta, dimostra che appartieni. Ma quello che succede dentro la stanza è separato dalla parola. Per questo il PSK deve essere forte: se qualcuno lo indovina, può entrare - anche se non può decifrare il traffico già catturato.
Errore: no private key found#
Al primo tentativo, ipsec up mustache falliva con:
no private key found for '192.168.64.200'
establishing connection 'mustache' failedIKE partiva correttamente (il tcpdump su UDP 500 lo confermava) ma l'autenticazione falliva. StrongSwan di default cerca un certificato X.509 - mancava authby=secret nel config per dirgli di usare il PSK.
Fix: aggiungere authby=secret al blocco conn mustache su entrambe le VM.
IKE - la negoziazione delle chiavi#
IKE è un handshake - più elaborato di quello TCP, ma stessa idea: prima di trasferire dati, i due host si mettono d'accordo. TCP handshake stabilisce solo la connessione. IKE stabilisce chi sei, come cifriamo e quale chiave usiamo in 2 round trip, poi sparisce.
Kali Ubuntu
│ │
│── IKE_SA_INIT ──────────────────►│ "propongo questi algoritmi"
│◄── IKE_SA_INIT response ─────────│ "accetto, ecco la mia chiave pubblica"
│ │
│ [entrambi derivano la stessa chiave senza mai trasmetterla - ECDH]
│ │
│── IKE_AUTH ────────────────────►│ "sono Kali - ecco il PSK firmato"
│◄── IKE_AUTH response ────────────│ "confermato, tunnel aperto"
│ │
│══ ESP - dati cifrati ════════════►│ IKE non esiste più sul cavoDopo IKE, le SA sono installate nel kernel e il traffico viaggia direttamente in ESP. IKE non è in mezzo - è servito solo per il setup.
Curiosità: IKE come nome e protocollo è specifico di IPsec (RFC 7296). Ma il concetto che implementa - negozia algoritmi, scambia chiavi, autenticati, poi sparisci - esiste in ogni protocollo sicuro:
Protocollo Il suo "IKE" Meccanismo IPsec IKE (UDP 500) ECDH + PSK o certificati TLS/HTTPS TLS Handshake ECDH + certificati X.509 WireGuard Handshake (2 messaggi) Noise Protocol + Curve25519 SSH Key Exchange Diffie-Hellman WPA2 WiFi 4-way handshake EAPOL + PMK WPA3 WiFi SAE (Dragonfly) Diffie-Hellman su curva ellittica Il meccanismo matematico sottostante è sempre ECDH. Il nome cambia, l'idea no.
Prima di mandare dati cifrati, IPsec deve stabilire le Security Association (SA) - gli accordi su algoritmi, chiavi e parametri. Questo è il lavoro di IKE (Internet Key Exchange) su UDP 500.
Su Ubuntu, tcpdump in ascolto:
sudo tcpdump -i enp0s1 udp port 500Da Kali, avvia il tunnel:
sudo ipsec up mustacheOutput completo:
initiating IKE_SA mustache[1] to 192.168.64.3
sending packet: from 192.168.64.200[500] to 192.168.64.3[500] (924 bytes)
received packet: from 192.168.64.3[500] to 192.168.64.200[500] (280 bytes)
selected proposal: IKE:AES_CBC_128/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/ECP_256
authentication of '192.168.64.200' (myself) with pre-shared key
sending packet: from 192.168.64.200[4500] to 192.168.64.3[4500] (448 bytes)
received packet: from 192.168.64.3[4500] to 192.168.64.200[4500] (272 bytes)
authentication of '192.168.64.3' with pre-shared key successful
IKE_SA mustache[1] established between 192.168.64.200...192.168.64.3
selected proposal: ESP:AES_CBC_256/HMAC_SHA2_256_128/NO_EXT_SEQ
CHILD_SA mustache{1} established with SPIs c36742cc_i c2359b1c_o and TS 192.168.64.200/32 === 192.168.64.3/32
connection 'mustache' established successfullyCosa è successo - IKEv2 in 2 fasi#
Fase 1 - IKE_SA_INIT (UDP 500): Kali e Ubuntu negoziano gli algoritmi e si autenticano con il PSK. IKE crea le SA - gli accordi su algoritmi, chiavi e durata. Proposta selezionata: AES_CBC_128 / HMAC_SHA2_256 / ECP_256.
Fase 2 - IKE_AUTH (UDP 4500): Noti il cambio di porta da 500 a 4500 - è NAT-T (NAT Traversal). IPsec ha rilevato di essere dietro NAT (UTM) e ha incapsulato il traffico in UDP 4500. Senza NAT-T, ESP non passerebbe il NAT: non ha porte TCP/UDP, il NAT non sa a chi girare i pacchetti.
Risultato: CHILD_SA stabilita con due SPI:
c36742cc_i- inboundc2359b1c_o- outbound
Ogni pacchetto ESP porta il suo SPI nell'header - il ricevitore lo usa per trovare la SA giusta e sapere come decifrare.
Traffic Selector: 192.168.64.200/32 === 192.168.64.3/32 - solo il traffico tra questi due IP entra nel tunnel.
Kali (192.168.64.200) Ubuntu (192.168.64.3)
│ │
│── IKE_SA_INIT ──────── UDP 500 ────────►│
│ 924 byte - negozia algoritmi │
│◄── IKE_SA_INIT response ────────────────│
│ 280 byte - proposta accettata │
│ │
│ [NAT rilevato → switch a UDP 4500]
│ │
│── IKE_AUTH ─────────── UDP 4500 ───────►│
│ 448 byte - PSK auth + richiede CHILD │
│◄── IKE_AUTH response ───────────────────│
│ 272 byte - CHILD_SA stabilita │
│ │
│ SPI outbound Kali: 0xc2359b1c │
│ SPI outbound Ubuntu: 0xc36742cc │
│ │
│══ traffico ESP (proto 50) ══════════════►│ESP - il traffico cifrato sul cavo#
sudo tcpdump -i enp0s1 proto 50ping 192.168.64.3 # da Kali192.168.64.200 > barno-server: ESP(spi=0xc2359b1c,seq=0x1), length 136
barno-server > 192.168.64.200: ESP(spi=0xc36742cc,seq=0x1), length 136
192.168.64.200 > barno-server: ESP(spi=0xc2359b1c,seq=0x2), length 136
barno-server > 192.168.64.200: ESP(spi=0xc36742cc,seq=0x2), length 136
192.168.64.200 > barno-server: ESP(spi=0xc2359b1c,seq=0x3), length 136Tre cose visibili nell'output:
1. SPI in ogni pacchetto - 0xc2359b1c è l'SPI outbound di Kali, 0xc36742cc è l'SPI outbound di Ubuntu. Matchano esattamente i valori che ipsec up aveva stampato: SPIs c36742cc_i c2359b1c_o. Il ricevitore usa l'SPI per trovare la SA giusta.
2. seq che incrementa - anti-replay. Se qualcuno intercetta il pacchetto seq=0x1 e lo rimanda, Ubuntu lo scarta: quel sequence number è già stato processato.
3. Nessun payload visibile - solo length 136. ESP ha cifrato tutto con AES-256. A differenza di AH che vedremo dopo, non c'è nulla da leggere dentro.
Pacchetto ESP sul cavo (enp0s1) - vista dell'attaccante:
┌────────────────────────────────────────────────┐
│ IP header: 192.168.64.200 → 192.168.64.3 │ ← visibile
│ proto: 50 (ESP) │ ← visibile
├──────────────┬─────────────────────────────────┤
│ SPI │ 0xc2359b1c │ ← visibile (identifica la SA)
│ seq │ 0x01, 0x02, 0x03... │ ← visibile (anti-replay)
├──────────────┴─────────────────────────────────┤
│ Encrypted payload │ ← cifrato con AES-256
│ [ ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ] │
│ │
│ dentro c'è: ICMP echo → 192.168.64.3 │ ← illeggibile dall'esterno
└────────────────────────────────────────────────┘AH - autenticazione senza cifratura#
AH (Authentication Header, IP protocol 51) firma ogni pacchetto ma non cifra nulla. Il payload viaggia in chiaro sul cavo.
Aggiungo una connessione AH in /etc/ipsec.conf su entrambe le VM:
conn mustache-ah
left=192.168.64.3
right=192.168.64.200
type=transport
ah=sha256
ikelifetime=1h
keylife=30m
authby=secret
auto=addSu Kali: stessa struttura con left=192.168.64.200 and right=192.168.64.3.
sudo ipsec restartAvvio il tunnel AH da Kali:
sudo ipsec up mustache-ahSu Ubuntu, tcpdump in ascolto su proto 51 (AH):
sudo tcpdump -i enp0s1 -n proto 51 -vDa Kali, ping:
ping 192.168.64.3Output tcpdump:
192.168.64.200 > 192.168.64.3: AH(spi=0x...,seq=0x1):
192.168.64.200 > 192.168.64.3: ICMP echo request, id 1, seq 1, length 64
192.168.64.3 > 192.168.64.200: AH(spi=0x...,seq=0x1):
192.168.64.3 > 192.168.64.200: ICMP echo reply, id 1, seq 1, length 64Differenza netta con ESP: tcpdump legge ICMP echo request dentro il pacchetto AH. Il payload non è cifrato - chiunque annusi il cavo vede cosa passa. AH garantisce solo che il pacchetto non sia stato manomesso (integrità) e che venga da chi dice di essere (autenticazione). Non garantisce riservatezza.
AH e NAT non vanno d'accordo: AH include nell'HMAC anche gli IP header. Se un NAT modifica l'IP sorgente durante il transito, la firma non batte più e il pacchetto viene scartato. ESP non ha questo problema perché firma solo il payload cifrato, non gli IP header esterni.
ESP vs AH - il confronto#
| ESP (proto 50) | AH (proto 51) | |
|---|---|---|
| Cifratura | Si (AES-256) | No |
| Autenticazione | Si | Si |
| Funziona con NAT | Si (via NAT-T, UDP 4500) | No |
| Payload visibile in tcpdump | No | Si |
| Usato in pratica | Sempre | Quasi mai |
AH esiste ancora come standard ma nessuno lo usa in produzione: non cifra, e non passa il NAT. Security+ lo chiede perché è un esame, non perché lo vedrai in un firewall reale.
Tunnel mode vs Transport mode#
Finora la connessione mustache usava type=tunnel. Esiste anche type=transport - cambio l'unica riga e riavvio:
conn mustache
...
type=transport # era: tunnel
...sudo ipsec restart
sudo ipsec up mustachetcpdump su enp0s1 con tunnel mode:
192.168.64.200 > 192.168.64.3: ESP(spi=0xc2359b1c,seq=0x1), length 136tcpdump su enp0s1 con transport mode:
192.168.64.200 > 192.168.64.3: ESP(spi=0x...,seq=0x1), length 112Il payload è più corto: in transport mode manca l'inner IP header cifrato.
Tunnel mode - wrappa tutto il pacchetto originale:
┌── outer IP ──┬── ESP ──┬─── cifrato ─────────────────┐
│ .200 → .3 │ header │ inner IP (.200→.3) + ICMP │
└─────────────┴─────────┴─────────────────────────────┘
↑ aggiunge un nuovo IP header IP originale nascosto ↑
Transport mode - protegge solo il payload:
┌── IP ────────┬── ESP ──┬── cifrato ──┐
│ .200 → .3 │ header │ ICMP │
└─────────────┴─────────┴─────────────┘
↑ IP header originale rimane payload nascosto ↑Tunnel mode: usato nei VPN gateway - nasconde la topologia interna (gli IP interni sono dentro il tunnel cifrato). Transport mode: usato host-to-host su LAN dove gli IP sono già noti - overhead minore perché non duplica l'IP header.
exit 0#
Tre protocolli, un lab. IKE negozia in segreto su UDP 500, passa a 4500 se c'è NAT, poi sparisce. Sul cavo rimane solo ESP o AH.
ESP cifra: tcpdump vede SPI e seq, il payload è ??. AH autentica: tcpdump vede tutto, incluso l'ICMP dentro. Per questo AH non si usa - nessuno vuole mandare dati in chiaro solo perché sono "firmati".
La domanda Security+ più comune su IPsec: quale protocollo fornisce confidentiality? - ESP. AH no.
Comandi usati: ipsec · tcpdump · ping Concetti correlati: ipsec · cryptography · tcp-udp Approfondimento: cap-04-securing-your-network · il-tunnel-che-sceglie




