Home/ | www.icosaedro.it | ![]() |
Si descrive una soluzione VPN (Virtual Private Network) basata su S.O. Linux. Il canale autenticato e crittato viene creato con OpenSSH. A differenza di altre soluzioni analoghe, qui non si fa uso di altri programmi oltre al pppd e ssh e si sfrutta l'opzione pty disponibile nelle nuove versioni di pppd.
La stessa soluzione si trova descritta nel documento "VPN PPP-SSH Mini-HOWTO" di Scott Bronson citato nei riferimenti, che contiene anche una utile discussione di questa tecnica e contiene un quadro delle soluzioni alternative disponibili.
Le VPN sono sempre più diffuse come soluzione WAN (Wide Area Network) a basso costo: esse sfruttano una rete pubblica preesistente, come Internet, per mettere in comunicazione fra di loro reti locali distanti. Un'altra applicazione fa uso di una VPN per mettere in comunicazione tra di loro due o più macchine che, per ragioni di sicurezza, devono comunicare fra loro in modo riservato e al riparo da ogni possibile intercettazione, come ad esempio in un sistema three-tier.
Il primo tipo di applicazione resta la più diffusa: tipicamente avremo la sede centrale di una azienda, dotata di una LAN (Local Area Network), che intende condividere i servizi di rete locale con la LAN di una o più sedi distaccate. I computer così interconnessi possono avvalersi delle soluzioni di rete tipiche di una LAN, come la condivisione dei file e delle stampanti, esattamente come se tutti i computer fossero interconnessi alla stessa LAN.
Per rendere la soluzione ancora più versatile ed economica, si può usare un sistema operativo come GNU/Linux con l'aggiunta del programma OpenSSH (o SSH). Di solito la stessa macchina funzionerà anche da gateway, firewall e router per la LAN, e magari svolgerà anche funzioni di mail server, WEB server, file server, ...
I nuovi kernel Linux incorporano una versione di ppp che implementa l'opzione pty: grazie a questa opzione è ancora più semplice realizzare un canale PPP senza bisogno di avvalersi di altri programmi (come ptyredir, tipicamente usato in altre soluzioni VPN di questo tipo).
L'installazione prototipo alla quale faremo riferimento prevede il collegamento tra una macchina A e una macchina B facenti parte di due reti locali distinte LAN A e LAN B rispettivamente. Le due macchine sono anche gateway Internet ed eseguono anche il masquerading degli indirizzi. Ecco il diagramma delle connessioni:
LAN A LAN B PC-A-1 -----------| |----------- PC-B-1 PC-A-2 ---------| | | |--------- PC-B-2 PC-A-3 -------| | | | | |------- PC-B-3 PC-A-4 -----| | | | | | | |----- PC-B-4 ... | | | | | | | | ... | | | | | | | | +-o-o-o-o-+ +-o-o-o-o-+ | hub | | hub | +----o----+ +----o----+ | | | | | eth0 (192.168.0.1) | eth0 (192.168.1.1) +------o------+ +------o------+ | | | | | A | | B | | | | | +------o------+ +------o------+ | eth1 (11.11.11.11) | eth1 (22.22.22.22) | | | | ******************************************************** * * * * * * * I n t e r n e t * * * ******************************************************** |
Le reti locali usano la classica numerazione in classe C 192.168.x.y, dove x=0 per LAN A e x=1 per LAN B. Di conseguenza i computer client della LAN A avranno indirizzi della forma 192.168.0.y, mentre i computer client della LAN B avranno indirizzi della forma 192.168.1.y.
Le due reti locali si collegano ad Internet attraverso i gateway A e B. Ciascun gateway e' dotato di due interfacce di rete: eth0 verso la LAN e eth1 verso Internet. Tipicamente eth1 viene collegato ad un router o a un modem ADSL dotato di interfaccia Ethernet. A volte il collegamento avviene via PPTP, e allora l'interfaccia per il collegamento ad Internet da considerare nel nostro discorso sara' una porta PPP di nome ppp0, e quindi dove qui scrivero' "eth1" bisognera' sostituire in questo caso "ppp0".
I gateway rendono irraggiungibili dall'esterno i computer delle reti locali, mentre viceversa i computer delle reti locali possono raggiungere Internet perche' i gateway svolgono la funzione di NAT o di proxy.
Consideriamo il computer PC-A-1 con indirizzo, poniamo, 192.168.0.101 inserito nella LAN-A, e consideriamo un altro computer PC-B-1 inserito nella LAN-B con indirizzo, poniamo, 192.168.1.101. Sebbene questi due computer possano comunicare sulla propria rete locale e con Internet, non possono comunicare direttamente tra di loro per vari motivi:
Tuttavia vogliamo comunque far comunicare i computer PC-A e PC-B sfruttando lo stesso canale di comunicazione Internet: ci serve una soluzione VPN.
L'idea, in sostanza, e' questa: stabilisco un canale di comunicazione basato sul protocollo SSH tra le macchine A e B; all'interno di questo canale apro un collegamento PPP; impostando opportunamente forwarding, masquerading e routing alle due estremita' del canale, ottengo la VPN.
Una volta attivato il collegamento VPN, tutti i computer della LAN-A potranno comunicare direttamente con tutti i computer della LAN-B. Vediamo come la cosa funziona seguendo il percorso di un pacchetto emesso da PC-A (un generico computer della LAN-A) verso PC-B:
I pacchetti che vanno da PC-B verso PC-A subiscono un procedimento analogo.
Qualora si debbano collegare fra di loro piu reti LAN che devono comunicare via VPN con i computer della sede centrale installati su LAN-A, la struttura logica della VPN sara' una stella con al centro il computer A. Gli indirizzi da assegnare ai computer delle varie reti potrebbero essere cosi' scelti:
Rete Indirizzo Netmask ---- ----------- ------------- LAN-A 192.168.0.x 255.255.255.0 LAN-B 192.168.1.x 255.255.255.0 LAN-C 192.168.2.x 255.255.255.0 ... ... ...
Su ogni LAN ci sara' un computer A, B, C con funzione di gateway Internet e di router per la VPN. La struttura logica delle interconnessioni tra le varie LAN ha la forma di una stella, con il gateway A al centro:
B ----- LAN B / / / LAN A ------ A -------- C ----- LAN C \ \ \ D ----- LAN DFigura 2. Struttura a stella della WAN.
Il protocollo SSH permette la comunicazione autenticata e crittata tra due macchine: nella macchina client A bastera' installare il client ssh, mentre nella macchina B si installera' il server sshd.
Per brevita' qui indichero' con A il numero di nodo IP o il nome pubblici
del primo gateway, e similmente B e' il secondo gateway. Inoltre
la scrittura root@A
individua l'utente root su A, e similmente
root@B
individua l'utente root su B.
L'utente su A che avvia la VPN dovra' potersi autenticare in modo automatico su B. Per evitare che venga richiesta la password, A deve creare la sua coppia di chiavi e poi copiare la chiave pubblica nel keyring di B. Per creare la propria coppia di chiavi, A dovra' fare:
root@A # ssh-keygen -t rsa1 Generating RSA keys: .............ooooooO................ooooooO Key generation complete. Enter file in which to save the key (/root/.ssh/identity): Enter passphrase (empty for no passphrase): (QUI PREMERE SOLO ENTER!) Enter same passphrase again: (QUI PREMERE SOLO ENTER!) Your identification has been saved in /root/.ssh/identity. Your public key has been saved in /root/.ssh/identity.pub. The key fingerprint is: 19:e6:25:7b:df:7e:25:79:4a:b2:43:bc:44:cc:0b:c7 root@A
Come indicato nel dialogo, ora root@A possiede una chiave segreta
nel file /root/.ssh/identity
e una chiave pubblica nel file
/root/.ssh/identity.pub
.
Notare che abbiamo lasciato vuota la passphrase: cio' significa che la chiave segreta di A rimane in chiaro, cioe' non viene crittata: questo e' essenziale, altrimenti all'atto del collegamento verrebbe richiesta la passphrase e l'automatismo di avviamento non funzionerebbe.
Inviare a B la chiave pubblica identity.pub
cosi' generata:
root@A # scp /root/.ssh/identity.pub root@B:
e infine, da B, aggiungere la chiave pubblica di A al keyring:
root@B # cat /root/identity.pub >> /root/.ssh/authorized_keys
Verificare da A se ora si puo' loggare in B direttamente e senza richiesta di password:
root@A # ssh root@B Last login: Wed Jan 16 17:05:07 root@B #
Perfetto! Adesso root@A puo' stabilire in modo automatico una connessione ssh con root@B. Il comando ssh supporta l'opzione -t che crea un canale permanente (pseudo-tty) che funziona similmente a una interfaccia seriale: esattamente quello che ci vuole per instradare il protocollo PPP.
Il kernel di Linux ha il supporto integrato per il protocollo IP, TCP e PPP (a meno che queste funzionalita' non siano state espressamente rimosse ricompilando il kernel). Sulla distribuzione Red Hat 6.2 e 7.x il pacchetto da installare e' ppp, che contiene il comando pppd.
Il protocollo PPP e' stato inventato per trasportare vari protocolli a pacchetti, incluso IP, su di una connessione tipicamente seriale (cavo, modem, ecc.) del tipo punto-a-punto (cioe' ai due estremi della connessione ci sono due macchine con assegnato indirizzo IP che estraggono e smistano i pacchetti).
In Linux il comando pppd
permette di controllare il
supporto al protocollo PPP integrato nel kernel: avviamento, arresto,
configurazione. Le prime versioni di pppd supportavano solo interfacce
seriali /dev/ttySx
. Le versioni piu' recenti (almeno
dalla 2.4.0 in poi, che io sappia) supportano l'opzione pty
:
con questa opzione il comando che attiva il collegamento (che per noi
sara' ssh) lascia aperto il suo stdin e stdout creando un canale pty
virtuale dove si attacca il PPP.
In definitiva, il canale VPN si attiva da A con un comando di questo tipo (ometto le opzioni meno interessanti):
root@A # pppd pty \ "ssh -t -l root 22.22.22.22 'pppd passive 192.168.1.254:192.168.0.254'" \ 192.168.0.254:192.168.1.254 local
dove 22.22.22.22 e' l'indirizzo pubblico di B (v. figura 1).
: : | | | | | eth0 (192.168.0.1) | eth0 (192.168.1.1) +------o------+ +------o------+ | | | | | A | | B | | | | | +------o------+ +------o------+ : ppp0 (192.168.0.254) : ppp0 (192.168.1.254) : : : : ******************************************************** * . Tunnel SSH per PPP . * * . . . . . . . . . . . . . . . . . . * * * * I n t e r n e t * * * ******************************************************** |
La figura 3 illustra la situazione per quanto riguarda il canale (o tunnel) VPN tra A e B: le due nuove interfacce virtuali ppp0 e i rispettivi numeri di nodo IP ci permetteranno di instradare correttamente i pacchetti da e verso il tunnel: tutti i pacchetti generati nella LAN A e destinati ad indirizzi della forma 192.168.1.x verranno inviati all'interfaccia ppp0 di A, e da questa al tunnel; similmente tutti i pacchetti generati nella LAN B e destinati ad indirizzi della forma 192.168.0.x verranno inviati all'interfaccia ppp0 di B, e da questa al tunnel.
vpn
che attiva la VPN descritta.
#!/bin/bash # # Attiva/Disattiva VPN # # # IP addr. privato interfaccia PPP su A: gw1="192.168.0.254" # IP addr. privato interfaccia PPP su B: gw2="192.168.1.254" # IP addr. pubblico host remoto B: gw2_name=22.22.22.22 user=root pppd=/usr/sbin/pppd options="lock nopersist debug asyncmap 0 noauth ipcp-restart 5 lcp-restart 5" IFCONFIG="/sin/ifconfig" function which_ppp() # # Dato l'IP addr. del gateway, ritorna il device pppx associato. # Se non lo trova ritorna la stringa vuota. # { for d in $($IFCONFIG | grep ^ppp | while read p x; do echo "$p"; done); do if $IFCONFIG $d | grep -q "inet addr:$1 " 2>/dev/null; then echo $d fi done echo "" } case $1 in start) echo "Start della VPN..." $pppd nopersist $options \ connect-delay 60000 \ pty \ "ssh -o 'KeepAlive no' -e none -c blowfish -t -l $user $gw2_name \ '$pppd $options passive $gw2:$gw1'" $gw1:$gw2 local aspetta=10 echo "(attendere $aspetta secondi...)" sleep $aspetta dev=$( which_ppp $gw1 ) if [ -z "$dev" ]; then echo "Nessun device PPP associato al gateway $gw1: collegamento fallito" exit 1 fi /sbin/route add -net 192.168.1.0/24 gw $gw1 $dev ssh -o 'Batchmode yes' -l $user $gw2_name /root/vpn-route err=$? if [ $err -ne 0 ]; then echo "Fallito avvio di /root/vpn-route su gw remoto $gw2_name (err=$err)." fi /sbin/ipfwadm -F -a accept -m -S 192.168.1.0/24 -D 192.168.0.0/24 /sbin/ipfwadm -F -a accept -m -S 192.168.0.0/24 -D 192.168.1.0/24 echo "VPN avviata." ;; stop) echo "Stop della VPN..." ssh -o 'Batchmode yes' -l $user $gw2_name 'killall pppd' err=$? if [ $err -ne 0 ]; then echo "Fallito killall pppd su gw remoto $gw2_name (err=$err)." fi echo "(attendere 10 secondi...)" sleep 10 p=$( ps axw | grep pppd | grep "ssh" ) if [ -z "$p" ]; then echo "Nessun processo PPPD verso $gw2" else echo "TROVATO: $p" pid=$( echo "$p" | ( read n x; echo $n ) ) echo "kill verso PID=$pid..." kill $pid fi ;; restart) $0 stop ; $0 start ;; status) dev=$( which_ppp $gw1 ) if [ -z "$dev" ]; then echo "Nessun device PPP associato al gateway $gw1" else echo "VPN attiva su questo device:" $IFCONFIG $dev fi ;; *) echo "Usage: vpn start|stop|restart|status" ;; esac # FINE!
vpn
.
Per impostare correttamente il computer B, il programma vpn
invoca il programma vpn-route
predisposto sul
computer B che si occupa della configurazione della tabella di routing
e del masquerading secondo necessità:
#!/bin/bash # IP addr. privato interfaccia PPP su A: gw1="192.168.0.254" # IP addr. privato interfaccia PPP su B: gw2="192.168.1.254" # IP addr. pubblico host remoto B: gw2_name=22.22.22.22 user=root pppd=/usr/sbin/pppd options=/root/pppd-options IFCONFIG="/sbin/ifconfig" function which_ppp() # # Dato l'IP addr. del gateway, ritorna il device pppx associato. # Se non lo trova ritorna la stringa vuota. # { for d in $($IFCONFIG | grep ^ppp | while read p x; do echo "$p"; done); do if $IFCONFIG $d | grep -q "inet addr:$1 " 2>/dev/null; then echo $d fi done echo "" } ppp=$( which_ppp $gw2 ) if [ -z "$ppp" ]; then echo "$0: nessun ppp verso $gw2: non posso configurare il route." exit 1 fi /sbin/route add -net 192.168.0.0/24 gw 192.168.1.254 $ppp /sbin/ipfwadm -F -a accept -m -S 192.168.0.0/24 -D 192.168.1.0/24 /sbin/ipfwadm -F -a accept -m -S 192.168.1.0/24 -D 192.168.0.0/24
vpn-route
.
La funzione which_ppp()
e' comune ai due programmi, e
permette di risalire al nome della interfaccia ppp assegnata al gateway
indicato come parametro.
vpn
accetta come parametri i consueti
start|stop|restart|status con ovvio significato.
Il computer A e' il cuore del sistema VPN, ed e' da qui che potremo sorvegliare il buon funzionamento del tutto.
Verifica dell'ssh. Dal computer A, loggati come root, proviamo il collegamento ssh con B:
root@A # ssh root@22.22.22.22
che dovrebbe portare immediatamente al prompt dei comandi di root sul computer B. Se non avviene, controllare che su B stia girando il demone sshd. Se viene richiesta la password, probabilmente non abbiamo inserito la chiave pubblica di root copiandola da A a B. Eventualmente usare il flag -v di ssh per vedere il procedere del collegamento.
Verifica del programma vpn. Una volta configurato lo script
vpn
su A e lo script vpn-route
su B, provare
ad avviare la VPN da A con il comando
root@A # vpn start
Se compare "VPN avviata." siamo a cavallo. Altrimenti,
controllare i parametri configurati negli script. Usare i
comandi ifconfig
, route -n
e traceroute -n
per diagnosticare eventuali
problemi. L'opzione -n
evita il tentativo di risolvere in
nomi degli indirizzi IP: poiche' probabilmente non avremo ancora settato
il DNS, senza questa opzione questi comandi inpiegherebbero molto tempo
prima di mostrare il loro output.
Prova del ping. Provare a pingare le varie macchine delle varie
reti locali usando ping -n
. Magari da un'altra finestra
terminale, verificare con tcpdump -n -i ppp1
cosa passa nel canale VPN di A, e similmente di B. Rifare le stesse
prove pingando fra di loro le macchine delle reti locali.
PC-A-1 IN A 192.168.0.101 PC-A-2 IN A 192.168.0.102 ... PC-B-1 IN A 192.168.1.101 PC-B-2 IN A 192.168.1.102 ...
e avremo tanti file di risoluzione inversa quanti sono le LAN connesse (due, nel nostro esempio).
A questo punto diventa facile anche il collegamento via SAMBA da Linux e via Explorer da Windows:
$ smbclient //PC-A-1/condivisione -U nomeutente
Avvio -> Trova -> Computer -> indicare il nome del computer.Notare che da "Risorse di rete" si vedranno solo i PC della LAN e non quelli della WAN, per i quali bisogna indicare esplicitamente il nome. Notare anche che in Windows, una volta individuato il computer remoto, si puo' procedere come al solito per il trasferimento di file, l'impostazione di una lettera di unita' virtuale, la stampa, ecc.
Per elevare le prestazioni sul trasferimento di file non compressi, forse puo' convenire aggiungere il flag -C al comando ssh: verificare se cio' porta ad effettivi miglioramenti o magari peggiora il trasferimento di dati gia' compressi.
Il controllo dello stato della VPN si puo' fare con il comando vpn
status
, ma e' necessario verificare prima che sia raggiungibile
l'host B:
if ping <B> -q -c 1 -w 5 > /dev/null 2>&1 \ && vpn status 2>/dev/null | grep ^Nessun ; then echo "VPN DOWN: INTERVENIRE!" fi
Per quanto riguarda il ripristino, il semplice lancio del programma
vpn restart
non sembra sufficiente per via delle
condizioni non ben definite in cui si verrebbero a trovare le macchine A e
B, anche se la VPN verrebbe comunque ripristinata. Inoltre, una sequenza
troppo ravvicinata di riavvii della VPN senza successo, potrebbe portare
all'esaurimento delle label di interfaccia virtuale ppp.
Il programma check-vpn
proposto di seguito e' una prima
implementazione di questi concetti:
#!/bin/bash # # Controlla stato VPN # # vpn=/root/bin/vpn # IP addr. privato interfaccia PPP su A: gw1="192.168.0.254" # IP addr. privato interfaccia PPP su B: gw2="192.168.1.254" # IP addr. pubblico host remoto B: gw2_name=22.22.22.22 log=/root/vpn.log data=$( date +%Y-%m-%d,\ %H:%M ) { if ping $gw2_name -q -c 1 -w 5 > /dev/null 2>&1 ; then if $vpn status | grep -q ^Nessun 2>/dev/null; then echo "$data: $0: VPN DOWN" echo "$data: $0: Tento il riavvio della VPN..." $vpn restart else if ! tail -1 $log | grep -q "VPN up$"; then echo "$data: $0: VPN up" fi fi else echo "$data: $0: VPN DOWN - manca collegamento con $gw2_name" fi } >> $log 2>&1 #FINE!
check-vpn
.
Il programma check-vpn
lo si puo' mettere nella crontab di
root@A ad esecuzione periodica (ad es. ogni 10 minuti):
*/10 * * * * /path/del/progr/check-vpn
In questo modo, ogni 10 minuti il programma viene avviato e controlla lo stato della VPN e, in caso di problemi, provvede al suo riavvio.
http://www.tldp.org/HOWTO/ppp-ssh/
.
Umberto Salsi | Commenti | Contatto | Mappa | Home/ |
Still no comments to this page. Use the Comments link above to add your contribute.