sécurité VPN
OpenVPN est un logiciel libre multiplateforme utilisant OpenSSL pour créer un tunnel VPN SSL/TLS chiffré.
Ce mode opératoire décrit la mise en place d'un tunnel routé avec certificats entre un serveur Linux (Ubuntu ou Debian 8) et plusieurs clients, peu importe leur système d'exploitation.
Avant de commencer il faut savoir qu'il existe 2 types de tunnels :
… et 2 types d'authentification :
Si comme la plupart des gens no ne possède pas de CA, la méthode consiste à générer toutes les clés sur le serveur Linux (il jouera le rôle de CA) pour signer toutes les clés ; puis on installera ces clés sur chaque machine.
La première chose pour utiliser l'authentification par certificat est de générer la paire de clés de l'autorité de certification (CA). Ces clés seront utilisées pour certifier l'authenticité (signer) les clés publiques du serveur et des clients. Dans notre cas nous allons auto-signer les certificats sur le serveur, qui aura donc une double casquette (donc une double paire de clés) : serveur VPN + CA.
Nous allons voir comment générer des certificats sous Linux (mais c'est aussi faisable sous Windows avec les mêmes commandes), puis nous les importerons sur les clients. Les manips qui vont suivre se font en root.
Installation du paquet Openvpn sous Debian/Ubuntu :
aptitude install openvpn
OpenVPN contient des outils permettant de générer facilement des couples de clés. Ils sont situés dans le répertoire /usr/share/doc/openvpn/examples/easy-rsa/ (ou /usr/share/easy-rsa
sous Debian 8 (jessie)) ; il est conseillé de les copier dans un autre endroit, /etc/openvpn/easy-rsa par exemple, afin qu'une éventuelle mise à jour du paquet n'efface pas tout ce que nous allons faire.
mkdir /etc/openvpn/easy-rsa cp -R /usr/share/doc/openvpn/examples/easy-rsa/2.0/* /etc/openvpn/easy-rsa # alternative pour Debian 8 : cp -a /usr/share/easy-rsa /etc/openvpn # puis dans tous les cas, aller dans le répertoire copié cd /etc/openvpn/easy-rsa
Pour facilité notre travail, on va configurer des variables qui seront partagées par tous les certificats (et quo'n n'aura pas à ressaisir à chaque fois).
Pour cela, éditer le fichier vars
et remplir (au moins) les champs suivants, dans l'idée :
export KEY_COUNTRY="FR" export KEY_PROVINCE="FR" export KEY_CITY="Paris" export KEY_ORG="pteuz-corp" export KEY_EMAIL="pteu@google.fr" export KEY_OU="DSI" # X509 Subject Field export KEY_NAME="pteu"
Puis intégrer ces variables et supprimer les précédents éventuels certificats présents dans le répertoire keys
:
source vars ./clean-all
Pour générer la paire de clé de la CA :
./build-ca
L'ensemble des paramètres demandés sera déjà rempli avec les variables saisie à l'étape précédente ; seul le Common Name
est spécifique et doit être renseigné avec le nom de la machine.
Par sécurité il vaut mieux saisir un “Challenge password” pour utiliser le certificat, mais ce n'est pas obligatoire.
Si tout se passe bien, le répertoire keys
est créé et contient les fichiers ca.crt, ca.key, index.txt et serial.
On procède presque de la même façon pour générer les certificats du serveur et des clients :
./build-key-server server ./build-key client1
A chaque clé un prompt vous demande s'il faut signer les clés avec le CA ; il faut dire oui (“y”) !
Certificate is to be certified until Sep 2 15:10:41 2019 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
Maintenant il faut générer les paramètres Diffie Hellman qui permet l'échange de clés sécurisé lors de l'établissement du VPN :
./build-dh Generating DH parameters, 1024 bit long safe prime, generator 2 This is going to take a long time .......+......................................................+................. [...] .................+.........+.+....+...................+.++*++*++*
(NB : maintenant c'est plutôt 2048 bits)
Les fichiers que l'on a générés :
ls keys/ 01.pem ca.key dude.csr index.txt.attr serial server.csr 02.pem dh1024.pem dude.key index.txt.attr.old serial.old server.key ca.crt dude.crt index.txt index.txt.old server.crt
.key
sont les clés privées donc secrètes.crt
sont les certificats, publicsdh1024.pem
(ou dh2048.pem
) ce sont les paramètres Diffie-Hellman (échange de clé partagées lors de l'établissement du VPN)Pour une machine Windows : se connecter en webUI
Pour une machine Linux on peut s'inspirer directement des modèles fournis par easy-rsa :
gunzip -c /usr/share/doc/openvpn/examples/sample-config-files/server.conf.gz > /etc/openvpn/server.conf
Le fichier de conf de base est très complet et contient des explication et des exemples pour chaque option ; je vous encourage à le parcourir. Les paramètres minimaux à retoucher sont :
# préciser où sont les certificats et DH ca /etc/openvpn/easy-rsa/keys/ca.crt cert /etc/openvpn/easy-rsa/keys/server-cert.crt key /etc/openvpn/easy-rsa/keys/server-cert.key dh /etc/openvpn/easy-rsa/keys/dh2048.pem # on modifie la passerelle du client pour faire passer tout son trafic dans le VPN push "redirect-gateway def1 bypass-dhcp" # on modifie le DNS du client par celui du serveur push "dhcp-option DNS 8.8.8.8" # on limite les risques en faisant tourner le deamon openvpn sous un utilisateur sans privilège user nobody group nogroup
D'autres paramètres intéressants :
# envoyer cette route aux clients (pour qu'ils puissent accéder au réseau de votre LAN) push "route 192.168.2.0 255.255.255.0" # le réseau utilisé pour les tunnels # NB : OpenVPN utilise l'IP .1 du premier /30 pour lui # les /30 suivants sont pour chaque tunnel # ex : 192.168.3.4/30 pour le premier client, 192.168.3.8/30 pour le second, etc... server 192.168.3.0 255.255.255.0 # spécification de l'algorithme de chiffrement # pour lister tous les algos possibles : "openvpn --show-ciphers" # (NB : ''openvpn --show-digests'' permet de lister les algo de hashage) cipher AES-256-CBC # activer la communication entre les clients client-to-client # limiter le nombre max de client simultanés #max-clients 3 # activer la compression (à placer dans les confs clientes également) comp-lzo # augmenter ou diminuer le niveau de verbosité (de 0 à 15) pour debugger si ça monte pas ! #verb 4
Pour transformer notre serveur en routeur, on doit également :
echo "1" > /proc/sys/net/ipv4/ip_forward # pour pérenniser ce paramètre après le prochain reboot, il faut l'écrire dans /etc/sysctl.conf : vi /etc/sysctl.conf net.ipv4.ip_forward=1
eth0
par l'interface réseau utilisée sur votre serveur)iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE
# connexions ouvertes iptables -A FORWARD -p tcp -m state --state ESTABLISHED,RELATED -j ACCEPT # web iptables -A FORWARD -p tcp --sport 1024:65535 --dport 80 -j ACCEPT iptables -A FORWARD -p tcp --sport 1024:65535 --dport 443 -j ACCEPT # DNS iptables -A FORWARD -p udp --sport 1024:65535 --dport 53 -j ACCEPT iptables -A FORWARD -p udp --sport 53 --dport 1024:65535 -j ACCEPT #icmp iptables -A FORWARD -p icmp -j ACCEPT
Pour pérenniser cette config, on peut utiliser le paquet iptables-persistent :
aptitude install iptables-persistent /etc/init.d/netfilter-persistent save
Dans un premier temps on doit installer le logiciel client openvpn, qui va dépendre de la plateforme. Puis on devra importer les certificats : ca.crt, client.crt et client.key.
Pour la configuration, là encore on va s'inspirer du modèle fourni par easy-rsa :
cp /usr/share/doc/openvpn/examples/sample-config-files/client.conf /etc/openvpn/
Le fichier de conf est très similaire à celui du serveur ; voici un exemple de conf pour une tablette (l'application client que j'utilise est OpenVPN Connect, à télécharger sur le Play Store) :
client dev tun proto udp remote <IP_DU_SERVER> 1194 resolv-retry infinite nobind persist-key persist-tun ca /sdcard/Sync/Nexus9/openvpn/ca.crt cert /sdcard/Sync/Nexus9/openvpn/nexus9.crt key /sdcard/Sync/Nexus9/openvpn/nexus9.key ns-cert-type server comp-lzo verb 3
NB : avec OpenVPN Connect, le fichier de conf doit avoir l'extension .ovpn, il n'accepte pas les .conf :/.
Plutôt que de pointer vers des fichiers externes, openvpn permet d'intégrer les certificats dans le fichier de configuration ovpn ; c'est le cas pour les options ca
, cert
, key
, dh
(et d'autres). Il faut alors remplacer l'include du fichier et copier/coller son contenu entre les balises <option></option>
. Par exemple pour le certificat de la CA :
#ca /sdcard/Sync/Nexus9/openvpn/ca.crt <ca> -----BEGIN CERTIFICATE----- MIIEoTCCA4mgAwIBAgIJAKWLtfNAkVCcMA0GCSqGSIb3DQEBCwUAMIGRMQswCQYD [blabla...] +/WEiArZqzcmW4tQBnmBIgfGjYfJ+5MK3an2TVlnqoMy34ZD4AJDIrzU0aZwI75w 9KljFHVPKEAFhZzokOREYCG+8yVA5F6Uk2miMw6ZVGDgbrYvWA== -----END CERTIFICATE----- </ca>
Cela simplifie beaucoup la mise en œuvre, mais possède l'inconvénient de laisser la clé privée du client lisible par tous les utilisateurs pouvant lire le fichier de conf.
Sur le serveur, on peut spécifier une configuration dédiée à un seul client en modifiant ainsi la conf :
# indique ou sont situes les fichiers dédiés client-config-dir /tmp/openvpn/ccd # interdit la connexion des clients qui n'ont pas de fichier dédié #ccd-exclusive
Ensuite place la config dédiée dans fichier portant le nom du client (le Common Name (CN) de son certificat).
client-config-dir
indique le répertoire contenant les fichiers nommés <common name>
: le serveur utilisera le fichier idoine en récupérant ce champ dans le certificat du client. Par exemple pour configurer le client toto on créera un fichier /tmp/openvpn/ccd/toto
. Ce dernier pourra contenir, par exemple :# son IP réservée 192.168.3.10 ainsi que l'IP du tunnel côté serveur : .9 ifconfig-push 192.168.3.10 192.168.3.9 # une route pour que le client puisse avoir accès à un réseau connecté au serveur # sa passerelle sera l'IP du tunnel du serveur (.9) push "route 192.168.2.0 255.255.255.0" # LAN pour que le serveur accède au réseau du client (via 192.168.3.10) iroute 192.168.100.0 255.255.255.0 # NB : on doit ajouter également dans la conf serveur : "route 192.168.100.0 255.255.255.0"
Le driver TAP sous Windows impose l'utilisation d'un adressage en /30 (255.255.255.252) car c'est du point-à-point (2 IPs utilisables). Ainsi on ne peut utiliser les couples d'IPs suivantes (serveur/client) : (192.168.0.5,192.168.0.6), (9,10), (13,14), (17,18), etc…
On peut couper la connexion si inactivité du client avec l'option –ping-restart <time>
route
permet d'ajouter une route statique dans le kernel Linux
iroute
permet de déclarer une route vers un client (à définir dans un fichier client dans le client-config-dir
). Pour être ajoutée par le kernel elle doit être également déclarée dans la conf principale du serveur ovpn avec une directive route
, voir exemple ci-dessus) :
mute 10
permet de ne plus logguer les messages qui se répètent plus de 10 fois
On va s'atteler à configurer les utilitaires du sytème pour une bonne intégration d'openvpn.
Pour lancer openvpn automatiquement au démarrage de la machine :
update-rc.d openvpn defaults
Pour orienter les log du daemon openvpn, on peut définir l'option idoine (log-append openvpn.log
) dans la conf openvpn, mais je préfère les faire traiter par le syslog du système, c'est plus propre (et ça tombe bien, c'est la configuration par défaut d'openvpn !). On configurera le démon ®syslog, en créant un fichier .conf du genre /etc/rsyslog/openvpn.conf
:
# Create a template for the vpn log location $template OpenVPN,"/var/log/openvpn/ovpn.log" # Save log events where the programname starts with ovpn like ovpn-server to the # location mentioned in the template :programname, startswith, "ovpn-" -?OpenVPN # Stop processing ovpn-* log events :programname, startswith, "ovpn-" ~
Relancer ensuite le syslog :
systemctl restart rsyslog
Une configuration classique, à déposer dans /etc/logrotate.d/openvpn
:
/var/log/openvpn/*.log { rotate 12 size 500M monthly missingok notifempty delaycompress compress copytruncate # pour ne pas interrompre les logs du daemon openvpn }
On déclare le fichier à surveiller dans en créant un /etc/fail2ban/jail.d/openvpn.conf
:
[openvpn] enabled = true protocol = udp port = 5594 filter = openvpn logpath = /var/log/openvpn/ovpn.log
Et on défini les filtres dans /etc/fail2ban/filter.d/openvpn.conf
:
[Definition] failregex = <HOST>:\\d{1,5} TLS Auth Error <HOST>:\\d{1,5} VERIFY ERROR: <HOST>:\\d{1,5} TLS Error: TLS handshake failed
dev-node “nom_de_linterface_reseau”
permet de spécifier quelle interface utiliser (elle doit être créée avec addtap
).crl-verify crl.pem
pour spécifier une liste de révocationstatus openvpn-status.log
pour spécifier un fichier contenant la liste des clients connectés, mise à jour chaque minuteerror=unsupported certificate purpose
: typiquement cette erreur apparait quand on utilise un certificat “serveur” sur un client ou l'inverse (un certificat client sur un serveur). Openvpn vérifie le champ nsCertType du certificat, et il doit correspondre à l'utilisation qu'on en fait (serveur ou client). Pour résoudre ce problème, avec easy-rsa, générer le certificat d'un serveur openvpn avec build-key-server
et ceux des clients avec build-key
.topology subnet # copier/coller de l'IP attribuée par la Freebox à notre client ifconfig 192.168.27.65 255.255.255.224 # empêche d'accepter la configuration du serveur pull-filter ignore "ifconfig" # ip de l'interface tunnel de la Freebox route-gateway 192.168.27.94