Realizzare una VPN per collegare gli utenti itineranti alla sede centrale usando Linux e OpenVPN

Capo: “Come faccio a vedere i miei documenti mentre sono in giro?”
Tu: “C’è Dropbox, capo. Metti tutto lì, e accedi ai tuoi documenti ovunque ti trovi”.
Capo: “Sì, ma voglio vedere anche i documenti di Tizio e di Caio”.
Tu: “Dì a Tizio e a Caio di archiviare i loro documenti su Dropbox, e di condividere le directory con te”.
Capo: “Condividere le dir…che? E poi come faccio a entrare nel gestionale? E a vedere i miei cruscotti mentre sono all’aeroporto? Duecentomila euro di sistema gestionale e non si può neanche accedere da internet. Bell’affare. E poi mia moglie vuole lavorare da casa, vedi per favore di farle il collegamento”.
Tu: “(Sì, sì. Lavorare da casa.)”, pensi. E invece dici: “Va bene, capo. Penso ad una soluzione”.
Dite la verità, cari colleghi amministratori di rete. Quante volte vi hanno fatto richieste del genere? Una soluzione c’è e si chiama VPN (Virtual Private Network, per chi è a digiuno); oggi vedremo come realizzarne una particolarmente adatta agli utenti itineranti come il vostro capo (e sua moglie dirigente-casalinga) che desiderano accedere a tutte le risorse aziendali in modo trasparente dal loro portatile, sia che si trovino in ufficio, sia che si trovino a casa, sia che stiano sorvolando il Polo Sud in mongolfiera.

Cos’è una VPN?

Come già accennato, il termine VPN è l’acronimo di Virtual Private Network. Altro non è che una rete di telecomunicazioni privata, realizzata però sfruttando un sistema di trasmissione pubblico e condiviso, come ad esempio Internet. Da qui l’attributo Virtual: la rete è privata, ma solo virtualmente. La privacy del traffico immesso sulla rete pubblica è di norma ottenuta tramite una qualche forma di crittografia: i dati vengono cifrati appena prima di entrare nella rete pubblica, e vengono decifrati all’altro capo della VPN. In realtà, il termine VPN è generico e non rappresenta un marchio, una tecnologia in particolare o uno standard, ma bensì il concetto, l’idea (sebbene esistano alcune specifiche implementazioni, largamente utilizzate e rigorosamente standardizzate, come IPSec). La nostra implementazione si baserà su OpenVPN, un software Open Source multipiattaforma, stabile e versatile, che si comporta egregiamente anche in presenza di topologie di rete “difficili”, come ad esempio in reti mascherate da NAT o firewall, che normalmente creano qualche problema alle tradizionali VPN basate su IPSec.

Panoramica dell’installazione

Realizzeremo un server VPN basato sul sistema operativo Linux. La mia scelta, come sempre, cade su Debian, ma ciascuno è libero di scegliere la distribuzione che preferisce, facendo attenzione alle possibili differenze, che di solito riguardano le directory di installazione dei pacchetti e la posizione dei file di configurazione. Installeremo e configureremo OpenVPN in modalità server, quindi metteremo all’opera Iptables per girare i pacchetti dalla LAN alla VPN e viceversa, e per rimappare la LAN su una subnet non comunemente usata, in modo da ridurre il rischio di collisione tra gli IP della LAN e quelli della rete in cui si trova il client. Il client sarà una macchina Windows XP, sulla quale installeremo e configureremo OpenVPN in modalità client, e l’utility OpenVPN GUI, in modo che l’avvio della connessione sia questione di un click. Il client, una volta creato il tunnel VPN, potrà vedere l’intera rete LAN utilizzando la subnet 172.25.12.x. L’immagine seguente illustra la topologia della nostra rete:
Topologia di rete

Requisiti di rete per il server

Prima di iniziare è necessario verificare che il server sia raggiungibile da Internet, ovvero che ci sia un indirizzo IP pubblico (non importa che sia statico). Notoriamente, alcuni ISP come Fastweb non forniscono l’IP pubblico, a meno che non lo si sia acquistato; in altre parole, Fastweb è come un’enorme rete locale nattata, e quindi sostanzialmente irraggiungibile dalla rete Internet esterna. Cari utenti Fastweb, noi ci salutiamo qui, a meno che non abbiate acquistato l’IP pubblico, come detto pocanzi.
Se il server è dietro un NAT 1:m si dovrà usare la tecnica del port forwarding per girare i pacchetti in arrivo sulla porta 1194/UDP al server, e probabilmente bisognerà aggiungere qualche regola al firewall perimetrale. Infine, se l’IP è pubblico ma non è statico, sarà meglio appoggiarsi a qualche servizio DNS dinamico, come ad esempio DynDNS, poiché il client deve risolvere di volta in volta l’IP del server per stabilire la connessione.

Installazione e configurazione di OpenVPN sul server

Bene, iniziamo. Portiamoci sul server Linux e installiamo OpenVPN:

$ sudo apt-get install openvpn

Spostiamoci nella directory di configurazione e prepariamo alcune sottodirectory:

$ cd /etc/openvpn
$ sudo mkdir certs keys ccd
$ sudo chmod 0600 keys

OpenVPN, dal punto di vista dell’autenticazione e della crittografia, può lavorare sostanzialmente in due modi:

  • con chiavi statiche: server e client condividono la medesima chiave;
  • con certificati RSA: detta anche modalità SSL/TLS. Server e client dispongono ciascuno di un certificato. OpenVPN utilizza le informazioni contenute nei certificati per autenticare i due endpoint della connessione e per criptare i dati in transito nel tunnel VPN.

La modalità a chiave statica è ideale per connessioni point-to-point o per condurre test. La modalità SSL/TLS è considerata più sicura, e permette l’accesso simultaneo di più client. Noi utilizzeremo quest’ultima.
Nella directory certs metteremo i certificati, che possono essere resi pubblici. La directory keys ospiterà invece le chiavi private del server, che devono rimanere rigorosamente segrete. Infine, la directory ccd servirà per eventuali parametri di configurazione per-utente. Vedremo nel dettaglio questo aspetto più avanti.
Ci servono ora gli script di easy-rsa, un insieme di utility distribuite con OpenVPN che facilitano la creazione delle chiavi e dei certificati necessari al funzionamento del software. Gli script sono posti in /usr/share/doc/openvpn/examples/easy-rsa: copiamo l’intera directory nel percorso attuale per comodità:

$ tar -C /usr/share/doc/openvpn/examples -c -f- easy-rsa | sudo tar -x -p -f-

Dobbiamo adesso editare alcune variabili, in modo da riflettere la nostra configurazione.

$ sudo nano easy-rsa/2.0/vars

Spostiamoci verso il fondo. Troveremo un blocco di variabili con questo aspetto:

# These are the default values for fields
# which will be placed in the certificate.
# Don't leave any of these fields blank.
export KEY_COUNTRY="US"
export KEY_PROVINCE="CA"
export KEY_CITY="SanFrancisco"
export KEY_ORG="Fort-Funston"
export KEY_EMAIL="me@myhost.mydomain"

Inseriamo i nostri dati, ed aggiungiamone altri che serviranno ad accontentare gli script di gestione del CRL (Certificate Revocation List, una lista "nera" dove inserire tutti i certificati compromessi o ai quali si vuole revocare l’accesso):

# These are the default values for fields
# which will be placed in the certificate.
# Don't leave any of these fields blank.
export KEY_COUNTRY="IT"
export KEY_PROVINCE="Lodi"
export KEY_CITY="Casalpusterlengo"
export KEY_ORG="Acme"
export KEY_EMAIL="admin@acme.it"
export KEY_OU=""
export KEY_CN=""
export KEY_NAME=""
export PKCS11_MODULE_PATH="dummy"
export PKCS11_PIN="dummy"

Salviamo e abbandoniamo nano. È ora di preparare la PKI (Public Key Infrastructure). Diventiamo root e spostiamoci nella directory easy-rsa:

$ su
Password: 
# cd easy-rsa/2.0

Attenzione a quello che fate da adesso in avanti: l’utente root è plenipotenziario, e può anche fare danni gravi! Da un grande potere derivano grandi responsabilità.
Dobbiamo per prima cosa leggere le variabili che abbiamo appena editato; quindi creeremo i parametri di Diffie-Hellman, la Certification Authority, il certificato del server, il certificato del client e il CRL.

# . ./vars
NOTE: If you run ./clean-all, I will be doing a rm -rf on /etc/openvpn/easy-rsa/2.0/keys
# ./clean-all
# ./build-dh
Generating DH parameters, 1024 bit long safe prime, generator 2
This is going to take a long time
....................+.............   ... ...   ++*++*

Creiamo la Certification Authority (CA):

# ./build-ca
Generating a 1024 bit RSA private key
.......++++++
......++++++
writing new private key to 'ca.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [IT]:
State or Province Name (full name) [Lodi]:
Locality Name (eg, city) [Casalpusterlengo]:
Organization Name (eg, company) [Acme]:
Organizational Unit Name (eg, section) []:Ricerca & Sviluppo
Common Name (eg, your name or your server's hostname) [Acme CA]:
Name []:
Email Address [admin@acme.it]:rd@acme.it

Creiamo il certificato del server:

# ./build-key-server server
Generating a 1024 bit RSA private key
.++++++
...............................++++++
writing new private key to 'server.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [IT]:
State or Province Name (full name) [Lodi]:
Locality Name (eg, city) [Casalpusterlengo]:
Organization Name (eg, company) [Acme]:
Organizational Unit Name (eg, section) []:Ricerca & Sviluppo
Common Name (eg, your name or your server's hostname) [server]:
Name []:
Email Address [admin@acme.it]:rd@acme.it

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
Using configuration from /etc/openvpn/easy-rsa/2.0/openssl.cnf
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
countryName           :PRINTABLE:'IT'
stateOrProvinceName   :PRINTABLE:'Lodi'
localityName          :PRINTABLE:'Casalpusterlengo'
organizationName      :PRINTABLE:'Acme'
organizationalUnitName:T61STRING:'Ricerca & Sviluppo'
commonName            :PRINTABLE:'server'
emailAddress          :IA5STRING:'rd@acme.it'
Certificate is to be certified until Sep 27 18:53:43 2022 GMT (3650 days)
Sign the certificate? [y/n]:y

1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated

Una nota: OpenVPN utilizza il ‘Common Name’ per identificare tutte le entità coinvolte nel processo di autenticazione. Consiglio di scegliere nomi significativi ed univoci. Uno schema che di solito utilizzo è ‘server’ per il certificato del server, mentre per i certificati client utilizzo l’iniziale del nome e il cognome della persona a cui consegnerò il certificato (ad esempio: mrossi, gbianchi, averdi).
Bene, proseguiamo creando il certificato client per il collega Mario Rossi dell’Ufficio Commerciale:

# ./build-key mrossi
Generating a 1024 bit RSA private key
.++++++
.......................................++++++
writing new private key to 'mrossi.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [IT]:
State or Province Name (full name) [Lodi]:
Locality Name (eg, city) [Casalpusterlengo]:
Organization Name (eg, company) [Acme]:
Organizational Unit Name (eg, section) []:Ufficio Commerciale
Common Name (eg, your name or your server's hostname) [mrossi]:
Name []:Mario Rossi
Email Address [admin@acme.it]:mrossi@acme.it

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
Using configuration from /etc/openvpn/easy-rsa/2.0/openssl.cnf
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
countryName           :PRINTABLE:'IT'
stateOrProvinceName   :PRINTABLE:'Lodi'
localityName          :PRINTABLE:'Casalpusterlengo'
organizationName      :PRINTABLE:'Acme'
organizationalUnitName:PRINTABLE:'Ufficio Commerciale'
commonName            :PRINTABLE:'mrossi'
name                  :PRINTABLE:'Mario Rossi'
emailAddress          :IA5STRING:'mrossi@acme.it'
Certificate is to be certified until Sep 27 19:08:59 2022 GMT (3650 days)
Sign the certificate? [y/n]:y

1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated

Generiamo la Certificate Revocation List (CRL):

# cd keys
# openssl ca -gencrl -out crl.pem -config $KEY_CONFIG

Vediamo tutto quello che è stato prodotto:

# ls
01.pem  ca.crt  crl.pem     index.txt       index.txt.attr.old  mrossi.crt  mrossi.key  serial.old  server.csr
02.pem  ca.key  dh1024.pem  index.txt.attr  index.txt.old       mrossi.csr  serial      server.crt  server.key

Carramba che orgia! Niente panico. Sul lato server ci serviranno cinque file: dh1024.pem, ca.crt, crl.pem, server.crt e server.key; i primi quattro possono essere pubblici, l’ultimo deve rimanere segretissimo. Al signor Rossi, invece, daremo tre file: ca.crt, mrossi.crt e mrossi.key; anche in questo caso, i primi due possono essere pubblici, l’ultimo deve rimanere segreto. Se vogliamo inferire una semplice regola: i file .key restano segreti, tutti gli altri si possono distribuire senza particolari accorgimenti.
Copiamo i file necessari nelle directory che abbiamo creato prima:

# cp dh1024.pem ca.crt crl.pem server.crt /etc/openvpn/certs
# cp server.key /etc/openvpn/keys

Ci rimane un’ultima chiave da generare, che useremo per attivare l’opzione –tls-auth di openvpn. In due parole, con questa opzione viene aggiunto un livello di autenticazione HMAC al canale di controllo TLS; tutti i pacchetti provvisti di firma HMAC errata vengono scartati immediatamente. Ciò consente di limitare il rischio di attacchi DoS in ambienti, come il nostro, dove è impossibile porre restrizioni sulla provenienza della connessione entrante. Si rimanda alla pagina man di OpenVPN per una spiegazione esaustiva dell’argomento; ci basti per ora sapere che la chiave serve per "firmare" i pacchetti di rete scambiati tra client e server, scartando immediatamente i pacchetti non validi (e quindi presumibilmente provenienti da un attacker).

# cd /etc/openvpn/keys
# openvpn --genkey --secret tlsauth.pem

Possiamo abbandonare le vesti di root. È venuto il momento di creare il file di configurazione:

# exit
$ sudo nano /etc/openvpn/server.conf

Inseriamo le seguenti direttive:

server 10.1.1.0 255.255.255.0
proto udp
port 1194
dev tun0
ca certs/ca.crt
cert certs/server.crt
key keys/server.key
dh certs/dh1024.pem
crl-verify certs/crl.pem
tls-auth keys/tlsauth.pem
topology subnet
push "route 172.25.12.0 255.255.255.0"
push "dhcp-option DOMAIN acme.local"
push "dhcp-option DISABLE-NBT"
duplicate-cn
client-config-dir ccd
keepalive 15 120
comp-lzo
max-clients 10
user nobody
group nogroup
persist-key
persist-tun
status openvpn-status.log
verb 3

È doverosa qualche spiegazione. Ecco cosa stiamo dicendo a OpenVPN:

  • server 10.1.1.0 255.255.255.0: il software deve partire in modalità server. Gli indirizzi dei client verranno assegnati dalla subnet 10.1.1.0/24. Il server prende l’indirizzo 10.1.1.1
  • proto udp: usiamo il protocollo UDP. Creare un tunnel TCP sopra il protocollo TCP non è una buona idea, per motivi legati ai timeout e all’algoritmo di ritrasmissione del TCP. Per un approfondimento: http://sites.inka.de/~bigred/devel/tcp-tcp.html
  • port 1194: usiamo la porta 1194, che è quella di default. Ricordiamoci di impostare il port forwarding sul router!
  • dev tun0: il nome della interfaccia di rete virtuale che identifica la VPN.
  • ca certs/ca.crt: il certificato della CA (Certification Authority). Tutti gli altri certificati vengono validati da questo.
  • cert certs/server.crt: il certificato del server. Il client verifica questo certificato con la sua copia di ca.crt al momento della connessione, allo scopo di autenticare il server.
  • key keys/server.key: la chiave privata del server. Il server usa questa chiave per criptare i dati.
  • dh certs/dh1024.pem: il parametro di Diffie-Hellman. Usato per lo scambio iniziale di chiavi.
  • crl-verify certs/crl.pem: la Certificate Revocation List. Tutti i certificati elencati in questa lista vengono considerati non validi.
  • tls-auth keys/tlsauth.pem: come spiegato pocanzi, abilitiamo il controllo HMAC dei pacchetti.
  • topology subnet: niente point-to-point. Vogliamo una topologia di rete, dove ogni client prende un indirizzo IP.
  • push "route 172.25.12.0 255.255.255.0": ci servirà più avanti. Diciamo al server di inviare una route al client; ci servirà per raggiungere l’intera LAN da remoto.
  • push "dhcp-option DOMAIN acme.local": passiamo al client anche il suffisso DNS.
  • push "dhcp-option DISABLE-NBT": infine, diciamo al client di disabilitare NetBIOS sull’interfaccia VPN.
  • duplicate-cn: questo parametro istruisce OpenVPN ad accettare connessioni multiple dal medesimo “Common Name”. In pratica, se viene data questa direttiva, OpenVPN accetterà connessioni da più endpoint che hanno certificati con lo stesso Common Name; in caso contrario, l’ultima connessione in arrivo invaliderà l’eventuale connessione esistente con lo stesso Common Name. Se si vuole distribuire certificati diversi ad ogni utente remoto, è preferibile eliminare questa direttiva. Chi invece, per semplicità, intende produrre un unico certificato e distribuirlo a tutti gli utenti, deve usare questa direttiva per permetterne l’accesso simultaneo.
  • client-config-dir ccd: questa direttiva istruisce OpenVPN a cercare dei file di configurazione per-utente nella directory ccd. Ad ogni connessione, OpenVPN cerca nella directory ccd un file con nome uguale al Common Name dell’endpoint che si sta connettendo, e aggiunge i parametri di configurazione che vi trova a quelli generali. In questo modo, è possibile ad esempio differenziare le configurazioni di mrossi e gbianchi.
  • keepalive 15 120: invia un "ping" sul canale di controllo se non sono stati spediti pacchetti sopra la VPN negli ultimi 15 secondi; invia un segnale di restart SIGUSR1 al demone se non sono stati ricevuti pacchetti o ping dall’endpoint remoto per oltre 120 secondi. Grazie a questo meccanismo, OpenVPN tiene sotto controllo la vitalità della connessione, e la riavvia in caso questa risulti inattiva.
  • comp-lzo: usa la compressione LZO per i dati inviati nel tunnel VPN.
  • max-clients 10: limitiamo il numero di connessioni simultanee. Da impostare secondo le proprie esigenze.
  • user nobody: subito dopo aver letto i file di configurazione, le chiavi e quant’altro gli serve per partire, OpenVPN abbandona i delicati privilegi di root per diventare "nobody".
  • group nogroup: ditto.
  • persist-key: su ricevimento di un segnale SIGUSR1, che determina un restart, conserva le chiavi che ha in memoria. Questa direttiva è fondamentale se si è specificato "user nobody", poiché OpenVPN, dopo aver abbandonato i privilegi di root, non è più in grado di accedere ai file delle chiavi private.
  • persist-tun: non chiude e riapre il device TUN su ricevimento del segnale SIGUSR1.
  • status openvpn-status.log: scrive lo stato di servizio nel file openvpn-status.log.
  • verb 3: imposta il livello di dettaglio dei messaggi su 3 (un buon compromesso).

Possiamo far partire il demone OpenVPN:

$ sudo service openvpn restart
Stopping virtual private network daemon: .
Starting virtual private network daemon: server.

Osserviamo la presenza dell’interfaccia tun0, a cui è stato assegnato l’IP 10.1.1.1/24 come previsto:

$ /sbin/ifconfig
...
tun0      Link encap:UNSPEC  HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
          inet addr:10.1.1.1  P-t-P:10.1.1.1  Mask:255.255.255.0
          UP POINTOPOINT RUNNING NOARP MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:100
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

Possiamo passare ora al setup del client. Copiamo su un dischetto o su una chiavetta USB i file necessari:

  • /etc/openvpn/certs/ca.crt
  • /etc/openvpn/keys/tlsauth.pem
  • /etc/openvpn/easy-rsa/2.0/keys/mrossi.crt
  • /etc/openvpn/easy-rsa/2.0/keys/mrossi.key

Spostiamoci quindi sul portatile del collega Mario Rossi.

Installazione e configurazione di OpenVPN sul client

Scarichiamo l’installer di OpenVPN per Windows da questo link: http://openvpn.net/index.php/download.html e installiamolo. Durante l’installazione Windows ci avvisa che il driver TAP-Win32 Adapter V9 non ha superato il testing del programma Windows Logo. Ovviamente mettiamolo a dormire cliccando su Continua.
Uno degli warning dementi di Windows
Conclusa l’installazione, andiamo nella gestione delle connessioni di rete (Risorse di rete / tasto destro / Proprietà). Troveremo una nuova interfaccia, che il sistema avrà battezzato “Connessione alla rete locale (LAN) X”. Attenzione: dobbiamo individuare la connessione giusta, quella gestita dal driver “TAP-Win32 Adapter V9”, e non la nostra connessione ethernet fisica:
Connessioni di rete
Selezioniamola e mettiamogli un nome più appropriato. La chiameremo "VPN Ufficio". Questo sembra un dettaglio estetico, ma non lo è: OpenVPN utilizza il nome del dispositivo, in fase di connessione, per individuare l’interfaccia giusta ed applicare i vari parametri (indirizzo IP, maschera di rete, eccetera). Il nome che diamo qui lo useremo fra poco nel file di configurazione del client.
Spostiamoci ora in C:\Programmi\OpenVPN\config. Creiamo un nuovo file di testo (ATTENZIONE: deve essere un file ASCII, editabile con il blocco note o con altri editor di testo come Notepad++. Non usare Microsoft Word, Wordpad, Libre Office Writer o altri editor che applicano formattazione al testo). Chiamiamo il file ufficio.ovpn (ATTENZIONE: se è stata abilitata l’opzione "nascondi le estensioni per i tipi di file conosciuti", potreste aver creato un file con doppia estensione: ufficio.ovpn.txt. Consiglio di disabilitare definitivamente questa opzione che sfiora la demenza, e che per default è abilitata in tutti i sistemi Microsoft, persino nella fascia server), quindi editiamolo inserendo le seguenti direttive:

client
dev tun
dev-node "VPN Ufficio"
proto udp
remote acme.dyndns.org 1194
resolv-retry infinite
nobind
persist-key
persist-tun
ca "C:\\Documents and Settings\\All Users\\Dati applicazioni\\OpenVPN\\Keys\\ca.crt"
cert "C:\\Documents and Settings\\All Users\\Dati applicazioni\\OpenVPN\\Keys\\mrossi.crt"
key "C:\\Documents and Settings\\All Users\\Dati applicazioni\\OpenVPN\\Keys\\mrossi.key"
tls-auth "C:\\Documents and Settings\\All Users\\Dati applicazioni\\OpenVPN\\Keys\\tlsauth.pem"
ns-cert-type server
comp-lzo
verb 3

Ecco qualche spiegazione:

  • client: modalità client. Ovvio…
  • dev tun: diciamo a OpenVPN di creare un’interfaccia di tipo TUN. Per chi ha voglia di studiare la differenza tra TUN e TAP, rimando alla pagina man di OpenVPN.
  • dev-node "VPN Ufficio": ecco la nostra interfaccia di rete. OpenVPN la identificherà tramite questo nome.
  • proto udp: come per il server
  • remote acme.dyndns.org 1194: l’endpoint remoto e la porta da utilizzare. Modificare secondo le proprie esigenze. Si può anche specificare l’IP, se se ne possiede uno statico.
  • resolv-retry infinite: nel mio caso, disponendo di un IP dinamico, dico a OpenVPN che non deve tentare di "memorizzare" l’indirizzo IP del server, ma deve invece risolverlo ogni volta interrogando DynDNS.
  • nobind: diciamo a OpenVPN di non eseguire il bind, ma di lasciare che sia lo stack TCP/IP a scegliere una porta per i pacchetti di ritorno. Senza questa direttiva, OpenVPN potrebbe trovare la porta 1194 già occupata da altre connessioni, e fallire.
  • persist-key: come per il server, anche se in ambiente Windows non vi è il concetto di "abbandonare i privilegi di root".
  • persist-tun: come per il server.
  • ca "C:\\Documents and Settings\\All Users\\Dati applicazioni\\OpenVPN\\Keys\\ca.crt": il certificato della Certification Authority.
  • cert "C:\\Documents and Settings\\All Users\\Dati applicazioni\\OpenVPN\\Keys\\mrossi.crt": il certificato di Mario Rossi.
  • key "C:\\Documents and Settings\\All Users\\Dati applicazioni\\OpenVPN\\Keys\\mrossi.key": la sua chiave privata.
  • tls-auth "C:\\Documents and Settings\\All Users\\Dati applicazioni\\OpenVPN\\Keys\\tlsauth.pem": la chiave per il controllo HMAC.
  • ns-cert-type server: applichiamo una restrizione alla validità del certificato del server, istruendo OpenVPN ad accettare solo certificati di tipo server. Questo aiuta a prevenire attacchi di tipo man-in-the-middle, dove un client autorizzato potrebbe tentare di connettersi ad un altro client, impersonando il server.
  • comp-lzo: come per il server.
  • verb 3: ditto.

Il doppio backslash nei percorsi è d’obbligo, poiché OpenVPN interpreta il backslash come carattere di escape all’interno delle stringhe, quindi esso deve essere raddoppiato per essere interpretato letteralmente.
Spostiamoci ora in C:\Documents and Settings\All Users\Dati applicazioni (se avete disabilitato la visualizzazione dei file nascosti, potreste dover digitare manualmente il percorso nella barra “Indirizzo” di Explorer). Creiamo una directory OpenVPN, e al suo interno una sottodirectory Keys. Entriamoci; preleviamo dal dischetto o dalla penna USB tutti i file crt, key e pem copiati precedentemente dal server, e copiamoli qui dentro. I sistemisti ansiosi possono "blindare" la directory, manipolando i permessi su di essa. Direi che assegnare full control a SYSTEM e al gruppo Administrators dovrebbe bastare.
Siamo pronti per un primo test. Se abbiamo installato tutto a dovere, dovremmo avere sul desktop il collegamento all’applicazione "OpenVPN GUI": avviamola. Apparirà una piccola iconcina in basso a destra, vicino all’orologio di Windows:
OpenVPN GUI
Clicchiamoci sopra con il tasto destro, quindi selezioniamo Connect. Vedremo comparire una finestra che mostra la progressione delle operazioni di connessione. Se tutto fila liscio, dopo qualche secondo vedremo comparire una nuvoletta beige vicino all’icona di OpenVPN GUI, che ci avvisa che la connessione è attiva:
Champagne!!!
Pinghiamo il server:
Ping
Champagne!!! Champagne?!? Ma quale champagne. Andate a farvi un caffè che vi voglio belli svegli. Abbiamo appena iniziato!

Il server OpenVPN come router verso la LAN

Qualcuno si sarà chiesto: per quale motivo si è parlato di una LAN con subnet 192.168.1.x rimappata in remoto sulla subnet 172.25.12.x? La risposta è semplice. Immaginate che in un dato momento il client si trovi in una rete con subnet 192.168.1.x; è, per esempio, in un bar dotato di rete WiFi aperta, ed il collega Rossi decide di connettersi mentre beve il suo caffè. Una volta connesso, sul suo portatile vi saranno due reti con la medesima subnet: quella aziendale e quella del bar. Come può lo stack TCP/IP del portatile di Rossi sapere dove instradare un pacchetto per un dato host nella subnet 192.168.1.x? Si tratta della rete locale (bar) o della rete remota (azienda)? Cioè, deve essere instradato attraverso l’interfaccia wireless, oppure attraverso l’interfaccia virtuale di OpenVPN? Visto che esisterebbero due route per la medesima subnet, comanderebbe la metrica. Forzando una metrica molto alta sulla interfaccia VPN, la rete del bar avrebbe la meglio e la rete aziendale risulterebbe irraggiungibile; facendo il contrario, verrebbe preferita la route attraverso l’interfaccia VPN, e la rete del bar risulterebbe irraggiungibile (compreso il default gateway, con conseguenze orrende a cui non voglio nemmeno pensare). Comunque la si giri, avere una rete locale ed una rete remota sulla stessa subnet è MALE. Che fare allora? Be’ se in azienda si hanno quattro PC messi in croce, la cosa più semplice è rinumerare la LAN, assegnando una subnet non comunemente usata (ad esempio, 172.25.12.x, appunto). Ma se in azienda ci sono decine di computer, server, applicazioni, router, eccetera, e si è fatta la scelta infelice di usare la subnet più gettonata del pianeta, cioè 192.168.1.x, allora la cosa può assumere toni tra il drammatico e l’improponibile. La soluzione, in questi casi, è lasciare stare la LAN; faremo lavorare il nostro server OpenVPN, in modo che sia lui, grazie a Iptables, a rimappare al volo l’intera rete aziendale su una subnet meno comune.

Uno script per avviare iptables

Iniziamo a redigere uno script contenente alcune funzioni per la manipolazione di Iptables. L’idea è di creare un servizio firewall, che andremo poi a integrare nell’architettura Dependency Based Boot di cui Debian beneficia. Lo script conterrà sostanzialmente due funzioni: iptables_start e iptables_stop, che utilizzeremo nello script di init.
Editiamo lo script:

$ sudo mkdir /etc/firewall
$ sudo nano /etc/firewall/firewall-functions

Ecco il listato dello script:

#!/bin/sh -e

PATH=/sbin:/usr/sbin:/bin:/usr/bin
export PATH

IPTABLES="/sbin/iptables"
HIPORT="1024:65535"
PIDFILE=/var/run/firewall.pid

[ -x $IPTABLES ] || exit 0
[ -x /sbin/ifconfig ] || exit 0
[ -x /bin/egrep ] || exit 0
[ -x /bin/grep ] || exit 0
[ -x /bin/sed ] || exit 0
[ -x /usr/bin/awk ] || exit 0

ifaddr() {
	#restituisce l'indirizzo IP di una data interfaccia
	local iface="$1"
	if ifconfig $iface | egrep '^\s+inet addr:' | sed -e 's/^[[:blank:]]*inet addr:\([0-9.]*\).*/\1/' 2>/dev/null ; then
		return 0
	else
		return 1
	fi
}

touch_pidfile() {
	#crea file pid
	local pidfile="$1"
	echo "0" > $pidfile
}

remove_pidfile() {
	#rimuove file pid
	local pidfile="$1"
	rm -f $pidfile 2>/dev/null || true
}

check_running() {
	local pidfile="$1"
	if [ -f $pidfile ] ; then
		return 0
	else
		return 1
	fi
}

has_iface() {
	local iface="$1"
	if awk '/:/ { sub(":", "", $1); print $1 }' /proc/net/dev | grep "$iface" 1>/dev/null ; then
		return 0
	else
		return 1
	fi
}

ETH0=$(ifaddr "eth0")
LAN24=$(echo "$ETH0" | sed -e 's/\.[0-9]*$/.0\/24/')
BCAST=$(echo "$ETH0" | sed -e 's/\.[0-9]*$/.255/')

HASTUN=no
TUN0=""

#Attendiamo che sia disponibile l'interfaccia tun.
#Facciamo questo in un loop perche' l'interfaccia tun potrebbe rendersi disponibile
#qualche attimo dopo l'avvio del processo di openvpn
COUNT=10
while [ "$COUNT" -gt 0 ] ; do
	if has_iface "tun0" ; then
		#ok, tun disponibile. Usciamo dal loop
		HASTUN=yes
		break
	else
		if ls /var/run/openvpn.*.pid >/dev/null 2>&1 ; then
			#openvpn avviato; attendiamo ancora un po'
			COUNT=$(($COUNT - 1))
			sleep 1
			continue
		fi
		#openvpn non e' in funzione; inutile aspettare. Usciamo dal loop
		break
	fi
done

#Stesso discorso per l'IP dell'interfaccia tun: attendiamo finche' non e' stato assegnato
if [ "$HASTUN" = "yes" ] ; then
	COUNT=10
	while [ "$COUNT" -gt 0 ] ; do
		TUN0=$(ifaddr "tun0") && break || COUNT=$(($COUNT - 1))
		sleep 1
	done
fi

VPN0=$(echo "$TUN0" | sed -e 's/\.[0-9]*$/.0\/24/')

iptables_start () {
	if check_running $PIDFILE ; then
		echo "Iptables e' gia' in esecuzione!"
		return 1
	fi

	#sicurezza aggiuntiva a livello kernel
	echo "0" > /proc/sys/net/ipv4/conf/eth0/rp_filter
	echo "0" > /proc/sys/net/ipv4/conf/eth0/accept_redirects
	echo "0" > /proc/sys/net/ipv4/conf/eth0/accept_source_route
	echo "0" > /proc/sys/net/ipv4/conf/eth0/bootp_relay
	echo "1" > /proc/sys/net/ipv4/conf/eth0/log_martians
	echo "1" > /proc/sys/net/ipv4/ip_forward
	echo "1" > /proc/sys/net/ipv4/icmp_echo_ignore_broadcasts
	echo "1" > /proc/sys/net/ipv4/tcp_syncookies
	echo "1" > /proc/sys/net/ipv4/icmp_echo_ignore_broadcasts
	echo "1" > /proc/sys/net/ipv4/icmp_ignore_bogus_error_responses

	#regole di default
	$IPTABLES -P INPUT DROP
	$IPTABLES -P FORWARD DROP
	$IPTABLES -P OUTPUT ACCEPT
	$IPTABLES -N inchain
	$IPTABLES -N syn_flood
	$IPTABLES -N ping_flood
	$IPTABLES -N vpn_lan
	$IPTABLES -N lan_vpn

	###########
	# INCHAIN #
	###########
	#apertura porte
	#SSH
	$IPTABLES -A inchain -p tcp --dport 22 --sport $HIPORT -s $LAN24 -j ACCEPT
	#Openvpn
	$IPTABLES -A inchain -p udp --dport 1194 --sport $HIPORT -j ACCEPT
	#droppa tutto il resto
	$IPTABLES -A inchain -j DROP

	#############
	# SYN_FLOOD #
	#############
	$IPTABLES -A syn_flood -m limit --limit 3/s -j RETURN
	$IPTABLES -A syn_flood -j DROP

	##############
	# PING_FLOOD #
	##############
	$IPTABLES -A ping_flood -m limit --limit 3/s -j RETURN
	$IPTABLES -A ping_flood -j DROP

	###########
	# VPN_LAN #
	###########
	$IPTABLES -A vpn_lan -j ACCEPT

	###########
	# LAN_VPN #
	###########
	$IPTABLES -A lan_vpn -j ACCEPT

	##################
	# REGOLE DI BASE #
	##################
	#connessioni esistenti
	$IPTABLES -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

	#calmiere syn e ping
	$IPTABLES -A INPUT -p tcp --syn -j syn_flood
	$IPTABLES -A INPUT -p icmp --icmp-type echo-request -j ping_flood

	#icmp
	$IPTABLES -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
	$IPTABLES -A INPUT -p icmp --icmp-type echo-reply -j ACCEPT
	$IPTABLES -A INPUT -p icmp --icmp-type time-exceeded -j ACCEPT
	$IPTABLES -A INPUT -p icmp --icmp-type parameter-problem -j ACCEPT
	$IPTABLES -A INPUT -p icmp --icmp-type fragmentation-needed -j ACCEPT

	#loopback
	$IPTABLES -A INPUT ! -i lo -s 127.0.0.0/8 -j DROP
	$IPTABLES -A INPUT -i lo ! -s 127.0.0.0/8 -j DROP
	$IPTABLES -A INPUT -i lo -j ACCEPT
	$IPTABLES -A OUTPUT -o lo -j ACCEPT

	#il traffico da/verso la vpn viene instradato su apposite chain
	if [ "$HASTUN" = "yes" ] ; then
		$IPTABLES -A FORWARD -i tun0 -o eth0 -j vpn_lan
		$IPTABLES -A FORWARD -i eth0 -o tun0 -j lan_vpn
		#mapping dell'intera subnet locale su 172.25.12.0/24
		$IPTABLES -t nat -A PREROUTING -i tun0 -d 172.25.12.0/24 -j NETMAP --to $LAN24
		$IPTABLES -t nat -A POSTROUTING -o tun0 -s $LAN24 -j NETMAP --to 172.25.12.0/24
		#NAT traffico in uscita da eth0
		$IPTABLES -t nat -A POSTROUTING -o eth0 -j SNAT --to-source $ETH0
		#tutto il traffico in entrata dalla vpn viene accettato
		$IPTABLES -A INPUT -i tun0 -j ACCEPT
	fi

	#tutto il traffico in entrata e' gestito da inchain
	$IPTABLES -A INPUT -i eth0 -j inchain

	touch_pidfile $PIDFILE

	return 0
}

iptables_stop () {
	#svuotamento regole
	$IPTABLES -F
	$IPTABLES -F -t mangle
	$IPTABLES -F -t nat
	$IPTABLES -X
	$IPTABLES -X -t mangle
	$IPTABLES -X -t nat
	$IPTABLES -F INPUT
	$IPTABLES -F OUTPUT
	$IPTABLES -F FORWARD

	#regole di default
	$IPTABLES -P INPUT ACCEPT
	$IPTABLES -P FORWARD ACCEPT
	$IPTABLES -P OUTPUT ACCEPT

	remove_pidfile $PIDFILE

	return 0
}

Il listato dello script è disponibile qui: /download/firewall-functions
Modifichiamo i permessi per ragioni di sicurezza. Non vogliamo che qualche curioso veda come abbiamo configurato il nostro firewall.

$ sudo chmod 0600 /etc/firewall/firewall-functions

Creiamo ora lo script LSB per lo start / stop automatico del firewall:

$ sudo nano /etc/init.d/firewall

Ecco il listato dello script:

#!/bin/sh -e

### BEGIN INIT INFO
# Provides:          firewall
# Required-Start:    $remote_fs $network
# Required-Stop:     $remote_fs $network
# Should-Start:      $syslog openvpn
# Should-Stop:       $syslog openvpn
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Start and stop firewall
# Description:       Firewall implemented using iptables
### END INIT INFO

PATH=/sbin:/usr/sbin:/bin:/usr/bin
export PATH

test -e /etc/firewall/firewall-functions || exit 0

. /lib/lsb/init-functions
. /etc/firewall/firewall-functions

case "$1" in
	start)
		log_daemon_msg "Starting firewall" "iptables"
		iptables_start
		log_end_msg $?
		;;
	stop)
		log_daemon_msg "Stopping firewall" "iptables"
		iptables_stop
		log_end_msg $?
		;;
	force-reload|restart)
		$0 stop
		$0 start
		;;
	status)
		if check_running ; then
			log_success_msg "Firewall is running"
			exit 0
		else
			log_failure_msg "Firewall is not running"
			exit $?
		fi
		;;
	*)
		echo "Usage: $0 {start|stop|restart|force-reload|status}"
		exit 1
		;;
esac

exit 0

Il listato dello script è disponibile qui: /download/firewall
Rendiamo lo script eseguibile ed inseriamolo nella catena di boot:

$ sudo chmod 0755 /etc/init.d/firewall
$ sudo insserv firewall

A questo punto, con Iptables in funzione, dovremmo poter pingare un host qualsiasi della rete 192.168.1.x con l’IP rimappato 172.25.12.x. Proviamo:
Ping remap
Apriamolo in gestione risorse:
Browse remap
Ehi Gringo, la macchina va va vuma! (Chi non l’ha capita vuol dire che ha meno di 35 anni, tiri dritto e non faccia domande).

Un remapping DNS proxy per la LAN

Rimane un problema spinoso da risolvere. Gli indirizzi IP della LAN vengono rimappati al volo, e questo riduce all’osso il rischio di collisioni. Ma chi glie lo spiega, al signor Rossi, che mentre è al bar e vuole accedere alla directory condivisa del server, deve farlo digitando l’indirizzo \\172.25.12.20, mentre quando è in ufficio può accedervi con il nome \\VMW2008R2? Il signor Rossi, ma soprattutto il suo amministratore di rete, vuole piazzare un bel link sul desktop del PC, e spiegare a Rossi che se vuole vedere i suoi file deve fare "doppio click sull’icona".
Usare il file hosts? Mmmmm, no. Quando Rossi torna in ufficio, non gli funziona più niente. E poi, come la mettiamo con le reti dotate di server DHCP, dove gli indirizzi possono cambiare in continuazione? Servirebbe… ecco: un Remapping DNS Proxy. Un pezzo di software che, all’occorrenza:

  • intervenga nel meccanismo di risoluzione dei nomi
  • intercetti i nomi relativi alla LAN
  • giri la richiesta DNS al server di competenza (il DNS interno alla LAN oppure il DNS di default)
  • faccia un remap della risposta, se opportuno
  • giri quest’ultima al richiedente

In sostanza, vogliamo che il nome VMW2008R2.acme.local venga risolto nell’IP 192.168.1.20 se il client è dentro alla LAN, e nell’IP 172.25.12.20 se invece il client è fuori dalla LAN.
Ho cercato a lungo la disponibilità di un simile pezzo di software; alla fine, non avendolo trovato, l’ho realizzato io stesso in C#, sfruttando l’ottima libreria ARSoft.Tools.Net per la gestione del protocollo DNS. Il programma parte come servizio e resta in ascolto sulla loopback; i parametri di funzionamento sono contenuti in un file di configurazione xml. L’idea di base è che, su partenza di una connessione VPN, il DNS proxy venga attivato e il DNS di sistema venga dirottato su 127.0.0.1; a questo punto il nostro proxy riceve tutte le richieste e le smista al DNS opportuno: riconosce le richieste relative alla LAN in base al suffisso, le inoltra al DNS remoto, quindi effettua il remapping della risposta; le richieste non pertinenti alla LAN, invece, vengono inoltrate al DNS di sistema, e restituite intoccate. Il programma è dotato di un semplice meccanismo di caching che ne migliora notevolmente le prestazioni.
Il sorgente ed il programma compilato sono scaricabili qui: /download/vpndns.7z.
Per il funzionamento, il software necessita del Net Framework 4.0 Client Profile scaricabile qui: http://www.microsoft.com/it-it/download/details.aspx?id=24872.
Una volta installato il framework Net, l’installazione del servizio è semplice. Bisogna aprire l’archivio 7z, entrare nella directory vpndns\vpndns\bin\Release ed estrarre i file vpndns.exe, ARSoft.Tools.Net.dll e config.xml copiandoli nella directory C:\Programmi\vpndns. Il file config.xml dovrà essere editato secondo le esigenze. Ecco il listato utilizzato per il nostro esempio:

<?xml version="1.0" encoding="utf-8"?>
<vpndns>
  <dnsservers timeout="5000">
    <dnsserver address="192.168.1.20" />
  </dnsservers>
  <zones>
    <sentineldir path="C:\batch\openvpn\run" filter="*.run" />
    <zone name="Acme" suffixes="acme.local" sentinel="ufficio.ovpn.run">
      <dnsservers timeout="5000">
        <dnsserver address="172.25.12.20" />
      </dnsservers>
      <remap from="192.168.1.0/24" to="172.25.12.0/24" />
    </zone>
  </zones>
</vpndns>

La sintassi è quella del formato XML; la struttura è abbastanza intuitiva, non spenderò quindi molte parole. Mi soffermo solo sul nodo sentineldir: esso istruisce vpndns ad osservare una certa directory; la comparsa o scomparsa di un certo file abilita o disabilita, rispettivamente, il redirect verso una certa zona. Nel nostro esempio, se viene creato il file C:\batch\openvpn\run\ufficio.ovpn.run, la zona “Acme” si abilita; tutte le query DNS con suffisso .acme.local vengono girate al server 172.25.12.20, e i risultati rimappati da 192.168.1.0/24 a 172.25.12.0/24. Appena il file viene cancellato, la zona Acme viene disabilitata e le query DNS con suffisso .acme.local vengono dirottate verso il server DNS 192.168.1.20, come tutte le altre. Un ultimo dettaglio: abbiamo specificato il server DNS 192.168.1.20 come server a cui girare tutte le query che non coinvolgono connessioni VPN. E se il server fosse assegnato da DHCP? Semplice, si elimina il nodo dnsserver. Ecco la sintassi:

<?xml version="1.0" encoding="utf-8"?>
<vpndns>
  <dnsservers timeout="5000" />
  <zones>
    <sentineldir path="C:\batch\openvpn\run" filter="*.run" />
    <zone name="Acme" suffixes="acme.local" sentinel="ufficio.ovpn.run">
      <dnsservers timeout="5000">
        <dnsserver address="172.25.12.20" />
      </dnsservers>
      <remap from="192.168.1.0/24" to="172.25.12.0/24" />
    </zone>
  </zones>
</vpndns>

Sistemata la configurazione, aprire un prompt dei comandi e dare le seguenti istruzioni:

> cd C:\Programmi\vpndns
> C:\Windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe vpndns.exe

L’utility di installazione dovrebbe avvertirci che la transazione è andata a buon fine.
Modifichiamo dunque la configurazione di Rossi, in modo che OpenVPN inneschi l’avvio del nostro proxy. Apriamo il file di configurazione ufficio.ovpn, ed aggiungiamo le seguenti direttive:

script-security 2
up "C:\\batch\\openvpn\\bin\\updown.bat"
down "C:\\batch\\openvpn\\bin\\updown.bat"

Apriamo il disco C: e creiamo la directory C:\batch\openvpn. Al suo interno creiamo due sottodirectory: bin e run. Ecco come appare la struttura delle directory:
Struttura directory
Entriamo nella directory bin, e creiamo un file di testo che chiameremo updown.bat. Editiamolo, inserendo le seguenti istruzioni:

set PATH=C:\Windows\System32;C:\WINDOWS\system32\wbem
set iface="Connessione alla rete locale (LAN)"
set dns=192.168.1.20
set dir=%~dp0..\run
set ext=run

if "%script_type%"=="up" goto up
if "%script_type%"=="down" goto down
goto theend

:up
net.exe start | egrep.exe -i "VPN remapping DNS proxy"
if ERRORLEVEL 1 (
  net.exe start "VPN remapping DNS proxy"
)
if not exist "%dir%\*.%ext%" (
  C:\Programmi\NetSetMan\netsetman.exe -as 1
  ipconfig.exe /flushdns
)
echo 1 > "%dir%\%config%.%ext%"
goto theend

:down
del /q "%dir%\%config%.%ext%"
if not exist "%dir%\*.%ext%" (
  C:\Programmi\NetSetMan\netsetman.exe -as 0
  net.exe stop "VPN remapping DNS proxy"
  ipconfig.exe /flushdns
)
goto theend

:theend

Siamo in ambiente Windows, e si sente. Quando si è sperimentata la potenza della shell di Unix, tornare al linguaggio batch di Windows è come montare su una bicicletta con le ruote sgonfie. PowerShell, dite? Ma per favore…
Passiamo alle spiegazioni. Lo script viene invocato in risposta al’avvio o all’arresto di una connessione VPN (direttiva up e down del file .ovpn), e gli vengono passate diverse variabili d’ambiente. Tra queste, %script_type% indica se siamo in un contesto up oppure down. In risposta all’evento up, lo script esegue queste istruzioni:

  • avvia il servizio VPN Remapping DNS Proxy, nel caso non fosse già in esecuzione;
  • invoca il programma NetSetMan con il profilo di rete 1 (cioè profilo VPN, come vedremo più avanti);
  • crea un file .run relativo a questa connessione nella sottodirectory run che abbiamo creato prima. Questo file serve da "sentinella" per il servizio proxy, segnalandogli che la VPN verso “acme.local” è attiva e che quindi deve intervenire su tutte le richieste DNS pertinenti. Grazie a questo meccanismo, è possibile installare il servizio su client che dispongono di più connessioni VPN: il servizio proxy lavora sulle singole VPN man mano che queste vengono attivate.

Su evento down, invece, ecco cosa fa:

  • cancella il file .run relativo a questa connessione;
  • se non esistono più file .run, ferma il servizio proxy e invoca NetSetMan con il profilo di rete 0 (cioè profilo LAN, come vedremo più avanti).

Ci servono ora un paio di altri software:

  • il programma egrep, che gli utenti Unix conosceranno bene, serve a cercare modelli di testo, espressi in forma di espressioni regolari, all’interno di file di testo. Il programma è scaricabile qui: /download/egrep.exe. Copiare il file in C:\Windows\System32.
  • il programma NetSetMan è una comoda utility che memorizza vari profili di rete ed è in grado di applicarli con un click. Per i nostri scopi, avrebbe potuto andare benissimo il programma netsh, incluso in Windows. Sfortunatamente, la versione distribuita con Windows XP si lamenta se si specifica 127.0.0.1 come indirizzo DNS:
    Siamo andati oltre la demenza
    Patetico… per fortuna NetSetMan ha anche un’interfaccia da linea di comando, quindi possiamo usarlo nella nostra batch. Scarichiamolo da qui: http://www.netsetman.com/

Apriamo NetSetMan e creiamo i due profili che ci servono. ATTENZIONE: create i profili nell’ordine che vedete, altrimenti lo script funzionerà al contrario!
NetSetMan LAN
NetSetMan VPN
Siamo pronti per un test. Piazziamo Rossi nel suo ufficio, e facciamo un ping.
Risoluzione DNS in LAN
Adesso catapultiamolo al bar, e facciamogli partire la connessione VPN. Vedremo avviarsi la solita finestra di startup, ed in più vedremo per pochi secondi la finestra di NetSetMan che lavora. Pinghiamo di nuovo il server:
Risoluzione DNS in VPN
Yippeee! Problema risolto. Ricordiamoci di usare sempre nomi di rete FQDN sul portatile di Rossi, e non riceveremo da lui la minima lamentela!

Un ultimissimo dettaglio

Se Rossi si dimentica di disconnettere la VPN prima di spegnere il PC, al prossimo avvio la connessione di rete avrà il DNS impostato su 127.0.0.1 e ciò non è desiderabile.
Si può risolvere con una piccola batch da far partire con l’utilità “Pianificazione attività” di Windows. La batch dovrà essere schedulata per l’esecuzione all’accesso dell’utente.
Ecco il listato della batch:

rem controlla se il DNS è ancora impostato su 127.0.0.1
rem in caso affermativo fa partire NetSetMan per ripristinare il DNS corretto
netsh.exe interface ip show dns name="Connessione alla rete locale (LAN)" | egrep -i "^[[:blank:]]+Server DNS configurati \w+:[[:blank:]]+127\.0\.0\.1[[:blank:]]*$"
if not errorlevel 1 (
  C:\Programmi\NetSetMan\netsetman.exe -as 0
)
rem pulizia di eventuali file .run residui
del /q C:\batch\openvpn\run\*.run

Fare a meno di NetSetMan

Per chi non gradisse NetSetMan, ho realizzato una piccola utility che emula l’attività di netsh sui DNS, superandone il limite riscontrato poco sopra. Il software è scaricabile qui: /download/setdns.7z
È sufficiente copiare l’eseguibile precompilato dalla directory Release a C:\Windows\System32, quindi modificare le varie batch sostituendo al comando

C:\Programmi\NetSetMan\netsetman.exe

il comando

setdns.exe

Ecco come appare ad esempio la batch updown.bat modificata:

set PATH=C:\Windows\System32;C:\WINDOWS\system32\wbem
set iface="Connessione alla rete locale (LAN)"
set dns=192.168.1.20
set dir=%~dp0..\run
set ext=run

if "%script_type%"=="up" goto up
if "%script_type%"=="down" goto down
goto theend

:up
net.exe start | egrep.exe -i "VPN remapping DNS proxy"
if ERRORLEVEL 1 (
  net.exe start "VPN remapping DNS proxy"
)
if not exist "%dir%\*.%ext%" (
  setdns.exe -s 127.0.0.1 %iface%
  ipconfig.exe /flushdns
)
echo 1 > "%dir%\%config%.%ext%"
goto theend

:down
del /q "%dir%\%config%.%ext%"
if not exist "%dir%\*.%ext%" (
  setdns.exe -s %dns% %iface%
  net.exe stop "VPN remapping DNS proxy"
  ipconfig.exe /flushdns
)
goto theend

:theend

Digitare setdns.exe –help per un breve elenco delle opzioni disponibili.

Revocare l’accesso ad un utente

“Mario se n’è andato e non ritorna più”, canterebbe la Laura nazionale. Il collega Rossi adesso è collega di qualcun altro, ma possiede ancora le chiavi di casa. Che fare? Niente paura. Nelle fasi iniziali della preparazione della Certification Authority abbiamo predisposto un CRL (Certificate Revocation List), ricordate? Sarà sufficiente includere il certificato di Rossi nel CRL, ed egli non sarà più in grado di accedere alla VPN.
Diventiamo root e spostiamoci nella directory di easy-rsa:

$ su
Password: 
# cd /etc/openvpn/easy-rsa/2.0

Carichiamo le variabili d’ambiente necessarie al funzionamento degli script:

# . ./vars
NOTE: If you run ./clean-all, I will be doing a rm -rf on /etc/openvpn/easy-rsa/2.0/keys

Revochiamo il certificato:

# ./revoke-full mrossi
Using configuration from /etc/openvpn/easy-rsa/2.0/openssl.cnf
Revoking Certificate 02.
Data Base Updated
Using configuration from /etc/openvpn/easy-rsa/2.0/openssl.cnf
mrossi.crt: /C=IT/ST=Lodi/L=Casalpusterlengo/O=Acme/OU=Ufficio Commerciale/CN=mrossi/name=Mario Rossi/emailAddress=mrossi@acme.it
error 23 at 0 depth lookup:certificate revoked

Controlliamo cosa è successo al CRL:

# openssl crl -in keys/crl.pem -text
Certificate Revocation List (CRL):
        Version 1 (0x0)
        Signature Algorithm: md5WithRSAEncryption
        Issuer: /C=IT/ST=Lodi/L=Casalpusterlengo/O=Acme/OU=Ricerca & Sviluppo/CN=Acme CA/emailAddress=rd@acme.it
        Last Update: May 28 07:42:50 2015 GMT
        Next Update: Jun 27 07:42:50 2015 GMT
Revoked Certificates:
    Serial Number: 02
        Revocation Date: May 28 07:42:50 2015 GMT
    Signature Algorithm: md5WithRSAEncryption
        0d:7c:02:16:ab:11:d8:4e:b1:7b:db:17:96:ad:5e:6c:26:d7:
        78:d0:d4:9b:8e:f4:43:17:cf:18:7a:6b:8b:c1:27:85:a5:54:
        d1:4f:b3:a3:98:94:fd:4b:b1:74:4b:a2:44:7f:fd:75:89:ac:
        33:ed:12:2d:85:a4:fc:68:81:00:6e:7a:0c:53:2e:5b:eb:16:
        1f:80:72:61:9a:3d:d7:a5:43:0b:3e:98:ae:a7:d5:28:d1:5b:
        79:26:40:24:94:11:7f:b0:ea:de:f3:d3:77:93:ad:6c:aa:2b:
        fc:11:5e:c2:0f:99:39:5c:4b:42:ee:0b:55:c8:e0:27:f5:dc:
        2c:ef
-----BEGIN X509 CRL-----
MIIBbDCB1jANBgkqhkiG9w0BAQQFADCBkDELMAkGA1UEBhMCSVQxDTALBgNVBAgT
BExvZGkxGTAXBgNVBAcTEENhc2FscHVzdGVybGVuZ28xDTALBgNVBAoTBEFjbWUx
GzAZBgNVBAsUElJpY2VyY2EgJiBTdmlsdXBwbzEQMA4GA1UEAxMHQWNtZSBDQTEZ
MBcGCSqGSIb3DQEJARYKcmRAYWNtZS5pdBcNMTUwNTI4MDc0MjUwWhcNMTUwNjI3
MDc0MjUwWjAUMBICAQIXDTE1MDUyODA3NDI1MFowDQYJKoZIhvcNAQEEBQADgYEA
DXwCFqsR2E6xe9sXlq1ebCbXeNDUm470QxfPGHpri8EnhaVU0U+zo5iU/UuxdEui
RH/9dYmsM+0SLYWk/GiBAG56DFMuW+sWH4ByYZo916VDCz6YrqfVKNFbeSZAJJQR
f7Dq3vPTd5OtbKor/BFewg+ZOVxLQu4LVcjgJ/XcLO8=
-----END X509 CRL-----

Notate: il certificato con seriale 02, che corrisponde al certificato di Rossi, è elencato tra i certificati revocati. Non ci resta che spostare il nuovo CRL nella directory opportuna, e riavviare il server:

# cp keys/crl.pem /etc/openvpn/certs/
# service openvpn restart
Stopping virtual private network daemon: server.
Starting virtual private network daemon: server.

Andate a dormire tranquilli, Rossi non accederà più alla rete aziendale. Vogliamo controllare?

# tail /var/log/syslog
May 28 10:10:14 vpnserver ovpn-server[1869]: 192.168.50.19:1118 Local Options hash (VER=V4): '3f08d474'
May 28 10:10:14 vpnserver ovpn-server[1869]: 192.168.50.19:1118 Expected Remote Options hash (VER=V4): '02af3434'
May 28 10:10:14 vpnserver ovpn-server[1869]: 192.168.50.19:1118 TLS: Initial packet from [AF_INET]192.168.50.19:1118, sid=574c8ce7 a551c8c0
May 28 10:10:14 vpnserver ovpn-server[1869]: 192.168.50.19:1118 CRL CHECK OK: /C=IT/ST=Lodi/L=Casalpusterlengo/O=Acme/OU=Ricerca___Sviluppo/CN=Acme_CA/emailAddress=rd@acme.it
May 28 10:10:14 vpnserver ovpn-server[1869]: 192.168.50.19:1118 VERIFY OK: depth=1, /C=IT/ST=Lodi/L=Casalpusterlengo/O=Acme/OU=Ricerca___Sviluppo/CN=Acme_CA/emailAddress=rd@acme.it
May 28 10:10:14 vpnserver ovpn-server[1869]: 192.168.50.19:1118 CRL CHECK FAILED: /C=IT/ST=Lodi/L=Casalpusterlengo/O=Acme/OU=Ufficio_Commerciale/CN=mrossi/name=Mario_Rossi/emailAddress=mrossi@acme.it is REVOKED
May 28 10:10:14 vpnserver ovpn-server[1869]: 192.168.50.19:1118 TLS_ERROR: BIO read tls_read_plaintext error: error:140890B2:SSL routines:SSL3_GET_CLIENT_CERTIFICATE:no certificate returned
May 28 10:10:14 vpnserver ovpn-server[1869]: 192.168.50.19:1118 TLS Error: TLS object -> incoming plaintext read error
May 28 10:10:14 vpnserver ovpn-server[1869]: 192.168.50.19:1118 TLS Error: TLS handshake failed
May 28 10:10:14 vpnserver ovpn-server[1869]: 192.168.50.19:1118 SIGUSR1[soft,tls-error] received, client-instance restarting

Conclusioni

La tecnica proposta è complessa e molto ricca di variabili. Lo scenario che ho tentato di descrivere è, secondo la mia esperienza, molto comune, e molti amministratori di rete vi troveranno specchiata la propria configurazione di rete. In caso contrario, raccomando di procedere cum grano salis. Inoltre, con qualche accorgimento il sistema funziona benissimo anche con client Windows 7, anche a 64 bit. Ho voluto basare il tutorial su Windows XP perché c’è ancora una certa idiosincrasia verso i successori di XP nel mondo business, e quindi avrei appesantito il tutorial già di per sé voluminoso con dettagli utili a pochi. Naturalmente la mia consulenza è a disposizione di chiunque sia interessato ad avvalersene. Consultate la pagina Contatti.

9 thoughts on “Realizzare una VPN per collegare gli utenti itineranti alla sede centrale usando Linux e OpenVPN

  1. Complimenti per la ricchezza ed il dettaglio delle informazioni contenute nell’articolo

  2. Salve. Complimenti veramente per la guida è veramente ben fatta e mi ha aiutato a risolvere in parte un mio problema lato server. Volevo chiederle se gentilmente è a conoscenza o sa come eseguire una procedura simile lato Client. Mi spiego meglio, ho un pc-box linux che funge da client con due interfacce ethernet (eth0 e eth1) su eth0 ho la mia connessione internet e riesco collegarmi al server vpn senza nessun problema. Mentre su eth1 ho una LAN composta da un paio di pc con sottorete 192.168.0.x. L’idea è di eseguire anche qui un mapping di rete 192.168.0.x —172.17.1.x e raggiungere la LAN dal server. Saprebbe aiutarmi? Grazie.

    • Mi corrego. La procedura mi servirebbe per poter raggiungere tutti i Client della LAN collegati sull’eth1 del Client pc-box

    • Davide, diamoci del tu. Credo che questa più o meno dovrebbe essere la strada:
      iptables -A FORWARD -i tun0 -o eth1 -j ACCEPT
      iptables -A FORWARD -i eth1 -o tun0 -j ACCEPT
      iptables -t nat -A PREROUTING -i tun0 -d 172.17.1.0/24 -j NETMAP –to 192.168.0.0/24
      iptables -t nat -A POSTROUTING -o tun0 -s 192.168.0.0/24 -j NETMAP –to 172.17.1.0/24
      iptables -t nat -A POSTROUTING -o eth1 -j SNAT –to-source 192.168.0.XXX <<< IP eth1 Però, se vuoi, fai uno schizzo della tua topologia di rete e mandamela in PM. Il mio indirizzo email lo trovi sul sito.

  3. Salve Sig. Monti, mi unisco ai commenti positivi, le ho inviato anche una email. Saluti

    • Un invito a chiunque mi scriva. Chi ha bisogno di aiuto, chieda aiuto. Se posso, glielo darò quando potrò. Chi desidera offrire possibilità di lavoro, lo faccia. Sono un freelance e valuto ogni richiesta o proposta. Chi però tenta di farmi lavorare gratuitamente con allusioni a successive collaborazioni professionali, finisce dritto nella mia black list. L’aiuto si chiede, il lavoro si paga.

I commenti sono chiusi.