SSD Nodes Learn
Guide Matt ConnorDi Matt Connor · Aggiornato 2026-07-24

Installare WireGuard su VPS Linux

Guida alla configurazione di WireGuard su VPS Linux. Scopri come gestire wg0.conf, IP forwarding, NAT e risolvere i problemi di handshake e DNS.

Cosa stai costruendo

Un VPN WireGuard su un server di tua proprietà richiede circa quaranta righe di configurazione: una coppia di chiavi, un file di interfaccia, un comando sysctl, una regola NAT e una regola firewall. L'installazione è semplice; pertanto, gran parte di questa guida riguarda i problemi comuni: permessi delle chiavi, AllowedIPs, forwarding e DNS.

WireGuard è un tunnel di Layer 3 integrato nel kernel, presente nel mainline da Linux 5.6; quindi Ubuntu 24.04 e Debian 13 lo includono senza moduli esterni. Non esiste negoziazione dei cipher, né autorità di certificazione, né autenticazione tramite username/password: un peer è costituito da una chiave pubblica più gli indirizzi IP che tale chiave può utilizzare. Un pacchetto che fallisce il controllo MAC viene scartato senza inviare una risposta, quindi la porta non risponde agli scan. Il rovescio della medaglia: non esiste un server di autenticazione, quindi per revocare l'accesso è necessario eliminare il peer dal sistema.

Controlla prima la virtualizzazione

WireGuard richiede un kernel in cui sia possibile caricare un modulo; su un VPS KVM funziona nativamente. Sulle virtualizzazioni basate su container che condividono il kernel dell'host — OpenVZ, LXC — il primo comando fallisce con RTNETLINK answers: Operation not supported. In questo caso si utilizza l'implementazione userspace wireguard-go. Verifica prima lo stato con sudo modprobe wireguard && echo ok.

Generare chiavi senza esporle

Un file /etc/wireguard/server.key con permessi di lettura per tutti equivale ad avere una VPN assente. La pratica comune di usare umask 077 && wg genkey | sudo tee ... non è affidabile, poiché sudo applica il proprio umask al file creato da tee. Impostare la modalità esplicitamente.

sudo apt update && sudo apt install -y wireguard nftables
sudo install -d -m 700 /etc/wireguard
sudo sh -c 'umask 077; wg genkey > /etc/wireguard/server.key'
sudo sh -c 'wg pubkey < /etc/wireguard/server.key > /etc/wireguard/server.pub'
sudo chmod 600 /etc/wireguard/server.key

Generare la coppia client con la stessa procedura. wg genpsk aggiunge una pre-shared key opzionale, presente in una riga di ogni config.

L'interfaccia del server: /etc/wireguard/wg0.conf

[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = <contents of /etc/wireguard/server.key>

[Peer]
PublicKey = <laptop public key>
PresharedKey = <psk, optional>
AllowedIPs = 10.8.0.2/32

chmod 600 it; un avviso all'avvio che indica che il file è accessibile a tutti significa che è stato saltato. Address è l'indirizzo del server all'interno del tunnel e include la maschera di tutta la subnet VPN. Scegli un intervallo che non utilizzi nella rete locale — 192.168.1.0/24 collide con metà dei router domestici dietro i quali si trovano i client, causando la perdita del traffico del tunnel a favore della rotta locale.

Il AllowedIPs di un peer sul lato server è un /32, l'unico indirizzo del tunnel di proprietà del client. Se assegni lo stesso IP consentito a due peer, il sistema lo sposta sull'ultimo configurato; il primo smetterà di ricevere traffico senza che venga stampato alcun errore. Lascia SaveConfig non impostato, altrimenti wg-quick down riscrive questo file dallo stato attivo.

Trasforma il box in un router

Un server Linux scarta i pacchetti non indirizzati a se stesso. Di default, il forwarding e il source NAT non sono attivi.

printf 'net.ipv4.ip_forward = 1\nnet.ipv6.conf.all.forwarding = 1\n' \
  | sudo tee /etc/sysctl.d/99-wireguard.conf
sudo sysctl --system
sysctl net.ipv4.ip_forward

Un sysctl -w nudo funziona fino al prossimo reboot, dopodiché smette di funzionare senza errori. Il NAT richiede l'interfaccia di egress — la NIC che raggiunge internet, non wg0. Non dare per scontato eth0; recupera il tuo valore da ip route show default, poiché le immagini attuali utilizzano nomi come enp1s0 o ens3.

Firewall: the port, and the forward path

One nftables file covers filter and NAT. Write /etc/nftables.conf — it flushes the existing ruleset, so skip this on a box already managed by ufw or Docker.

#!/usr/sbin/nft -f
flush ruleset

table inet filter {
  chain input {
    type filter hook input priority filter; policy drop;
    ct state established,related accept
    iif lo accept
    tcp dport 22 accept
    udp dport 51820 accept
  }
  chain forward {
    type filter hook forward priority filter; policy drop;
    ct state established,related accept
    iifname "wg0" oifname "enp1s0" accept
  }
}

table ip nat {
  chain postrouting {
    type nat hook postrouting priority srcnat; policy accept;
    ip saddr 10.8.0.0/24 oifname "enp1s0" masquerade
  }
}

Apply it with sudo systemctl enable --now nftables, keeping a second SSH session open: policy drop plus a typo in the SSH rule locks you out of your own server. Note what the forward chain does not allow — wg0 to wg0. Peers reach the internet, not each other; add iifname "wg0" oifname "wg0" accept for a peer-to-peer VPN. The same chain governs what a peer may touch on the server itself, which matters when the box doubles as a remote development box running Claude Code in tmux and you would rather not expose that side of it publicly.

On a ufw box: ufw allow 51820/udp, DEFAULT_FORWARD_POLICY="ACCEPT" in /etc/default/ufw, and a *nat POSTROUTING MASQUERADE rule at the top of /etc/ufw/before.rules.

Avvio tramite systemd

sudo systemctl enable --now wg-quick@wg0
sudo wg show

wg-quick crea l'interfaccia, aggiunge gli indirizzi e installa le rotte derivate da AllowedIPs. enable --now è la parte critica: una configurazione manuale tramite wg-quick up wg0 viene persa dopo il prossimo reboot. Gli aggiornamenti del kernel richiedono il reboot del sistema.

La configurazione del client e l'impostazione che tutti sbagliano

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

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

AllowedIPs svolge due funzioni diverse contemporaneamente. Confonderle è la causa della maggior parte degli errori con WireGuard.

In uscita, è una tabella di routing. Un pacchetto il cui indirizzo di destinazione corrisponde all'AllowedIPs di un peer viene criptato e inviato a quel peer. 0.0.0.0/0, ::/0 invia tutto il traffico attraverso il tunnel — un tunnel completo, con il server come route predefinita. Un split tunnel utilizza un elenco più ristretto: AllowedIPs = 10.8.0.0/24, 10.20.0.0/16 trasporta il traffico VPN più una rete privata situata dietro il server, mentre tutto il resto mantiene la propria route locale. Questo elenco ristretto permette di isolare completamente i servizi da internet pubblico — un istanza privata di Nextcloud su un VPS vincolata all'indirizzo del tunnel, o le VM di un lab di nested-virtualisation in esecuzione sulla stessa macchina, rimangono raggiungibili dai peer e invisibili a tutti gli altri.

In entrata, è una lista di controllo degli accessi (ACL). Un pacchetto decriptato proveniente da un peer il cui indirizzo sorgente non è incluso nell'AllowedIPs di quel peer viene scartato. Ecco perché il server elenca l'10.8.0.2/32 per il laptop: un'impostazione di 0.0.0.0/0 permetterebbe a quel client di falsificare qualsiasi indirizzo all'interno del tunnel.

PersistentKeepalive serve per i client dietro NAT, dove il router mantiene aperta la mappatura UDP solo finché fluiscono pacchetti. Quando la sessione scade, il server non può più raggiungere il client. PersistentKeepalive = 25 mantiene aperta la mappatura — impostalo sul client, non su un server con IP pubblico.

DNS, e la fuga che nessuno nota

Con AllowedIPs = 0.0.0.0/0 e senza la riga DNS =, il client mantiene il resolver appreso dalla rete locale — il router del café su 192.168.1.1. Quella rotta è più specifica della rotta di default; pertanto, le query DNS escono sul link locale in chiaro, mentre tutto il resto è incanalato nel tunnel. Il traffico è privato; l'elenco dei nomi no.

Esistono due opzioni valide. Puntare DNS a un resolver pubblico (DNS = 9.9.9.9) per far transitare le query nel tunnel e farle uscire dal server, sebbene il resolver possa comunque vederle. Oppure eseguire unbound o dnsmasq collegati a 10.8.0.1, impostare DNS = 10.8.0.1 e aggiungere udp dport 53 iifname "wg0" accept alla catena input — configurando quella riga si ignora il resolver, ma nessuna risoluzione sarà possibile.

Sui client Linux, wg-quick applica DNS tramite resolvconf; se è assente si ottiene resolvconf: command not found. Installare openresolv, oppure impostare PostUp = resolvectl dns %i 10.8.0.1 su un client systemd-resolved.

Aggiunta e rimozione di peer senza interrompere il tunnel

Riavviare l'interfaccia per aggiungere un utente disconnette tutti i client connessi. Aggiungi il blocco [Peer] a wg0.conf, quindi ricarica il peer set direttamente.

sudo bash -c 'wg syncconf wg0 <(wg-quick strip wg0)'

wg-quick strip stampa la configurazione senza le chiavi wg-quick-only (Address, DNS, PostUp), e syncconf applica le modifiche mantenendo attive le sessioni esistenti. Aggiorna solo i peer: una modifica a Address richiede comunque un comando down/up completo. Revoca con sudo wg set wg0 peer <public key> remove, quindi elimina il blocco dal file per evitare che venga ricaricato al prossimo avvio.

Modalità di errore e stringhe visualizzate

L'handshake non si completa mai. wg show elenca il peer senza latest handshake, e il client riporta nei log:

Handshake for peer 1 (10.0.0.10:51820) did not complete after 5 seconds, retrying (try 2)

Nessun dato arriva o nessun dato viene accettato. Verificare i seguenti punti: la porta UDP 51820 è aperta sul firewall del VPS e sul firewall della rete del provider (un controllo separato nella maggior parte dei pannelli); l'indirizzo e la porta Endpoint sono corretti; le chiavi sono incrociate correttamente. La chiave nel blocco [Peer] del client deve essere la chiave pubblica del server, e viceversa. Inserire una chiave privata o la chiave pubblica del client causa esattamente questo errore. sudo tcpdump -ni any udp port 51820 sul server indica se i pacchetti arrivano. Il modulo kernel non genera log di default; i messaggi WireGuard appaiono in dmesg solo dopo aver abilitato il dynamic debug (echo module wireguard +p | sudo tee /sys/kernel/debug/dynamic_debug/control). Con questa opzione attiva, un errore di corrispondenza delle chiavi appare come un drop di tipo invalid-MAC.

L'handshake funziona, ma non c'è internet. ping 10.8.0.1 ha successo ma ping 1.1.1.1 va in timeout: il forwarding o il NAT non sono configurati. Verificare che sysctl net.ipv4.ip_forward legga 1, quindi monitorare i contatori durante il ping del client con sudo nft list ruleset o sudo iptables -t nat -L POSTROUTING -n -v. Zero pacchetti sulla regola di masquerade indica che il nome dell'interfaccia di uscita è errato; un contatore in aumento senza risposte indica un problema con la policy della catena forward.

Internet funziona, ma i nomi non vengono risolti. ping 1.1.1.1 ha successo e curl https://example.com restituisce Could not resolve host. La riga DNS è assente, oppure indica un resolver non raggiungibile dall'interno del tunnel.

Alcuni siti HTTPS si bloccano. SSH e ping funzionano correttamente; le pagine pesanti si bloccano. Il problema è il path MTU: il tunnel aggiunge overhead e un nodo intermedio scarta i pacchetti troppo grandi senza inviare un messaggio ICMP di ritorno. Ridurre MTU sul client [Interface] — provare 1420, poi 1380, infine 1280.

L'interfaccia non si avvia. Address already in use indica che un altro processo sta utilizzando la porta UDP 51820. Cannot find device wg0 dopo un fallito up indica solitamente che la configurazione è stata rifiutata; consultare journalctl -u wg-quick@wg0 -n 50.

Migrazione da Streisand o OpenVPN

Streisand non è più mantenuto e il suo repository è stato archiviato. Utilizzare una VPN basata su un sistema di automazione abbandonato crea problemi di sicurezza a lungo termine. Non è possibile eseguire un upgrade sul sistema esistente. La PKI di OpenVPN non è compatibile con WireGuard: WireGuard non utilizza certificati, CA o scadenze; ogni client riceve una nuova coppia di chiavi.

Eseguire la migrazione in parallelo: WireGuard su UDP 51820 può coesistere con OpenVPN sulla porta 1194 sullo stesso server. Installare wg0, spostare i client uno alla volta e infine arrestare il vecchio servizio. Il modello di username/password e di revoca di OpenVPN non è compatibile con WireGuard; se sono necessari account o log di audit, implementare tali funzioni sopra WireGuard.

Backups, upgrades, and what strains at scale

/etc/wireguard è il server. Effettua un backup (sudo tar czf wg-backup.tgz -C /etc wireguard, modalità 600, salvato su un dispositivo esterno) per poter ricostruire il sistema su un nuovo VPS in pochi minuti. Se perdi la chiave privata del server, ogni configurazione client deve essere rigenerata, poiché i client verificano la chiave pubblica del server. Gli upgrade sono normali apt upgrade seguiti da un reboot per gli aggiornamenti del kernel; wg-quick@wg0 si riavvia automaticamente se è stato abilitato.

Lo stato per ogni peer è ridotto e la crittografia viene eseguita nel kernel. Il limite è quindi rappresentato dalla CPU e dalla banda del VPS, non da parametri in questa configurazione. Misura le prestazioni con iperf3 attraverso il tunnel invece di fare affidamento su dati pubblicati. Il problema principale su larga scala è la gestione operativa. Ogni peer richiede un IP del tunnel unico; modificare manualmente sessanta blocchi [Peer] causa l'inserimento di duplicati AllowedIPs: genera le configurazioni tramite uno script. Un singolo server rappresenta un unico endpoint UDP e un singolo punto di guasto. WireGuard non supporta il clustering: la ridondanza richiede un secondo server con le proprie chiavi. La rotazione delle chiavi rimane manuale; annota chi possiede quale chiave e la procedura per revocarne una.

Tutto questo richiede un sistema Linux sotto il tuo controllo: un IP pubblico, un kernel in cui caricare un modulo e un firewall gestito interamente da te.

FAQ

Perché l'handshake di WireGuard non si completa mai?

Se wg show elenca un peer senza latest handshake, i pacchetti non arrivano o non vengono accettati. Controlla la porta UDP 51820 sul firewall del VPS e sul firewall di rete separato del provider. Conferma l'host e la porta in Endpoint. Verifica che le chiavi non siano invertite: il blocco [Peer] del client deve contenere la chiave public del server. sudo tcpdump -ni any udp port 51820 sul server indica se i pacchetti arrivano. dmesg riporta i fallimenti dell'handshake di WireGuard solo dopo aver abilitato il debug dinamico (echo module wireguard +p | sudo tee /sys/kernel/debug/dynamic_debug/control); in quel caso, un errore di corrispondenza delle chiavi appare come un drop di tipo invalid-MAC.

Il tunnel è connesso ma non ho accesso a internet. Cosa manca?

Se ping 10.8.0.1 funziona mentre ping 1.1.1.1 va in timeout, il problema riguarda il forwarding o il NAT. Conferma che sysctl net.ipv4.ip_forward legga 1 e che sia configurato in /etc/sysctl.d/, non solo tramite un sysctl -w che scompare al riavvio. Controlla poi che la regola di masquerade indichi l'interfaccia di uscita reale da ip route show default — ovvero enp1s0 o ens3, raramente eth0.

Devo inserire la riga DNS = nella configurazione del client?

Con un tunnel completo e senza la riga DNS =, il client utilizza il resolver appreso dalla rete locale. Le query DNS vengono inviate in chiaro sul link locale, mentre tutto il resto passa nel tunnel. Punta DNS a un resolver pubblico, oppure esegui unbound/dnsmasq collegati a 10.8.0.1 e apri la porta udp dport 53 iifname "wg0" nella catena input.

Cosa controlla effettivamente AllowedIPs?

Svolge due funzioni. In uscita agisce come tabella di routing: il traffico che corrisponde all'AllowedIPs di un peer viene criptato e inviato a quel peer. In entrata agisce come lista di controllo degli accessi: un pacchetto decriptato la cui sorgente è esterna all'AllowedIPs di quel peer viene scartato. Per questo motivo il server elenca un /32 per ogni client, mentre il client può elencare 0.0.0.0/0.

WireGuard può girare su qualsiasi VPS?

Su un VPS KVM funziona con il modulo in-kernel senza configurazioni aggiuntive. Sulla virtualizzazione a container che condivide il kernel dell'host, come OpenVZ o LXC, modprobe wireguard fallisce con Operation not supported e la soluzione alternativa è l'implementazione userspace wireguard-go. Esegui sudo modprobe wireguard && echo ok prima di qualsiasi altra operazione.

#wireguard#vpn#linux-networking#nftables#systemd#self-hosting