Blog

Freelan, un VPN qui vous veut du bien

Écrit le 30 09 2016 par Kévin MET _

Introduction

Pourquoi un nouveau VPN ? Pourquoi ne pas utiliser ce bon vieux OpenVPN ? Car il s'agit d'un peer to peer VPN et c'est là que ça devient très intéressant ! Avec OpenVPN nous étions obligé de passer par un serveur maître pour communiquer entre les serveurs enfants alors qu'avec Freelan nous allons pouvoir communiquer d'un noeud à l'autre sans passer par un noeud intermédiaire. On passe d'une topologie en étoile à une topologie maillée ce qui présente un grands nombre d'avantages lorsque l'on fait communiquer plus de deux serveurs entre eux. Nous allons donc voir comment installer Freelan sur Debian Jessie et comment le configurer pour le faire fonctionner sur 3 noeuds en p2p.

Installation

Il existe bien un paquet Debian mais il est uniquement disponible sur sid, or on veut l'installer sur Debian Jessie donc une Debian stable. Nous allons donc devoir compiler le binaire de freelan mais on va s'appuyer sur le paquet disponible pour sid pour le script d'init et la configuration. Pour commencer on va installer les dépendances, puis on va installer les paquets nécéssaires à la compilation. Comme ce VPN est peer to peer, tout cela est à faire sur chacun de vos noeuds qui seront identiques.

On commence par les dépendances :


root@hv0:~# apt-get update
root@hv0:~# apt-get install libssl1.0.0 libboost-system1.55.0 libboost-thread1.55.0 libboost-filesystem1.55.0 libboost-date-time1.55.0 libboost-program-options1.55.0 libboost-iostreams1.55.0 libcurl4-openssl-dev

Puis on passe aux paquets permettant de compiler Freelan :


root@hv0:~# apt-get install g++ git scons libssl-dev libboost-system-dev  libboost-thread-dev libboost-filesystem-dev libboost-date-time-dev libboost-program-options-dev libboost-iostreams-dev

Pour toutes mes compilations je fais cela dans un dossier /root/install, une vieille habitude que j'ai gardé lors de mon passage chez un hébergeur bien connu. Ceux qui y ont travaillé reconnaitront ce mythique dossier 😉 On va donc faire notre compil' dans ce dossier et utiliser un prefix de base ce qui copiera le fichier de configuration dans /etc/freelan/freelan.cfg et le binaire dans /bin/freelan :


root@hv0:~# mkdir /root/install
root@hv0:~# cd /root/install
# On clone le dépôt git et on se place dans la branche master
root@hv0:~# git clone https://github.com/freelan-developers/freelan.git
root@hv0:~# cd freelan && git checkout master
# on fait la compil' et l'install' via une unique commande
root@hv0:~# scons install prefix=/

On va ensuite récupérer le paquet disponible pour Debian Sid et le décompacter pour en utiliser quelques fichiers. Pour cela on revient dans le dossier /root/install, on télécharge le paquet et on le décompresse dans le dossier freelan_deb :


root@hv0:~# cd /root/install
root@hv0:~# wget http://ftp.fr.debian.org/debian/pool/main/f/freelan/freelan_2.0-1+b1_amd64.deb
root@hv0:~# dpkg -x freelan_2.0-1+b1_amd64.deb freelan_deb

On va ensuite pouvoir copier les fichiers de conf' et le script d'init. On va également ajouter le service au démarrage et faire un lien symbolique sur le binaire car dans le script d'init il est indiqué dans /usr/sbin/freelan :


root@hv0:~# cd freelan_deb/
root@hv0:~# cp etc/init.d/freelan /etc/init.d/
root@hv0:~# cp etc/default/freelan /etc/default/
root@hv0:~# update-rc.d freelan defaults
root@hv0:~# ln -s /bin/freelan /usr/sbin/freelan

Configuration via clé partagée

Nous allons enfin pouvoir passer à la configuration de nos noeuds. Pour utiliser Freelan vous avez le choix d'utiliser des certificats ou une clé pré-partagée. Nous allons voir les deux façons de faire et nous terminerons par un très rapide benchmark de ces différentes solutions. Nous allons commencer par une configuration utilisant une clé pré-partagée qui à l'avantage d'être la plus simple à mettre en oeuvre mais à l'inconvénient d'être la moins sécurisée. Pour cette exemple, je vais utiliser 3 noeuds ayant les caractéristiques suivantes :

  • hv0 : ip publique : 94.23.45.72, ip privée : 9.0.0.10
  • hv1 : ip publique : 46.105.121.181, ip privée : 9.0.0.11
  • hv2 : ip publique : 188.165.246.167, ip privée : 9.0.0.12
  • Clé pré-partagée : masupercledelamortquitue

On commence par éditer le fichier /etc/default/freelan qui est bien commenté donc assez explicite pour que je n'ajoute rien à son sujet. On va nommer notre première configuration key. Il faudra donc l'indiquer dans le fichier /etc/default/freelan et créer un fichier /etc/freelan/key.conf


root@hv2:~# cat /etc/default/freelan 
# This is the configuration file for /etc/init.d/freelan

# Configuration files to load.
#
# Values should be separated by spaces.
# Each entry must match a file at /etc/freelan/.conf
#
CONFIGURATIONS="key"

# Additional options that are passed to the Daemon.
#
#DAEMON_OPTS=""

Et vous allez voir que le fichier de conf sur chaque noeud est vraiment très basique avec ce genre de configuration :


root@hv1:~# cat /etc/freelan/key.conf 
[client]
public_endpoint=0.0.0.0
[fscp]
contact=94.23.45.72:12000
contact=188.165.246.167:12000
[tap_adapter]
ipv4_address_prefix_length=9.0.0.11/24
[security]
passphrase=masupercledelamortquitue

Et voici la liste des paramètres avec les explications :

  • public_endpoint : indique que freelan écoute sur toutes les interfaces sur le port par défaut (12000)
  • contact : la liste des noeuds avec lesquels on souhaite communiquer
  • ipv4_address_prefix_length : l'ip privée et le subnet dans notre VPN

Il faut bien penser à changer la liste de contact et l'ipv4_address_prefix_length sur chaque noeuds et normalement tout roule, vous pouvez pinguez vos noeuds sans problème.


root@hv0:~# ping 9.0.0.12
PING 9.0.0.12 (9.0.0.12) 56(84) bytes of data.
64 bytes from 9.0.0.12: icmp_seq=1 ttl=64 time=1.05 ms
64 bytes from 9.0.0.12: icmp_seq=2 ttl=64 time=0.949 ms
64 bytes from 9.0.0.12: icmp_seq=3 ttl=64 time=0.982 ms
^C
--- 9.0.0.12 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2002ms
rtt min/avg/max/mdev = 0.949/0.996/1.057/0.045 ms

Configuration via certificats

Maintenant que ça marche avec une clé, on va pouvoir passer aux choses sérieuses et se faire le même genre de config' en utilisant des certificats. Pour cela on va pas trop se prendre la tête, on va faire tous nos certificats sur le même noeud et les envoyer sur le deux autres par la suite. On commence par générer un CA. Pour cela on va se mettre dans le dossier /etc/freelan et télécharger l'archive fournit par Freelan contenant la conf pour la génération du CA.


root@hv0:~# cd /etc/freelan
root@hv0:~# wget https://www.freelan.org/static/files/certificate_authority.zip
root@hv0:~# unzip certificate_authority.zip
root@hv0:~# cd certificate_authority

On va ensuite pouvoir créer notre certificat avec openssl :


root@hv0:# openssl req -new -x509 -extensions v3_ca -keyout key/ca.key -out crt/ca.crt -config ca.cnf

Voici un exemple de sortie. Surtout n'oubliez pas le mot de passe qui vous servira à signer vos certificats par la suite !


Generating a 4096 bit RSA private key
....................................................++
........++
writing new private key to 'key/ca.key'
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
-----
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) [AU]:FR
State or Province Name (full name) [Some-State]:Paris
Locality Name (eg, city) []:Paris
Organization Name (eg, company) [Internet Widgits Pty Ltd]:MNT-TECH
Organizational Unit Name (eg, section) []:
Common Name (eg, YOUR name) []:MNT-TECH
Email Address []:kevin.met@mnt-tech.fr

On va ensuite générer une clé privée pour chaque noeud.


root@hv0:# openssl genrsa -out hv0.key 4096

Ceci est à répéter autant de fois que vous avez de noeuds. Puis on va générer une CSR à partir de la clé privée. Encore une fois, à faire pour chaque noeuds.


root@hv0:# openssl req -new -sha1 -key hv0.key -out hv0.csr

Voici un exemple de sortie.


root@hv0:# openssl req -new -sha1 -key hv0.key -out hv0.csr
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) [AU]:FR
State or Province Name (full name) [Some-State]:Paris
Locality Name (eg, city) []:Paris
Organization Name (eg, company) [Internet Widgits Pty Ltd]:MNT-TECH
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:hv0.mnt-tech.fr
Email Address []:kevin.met@mnt-tech.fr

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

Et enfin on va signer notre CSR avec notre CA pour générer un certificat. Encore une fois, à faire pour chaque noeuds.


root@hv0:# openssl ca -out hv0.crt -in hv0.csr -config ca.cnf

Voici un exemple de sortie.


root@hv0:# openssl ca -out hv1.crt -in hv1.csr -config ca.cnf
Using configuration from ca.cnf
Enter pass phrase for ./key/ca.key:
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 2 (0x2)
        Validity
            Not Before: Sep 28 13:55:04 2016 GMT
            Not After : Sep 26 13:55:04 2026 GMT
        Subject:
            countryName               = FR
            stateOrProvinceName       = Paris
            organizationName          = MNT-TECH
            commonName                = hv1.mnt-tech.fr
            emailAddress              = kevin.met@mnt-tech.fr
        X509v3 extensions:
            X509v3 Basic Constraints: 
                CA:FALSE
            Netscape Comment: 
                OpenSSL Generated Certificate
            X509v3 Subject Key Identifier: 
                6C:34:BE:3E:7C:72:A7:D7:A8:B8:FC:4F:7B:BA:41:84:EF:3A:79:69
            X509v3 Authority Key Identifier: 
                keyid:4C:D0:D6:F4:B5:2F:6D:73:89:04:8F:F1:04:88:6F:F6:3C:A5:F9:F4

Certificate is to be certified until Sep 26 13:55:04 2026 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

Par la suite on va envoyer nos certificats sur les autres noeuds via scp. Il faut envoyer le CA, la clé privée et le certificat.


root@hv0:~# scp /etc/freelan/certificate_authority/crt/ca.crt root@hv1.mnt-tech.fr:/etc/freelan/
root@hv0:~# scp /etc/freelan/certificate_authority/hv1.key root@hv1.mnt-tech.fr:/etc/freelan/
root@hv0:~# scp /etc/freelan/certificate_authority/hv1.crt root@hv1.mnt-tech.fr:/etc/freelan/

Il faut répèter cette opération sur tous les noeuds et il faut également copier les fichiers dans le répertoire /etc/freelan du noeud depuis lequel on vient de générer les certificats. Dans notre cas, hv0.


root@hv0:~# cp /etc/freelan/certificate_authority/crt/ca.crt /etc/freelan/
root@hv0:~# cp /etc/freelan/certificate_authority/hv0.key /etc/freelan/
root@hv0:~# cp /etc/freelan/certificate_authority/hv0.crt /etc/freelan/

Pour la configuration, elle ne change pas beaucoup. On va juste créer un nouveau fichier de configuration dans /etc/freelan se nommant certificates.conf et on va adapter le fichier /etc/default/freelan pour prendre en compte ce fichier de configuration. Cela donne ceci :


root@hv0:~# cat /etc/default/freelan 
# This is the configuration file for /etc/init.d/freelan

# Configuration files to load.
#
# Values should be separated by spaces.
# Each entry must match a file at /etc/freelan/.conf
#
CONFIGURATIONS="certificates"

# Additional options that are passed to the Daemon.
#
#DAEMON_OPTS=""

Et le fichier de config :


root@hv0:~# cat /etc/freelan/certificates.conf 
[client]
public_endpoint=0.0.0.0
[fscp]
contact=46.105.121.181:12000
contact=188.165.246.167:12000
[tap_adapter]
ipv4_address_prefix_length=9.0.0.10/24
ipv6_address_prefix_length=2aa1::1/8
[security]
signature_certificate_file=/etc/freelan/hv0.crt
signature_private_key_file=/etc/freelan/hv0.key
authority_certificate_file=/etc/freelan/ca.crt

Il ne reste plus qu'à lancer freelan et c'est fini pour vous :)

Résolution de problème

Si vous avez un problème et que freelan ne se lance pas, vous pouvez toujours le lancer à la main en mode debug grâce à l'option -d et le garder en foreground avec l'option -f ce qui vous permettra de savoir ce qui pose problème dans votre config'. Un exemple :


root@hv0:~# /usr/sbin/freelan -p /var/run/freelan.pid.key -c /etc/freelan/certificates.conf -fd
2016-09-30T12:42:30.876312 [INFORMATION] Reading configuration file at: "/etc/freelan/certificates.conf"
2016-09-30T12:42:30.877387 [ERROR] in security.signature_certificate_file: unable to load certificate at "/etc/freelan/hv0.crt" (No such file or directory)

On voit ici très rapidement qu'il manque un fichier de conf' empêchant Freelan de démarrer correctement.

Benchmarks

Pour commencer on va bencher entre deux noeuds en passant par l'interface publique. Pour ce bench on utilise iperf pendant 60 secondes. Sur le noeud hv0, on lance iperf -s qui servira de serveur et on lance les benchs depuis hv1 :


root@hv1:~# iperf -c 94.23.45.72 -t 60
------------------------------------------------------------
Client connecting to 94.23.45.72, TCP port 5001
TCP window size: 85.0 KByte (default)
------------------------------------------------------------
[  3] local 46.105.121.181 port 46945 connected with 94.23.45.72 port 5001
[ ID] Interval       Transfer     Bandwidth
[  3]  0.0-60.0 sec  1.80 GBytes   258 Mbits/sec

Ensuite on fait un petit bench avec la configuration utilisant la clé pré-partagée :


root@hv1:~# iperf -c 9.0.0.10 -t 60
------------------------------------------------------------
Client connecting to 9.0.0.10, TCP port 5001
TCP window size: 45.0 KByte (default)
------------------------------------------------------------
[  3] local 9.0.0.11 port 43599 connected with 9.0.0.10 port 5001
[ ID] Interval       Transfer     Bandwidth
[  3]  0.0-60.0 sec  1.71 GBytes   245 Mbits/sec

Et on finit avec le même bench en utilisant les certificats :


root@hv1:~# iperf -c 9.0.0.10 -t 60
------------------------------------------------------------
Client connecting to 9.0.0.10, TCP port 5001
TCP window size: 45.0 KByte (default)
------------------------------------------------------------
[  3] local 9.0.0.11 port 43602 connected with 9.0.0.10 port 5001
[ ID] Interval       Transfer     Bandwidth
[  3]  0.0-60.0 sec  1.72 GBytes   246 Mbits/sec

On peut voir que la configuration avec les certificats n'impacte pas les performances dans ce test. J'ai donc voule voir si en changeant le cipher on pouvait voir une influence sur le bench. J'ai donc ajouter ceci dans la conf pour forcer le cipher le moins gourmand en ressources CPU :


cipher_capability=ecdhe_rsa_aes128_gcm_sha256

Et voici le résultat du test :


root@hv1:~# iperf -c 9.0.0.10 -t 60
------------------------------------------------------------
Client connecting to 9.0.0.10, TCP port 5001
TCP window size: 45.0 KByte (default)
------------------------------------------------------------
[  3] local 9.0.0.11 port 43603 connected with 9.0.0.10 port 5001
[ ID] Interval       Transfer     Bandwidth
[  3]  0.0-60.0 sec  1.72 GBytes   246 Mbits/sec

Conclusion

On peut voir que le type de configuration ne fait pas varier les performances du VPN. Il faut tout de même prendre les résultats de ces tests avec des pincettes car ces serveurs ne sont pas en prod et ne me servent qu'à tester des techno. Il est donc possible que le cipher puisse par exemple avoir un impact sur les performances du VPN dans le cas ou le CPU est déjà un peu chargé... Il faudrait pousser plus loin ces tests pour le savoir.

Je n'ai pas eu vraiment l'occasion de tester ce VPN en prod car je l'ai utilisé pour faire quelques tests de diverses choses et pour avoir un réseau privé entre 3 noeuds sans passer par un serveur central. Tout ce que j'ai pu en voir c'est que je n'ai pas eu le moindre problème de stabilité sur plusieurs jours de tests et que le réseau à continuer de fonctionner sans discontinuité. Je serais donc prêt à faire un test en prod, avec pour commencer, un lien non critique comme une réplication MySQL master/slave par exemple. En tout cas, je suis assez emballé par le produit car il est assez prometteur :) A vous de jouer avec !

Sources :

♥ Partage sur tes réseaux sociaux ♥
Kévin MET
Kévin MET

Auteur de ce blog et gérant de la société MNT-TECH, je publie sur ce blog lorsque le temps me le permet et lorsqu'un sujet qui me parait intéressant n'a pas encore été abordé en français. Toutes les informations techniques présentes sur cette page peuvent être réutilisées moyennant le fait de citer la source.