---
categories: vpn sysadmin security network
title: Howto OpenVPN
...

* Documentation : <https://openvpn.net/index.php/open-source/documentation.html>

[OpenVPN](https://openvpn.net/) permet de monter des tunnels [VPN](https://fr.wikipedia.org/wiki/Réseau_privé_virtuel) (Virtual Private Network) en utilisant [SSL/TLS](HowtoSSL) pour le chiffrement. Pour l'authentification, OpenVPN peut utiliser une simple clé partagée (PSK — Pre-Shared Key) ou des couples *utilisateur*/*mot de passe*, mais nous préférons utiliser des certificats avec une [PKI](https://fr.wikipedia.org/wiki/Infrastructure_%C3%A0_cl%C3%A9s_publiques) (Public Key Infrastructure).


## Installation

### Debian

~~~
# apt install openvpn

$ /usr/sbin/openvpn --version
OpenVPN 2.4.0 x86_64-pc-linux-gnu [SSL (OpenSSL)] [LZO] [LZ4] [EPOLL] [PKCS11] [MH/PKTINFO] [AEAD] built on Jul 18 2017
library versions: OpenSSL 1.0.2l  25 May 2017, LZO 2.08

# systemctl status openvpn
● openvpn.service - OpenVPN service
   Loaded: loaded (/lib/systemd/system/openvpn.service; enabled)
  Process: 20889 ExecStart=/bin/true (code=exited, status=0/SUCCESS)
 Main PID: 20889 (code=exited, status=0/SUCCESS)
   CGroup: /system.slice/openvpn.service
~~~

### OpenBSD

~~~
# pkg_add openvpn
$ openvpn --version
OpenVPN 2.4.4 x86_64-unknown-openbsd6.2 [SSL (OpenSSL)] [LZO] [LZ4] [MH/RECVDA] [AEAD] built on Sep 29 2017
library versions: LibreSSL 2.6.3, LZO 2.10
~~~

## Configuration serveur

On met en place une PKI sur le serveur via [shellpki](HowtoShellpki) :

~~~
# mkdir /etc/openvpn/ssl
# git clone https://forge.evolix.org/shellpki.git /etc/openvpn/ssl
# cd /etc/openvpn/ssl
# sh shellpki.sh init
# sh shellpki.sh create
~~~

Mettre un _Common Name_ du type `fw.vpn.example.com` avec une durée de plusieurs années. Pour plus d'informations, voir [HowtoShellpki](HowtoShellpki)

La configuration se fait via le fichier `/etc/openvpn/server.conf` avec les paramètres suivants à adapter :

* `server` : réseau privé sur lequel le serveur VPN et les clients communiqueront
* `local` : adresse IP du serveur, sur laquelle les clients se connecteront (adresse IP publique généralement)
* `push` : route vers le réseau que les clients VPN pourront joindre
* `cert` : certificat du serveur VPN (créé précédemment)
* `key` : clé du serveur VPN (créée précédemment)
* `ifconfig-pool-persist` : permet aux clients de conserver la même IP en cas de redémarrage

### Serveur sous Debian

~~~
#
# General settings
#

user  nobody
group nogroup

# Do not try to re-read key file and reopen tun device on restart since it runs
# without root privileges.
persist-key
persist-tun
#persist-remote-ip
#persist-local-ip

# Status file
status /var/log/openvpn/status.log 1
#log    /var/log/openvpn/openvpn.log
# Logging verbosity. Logs are sent to syslog.
verb 3

# Keepalive
keepalive 10 120
#reneg-sec 300

#
# Network settings
#

port  1194
proto udp
dev   tun

# Enable compression
# comp-lzo
# compress lzo (à partir d'OpenVPN 2.4)

#
# key/certificate
#

ca   /etc/openvpn/ssl/ca/cacert.pem
cert /etc/openvpn/ssl/files/fw.vpn.example.com-1278421834/fw.vpn.example.com.crt
key /etc/openvpn/ssl/files/fw.vpn.example.com-1278421834/fw.vpn.example.com.key
dh   /etc/openvpn/ssl/ca/dh2048.pem

#
# private network
#

server 192.0.2.0 255.255.0.0
mode server

# Management interface (used by check_openvpn for Nagios)
management 127.0.0.1 1195 /etc/openvpn/management-pwd
~~~

Générer un mot de passe pour l'accès à l'interface de management (nécessaire au check Nagios) :

~~~
# apg -n1 -m 12 > /etc/openvpn/management-pwd
~~~

On peut ensuite utiliser le [check Nagios suivant](/check_openvpn.pl):

~~~
# apt install libnet-telnet-perl   # Debian
# pkg_add p5-Net-Telnet-3.04       # OpenBSD
# /usr/lib/nagios/plugins/check_openvpn.pl -H 127.0.0.1 -p 1195 -P $(cat /etc/openvpn/management-pwd)"
~~~

### Serveur sous OpenBSD

~~~
daemon

port 1194
proto udp
dev tap0
dev-type tap
verb 4

user nobody
group nobody
chroot /var/empty
#compress lzo

max-clients 50
keepalive 15 120
tls-exit
persist-key
persist-tun
client-to-client
resolv-retry infinite

local 198.51.100.1
server 192.0.2.0 255.255.255.0
#client-to-client
ifconfig-pool-persist /etc/openvpn/ipp.txt
#route-method exe
#route-delay 2
push "route 203.0.113.0 255.255.255.0 192.0.2.1"

ca /etc/openvpn/ssl/ca/cacert.pem
dh /etc/openvpn/ssl/ca/dh2048.pem

cert /etc/openvpn/ssl/files/fw.vpn.example.com-1278421834/fw.vpn.example.com.crt
key /etc/openvpn/ssl/files/fw.vpn.example.com-1278421834/fw.vpn.example.com.key

log /var/log/openvpn.log
status /var/log/openvpn-status.log

cipher AES-128-CBC # AES

#fragment 1350
#mssfix
~~~

Pour créer automatiquement une interface `TUN` au démarrage de la machine et démarrer OpenVPN :

~~~
# cat << EOF > /etc/hostname.tun0
up
!/usr/local/sbin/openvpn --daemon --config /etc/openvpn/server.conf
EOF
~~~

Pour lancer manuellement OpenVPN sans redémarrer la machine :

~~~
# ifconfig tun0 up
# /usr/local/sbin/openvpn --daemon --config /etc/openvpn/server.conf
~~~

Rotation des logs :

Le fichier `/etc/newsyslog.conf` ne permet pas de faire une rotation propre des logs OpenVPN, car OpenVPN continue à écrire dans le file descriptor du fichier précédent. Cela peut mener à une saturation de la partition d'écriture, sans pouvoir supprimer le fichier qui n'existe plus, et où seul un restart d'OpenVPN peut régler le problème.

Pour tout de même utiliser newsyslog, il faut rajouter la ligne suivante dans le fichier `/etc/newsyslog.conf` :

~~~
/var/log/openvpn.log                    600  52    *    $W6D4 Z
~~~

Ainsi que plannifier en crontab un restart d'OpenVPN juste après cette rotation, pour qu'il puisse récupérer le nouveau file descriptor :

~~~
05 	4 	* 	* 	6	/etc/rc.d/openvpn restart
~~~

Si on ne veut pas avoir à redémarrer OpenVPN, on peut également utiliser un cron qui va copier le fichier de log, compresser la copie, et vider l'actuel (équivalent d'un copytruncate du logrotate). De cette manière, le file descriptor n'est pas changé :

~~~
0       4       *       *       6       cp /var/log/openvpn.log /var/log/openvpn.log.$(date +\%F) && echo "$(date +\%F' '\%R) - logfile turned over via cron" > /var/log/openvpn.log && gzip /var/log/openvpn.log.$(date +\%F) && find /var/log/ -type f -name "openvpn.log.*" -mtime +365 -exec rm {} \+
~~~

## Configuration client

Pour simplifier la configuration pour les utilisateurs finaux on peut générer un fichier de configuration embarquant les certificats :

~~~
client
dev tap0
proto udp

tls-client
remote vpn.example.com 1194

#compress lzo
nobind
user nobody
group nogroup
#chroot /var/empty
persist-key
persist-tun
max-routes 1000
#cipher AES-128-CBC

<ca>
-----BEGIN CERTIFICATE-----
[…]
-----END CERTIFICATE-----
</ca>

<cert>
Certificate:
    Data:
[…]
-----END CERTIFICATE-----
</cert>

<key>
-----BEGIN RSA PRIVATE KEY-----
[…]
-----END RSA PRIVATE KEY-----
</key>
~~~

### Nouveau certificat avec shellpki

Pour une nouvelle connexion VPN, il faudra créer un nouveau certificat :

~~~
# cd /etc/openvpn/ssl
# sh shellpki.sh create
~~~

* Choisir un _Common Name_ unique pour la machine (ex : `client1.vpn.example.com`)

* Récupérer le certificat et la clé dans `/var/www/htdocs/vpn/ssl`

### Renouveller un certificat avec shellpki

Il faut le révoquer, puis en recréer un :

~~~
# cd /etc/openvpn/ssl
# sh shellpki.sh revoke
# sh shellpki.sh create
~~~

Si c'est le certificat du serveur OpenVPN, il faut en plus modifier la configuration puis relancer le démon.

### Révoquer un certificat avec shellpki

Il faut regénérer une CRL après chaque révocation de certificat :

~~~
# cd /etc/openvpn/ssl
# sh shellpki.sh revoke
# sh shellpki.sh crl
~~~

Le fichier `/etc/openvpn/ssl/crl.pem` est alors créé. La configuration d'OpenVPN doit être ajustée pour vérifier la CRL :

~~~
crl-verify /etc/openvpn/ssl/crl.pem
~~~

#### Vérifier les certificats révoqués

~~~
# openssl crl -inform PEM -text -noout -in /etc/openvpn/ssl/crl.pem
~~~

L'association entre le numéro de série indiqué et le CN du certificat correspondant peut être faite à l'aide du fichier `/etc/openvpn/ssl/ca/index.txt`

### Client sous Linux

Pour démarrer le client OpenVPN, il existe un template systemd, pour le démarrer et l'activer au démarrage :

~~~
# systemctl start openvpn@client
# systemctl enable openvpn@client
~~~

### Client sous Windows

On peut télécharger un client 32 bits ou 64 bits sur <https://openvpn.net/index.php/open-source/downloads.html>.

Une fois installé, copier les fichiers `.ovpn` et le certificat de la CA dans le répertoire de configuration d'OpenVPN, par exemple `C:\Program Files\OpenVPN\config`.

Attention, il faut exécuter OpenVPN en tant qu'administrateur, cela peut se faire via un clic droit : `Exécuter en tant qu'administrateur`.

OpenVPN est alors disponible dans la barre des tâches.
Pour lancer une session, effectuez un clic-droit et sélectionnez `Connecter`.

### Client sous Mac OS X

Télécharger la dernière version stable de Tunnelblick sur <https://tunnelblick.net/downloads.html>

On suit les instructions d'installation (en anglais), puis :

* on choisit de générer la configuration (indiquer qu'on n'a pas de fichiers de configuration) ;
* cela génère une configuration standard, notamment un dossier sur le bureau avec un fichier `config.ovpn` ;
* on édite le fichier `config.ovpn` en ajustant les options `remote`, `ca`, `cert` et `key` ;
* on copie également la clé et les certificats (ca et cart) dans ce dossier ;
* on doit ensuite renommer ce dossier avec un nom se terminant par `.tblk` ;
* on « double-clic » ensuite dessus, et cela installe configuration ;
* on peut ensuite lancer le VPN via l'icone présente, puis choisir diverses options (connexion automatique, etc.).

### Client sous Android

On utilise l'application libre « OpenVPN for Android » disponible sur
[Google Play](https://play.google.com/store/apps/details?id=de.blinkt.openvpn)
et [F-Droid](https://f-droid.org/packages/de.blinkt.openvpn/) qui ne
nécessite pas de droits « root » avec Android 4 ou supérieur.

La configuration d'un VPN nécessite les informations suivantes :

* le certificat CA peut être importé sous format PEM ;
* le certificat client doit être au format PKCS#12 (fichier .p12 ou .pfx), on pourra ainsi le créer à partir de la clé/certificat :

~~~
$ openssl pkcs12 -export -in goyk3OkjeuPread8Sluld.privacy.example.com.crt -inkey goyk3OkjeuPread8Sluld.privacy.example.com.key -out goyk3OkjeuPread8Sluld.privacy.example.com.p12
~~~

* décocher « Compression LZO » si ce n'est pas supporté par votre serveur ;
* décocher « Vérification du certificat de l'hôte » (sinon cela ne fonctionne pas a priori…) ;
* forcer « Algorithme de chiffrement » à AES-128-CBC (à ajuster selon votre serveur).

Si erreur `ca md too weak` cela signifie que le certificat utilise du MD5. Pour forcer quand même, il faut mettre dans les options personnalisées :

~~~
tls-cipher "DEFAULT:@SECLEVEL=0"
~~~

### Client sous Apple iOS

Les systèmes iOS d'Apple disposent nativement du support de plusieurs types de VPN (IKEv2, IPsec ou L2TP), mais pas pour OpenVPN. On utilise alors l'application « OpenVPN Connect » (éditée par « OpenVPN Technologies ») qui va exploiter les API bas niveau de l'OS pour un support d'OpenVPN dans les interfaces du système.

Pour ajouter une configuration cliente, il suffit d'ouvrir le fichier `.ovpn`. Il est possible d'avoir plusieurs configurations disponibles.

Pour activer la connexion VPN il faut alors se rendre dans l'application « Réglages », puis « VPN » où sont listés les profils disponibles. On peut choisir celui à utiliser et activer la connexion. Si tout se passe bien, le symbole « VPN » apparaît dans la barre d'icônes tout en haut de l'écran.


## Erreurs fréquentes

### Erreur « --crl-verify fails » (serveur OpenVPN – OpenBSD)

~~~
Options error: --crl-verify fails with 'crl.pem': No such file or directory
Options error: Please correct these errors.
~~~

Il faut parfois regénérer un fichier CRL.

Si l'on utilise une version récente de [shellpki](https://forge.evolix.org/projects/shellpki) :

~~~
# cd /tmp
# shellpki.sh crl
# cp crl.pem /var/empty
~~~

### Erreur « CRL has expired » (serveur OpenVPN – OpenBSD)

~~~
VERIFY ERROR: depth=0, error=CRL has expired: C=FR, ST=FR, L=Versailles, O=foo.bar, CN=SoX, emailAddress=hostmaster@foo.bar
~~~

Il est possible que la CRL ne soit plus valide, et qu'il faille la regénérer comme ci-dessus.
Dans le cas de firewalls redondés, si une bascule sur le backup a récemment eu lieu, le fichier n'est sans doute pas synchronisé et expiré.

### Erreur « createipforwardentry » (client OpenVPN – Windows)

En cas d'erreur `ROUTE: route addition failed using createipforwardentry`, l'utilisateur n'a pas les droits suffisants pour ajouter une nouvelle route.
Il faut essayer d'exécuter les logiciels (`openvpn.exe` et `openvpngui.exe`) dans un mode de compatibilité _lancer en Administrateur_. Pour plus de détails, voir <http://www.bolehvpn.net/forum/index.php?topic=1746.0>.

### Erreur « no more TUN/TAP adapter » (client OpenVPN – Windows)

En cas d'erreur `no more TUN/TAP adapter`, aller dans « Menu démarrer » → « OpenVPN » → « Add new TUN/TAP Adapter ».

### Erreur « cipher final failed » (client OpenVPN – Android)

L'erreur `Authentificate/Decrypt packet error: cipher final failed` signifie que l'algorithme de chiffrement n'est pas synchronisé entre le client et le serveur.
Il faut donc ajuster la directive « cipher » sur le client.

### Erreur « Bad LZO decompression header byte » (client OpenVPN – Android)

L'erreur `Bad LZO decompression header byte` signifie que la compression LZO n'est pas activée sur le serveur, il faut donc désactiver la compression au niveau du client.

## FAQ

### Recevoir par mail un récapitulatif des prochaines expirations des certificats OpenVPN

Rajouter un cron sur le serveur OpenVPN :

~~~
# Récapitulatif expirations PKI OpenVPN
30      5       1       *       *   { echo -e Ci-dessous les prochaines expirations des certificats OpenVPN du serveur, par ordre d\'expiration : \\n ; grep "Not After" -r /etc/shellpki/certs/ | sed -e "s/^.*certs\/\([-.a-z0-9]*\).*After\ :\ \(.*\).*GMT$/\2\1/" | sed -e "s/^\(.*\)\ \(20..\)\ \(.*\)$/- \2 \1 \3/" | tr "X" "\n"  | sed '/^$/d' | sort -n -k 2 -k 3M -k 4 ; } | mail -E -s "PKI VPN XXX : recapitulatif expirations" contact@example.com
~~~

Penser à remplacer :

* L'horaire d'exécution du cron, si voulu ;
* L'emplacement des certificats (/etc/shellpki/certs/), ceux-ci peuvent se trouver dans "/etc/openvpn/ssl/certs/" ou ailleurs encore ;
* Le nom du surveur XXX dans le sujet "PKI VPN XXX" ;
* L'adresse mail de contact.

### Permettre aux clients de conserver la même IP au fil de connexions

#### ipp.txt

Le fichier `ipp.txt` contient une liste des clients et de leur adresse IP afin qu'en cas de redémarrage du serveur,
ils conservent la même adresse.

Dans le `server.conf` ou `server.ovpn` :

~~~
ifconfig-pool-persist /etc/openvpn/ipp.txt 0
~~~

Cela rend le fichier `ipp.txt` en lecture seule pour OpenVPN.
Il faudra donc ajouter une nouvelle ligne de la forme `CN,IP` à chaque ajout d'un nouveau client.

#### /etc/openvpn/ccd

Une autre méthode est d'ajouter la directive suivante pour le serveur OpenVPN :

~~~
client-config-dir /etc/openvpn/ccd
username-as-common-name
~~~

Le répertoire `/etc/openvpn/ccd/` contient des fichiers avec les _Common Name_ et contenant les adresses IP fixés ainsi :

~~~
ifconfig-push 172.16.1.1 172.16.1.2
~~~

Les IPs que l'on peut mettre doivent être prises selon des paires spécifiques, que l'on peut voir sur [la documentation OpenPVN](https://openvpn.net/community-resources/how-to/#configuring-client-specific-rules-and-access-policies) :

~~~
[  1,  2] [  5,  6] [  9, 10] [ 13, 14] [ 17, 18]
[ 21, 22] [ 25, 26] [ 29, 30] [ 33, 34] [ 37, 38]
[ 41, 42] [ 45, 46] [ 49, 50] [ 53, 54] [ 57, 58]
[ 61, 62] [ 65, 66] [ 69, 70] [ 73, 74] [ 77, 78]
[ 81, 82] [ 85, 86] [ 89, 90] [ 93, 94] [ 97, 98]
[101,102] [105,106] [109,110] [113,114] [117,118]
[121,122] [125,126] [129,130] [133,134] [137,138]
[141,142] [145,146] [149,150] [153,154] [157,158]
[161,162] [165,166] [169,170] [173,174] [177,178]
[181,182] [185,186] [189,190] [193,194] [197,198]
[201,202] [205,206] [209,210] [213,214] [217,218]
[221,222] [225,226] [229,230] [233,234] [237,238]
[241,242] [245,246] [249,250] [253,254]
~~~

### Authentification via Radius

Il faut installer le plugin :

~~~
# apt install openvpn-auth-radius
# cp /usr/share/doc/openvpn-auth-radius/examples/radiusplugin.cnf /etc/openvpn/
# openvpn --genkey --secret /etc/openvpn/ta.key
~~~

On ajoute dans la configuration du serveur :

~~~
client-cert-not-required
username-as-common-name
tls-auth ta.key 0
topology subnet
plugin /usr/lib/openvpn/radiusplugin.so /etc/openvpn/radiusplugin.cnf
status /var/log/openvpn/status.log 1
~~~

et on modifie le paramètre _sharedsecret_ du fichier `/etc/openvpn/radiusplugin.cnf` pour se connecter au serveur Radius.

Puis dans la configuration du client :

~~~
auth-user-pass
#auth-user-pass /etc/openvpn/passwd
auth-nocache
~~~

### comp-lzo ou compress ?

<https://community.openvpn.net/openvpn/wiki/DeprecatedOptions#a--comp-lzo>

À partir d'OpenVPN 2.4, _comp-lzo_ est déprécié et doit être remplacé par l'option _compress_ pour activer la compression (tant au niveau serveur que client).

Ne préciser que _compress_ n'active cependant aucun algorithme de compression. Il faut préciser l'algorithme, soit _lz4_, soit _lzo_. _compress lz4_ est plus performant, mais _compress lzo_ est rétro-compatible avec l'option _comp-lzo_ et est donc préférable dans le cas où des clients ont une version inférieure à 2.4.