Archivi tag: diffie-helman

Configurare una VPN IPsec site-to-site tra un router Cisco 2811 ed un router Cisco 877W

Affinchè si possa realizzare un canale di comunicazione “sicuro” tra due o più filiali, sfruttando, ad esempio, una normale linea ADSL, è necessario affidarsi ad una tecnologia VPN che supporti tale funzionalità. Lo standard “de facto” per i collegamenti Site-to-Site è rappresentato dal protocollo IPsec.

Adesso vedremo come configurare tale tipologia di VPN, utilizzando come piattaforme hardware un router Cisco 2811 (main office) ed un router Cisco 877W (branch office).

Lo schema di rete si può riassumere come segue:

vpn-ipsecConfigurazione del router Cisco 2811 (Main Office)

La prima cosa da fare è creare la policy ISAKMP per gestire le Security Associations (in gergo dette semplicemente SA), ovvero le due entità che vogliono instaurare il tunnel VPN.

R1(config)crypto isakmp policy 1
R1(config-isakmp)# encr 3des
R1(config-isakmp)# hash md5
R1(config-isakmp)# authentication pre-share
R1(config-isakmp)# group 2
R1(config-isakmp)# lifetime 86400

In particolare, sto definendo alcuni parametri relativi alla fase 1 IKE (in realtà ISAKMP è solo uno dei 3 protocolli definiti nella suite IKE), ovvero:

1) il tipo di cifratura simmetrica da utilizzare (3des);

2) l’algoritmo di hashing per l’HMAC (md5), utilizzato per garantire l’integrità dei pacchetti scambiati tra le parti;

3) il tipo di autentica (tramite chiave condivisa – pre-shared);

4) il gruppo Diffie-Helman da utilizzare (l’algoritmo in questione definisce un metodo “sicuro” per lo scambio delle chiavi tra le parti);

5) la durata del tunnel IPSec (in secondi).

Ora si può procedere con la definizione della chiave condivisa:

R1(config)# crypto isakmp key cisco address 2.2.2.3

A questo punto possiamo configurare la fase 2 IKE, impostando l’apposito transform set:

R1(config)# crypto ipsec transform-set TS esp-3des esp-md5-hmac

Esso si occupa di cifrare i dati trasmessi tra le due parti dopo l’instaurazione del tunnel.

Definiamo adesso l’ACL che si occuperà di indentificare il traffico VPN (detto, in gergo, interesting traffic):

R1(config)# ip access-list extended VPN-TRAFFIC
R1(config-ext-nacl)# permit ip 192.168.1.0 0.0.0.255 192.168.2.0 0.0.0.255

A questo punto sarà possibile definire la crypto map:

R1(config)# crypto map CMAP 6 ipsec-isakmp 
R1(config-crypto-map)# set peer 2.2.2.3
R1(config-crypto-map)# set transform-set TS
R1(config-crypto-map)# match address VPN-TRAFFIC

Inoltre, dobbiamo evitare che il traffico LAN-to-LAN venga nattato. E’ possibile fare ciò definendo un’ACL ad hoc:

R1(config)# access-list 100 remark --NO-NAT--
R1(config)# access-list 100 deny ip 192.168.1.0 0.0.0.255 192.168.2.0 0.0.0.255
R1(config)# access-list 100 permit ip 192.168.1.0 0.0.0.255 any
R1(config)# access-list 100 remark

associandola, successivamente, al processo di NAT vero e proprio:

R1(config)# ip nat inside source list 100 interface fastethernet0/0 overload

Come ultimo step dobbiamo associare la crypto MAP precedentemente definita all’interfaccia fa0/0 (Internet):

R1(config)# int fa0/0
R1(config-if)# crypto map CMAP

Configurazione del router Cisco 877W (Branch Office)

La configurazione del Branch Office è del tutto speculare a quella del Main office. L’unica stostaziale differenza sta nell’interfaccia esposta ad Internet: per il main office è la fa0/0 mentre per il branch office è la dia1 (protocollo PPPoE).

IKE fase 1:

R2(config)#  crypto isakmp policy 1
R2(config-isakmp)# encr 3des
R2(config-isakmp)# hash md5
R2(config-isakmp)# authentication pre-share
R2(config-isakmp)# group 2
R2(config-isakmp)# lifetime 86400

Pre-shared key:

R2(config)# crypto isakmp key cisco address 2.2.2.2

IKE fase 2 (transform set):

R2(config)# crypto ipsec transform-set TS esp-3des esp-md5-hmac

 ACL per il traffico LAN-to-LAN:

R2(config)# ip access-list extended VPN-TRAFFIC
R2(config-ext-nacl)# permit ip 192.168.2.0 0.0.0.255 192.168.1.0 0.0.0.255

 Crypto map:

R2(config)# crypto map CMAP 6 ipsec-isakmp 
R2(config-crypto-map)# set peer 2.2.2.2
R2(config-crypto-map)# set transform-set TS
R2(config-crypto-map)# match address VPN-TRAFFIC

No-NAT:

R2(config)# access-list 100 remark --NO-NAT--
R2(config)# access-list 100 deny ip 192.168.2.0 0.0.0.255 192.168.1.0 0.0.0.255
R2(config)# access-list 100 permit ip 192.168.2.0 0.0.0.255 any
R2(config)# access-list 100 remark
R2(config)# ip nat inside source list 100 int dia1 overload

Binding crypto map/interfaccia:

R2(config)# int dia1
R2(config-if)# crypto map CMAP
R2(config-if)# crypto ipsec df-bit clear

L’ultimo comando relativo alla configurazione dell’interfaccia dia1 ci mette al riparo da eventuali problemi dovuti ad un MTU mismatch (ad esempio, il router Cisco 2811 utilizza una MTU pari a 1500 byte mentre il router Cisco 877W, essendo configurato per un accesso ad Internet di tipo PPPoE, usa una MTU pari a 1492 byte).

Test e troubleshooting

Per testare l’effettivo funzionamento del tunnel VPN appena configurato è sufficiente, in primo luogo, lanciare un semplice ping verso la LAN del peer, avendo cura di definire come interfaccia sorgente quella esposta sulla rete privata:

R1(config)ping 192.168.2.1 source fa0/1
Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to 192.168.2.1, timeout is 2 seconds:
Packet sent with a source address of 192.168.1.1
.!!!!
Success rate is 80 percent (4/5), round-trip min/avg/max = 41/44/46 m

Il primo ping ha restituito un timeout poichè il tunnel VPN viene tirato su solo on-demand, ovvero dopo aver appurato la presenza di traffico LAN-to-LAN, per poi rimanere attivo fino al timeout definito nella policy ISAKMP (86400 secondi).

Altri comandi utili per verificare lo stato del tunnel VPN sono i seguenti:

show crypto session
show crypto isakmp sa
show crypto ipsec sa
debug crypto isakmp
debug crypto ipsec

E’ tutto. Alla prossima.

Creare una VPN SSL/TLS con OpenVPN

Lo standard de facto per realizzare i tunnel VPN è sicuramente IPSec. Tale protocollo è compatibile con dispositivi di vendor diversi (interoperabilità) e permette di creare dei tunnel altamente affidabili e robusti in termini di sicurezza. Per contro, però, esso risulta essere piuttosto complesso da configurare e proprio per questo motivo, soprattutto in ambito SOHO, si opta per soluzioni alternative. Una di queste è sicuramente OpenVPN, in grado di realizzare dei tunnel cifrati mediante i protocolli SSL/TLS.

Di seguito un breve howto in cui viene descritta la configurazione di OpenVPN per un bastion host dual-homed Linux.

Nello specifico, la rete locale è così definita:

Internet - Screened router - interfaccia UNTRUST del bastion - FW IPTABLES - interfaccia TRUST del bastion - LAN

Configurazione del server

Per prima cosa occorre installare l’applicativo in questione digitando il comando:

nightfly@nightbox:~$ sudo apt-get install openvpn

ad installazione completata, posizioniamoci nella directory /etc/openvpn e creiamo le subdir certificati e log:

 nightfly@nightbox:/etc/openvpn$ sudo mkdir log
 nightfly@nightbox:/etc/openvpn$ sudo mkdir certificati

Successivamente, creiamo i due file in cui verranno salvati i log:

 nightfly@nightbox:/etc/openvpn$ cd log
 nightfly@nightbox:/etc/openvpn/log$ sudo touch openvpn.log openvpn-status.log

A questo punto possiamo creare il file di configurazione:

nightfly@nightbox:/etc/openvpn$ sudo nano server.conf

il cui contenuto dovrà essere simile al seguente:

tls-server

port 5002

dev tun
ifconfig 192.168.110.1 192.168.110.2

persist-key
persist-tun

push "route 192.168.1.0 255.255.255.0"

#Percorsi dei certificati
ca certificati/ca.crt
cert certificati/server.crt
key certificati/server.key 
dh certificati/dh1024.pem
tls-auth certificati/ta.key 0 # 0 per il server ed 1 per il client, essenziale per la generazione dell'hash HMAC nell'header TLS

cipher BF-CBC
comp-lzo

keepalive 10 120

#log and security
user nobody
group nogroup
status log/openvpn-status.log
log-append log/openvpn.log
verb 3
scrip-security 2

Mediante la direttiva tls-server sto imponendo al mio bastion host di fungere da server per le sessioni TLS.

Con port specifico su quale porta OpenVPN deve rimanere in ascolto, mentre con dev tun sto scegliendo la tipologia di interfaccia da realizzare. A tal proposito, occorre precisare che i tipi di interfaccia che può tirar su OpenVPN sono 2, ovvero:

1) tun, per le VPN routed (livello 3);

2) tap, per le VPN bridged (livello 2).

Con ifconfig specifico l’indirizzo IP del tunnel VPN lato server (192.168.110.1) e lato client (192.168.110.2). Per la natura delle interfacce tun, trattasi di un collegamento point-to-point (/30).

con push “route 192.168.1.0 255.255.255.0” sto iniettando la rotta per raggiungere la mia LAN (interfaccia TRUST del bastion-host) sul client VPN.

Con le direttive:

 cipher BF-CBC
 comp-lzo

sto definendo il tipo di cifratura (BlowFish) ed il tipo di compressione (LZO – Lempel-Ziv-Oberhumer) messe in atto nel tunnel VPN.

Con keeplive faccio in modo che il tunnel rimanga attivo anche in assenza di traffico, mentre nella sezione #log and security definisco (in quest’ordine) i permessi, i file di log, il verbosity (accuratezza dei log) ed il livello di sicurezza di questo file di configurazione.

Salviamo il file in questione e procediamo con la generazione delle chiavi e dei certificati. Lato server i comandi da lanciare sono i seguenti:

nightfly@nightbox:~$ cd /usr/share/doc/openvpn/examples/easy-rsa/2.0/

nightfly@nightbox:~$ sudo nano vars

che andrà editato customizzando i campi indicati

nightfly@nightbox:~$ source ./vars

nightfly@nightbox:~$ ./clean-all

nightfly@nightbox:~$ ./build-ca

per la generazione del certificato relativo alla CA

nightfly@nightbox:~$ ./build-dh 

per lo scambio delle chiavi mediante l’algoritmo Diffie-Helman (leggasi premaster-key)

nightfly@nightbox:~$ ./build-key-server server

per la generazione della chiave privata associata al server, con relativa richiesta *.crt

nightfly@nightbox:~$ openvpn --genkey --secret ta.key

per la generazione della chiave segreta.

Una volta generate le chiavi ed i certificati copiamoli nella directory /etc/openvpn/certificati:

nightfly@nightbox:~$ sudo cp ca.crt server.crt server.key dh1024.pem ta.key /etc/openvpn/certificati

A questo punto possiamo avviare il demone OpenVPN:

nightfly@nightbox:~$ sudo service openvpn start

Verifichiamo che l’interfaccia tun0 sia attiva mediante il comando ifconfig:

nightfly@nightbox:~$ ifconfig

tun0     Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
indirizzo inet:192.168.110.1  P-t-P:192.168.110.2  Maschera:255.255.255.255
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
collisioni:0 txqueuelen:100
Byte RX:0 (0.0 B)  Byte TX:0 (0.0 B)

Configurazione di IPTABLES

Il bastion host ha DROP come azione di default per la chain FORWARD. Proprio per questo motivo, occorre consentire il transito del traffico dall’interfaccia tun0 a quella TRUST (eth0) e viceversa. Le regole da definire sono le seguenti:

 iptables -A FORWARD -i tun0 -o eth0 -j ACCEPT
 iptables -A FORWARD -i eth0 -o tun0 -j ACCEPT

Configurazione del client

A questo punto è possibile installare e configurare il client, che però dovrà utilizzare lo stesso file ca.crt del server, la medesima chiave ta.key del server ma una propria chiave privata (client.key) ed un proprio certificato (client.crt).

Per ambienti Windows vi consiglio questo client Openvpn (se utilizzate Windows 7 fate riferimento a questa pagina per la procedura di installazione).

Quest’altro client, invece, evitatelo come la peste (in quanto bacatissimo, d’altronde è una versione alpha).

Ad installazione completata potrete utilizzare il seguente file di configurazione:

remote <indirizzo IP pubblico del server OpenVPN> 5002
client
tls-client
nobind
dev tun
ifconfig 192.168.110.2 192.168.110.1
pull

ca key/ca.crt
cert key/client.crt
key key/client.key
tls-auth key/ta.key 1

persist-key
persist-tun

cipher BF-CBC

comp-lzo

keepalive 10 120

scrip-security 2

verb 3

Da notare che gli algoritmi di compressione e cifratura devono essere i medesimi di quelli utilizzati dal server, pena l’impossibilità di connettersi allo stesso.

Diagnostica

A connessione VPN avvenuta, potete verificare la raggiungibilità attraverso il tunnel degli host della LAN mediante un seplice ping, ad esempio:

ping 192.168.1.2

Se il ping dovesse andare in timeout, provate a mettere in ascolto tcpdump sulle interfacce tun0 ed eth0 e controllate la tabella di routing del vostro bastion host (attraverso il comando route).

Verificate inoltre che la rotta verso la LAN sia stata correttamente iniettata sul client (Windows), digitando:

route print

Fine del post, bye.

SSL e Diffie-Helman

Il protocollo SSL (Secure Socket Layer) prevede l’uso di alcuni algoritmi di cifratura nell’ambito dell’handshake. Uno di questi è il Diffie-Helman, che può essere adoperato in 3 varianti:

1) Anonymous Diffie-Helman (A_DH): il numero primo p e la radice primitiva a ad esso associata vengono inviati senza autentica. In realtà questa è la modalità di funzionamento “standard” del Diffie-Helman;

2) Fixed Diffie-Helman (F_DH): p ed a del server https vengono firmati mediante un certificato X.509 trusted. La chiave segreta è statica, ovvero è la medesima per ogni sessione.

3) Ephemeral Diffie-Helman (E_DH): viene generata una secret key (relativa al protocollo DH) temporanea (one-time o ephimeral), ovvero valida per una sola sessone, firmata mediante la propria chiave segreta RSA (o DSS). La controparte ne verificherà l’autenticità attraverso la chiave pubblica del mittente. Anche in questo caso si possono utilizzare dei certificati X.509 contenenti i paramentri a e p per semplificare le operazioni di autentica dei due peer.

Fine del post, alla prossima.

Algoritmo Diffie-Helman per lo scambio delle chiavi

L’algoritmo Diffie-Helman rappresenta il primo esempio di crittografia a chiave pubblica. In particolare, esso consente a 2 utenti che vogliono comunicare in modo “sicuro”, di scambiarsi una chiave segreta da usare in fase di cifratura/decifratura.

La robustezza di tale algoritmo si basa sostanzialmente sulla difficoltà di calcolo dei logaritmi discreti. Quindi, per capire bene il funzionamento dell’algoritmo in questione, occorre definire in modo chiaro cos’è un logaritmo discreto.

Sia a la radice primitiva di un numero primo p, dove per radice primitiva si intende il numero le cui potenze generano tutti gli interi compresi tra 1 e p-1. In altre parole, i seguenti valori:

a mod p, a^2 mod p, …, a^p-1 mod p

saranno tutti compresi nell’intervallo [1, p-1]. Notate che sto utilizzando il modulo in quanto abbiamo a che fare con numeri discreti.

Ora, dato un qualunque numero intero b ed una radice primitiva a di un numero primo p, allora è possibile trovare un unico esponente i tale che:

b = a^i mod p

dove 0 <= i <= (p-1). L’esponente i rappresenta proprio il logaritmo discreto di b rispetto alla base a mod p e può essere rappresentato mediante la seguente notazione:

i = dloga,p(b)

Bene, ora che sappiamo cosa si intende per logaritmo discreto, possiamo illustrare il funzionamento dell’algoritmo Diffie-Helman.

Supponiamo che 2 utenti (A e B) vogliano instaurare una comunicazione bidirezionale cifrata. Essi utilizzeranno due valori pubblici, ovvero q ed a, con q numero primo ed a radice primitiva di q.

A questo punto, l’utente A sceglierà un intero casuale XA, con XA < q, che manterrà segreto. Successivamente, calcolerà YA, ovvero la chiave pubblica, dove:

YA = a^XA mod q

Tale operazione verrà effettuata anche dall’utente B, per il quale avremo:

YB = a^XB mod q

Adesso che entrambe le parti hanno generato le rispettive chiavi pubbliche, esse calcoleranno la chiave condivisa (K) nel modo seguente:

Utente A

K = YB^XA

Utente B

K = YA^XB

Ora, si può dimostrare che le due chiavi K così generate sono identiche:

1) K = YB^XA mod q

Sapendo che

2) YB = a^XB mod q

sostituendo la 2) nella 1) avremo:

K = (a^XB mod q)^XA mod q = (a^XB)^XA mod q = (a^XA)^XB mod q = (a^XA mod q)^XB mod q = YA^XB mod q

Facendo il punto della situazione, abbiamo 2 valori privati, ovvero XA ed XB, e 4 valori pubblici: a, q, YA ed YB. Affinchè l’attaccante possa individuare la chiave privata dell’utente B, egli dovrà calcolare:

XB = dloga,q(YB)

che, come affermato in precedenza, è un’operazione molto complessa dal punto di vista computazionale.

Attacchi bruteforce

Nel caso in cui il numero primo q scelto da una delle due parti che vogliono condividere una chiave K sia troppo piccolo, l’attaccante può provare, mediante un approccio a forza bruta, a calcolare uno dei due valori segreti (XA oppure XB).

Ma facciamo un esempio. Supponiamo che q sia pari a 353. Per definizione di algoritmo discreto abbiamo che:

b = a^i mod p

dove b è la chiave pubblica, i il logaritmo discreto in base a mod p ed a rappresenta la radice primitiva di q.

Come già detto in precedenza, i valori associati a b, a e p sono noti, nella fattispecie:

a = 3;

b = 90;

q = 353.

Quindi l’attaccante dovrà risolvere la seguente equazione:

90 = 3^i mod 353

Per trovare il valore di i (che rappresenta proprio la chiave segreta XA o XB, a seconda di quale dei due utenti sia la vittima dell’attacco) gli basterà calcolare tutte le potenze di 3 mod 353 fino a quando non otterrà 90 come risultato. A questo punto calcolerà K utilizzando la chiave pubblica della controparte. Ovviamente tale operazione diventa improponibile per valori di q elevati.

Attacchi replay

Ma come avviene lo scambio dei valori pubblici a e q tra l’utente A e l’utente B? Una possibile soluzione consiste nel concordarli a priori, oppure nel fare in modo che l’utente A (che inizializza la comunicazione) ne calcoli il valore e lo includa, successivamente, nel primo messaggio inviato alla controparte. Un altro approccio prevede l’uso di una directortory condivisa e fidata (per garantire in qualche modo l’autenticità della fonte), in cui salvare le chiavi pubbliche dei vari utenti. In questo caso possono comunque essere attuati degli attacchi di tipo replay.

Attacchi man-in-the-middle

Analizziamo il seguente scenario: supponiamo che Alice (utente A) e Bob (utente B) vogliano scambiarsi una chiave segreta, e che Darth (l’attaccante, ovvero l’utente D) sia in grado di intercettare le chiavi pubbliche di Alice (YA) e di Bob (YB) (nel momento in cui avviene lo scambio delle stesse).

Prima di intercettare le chiavi, l’utente D genererà una coppia di valori privati, ovvero XD1 ed XD2, che verranno usati per creare rispettivamente le chiavi pubbliche YD1 ed YD2. Nel momento in cui A invierà a B la propria chiave pubblica (ovvero YA), l’utente D la intercetterà e la sostituirà con YD1.

A questo punto, l’utente D è in possesso della chiave pubblica di A e la utilizzerà per calcolare la chiave condivisa K2:

K2 = YA^XD2 mod q

L’utente B, avendo ricevuto YD1 come chiave pubblica dell’utente A, calcolerà anch’egli K2:

K2 = YD1^XB mod q

A questo punto l’utente B invierà all’utente A la propria chiave pubblica YB. Essa verrà intercettata dall’utente D e sostituita dallo stesso con YD2.

L’utente A, convinto che YD2 sia la chiave pubblica dell’utente B, calcolerà la chiave condivisa K1:

K1 = YD2^XA mod q

Anche l’utente D calcolerà la chiave condivisa K1:

K1 = YB^XD1 mod q

Ora, l’utente A e l’utente B crederanno di condividere una chiave segreta K. In realtà, l’utente A condivide con l’utente D la chiave K1, mentre l’utente B condivide, sempre con l’utente D, la chiave K2. Darth può quindi: intercettare le comunicazioni provenienti da Alice o da Bob e/o alterare le informazioni dirette da Alice verso Bob e viceversa (tampering). Da ciò si deduce che l’algoritmo Diffie-Helman non può essere utilizzato nell’ambito della cifratura simmetrica (dato che non garantisce l’autenticazione della fonte), tornando comunque utile per lo scambio di una chiave segreta ad autenticazioni delle parti avvenuta.

Fine del post, a presto.