SSD Nodes Learn Hosting plans →
Guide Matt ConnorDi Matt Connor · Aggiornato 2026-08-27

WireGuard: come funziona il cryptokey routing

Scopri perché AllowedIPs è insieme tabella di routing e ACL, come funzionano handshake Noise e rotazione delle chiavi, e come leggere ogni wg0.conf.

Come funziona WireGuard, in un'unica idea

WireGuard associa ogni pacchetto a una chiave pubblica. Questo meccanismo si chiama cryptokey routing e costituisce l'intero modello di progettazione: la riga AllowedIPs accanto a un peer è la tabella di routing per i pacchetti in uscita dal computer e l'elenco di controllo degli accessi per i pacchetti in arrivo da quel peer. Una sola impostazione svolge due funzioni. Interpretate AllowedIPs in questo modo e ogni file di configurazione di WireGuard diventa leggibile.

Non esiste una tabella delle sessioni indicizzata per indirizzo IP e non esiste un database degli utenti. Un peer è una chiave pubblica associata all'insieme degli indirizzi che quella chiave può utilizzare. L'handshake e i timer servono a mantenere valida questa associazione mentre cambia la rete sottostante. Se volete prima realizzare un tunnel funzionante e approfondire poi la teoria, createne uno seguendo questa guida per un VPN WireGuard self-hosted sul vostro VPS, quindi tornate qui quando una riga della configurazione vi sembra insolita.

AllowedIPs è una tabella di routing e un elenco di controllo degli accessi

Consideriamo prima la direzione in uscita. Il kernel instrada un pacchetto verso il dispositivo `wg0` nel modo consueto, tramite la tabella di routing principale. WireGuard confronta quindi l'indirizzo di destinazione del pacchetto con una tabella che contiene tutti i prefissi consentiti per ogni peer, a partire dal prefisso più specifico. Una corrispondenza identifica un peer, che a sua volta identifica una chiave pubblica, una chiave di sessione e un endpoint UDP. Il pacchetto viene cifrato per quel peer e inviato a tale endpoint.

Se nessun `AllowedIPs` dei peer include la destinazione, il pacchetto non viene inviato, perché non esiste una chiave con cui cifrarlo.

ping: sendmsg: Required key not available

Questo errore ha una sola spiegazione: l'indirizzo che si è tentato di raggiungere non è elencato per nessun peer. Un errore diverso, `ping: sendmsg: Destination address required`, indica che un peer corrisponde, ma WireGuard non dispone di un endpoint per quel peer, perché non ne è stato configurato uno e non ne è stato ancora appreso nessuno.

Consideriamo ora la direzione in ingresso. Un pacchetto UDP arriva sulla porta di ascolto. WireGuard identifica la sessione tramite l'indice del destinatario nell'intestazione, verifica il contatore rispetto a una finestra scorrevole anti-replay, quindi decifra e autentica il payload. Solo dopo legge il pacchetto interno e verifica che l'indirizzo di origine di quel pacchetto rientri nel `AllowedIPs` del peer mittente. In caso contrario, il pacchetto viene scartato. Con il debug dinamico abilitato, il kernel stampa il motivo in una riga simile a questa:

wg0: Packet has unallowed src IP (10.8.0.9) from peer 2 (203.0.113.10:51820)

Per questo un peer sul server riceve un `/32. Un peer configurato con AllowedIPs = 10.8.0.2/32 può inviare pacchetti da 10.8.0.2 e da nessun altro indirizzo. Se si inserisce invece 0.0.0.0/0`, quel singolo client può iniettare nel tunnel pacchetti che dichiarano un indirizzo di origine qualsiasi, incluso quello di un altro client.

I prefissi sovrapposti vengono risolti in base alla specificità, perché la ricerca usa il prefisso più lungo corrispondente. Prefissi identici configurati su due peer si comportano diversamente: la voce passa al peer configurato per ultimo e il primo peer smette di ricevere quel traffico senza che venga stampato alcun errore. `wg show wg0 allowed-ips` stampa la tabella effettivamente presente nel kernel, che è quella rilevante quando il file su disco e lo stato in esecuzione non sono più sincronizzati.

Lettura di un file di configurazione considerando il routing basato su chiavi crittografiche

Lato server:

[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = <server private key>

[Peer]
PublicKey = <laptop public key>
AllowedIPs = 10.8.0.2/32

Lato client:

[Interface]
Address = 10.8.0.2/32
PrivateKey = <laptop private key>

[Peer]
PublicKey = <server public key>
Endpoint = vpn.example.com:51820
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25

La stessa parola chiave ha un significato opposto sui due lati. Sul client indica di inviare ogni destinazione a questo peer. Sul server indica di accettare da questo peer soltanto questo indirizzo. L'asimmetria dipende dai valori, non dal ruolo.

Metà di queste chiavi non appartiene affatto al protocollo. Address, DNS, MTU, PostUp e SaveConfig appartengono a wg-quick, lo script shell che attiva l'interfaccia. Il kernel non le vede. wg-quick strip wg0 stampa la configurazione ridotta che lo strumento wg carica effettivamente. È il modo più rapido per vedere questa separazione.

Cosa fa realmente l'handshake

L'handshake di WireGuard è Noise_IKpsk2, derivato dal Noise Protocol Framework. La parte IK è quella più utile per un sysadmin: la chiave pubblica statica del responder è già nota all'initiator, perché è il PublicKey nel blocco [Peer], mentre l'initiator invia la propria chiave pubblica statica all'interno del primo messaggio, cifrata. Non avviene quindi alcuno scambio di certificati né un andata e ritorno per verificare l'identità. Un osservatore passivo non può sapere quale chiave sta effettuando la chiamata, a meno che non disponga della chiave privata del responder.

Il costo è un solo round trip. Il messaggio di avvio è di 148 byte, la risposta è di 92 byte e subito dopo inizia il trasferimento dei dati. Ogni lato genera una nuova coppia di chiavi effimere Curve25519 per ogni handshake. Le chiavi di sessione derivano da una catena di risultati Diffie-Hellman che combina le chiavi statiche e quelle effimere. Le chiavi private effimere vengono quindi eliminate. Questo garantisce la forward secrecy: anche se qualcuno registra il traffico oggi e sottrae la chiave privata del server l'anno prossimo, non può comunque leggere i dati registrati.

L'avvio di un handshake contiene un timestamp TAI64N. Ogni peer memorizza il timestamp più recente ricevuto dall'altro peer, quindi un avvio riprodotto viene rifiutato. I pacchetti dati contengono un contatore a 64 bit usato come nonce. Il destinatario mantiene una finestra scorrevole dei contatori ricevuti di recente. In questo modo gestisce replay e riordinamenti significativi senza mantenere uno stato della connessione come quello di TCP.

Le chiavi di sessione non durano a lungo e i timer sono inclusi nel binario, non configurabili.

ChartWireGuard protocol timers, in seconds
The data behind this chart
[
  {
    "label": "REKEY_TIMEOUT",
    "seconds": 5,
    "notes": "resend a handshake initiation that got no answer"
  },
  {
    "label": "KEEPALIVE_TIMEOUT",
    "seconds": 10,
    "notes": "send a keepalive after receiving data and sending none back"
  },
  {
    "label": "REKEY_ATTEMPT_TIME",
    "seconds": 90,
    "notes": "give up on the handshake and report the peer as down"
  },
  {
    "label": "REKEY_AFTER_TIME",
    "seconds": 120,
    "notes": "sender begins a fresh handshake for a new session key"
  },
  {
    "label": "REJECT_AFTER_TIME",
    "seconds": 180,
    "notes": "the old session key is refused and traffic stops"
  }
]

Questi sono valori costanti definiti dalla specifica del protocollo, non misurazioni. Il 5 di questi valori determina l'intero ciclo di vita della sessione. Dopo 120 secondi di utilizzo, il mittente avvia un nuovo handshake. Dopo 180 secondi, la vecchia chiave viene rifiutata senza eccezioni e il traffico si interrompe finché non viene completato un nuovo handshake. Un avvio che non riceve risposta viene ritrasmesso ogni 5 secondi e abbandonato dopo 90 secondi. Per questo wg show mostra latest handshake come età relativa e un tunnel attivo e funzionante mantiene questo valore basso. Se l'età aumenta mentre si sta inviando traffico, significa che gli handshake stanno fallendo, non che il tunnel è inattivo.

Perché un peer non ha un ruolo client o server

Entrambi i lati eseguono lo stesso codice e usano lo stesso formato di configurazione. Non esiste una modalità server. L'asimmetria percepita dipende da Endpoint, mentre Endpoint è facoltativo.

Un peer con un endpoint configurato può avviare un handshake. Un peer senza endpoint resta in attesa, quindi apprende l'indirizzo e la porta dell'altro lato dal primo pacchetto autenticato correttamente. L'endpoint appreso viene memorizzato e aggiornato ogni volta che arriva un pacchetto valido da un nuovo indirizzo. Questo è il funzionamento del roaming: un laptop che passa dal Wi-Fi alla rete mobile mantiene lo stesso tunnel, perché una sessione viene identificata tramite chiave e indice, non tramite indirizzo IP. Non viene ristabilita alcuna connessione, perché in senso TCP non era mai stata instaurata una connessione.

Lo stesso meccanismo porta a un dato utile da conoscere: il peer con l'indirizzo pubblico conserva l'ultimo IP pubblico noto dell'altro lato e wg show lo visualizza.

Primitivi fissi, senza negoziazione

In WireGuard non esiste un elenco di ciphersuite. ChaCha20-Poly1305 per la cifratura autenticata, Curve25519 per l'accordo delle chiavi, BLAKE2s per l'hashing, HKDF per la derivazione delle chiavi. Tutte le implementazioni usano questi primitivi, quindi non esiste una fase di negoziazione da analizzare né un percorso di downgrade verso un'opzione più debole. Il compromesso è concreto: se uno di questi primitivi viene compromesso, occorrono una nuova versione dell'intero protocollo e un aggiornamento su entrambe le estremità, non una modifica della configurazione. Questa scelta elimina gran parte del codice e delle modalità di errore presenti in un tunnel basato su TLS. È soprattutto questo il punto del confronto in WireGuard rispetto a OpenVPN.

Perché la porta non risponde a uno scanner

Ogni messaggio di handshake contiene un campo denominato mac1. È un codice di autenticazione del messaggio (MAC, message authentication code) calcolato sul messaggio con una chiave derivata dalla chiave pubblica statica del responder. Un mittente che non conosce questa chiave pubblica non può produrre un mac1 valido, quindi il ricevitore scarta il pacchetto senza inviare alcuna risposta. Nessun errore, nessun reset e nessun messaggio ICMP.

Il risultato visibile è una scansione UDP che non riceve alcuna risposta.

sudo nmap -sU -p 51820 vpn.example.com

nmap indica open|filtered, la stessa risposta che restituisce per una porta i cui pacchetti vengono scartati silenziosamente da un firewall. La porta si comporta allo stesso modo sia quando WireGuard è in ascolto sia quando non lo è, almeno per chi non possiede già la chiave pubblica.

Un secondo campo, mac2, gestisce la pressione causata dai tentativi di denial of service. Quando il ricevitore è sotto carico, risponde a un'iniziazione valida con una risposta cookie di 64 byte associata all'indirizzo sorgente del mittente e rifiuta di eseguire operazioni costose con la chiave pubblica finché il mittente non restituisce quel cookie. In questo modo dimostra che l'indirizzo sorgente è reale prima di utilizzare risorse CPU, e il meccanismo si attiva soltanto sotto carico.

Perché 0.0.0.0/0 trasforma un peer nella route predefinita

Poiché AllowedIPs è la tabella di routing, AllowedIPs = 0.0.0.0/0, ::/0 assegna a quel peer qualsiasi destinazione. Questa è l’intera configurazione full tunnel.

Il routing che la rende operativa è più interessante della riga in sé. Una route predefinita diretta tramite wg0 creerebbe un loop, perché anche il pacchetto UDP cifrato che trasporta il traffico deve uscire dalla macchina e corrisponderebbe alla propria route predefinita. wg-quick evita il problema con il policy routing. Contrassegna con un fwmark i pacchetti in uscita generati da WireGuard, inserisce la route predefinita del tunnel in una tabella di routing separata e aggiunge regole affinché soltanto il traffico senza marcatura la utilizzi. Esegui ip rule show per vedere il risultato:

32764:	from all lookup main suppress_prefixlength 0
32765:	not from all fwmark 0xca6c lookup 51820
32766:	from all lookup main

0xca6c è 51820 in formato esadecimale e 51820 è anche il numero della tabella. La regola suppress_prefixlength 0 fa sì che la tabella principale ignori la propria route predefinita. In questo modo le route specifiche, come quella verso la subnet locale, continuano ad avere la precedenza, mentre tutto il resto passa alla tabella del tunnel. Un split tunnel non richiede nulla di tutto questo: un elenco più ristretto, come AllowedIPs = 10.8.0.0/24, 10.20.0.0/16, diventa un insieme di route ordinarie nella tabella principale.

Un full tunnel non risolve automaticamente la risoluzione dei nomi, perché il resolver appreso dal client tramite la rete locale rimane in genere attivo e la relativa route è più specifica. Si tratta di un’attività separata, descritta in DNS che esce da un tunnel WireGuard.

A cosa serve realmente PersistentKeepalive

WireGuard non invia nulla quando non c'è traffico. Nessun heartbeat, nessun aggiornamento della sessione, nulla sulla rete. Questo riduce il consumo della batteria, è utile anche nel caso dello scanner descritto sopra e causa problemi in una configurazione specifica.

Un peer dietro NAT (network address translation) o dietro un firewall stateful è raggiungibile dall'esterno solo mentre sul dispositivo esiste una mappatura, creata da un pacchetto in uscita. I tempi di validità delle mappature UDP iniziano comunemente da circa 30 secondi. Quando la mappatura scade, il middlebox scarta i pacchetti provenienti dal lato pubblico e il tunnel sembra inattivo finché il peer dietro NAT non invia qualcosa. PersistentKeepalive = 25 invia un pacchetto autenticato vuoto ogni 25 secondi, un intervallo inferiore al più breve tempo di validità comune; in questo modo la mappatura resta aperta.

Impostalo sul peer dietro NAT. Un server con un indirizzo pubblico e una porta UDP aperta non ne ha bisogno; impostarlo su quel server aggiunge soltanto traffico. Non confonderlo con il keepalive automatico, che viene inviato 10 secondi dopo che un peer riceve dati e non ha nulla da inviare a sua volta. Questo meccanismo è sempre attivo e non può essere configurato.

Il routing della LAN attraverso il tunnel non è una funzionalità di WireGuard

Supponiamo che il peer B si trovi sulla rete domestica 192.168.50.0/24 e che il peer A debba raggiungerla. Devono essere configurati correttamente due sistemi distinti, e solo uno dei due è WireGuard.

Parte di WireGuard: aggiungere 192.168.50.0/24 a AllowedIPs di B su A. In questo modo A instrada il prefisso verso B e accetta da B i pacchetti con quegli indirizzi sorgente. Senza questa configurazione, il routing basato su cryptokey non dispone di una chiave per la destinazione e non autorizza l'indirizzo sorgente.

Parte del kernel: su B, net.ipv4.ip_forward deve essere impostato su 1, altrimenti il kernel elimina ogni pacchetto decifrato non indirizzato direttamente a B. La catena forward del firewall di B deve consentire il traffico. Gli host della LAN devono disporre di una route di ritorno verso 10.8.0.0/24, oppure B deve applicare il source NAT affinché le risposte passino nuovamente da B.

Il lavoro di WireGuard termina quando consegna il pacchetto decifrato al kernel. Tutto ciò che segue è il normale routing e filtraggio di Linux. Per questo l’errore compare nei contatori di nft list ruleset o in ip -s link show wg0, non in wg show. Se preferisci gestire i peer tramite un’interfaccia web, l’esecuzione di wg-easy in Docker genera automaticamente le configurazioni dei peer, ma le regole di forwarding restano a carico dell’host. La stessa separazione vale quando un livello di coordinamento distribuisce il prefisso automaticamente: la pubblicazione di una rete privata da un VPS con un subnet router Tailscale sostituisce la modifica manuale di AllowedIPs su ogni peer, ma il sysctl per il forwarding e le regole del firewall sul router restano a tuo carico.

Perché WireGuard risiede nel kernel

wg0 è un driver per dispositivi di rete. I pacchetti lo raggiungono tramite il normale stack di routing, vengono cifrati nel contesto softirq e lo lasciano attraverso un socket UDP senza mai passare nello spazio utente. Da qui deriva il throughput. Per questo il modulo resta di circa quattromila righe di codice, abbastanza compatto da poter essere revisionato e integrato nel kernel Linux mainline 5.6 nel marzo 2020. Ubuntu 24.04 e Debian 13 lo includono, quindi manca soltanto il pacchetto wireguard-tools.

Il fatto che sia un'interfaccia normale ha conseguenze pratiche. tcpdump -ni wg0 mostra i pacchetti interni in chiaro, mentre tcpdump -ni eth0 udp port 51820 mostra quelli esterni cifrati. Il confronto tra i due indica subito quale direzione presenta il problema. netfilter e il traffic shaping trattano wg0 come qualsiasi altro collegamento. Quando il modulo del kernel non è disponibile, ad esempio nella virtualizzazione dei container che condivide il kernel dell'host, wireguard-go implementa lo stesso protocollo nello spazio utente tramite un dispositivo TUN. Questo comporta una riduzione effettiva del throughput, perché ogni pacchetto attraversa due volte il confine tra kernel e spazio utente.

Cosa non protegge WireGuard

Il modello di minaccia è volutamente ristretto, ma un protocollo così discreto può indurre a conclusioni errate. È importante dichiararlo chiaramente.

  • Non nasconde che stai usando WireGuard. I messaggi di handshake hanno dimensioni fisse, il primo byte indica il tipo di messaggio e il trasporto usa UDP. La deep packet inspection lo riconosce facilmente e una rete che limita le VPN può bloccarlo. L'offuscamento è stato escluso deliberatamente.
  • Non nasconde il volume o la temporizzazione del traffico. I payload vengono completati soltanto fino a un limite di 16 byte, quindi un osservatore vede comunque quando invii dati e, approssimativamente, quanti.
  • Mantiene l'ultimo endpoint noto. Il peer con l'indirizzo pubblico memorizza l'indirizzo IP pubblico corrente dell'altra parte e wg show lo visualizza. Insieme a un indirizzo del tunnel fisso nella configurazione, questo costituisce un identificatore stabile che segue l'utente tra reti diverse. Sul tuo VPS è un comportamento normale. È anche il motivo per cui i servizi commerciali aggiungono un livello sopra il protocollo.
  • Autentica una chiave, non una persona. Chiunque possieda il file della chiave privata è considerato il peer. Mantieni /etc/wireguard con modalità 700 e i file delle chiavi con modalità 600.
  • Non esistono né un elenco di revoca né una scadenza. L'accesso termina quando elimini la voce del peer da ogni server che la contiene, mentre le chiavi statiche restano valide finché non le rimuovi.

Niente di tutto questo rende WireGuard insicuro. Lo rende essenziale, ed è proprio questo lo scopo: autentica e cifra, mentre lascia la gestione delle identità e l'allocazione degli indirizzi a ciò che costruisci sopra di esso. Un livello di coordinamento del tipo descritto in WireGuard a confronto con Tailscale esiste per colmare esattamente questa lacuna, usando lo stesso piano dati appena descritto. Una volta introdotto questo livello, la decisione successiva riguarda chi può raggiungere un servizio eseguito all'interno del tunnel. È questo che la scelta tra Tailscale serve e funnel definisce per una singola porta.

FAQ

Che cos'è il cryptokey routing in WireGuard?

Il cryptokey routing è la regola che associa ogni pacchetto a una chiave pubblica. Ogni voce peer contiene un elenco di prefissi in AllowedIPs. In uscita, WireGuard seleziona il peer confrontando la destinazione del pacchetto con l'elenco di ogni peer, a partire dal prefisso più specifico; l'elenco funziona quindi come una tabella di routing. In ingresso, dopo la decrittografia e l'autenticazione del pacchetto, l'indirizzo sorgente interno deve rientrare nell'elenco dello stesso peer. In caso contrario, il pacchetto viene scartato. L'elenco funziona quindi anche come una lista di controllo degli accessi. WireGuard non usa una configurazione di routing separata né un firewall interno separato, perché lo stesso elenco svolge entrambe le funzioni.

Devo configurare PersistentKeepalive su entrambi i peer?

No. Configuralo sul lato che si trova dietro NAT (network address translation) o dietro un firewall stateful, che di solito è il client. Quando il tunnel è inattivo, WireGuard non invia pacchetti. Di conseguenza, la mappatura che consente al lato remoto di raggiungere quel peer scade, spesso entro un minuto, e il tunnel sembra non funzionare in una sola direzione. PersistentKeepalive = 25 invia un pacchetto autenticato vuoto ogni 25 secondi e mantiene aperta la mappatura. Un peer con un indirizzo pubblico e una porta UDP aperta non ne ha bisogno.

Perché il ping attraverso il tunnel restituisce "Required key not available"?

Perché l'indirizzo di destinazione non rientra in alcun AllowedIPs del peer. Il cryptokey routing non ha quindi trovato una chiave con cui cifrare il pacchetto e il kernel ne ha rifiutato l'invio. Esegui wg show wg0 allowed-ips e confronta l'output con l'indirizzo verso cui stai eseguendo il ping. L'errore simile Destination address required indica un problema diverso: è stato trovato un peer corrispondente, ma WireGuard non dispone di un endpoint per quel peer, perché non ne è stato configurato alcuno e da quel peer non è ancora arrivato alcun pacchetto autenticato.

Un firewall può rilevare e bloccare WireGuard?

Sì. WireGuard autentica e cifra il traffico e non tenta di dissimulare il proprio protocollo. I messaggi di handshake hanno dimensioni fisse di 148 e 92 byte, il primo byte di ogni messaggio ne identifica il tipo e il trasporto usa UDP. Per questo, l'ispezione approfondita dei pacchetti identifica il protocollo senza difficoltà. Le reti che bloccano UDP o che usano il fingerprinting dei protocolli lo interrompono. Per nascondere il tunnel è necessario incapsularlo in un altro protocollo o strumento. Questa è una soluzione distinta da un'impostazione di WireGuard.