Che cosa costruiamo
Un server OpenVPN su Ubuntu 24.04 che permette a chi lavora fuori sede di entrare nella rete aziendale da qualsiasi dispositivo: portatili Windows e Mac, PC Linux, telefoni Android e iPhone. Ogni utente riceve un file .ovpn che contiene tutto il necessario: lo importa nel client, inserisce la passphrase se l’hai impostata, e si collega.
Le scelte della guida:
- Certificati per ogni utente, emessi da una tua autorità di certificazione (CA) con Easy-RSA. Un utente che lascia l’azienda si revoca in un minuto senza toccare gli altri.
- tls-crypt: una chiave condivisa cifra anche la fase iniziale della connessione. Il server non risponde a nessuno che non abbia la chiave, quindi resta invisibile alle scansioni.
- UDP sulla porta 1194, con cifrari moderni (AES-GCM e ChaCha20-Poly1305) e TLS 1.2 come minimo.
- Split tunnel: passa nella VPN solo il traffico verso la rete aziendale, mentre la navigazione resta sulla connessione dell’utente. Più avanti trovi anche la variante con tutto il traffico nel tunnel.
Il client IKEv2 è già dentro Windows, macOS e iOS (vedi Accesso remoto IKEv2), ma usa porte UDP 500 e 4500 che qualche rete di albergo o di ospiti blocca. OpenVPN richiede un’app sul dispositivo, in compenso è uguale su tutte le piattaforme, passa dappertutto con una sola porta (all’occorrenza anche in TCP 443) e si gestisce con un file per utente. Per un parco dispositivi misto è spesso la scelta più semplice.
Lo scenario
| Elemento | Valore nella guida |
|---|---|
| Nome pubblico della VPN | vpn.example.it → 203.0.113.10 |
| LAN della sede | 192.168.10.0/24, router e DNS 192.168.10.1 |
| Server OpenVPN | 192.168.10.5, interfaccia eth0 |
| Rete della VPN | 10.8.0.0/24, server 10.8.0.1 |
| Porta | UDP 1194 |
Sostituisci questi valori con i tuoi. La rete della VPN non deve sovrapporsi a nessuna rete già in uso, né in sede né, possibilmente, a casa degli utenti: per lo stesso motivo conviene che la LAN della sede non sia 192.168.0.0/24 o 192.168.1.0/24, le reti predefinite dei router domestici.
Il router della sede deve avere un IP pubblico, meglio se statico, oppure un nome dinamico (DDNS). Se l’operatore usa il CGNAT, cioè un indirizzo condiviso con altri clienti, le connessioni dall’esterno non arrivano: chiedi un IP pubblico o metti il server su una VPS.
Installazione
sudo apt update
sudo apt install openvpn easy-rsa
openvpn --version | head -1 # Ubuntu 24.04: OpenVPN 2.6.x
Il pacchetto installa il servizio openvpn-server@.service: openvpn-server@server legge /etc/openvpn/server/server.conf ed esegue OpenVPN dentro quella cartella, quindi nel file di configurazione i percorsi dei certificati possono essere relativi.
I certificati con Easy-RSA
La CA si crea in una cartella dell’amministratore, non in /etc/openvpn. La sua chiave privata, ca.key, è la cosa più preziosa di tutta l’installazione: chi la possiede può creare utenti validi. In un’installazione rigorosa la CA sta su un’altra macchina, anche offline; qui per semplicità la teniamo sul server, in una cartella leggibile solo dal tuo utente.
make-cadir ~/openvpn-ca
cd ~/openvpn-ca
chmod 700 ~/openvpn-ca
Modifica il file vars, togliendo il commento o aggiungendo queste righe, per usare chiavi a curva ellittica (più piccole e veloci delle RSA) e scadenze ragionevoli:
set_var EASYRSA_ALGO ec
set_var EASYRSA_CURVE prime256v1
set_var EASYRSA_DIGEST "sha256"
set_var EASYRSA_CA_EXPIRE 3650 # CA: 10 anni
set_var EASYRSA_CERT_EXPIRE 825 # server e client: circa 2 anni
set_var EASYRSA_CRL_DAYS 3650 # lista di revoca: vedi la nota più avanti./easyrsa init-pki
./easyrsa build-ca # chiede una passphrase per la CA: usala
./easyrsa build-server-full server nopass # certificato del server, senza passphrase
./easyrsa gen-crl # lista di revoca, per ora vuota
sudo openvpn --genkey secret /etc/openvpn/server/tc.key
La passphrase della CA verrà chiesta ogni volta che firmi o revochi un certificato. Il certificato del server invece non può averla, altrimenti il servizio non partirebbe da solo all’avvio.
sudo cp pki/ca.crt pki/issued/server.crt pki/private/server.key pki/crl.pem /etc/openvpn/server/
sudo chmod 600 /etc/openvpn/server/server.key /etc/openvpn/server/tc.key
sudo chmod 644 /etc/openvpn/server/crl.pem
crl.pem deve restare leggibile da tutti, perché OpenVPN la rilegge a ogni connessione dopo aver ceduto i privilegi di root all’utente nobody. Per lo stesso motivo la cartella deve essere attraversabile: ls -ld /etc/openvpn/server deve mostrare drwxr-xr-x; se non è così, sudo chmod 755 /etc/openvpn/server. Le chiavi restano protette dai loro permessi 600.
La configurazione del server
# --- rete e trasporto ---
port 1194
proto udp
dev tun
topology subnet
server 10.8.0.0 255.255.255.0
# --- certificati e chiavi ---
ca ca.crt
cert server.crt
key server.key
dh none # con chiavi EC non serve il file Diffie-Hellman
tls-crypt tc.key
crl-verify crl.pem
# --- sicurezza ---
tls-version-min 1.2
data-ciphers AES-256-GCM:AES-128-GCM:CHACHA20-POLY1305
remote-cert-tls client # accetta solo certificati emessi per client
# --- che cosa ricevono i client ---
push "route 192.168.10.0 255.255.255.0"
push "dhcp-option DNS 192.168.10.1"
push "dhcp-option DOMAIN sede.lan"
client-config-dir ccd # impostazioni per singolo utente
# --- stabilità ---
keepalive 10 60
explicit-exit-notify 1
user nobody
group nogroup
persist-key
persist-tun
verb 3topology subnetdà a ogni client un indirizzo della10.8.0.0/24come in una LAN normale. È la modalità consigliata; la vecchianet30sprecava quattro indirizzi per client.push "route …"dice ai client quali reti raggiungere attraverso la VPN. Se in sede ci sono più reti, aggiungi una riga per ciascuna.data-cipherselenca i cifrari accettati. Client e server scelgono il primo che hanno in comune; ChaCha20 è utile sui telefoni senza accelerazione AES.user nobodyfa cedere a OpenVPN i privilegi di root dopo l’avvio: se un giorno ci fosse una falla, l’attaccante si ritroverebbe con un utente senza permessi.- Il file di stato non serve nel file di configurazione: il servizio di Ubuntu lo scrive già in
/run/openvpn-server/status-server.log.
sudo mkdir -p /etc/openvpn/server/ccd
sudo chmod 755 /etc/openvpn/server/ccd
Inoltro dei pacchetti
Il server deve fare da router tra tun0 e la LAN:
net.ipv4.ip_forward = 1sudo sysctl --system | grep ip_forward
Avvio
sudo systemctl enable --now openvpn-server@server
systemctl status openvpn-server@server
ip -br addr show tun0 # tun0 UNKNOWN 10.8.0.1/24
Se il servizio non parte, il motivo è quasi sempre scritto nelle ultime righe di journalctl -u openvpn-server@server -n 30: un file non trovato, un permesso, un errore di battitura.
Firewall, NAT e router
Restano tre cose da sistemare: far entrare la porta UDP 1194, lasciar passare il traffico tra VPN e LAN, e fare in modo che le risposte dalla LAN tornino ai client.
Le regole sul server
Se usi il firewall descritto in Firewall completo con nftables, aggiungi queste regole alle catene esistenti della tabella inet filtro:
udp dport 1194 accept comment "OpenVPN" ct state established,related accept
iifname "tun0" oifname "eth0" ip daddr 192.168.10.0/24 accept comment "VPN verso LAN"Con la catena forward in policy drop, solo i client VPN possono aprire connessioni verso la LAN: le macchine della LAN rispondono, ma non iniziano connessioni verso i client.
Il ritorno: NAT o rotta statica
Quando un client 10.8.0.2 contatta il NAS 192.168.10.20, il NAS risponde al suo gateway, cioè il router 192.168.10.1, che non sa nulla della rete 10.8.0.0/24. Ci sono due soluzioni.
Soluzione A, consigliata: una rotta statica sul router della sede. Sul router aggiungi la rotta 10.8.0.0/24 via 192.168.10.5. I server della LAN vedono l’indirizzo VPN vero di ogni utente, ed è utile nei log e nelle regole.
Soluzione B: il NAT sul server OpenVPN. Se non puoi toccare il router, il server traduce gli indirizzi dei client nel proprio:
table ip natvpn {
chain postrouting {
type nat hook postrouting priority srcnat; policy accept;
ip saddr 10.8.0.0/24 oifname "eth0" masquerade
}
}sudo nft -c -f /etc/nftables.conf && sudo systemctl reload nftables
sudo systemctl restart openvpn-server@server
Dopo il reload del firewall riavvia OpenVPN: flush ruleset non tocca l’interfaccia tun0, ma è una buona abitudine verificare che il servizio riparta pulito.
Con UFW: sudo ufw allow 1194/udp, poi in /etc/default/ufw imposta DEFAULT_FORWARD_POLICY="ACCEPT" (o scrivi regole ufw route allow in on tun0 out on eth0), e il NAT va aggiunto in /etc/ufw/before.rules in una sezione *nat. Non mescolare UFW e un nftables.conf scritto a mano.
Il router della sede
Sul router crea un port forward: UDP 1194 dall’indirizzo pubblico a 192.168.10.5, porta 1194. Il nome di questa funzione cambia da modello a modello: “Port forwarding”, “Virtual server”, “NAT” o “Inoltro porte”.
Creare i profili dei client
Ogni utente riceve un unico file .ovpn con dentro la configurazione, i certificati e la chiave tls-crypt. È il formato che tutti i client importano senza fatica. Prepara prima la parte comune:
client
dev tun
proto udp
remote vpn.example.it 1194
resolv-retry infinite
nobind
persist-key
persist-tun
remote-cert-tls server
verify-x509-name server name
tls-version-min 1.2
data-ciphers AES-256-GCM:AES-128-GCM:CHACHA20-POLY1305
verb 3remote-cert-tls server e verify-x509-name server name fanno controllare al client che dall’altra parte ci sia proprio il tuo server, con il certificato di nome server, e non qualcuno che si spaccia per lui.
Poi uno script che crea certificato e profilo in un colpo solo:
#!/bin/bash
# Crea il certificato e il profilo .ovpn di un utente.
# Uso: ./crea-client.sh mario profilo senza passphrase
# ./crea-client.sh mario --passphrase la chiave dell'utente chiede una passphrase a ogni connessione
set -euo pipefail
cd "$(dirname "$0")"
NOME=${1:?Manca il nome utente, es.: ./crea-client.sh mario}
[[ $NOME =~ ^[a-zA-Z0-9._-]+$ ]] || { echo "Nome non valido: usa lettere, numeri, punto, trattino"; exit 1; }
OUT=~/client-ovpn
mkdir -p "$OUT"; chmod 700 "$OUT"
if [[ ${2:-} == --passphrase ]]; then
./easyrsa --batch build-client-full "$NOME"
else
./easyrsa --batch build-client-full "$NOME" nopass
fi
{
cat client-base.conf
echo "<ca>"; cat pki/ca.crt; echo "</ca>"
echo "<cert>"; sed -n '/BEGIN CERTIFICATE/,/END CERTIFICATE/p' "pki/issued/$NOME.crt"; echo "</cert>"
echo "<key>"; cat "pki/private/$NOME.key"; echo "</key>"
echo "<tls-crypt>"; sudo cat /etc/openvpn/server/tc.key; echo "</tls-crypt>"
} > "$OUT/$NOME.ovpn"
chmod 600 "$OUT/$NOME.ovpn"
echo "Profilo pronto: $OUT/$NOME.ovpn"chmod 700 ~/openvpn-ca/crea-client.sh
~/openvpn-ca/crea-client.sh mario
~/openvpn-ca/crea-client.sh laura --passphrase
Con --batch Easy-RSA non chiede conferme, ma per firmare il certificato chiede comunque la passphrase della CA. Con --passphrase, in più, chiede di scegliere quella dell’utente.
Contiene la chiave privata dell’utente: chi lo copia può entrare nella tua rete. Non mandarlo in chiaro per email o chat. Usa una cartella condivisa con accesso personale, un archivio cifrato con la password comunicata per telefono, oppure consegnalo di persona. Per i portatili, che si possono perdere, è meglio il profilo con passphrase.
Indirizzo fisso per un utente
Per dare a un utente sempre lo stesso indirizzo, per esempio per aprirgli un accesso nel firewall, crea un file con il suo nome (lo stesso del certificato) nella cartella ccd:
ifconfig-push 10.8.0.50 255.255.255.0Scegli gli indirizzi fissi nella parte alta della rete: il pool dinamico parte dall’inizio, 10.8.0.2. Il file viene letto alla connessione successiva, senza riavviare il server.
Client Windows
Ci sono due client affidabili, entrambi gratuiti:
- OpenVPN Community (OpenVPN GUI), da
openvpn.net/community-downloads. È il client open source, con la stessa versione 2.6 del server. - OpenVPN Connect, il client ufficiale di OpenVPN Inc., con un’interfaccia più semplice e uguale a quella dei telefoni.
Con OpenVPN GUI:
- Installa il pacchetto MSI con le opzioni predefinite (installa anche il driver di rete virtuale).
- Clic destro sull’icona OpenVPN nell’area di notifica → Importa file… e scegli
mario.ovpn. In alternativa, copia il file inC:\Users\<utente>\OpenVPN\config\. - Clic destro sull’icona → Connetti. L’icona diventa verde quando la VPN è attiva.
Con OpenVPN Connect: Import Profile → Upload File → trascina il file .ovpn → Connect.
Verifica dal Prompt dei comandi:
ipconfig | findstr 10.8.0
ping 192.168.10.1
route print 192.168.10.*
Client macOS
Su Mac il client più usato è Tunnelblick, gratuito e open source (tunnelblick.net). In alternativa c’è OpenVPN Connect, dal sito di OpenVPN.
- Installa Tunnelblick e avvialo; al primo avvio scegli Ho i file di configurazione.
- Fai doppio clic su
mario.ovpn: Tunnelblick chiede se installare la configurazione per te o per tutti gli utenti, e la password del Mac. - Dall’icona del tunnel nella barra dei menu → Connetti mario.
Tunnelblick applica da solo i DNS inviati dal server. Per verificare:
ifconfig | grep 10.8.0
ping -c 3 192.168.10.1
scutil --dns | grep -A3 sede.lan
Client Linux
Desktop con NetworkManager (Ubuntu, Fedora, Mint…)
sudo apt install network-manager-openvpn-gnome
nmcli connection import type openvpn file mario.ovpn
nmcli connection up mario
Dopo l’importazione la VPN compare anche nel menu di rete in alto a destra, da dove si accende e si spegne. Per avere lo split tunnel, nelle impostazioni della connessione → IPv4 attiva Usa questa connessione solo per le risorse della sua rete (equivale a nmcli connection modify mario ipv4.never-default yes).
Server o PC senza interfaccia grafica
sudo apt install openvpn openvpn-systemd-resolved
sudo cp mario.ovpn /etc/openvpn/client/mario.conf
sudo chmod 600 /etc/openvpn/client/mario.conf
Per far applicare i DNS della sede tramite systemd-resolved, aggiungi in fondo al file:
script-security 2
up /etc/openvpn/update-systemd-resolved
down /etc/openvpn/update-systemd-resolved
down-presudo systemctl enable --now openvpn-client@mario
journalctl -u openvpn-client@mario -n 20
resolvectl status tun0
Il servizio openvpn-client@mario si avvia anche al boot: è il modo giusto per collegare in modo permanente un server remoto alla sede. Per una prova al volo basta sudo openvpn --config mario.ovpn, e si chiude con Ctrl+C.
Client Android
- Installa OpenVPN Connect dal Play Store. Un’ottima alternativa open source è OpenVPN for Android, di Arne Schwabe.
- Copia
mario.ovpnsul telefono, per esempio in Download, con un cavo o da una cartella condivisa. - In OpenVPN Connect: Import Profile → scheda Upload File → Browse → scegli il file → OK.
- Attiva l’interruttore del profilo. La prima volta Android chiede di consentire la creazione della VPN: conferma.
Nelle impostazioni dell’app conviene attivare Seamless tunnel: mantiene il tunnel quando il telefono passa dal Wi-Fi al 4G. Per evitare che le app passino fuori dalla VPN quando il tunnel è giù, puoi attivare in Android Impostazioni → Rete → VPN → ⚙ → VPN sempre attiva.
Client iPhone e iPad
- Installa OpenVPN Connect dall’App Store.
- Porta il file
mario.ovpnsul telefono: con AirDrop da un Mac, oppure salvandolo nell’app File (iCloud Drive, cartella condivisa). Toccalo e scegli Condividi → OpenVPN. - OpenVPN Connect mostra il profilo importato: tocca Add, poi conferma la richiesta di iOS di aggiungere una configurazione VPN.
- Attiva l’interruttore. Nella barra di stato compare l’icona VPN.
Su iOS e Android il file con la passphrase è la scelta più sicura: senza passphrase chi ha il telefono sbloccato entra nella rete aziendale.
Variante: tutto il traffico nella VPN
A volte serve che anche la navigazione passi dalla sede: per uscire su Internet con l’IP aziendale (accesso a portali che filtrano per indirizzo) o per proteggere chi usa Wi-Fi pubblici. Sul server, al posto della rotta verso la sola LAN:
push "redirect-gateway def1 bypass-dhcp"
push "dhcp-option DNS 192.168.10.1"
push "block-outside-dns" # solo Windows: niente richieste DNS fuori dal tunnelIn questo caso il NAT della soluzione B è obbligatorio (o va fatto dal router per la rete 10.8.0.0/24), e nella catena forward devi permettere ai client di uscire verso Internet:
iifname "tun0" oifname "eth0" accept comment "VPN verso LAN e Internet"
Ricorda che tutta la banda di navigazione degli utenti passerà dalla linea della sede, in upload e in download.
Gestione quotidiana: chi è collegato, revoche, rinnovi
Chi è collegato
sudo cat /run/openvpn-server/status-server.log
journalctl -u openvpn-server@server --since today | grep -E 'Peer Connection Initiated|Inactivity timeout|SIGTERM'
Nel file di stato le righe CLIENT_LIST riportano nome del certificato, indirizzo pubblico, indirizzo VPN, byte scambiati e ora di connessione.
Revocare un utente
cd ~/openvpn-ca
./easyrsa revoke mario
./easyrsa gen-crl
sudo cp pki/crl.pem /etc/openvpn/server/crl.pem
sudo chmod 644 /etc/openvpn/server/crl.pem
OpenVPN rilegge la lista di revoca a ogni nuova connessione: mario non potrà più entrare. Se in quel momento è collegato, la sessione in corso resta attiva fino alla rinegoziazione della chiave (al massimo un’ora) o fino al riavvio del servizio: per buttarlo fuori subito, sudo systemctl restart openvpn-server@server.
Anche crl.pem ha una data di scadenza. Quando passa, OpenVPN rifiuta tutti i client con l’errore CRL has expired, e da un giorno all’altro nessuno riesce più a collegarsi. Easy-RSA usa per default 180 giorni: per questo nel file vars abbiamo messo EASYRSA_CRL_DAYS 3650. Controlla la scadenza con openssl crl -in /etc/openvpn/server/crl.pem -noout -nextupdate e rigenerala comunque a ogni revoca.
Rinnovare i certificati
I certificati di server e client durano circa due anni (825 giorni). Controlla le scadenze con:
for c in ~/openvpn-ca/pki/issued/*.crt; do
printf '%-30s %s\n' "$(basename "$c" .crt)" "$(openssl x509 -in "$c" -noout -enddate | cut -d= -f2)"
done
Per un client basta crearne uno nuovo con lo stesso script (dopo aver revocato il vecchio) e mandare il nuovo profilo. Per il server: ./easyrsa renew server nopass (con Easy-RSA 3.1 di Ubuntu 24.04; dalla 3.2 si usa expire seguito da sign-req), copia il nuovo server.crt in /etc/openvpn/server/ e riavvia il servizio. I profili dei client non cambiano, perché si fidano della CA e non del singolo certificato del server.
Problemi comuni
| Sintomo | Causa probabile e soluzione |
|---|---|
Il client resta su “Connecting…”, nel log TLS key negotiation failed to occur within 60 seconds | I pacchetti non arrivano al server: manca il port forward sul router, la porta 1194/udp è chiusa nel firewall, oppure il nome remote punta all’indirizzo sbagliato. Sul server, mentre il client prova, esegui sudo tcpdump -ni eth0 udp port 1194: se non vedi niente, il problema è prima del server. |
Nel log del server tls-crypt unwrap error | Il client usa una chiave tls-crypt diversa da quella del server, per esempio un profilo creato prima di rigenerare tc.key. Rigenera il profilo. |
VERIFY ERROR … certificate revoked | Il certificato dell’utente è stato revocato. Crea un nuovo certificato con un altro nome o rinnova quello esistente. |
Tutti i client rifiutati con CRL has expired | La lista di revoca è scaduta: ./easyrsa gen-crl, copia crl.pem e imposta EASYRSA_CRL_DAYS come nella sezione I certificati con Easy-RSA. |
Collegato, ping a 10.8.0.1 sì, alla LAN no | Manca l’inoltro (sysctl net.ipv4.ip_forward deve valere 1), la regola nella catena forward o la strada del ritorno: rotta statica sul router o NAT (soluzione A o B). |
| Raggiungo gli IP della LAN ma non i nomi | I DNS non sono stati applicati. Su Linux senza interfaccia grafica servono le righe update-systemd-resolved; altrove controlla che il server invii dhcp-option DNS. |
| La VPN si collega, ma alcuni siti o condivisioni si bloccano a metà | Problema di MTU: alcune linee, come PPPoE e 4G, hanno pacchetti più piccoli. Aggiungi mssfix 1360 nel file del server e in client-base.conf, poi rigenera i profili. |
Un client vecchio (OpenVPN 2.4) non si collega: cipher negotiation failed | Aggiorna il client: le versioni attuali di OpenVPN Connect, Tunnelblick e OpenVPN GUI supportano tutto quello che usa questa guida. |
| In un albergo o una rete ospiti non funziona | La rete blocca le porte UDP non standard. Puoi aggiungere una seconda istanza del server in proto tcp sulla porta 443 (un secondo file, per esempio server-tcp.conf, con una rete diversa come 10.9.0.0/24) e mettere nel profilo client due righe remote, una UDP e una TCP. |
Il passo successivo
Un server VPN esposto su Internet è un bersaglio come gli altri. tls-crypt lo rende già invisibile a chi non ha la chiave, ma vale la pena controllare chi si collega e da dove, con i comandi della guida Analizzare connessioni, porte e ban su un server Linux, e tenere l’intero server protetto con un firewall con policy restrittiva. Se ti serve collegare due sedi in modo permanente invece che singoli utenti, le guide VPN IPsec tra Cisco e Ubuntu e GRE su IPsec descrivono le soluzioni site-to-site.
Prova sempre la configurazione in laboratorio, salva quella attuale prima di modificarla e tieni un accesso di emergenza (console o accesso fuori banda) quando lavori su firewall e VPN da remoto.