Table des matières

OpenVPN - Installation d'un serveur VPN

CréationNicolas THOREZ 2022/08/03 08:44

Un VPN (Virtual Private Network) est, comme son nom l'indique, un réseau privé virtuel. Il s'agit d'une connexion entre 2 points distants (têtes de pont) créant un liaison (tunnel ou pont) et permettant à des ordinateurs distant de partager des informations et des services comme s'ils étaient sur le même réseau privé. Le tunnel étant généralement chiffré et son accès étant assujetti à une authentification spécifique (AD, LDAP, certificat, PAM, etc…), le VPN permet de sécuriser l'accès à des services n'ayant pas d'autres systèmes de sécurisation ou de donner un accès distant à des services n'ayant pas de moyen de publication sécurisée.

Niveau VPN, on trouve 2 types de tunnels :

OpenVPN existe en tant que serveur et en tant que client et utilise le protocole SSL.

Droits

Attention, cette procédure nécessite des droits root. La plus grande prudence est donc requise.

Installation

Pare-feu

Cette procédure nécessite un pare-feu fonctionnel. Je me baserai donc sur IPTables (Procédure d'installation trouvable ici).

sed -i 's/#net.ipv4.ip_forward=1/net.ipv4.ip_forward=1/' /etc/sysctl.conf
sysctl -p

apt update && apt install -y openvpn easy-rsa

Création de l'autorité de certification

cd /usr/share/easy-rsa/
./easyrsa init-pki --keysize=4096 --digest=sha512

./easyrsa --keysize=4096 --digest=sha512 build-ca nopass

./easyrsa --keysize=4096 --digest=sha512 build-server-full server nopass

./easyrsa --keysize=4096 --digest=sha512 gen-dh

openvpn --genkey secret ./pki/ta.key

ln -sf /usr/share/easy-rsa /etc/openvpn/easy-rsa
ln -sf /usr/share/easy-rsa/easyrsa /bin/easyrsa

Configuration du serveur

cp /usr/share/doc/openvpn/examples/sample-config-files/server.conf /etc/openvpn/

mkdir /etc/openvpn/ccd

Déploiement des routes communes

Si le VPN doit déployer des routes communes à tous les utilisateurs, il est préférable de les ajouter dans la configuration du serveur en ajoutant pour chaque route une entrée de type :

push "route address mask"

Exemple :

push "route 8.8.8.8 255.255.255.255"

IPTables

Pour chaque route ajoutée de cette manière, il ne faudra pas oublier d'ajouter les règles de pare-feu correspondantes (voir plus bas).

Déploiement du DNS

Si chaque utilisateur du VPN doivent utiliser un serveur DNS spécifique, on peut là aussi l'ajouter directement dans la configuration serveur avec une entrée de type (on remplace address par l'adresse IP du serveur DNS à utiliser) :

push "dhcp-option DNS address"

De plus, si ce serveur DNS doit résoudre des domaines privés, il est nécessaire d'en informer les clients en ajoutant dans la configuration serveur la ligne suivantes :

push "dhcp-option DOMAIN ~."

Pare-feu

*filter

# Allow OpenVPN connections
-A INPUT -p udp --dport 1194 -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT

COMMIT

#================#
# Partie serveur #
#================#

*nat

# Source-NAT pour toutes les connexions entrantes vers OpenVPN
-A POSTROUTING -o ens192 -s 172.16.0.0/24 -j SNAT --to-source 10.0.0.1

COMMIT

#==========================================#
# Partie clients pour chaque route commune #
#==========================================#

*filter

# Autorisation à créer selon chaque route commune sous la forme :
# -I FORWARD -i tun0 -o ens192 -s 172.16.0.0/24 -d 10.1.1.1 -j ACCEPT -m comment --comment "DNAT vers serv1"
# -I FORWARD -i ens192 -o tun0 -s 10.1.1.1 -d 172.16.0.0/24 -j ACCEPT -m comment --comment "SNAT depuis serv1"

#========================================================================================#
# Finalisation (doit toujours se trouver à la fin, après les règles de tous les clients) #
#========================================================================================#

# On drop tous les flux qui ne sont pas explicitement autorisés
-A FORWARD -s 172.16.0.0/24 -d 10.0.0.0/8 -j DROP

COMMIT

service iptables restart

Démarrage du service

service openvpn@server start

Options supplémentaires

Si vous avez ajouté les options pour l'interface de gestion et le 2FA, le services ne démarrera que lorsque ces modules seront installés.

Ajout d'une interface de gestion

Interface

L'interface OVPN-ADMIN est un projet open source sous licence Apache 2.0.

Source : GitHub

Script

Les scripts de connexions et de déconnexions sont inspirés des travaux de juris.

Source : GitHub

Pour ajouter l'interface de gestion, il est nécessaire d'ajouter les paramètres nécessaires dans la configuration serveur (voir au dessus). Ensuite :

cd /tmp
wget https://github.com/flant/ovpn-admin/releases/download/2.0.1/ovpn-admin-linux-amd64.tar.gz
tar -xvf ovpn-admin-linux-amd64.tar.gz
mv ovpn-admin /etc/openvpn/

{{- if (ne .ClientAddress "dynamic") }}
ifconfig-push {{ .ClientAddress }} 255.255.255.0
{{- end }}
{{- range $route := .CustomRoutes }}
push "route {{ $route.Address }} {{ $route.Mask }}" # {{ $route.Description }}
{{- end }}

# Configuration automatique via ovpn-admin
 
client
dev tun
dev-node ClientVPN
{{- range $server := .Hosts }}
proto {{ $server.Protocol }}
remote {{ $server.Host }} {{ $server.Port }}
{{- end }}
nobind
persist-key
persist-tun
comp-lzo
verb 4
# uncomment below lines for use with Google 2FA
#auth-user-pass
cipher AES-256-GCM
key-direction 1
tls-client
remote-cert-tls server
auth-nocache
explicit-exit-notify 1
reneg-sec 86400

# uncomment below lines for use with linux
#script-security 2
# if you use resolved
#up /etc/openvpn/update-resolv-conf
#down /etc/openvpn/update-resolv-conf
# if you use systemd-resolved first install openvpn-systemd-resolved package
#up /etc/openvpn/update-systemd-resolved
#down /etc/openvpn/update-systemd-resolved

<cert>
{{ .Cert -}}
</cert>
<key>
{{ .Key -}}
</key>
<ca>
{{ .CA -}}
</ca>
<tls-auth>
{{ .TLS -}}
</tls-auth>

#!/bin/bash
 
# Variables
 
OVPN_LISTEN_HOST=127.0.0.1                              # Adresse de l'hôte
OVPN_LISTEN_PORT=8088                                   # Port de l'hôte
OVPN_NETWORK=172.16.0.0/24                              # Plage réseau des clients OpenVPN
OVPN_SERVER=1.2.3.4:1194:udp                            # Adresse, port et protocole d'écoute pour OpenVPN
OVPN_MGMT=127.0.0.1:8989                                # Adresse et port de l'interface de gestion OpenVPN (pour les appels API)
OVPN_METRICS_PATH="/metrics"                            # URL pour les métriques (pour Grafana)
EASYRSA_PATH="/etc/openvpn/easyrsa"                     # Racine pour easy-RSA
OVPN_INDEX_PATH="/etc/openvpn/easyrsa/pki/index.txt"    # Chemin de la base de données pour les certificats gérés
OVPN_CCD_PATH="/etc/openvpn/ccd"                        # Stockage des configurations clientes
OVPN_TEMPLATES_CC_PATH="/etc/openvpn/client.conf.tpl"   # Chemin du modèle de configuration cliente
OVPN_TEMPLATES_CCD_PATH="/etc/openvpn/ccd.tpl"          # Chemin du modèle de routage client
LOG_LEVEL="debug"                                       # Niveau de log
 
 
/etc/openvpn/ovpn-admin \
        --listen.host=$OVPN_LISTEN_HOST \
        --listen.port=$OVPN_LISTEN_PORT \
        --log.level=$LOG_LEVEL \
        --easyrsa.path=$EASYRSA_PATH \
        --easyrsa.index-path=$OVPN_INDEX_PATH \
        --ovpn.network=$OVPN_NETWORK \
        --ovpn.server=$OVPN_SERVER \
        --metrics.path=$OVPN_METRICS_PATH \
        --ccd \
        --ccd.path=$OVPN_CCD_PATH \
        --templates.ccd-path=$OVPN_TEMPLATES_CCD_PATH \
        --templates.clientconfig-path=$OVPN_TEMPLATES_CC_PATH &

chmod +x /etc/openvpn/launch_ovpn_admin.sh

echo "# Daemon de gestion des clients OpenVPN" > /etc/cron.d/ovpn-admin
echo '*/10 * * * *       root [ "$(pidof -x ovpn-admin)" ] || /etc/openvpn/launch_ovpn_admin.sh &' >> /etc/cron.d/ovpn-admin

#!/bin/bash
 
#########################################################################
#                                                                       #
#   Script de création des règles IPTables à la connexion d'un client   #
#                                                                       #
#########################################################################
 
#-----------#
# Variables #
#-----------#
 
# Chemin des configurations clientes
CCD_DIR="/etc/openvpn/ccd"
 
# Interface de sortie du serveur
IF_OVPN="ens160"
 
#-----------------------------------------------#
# Memento des variables automatiques de OpenVPN #
#-----------------------------------------------#
 
#   $common_name                          Identifiant du client (fichier de configuration, certificat, etc...)
#   $ifconfig_pool_remote_ip              Adresse IP affecté au client par OpenVPN
#   $dev                                  Interface réseau du tunnel OpenVPN
 
#------------#
# Traitement #
#------------#
 
# Traitement si il y a une configuration cliente
if [ -f $CCD_DIR/$common_name ]; then
 
        # Configuration existante, on extrait les routes à établir
        grep "^push \"route" $CCD_DIR/$common_name | tr -d '"' | \
 
        # Boucle de création des règles
        while read LINE; do
 
                # Extraction de l'adresse
                ADDRESS=$(echo $LINE | cut -f3 -d" ")
 
                # Extraction du masque
                NETMASK=$(echo $LINE | cut -f4 -d" ")
 
                # Mise en forme des commentaires
                COMMENT=$(echo $LINE | cut -d"#" -f2)
                DNAT_COMMENT="$common_name DNAT vers $COMMENT"
                SNAT_COMMENT="$common_name SNAT vers $COMMENT"
 
                # Ajout de la règle de DNAT
                sudo /sbin/iptables -I FORWARD -i $dev -o $IF_OVPN -s $ifconfig_pool_remote_ip/32 -d $ADDRESS/$NETMASK -j ACCEPT -m comment --comment "$DNAT_COMMENT"
 
                # Ajout de la règle de SNAT
                sudo /sbin/iptables -I FORWARD -i $IF_OVPN -o $dev -s $ADDRESS/$NETMASK -d $ifconfig_pool_remote_ip/32 -j ACCEPT -m comment --comment "$SNAT_COMMENT"
        done
fi
 
exit 0

#!/bin/bash
 
############################################################################
#                                                                          #
#   Script de suppression des règles IPTables à la déconnexion du client   #
#                                                                          #
############################################################################
 
#-----------------------------------------------#
# Memento des variables automatiques de OpenVPN #
#-----------------------------------------------#
 
#   $common_name                          Identifiant du client (fichier de configuration, certificat, etc...)
#   $ifconfig_pool_remote_ip              Adresse IP affecté au client par OpenVPN
#   $dev                                  Interface réseau du tunnel OpenVPN
 
#------------#
# Traitement #
#------------#
 
# On vérifie la présence de règles pour le client
MAX=$(sudo /sbin/iptables -nL FORWARD --line-numbers | grep -E "[[:blank:]]$common_name[[:blank:]]" | wc -l)
if [[ $MAX -gt 0 ]]; then
 
        # On supprime toutes les règles de FORWARD contenant l'identifiant de l'utilisateur dans les commentaires
        for ((i=1;i<=$MAX;i++)); do
 
                # On récupère le numéro de la règle à supprimer
                RULE=$(sudo /sbin/iptables -nL FORWARD --line-numbers | grep -m 1 -E "[[:blank:]]$common_name[[:blank:]]" | awk '{print $1}')
 
                # On supprimme la règle
                sudo /sbin/iptables -D FORWARD $RULE
        done
fi
 
exit 0

chmod +x /etc/openvpn/ovpn_connect.sh
chmod +x /etc/openvpn/ovpn_disconnect.sh

L'interface est désormais disponible mais elle est accessible par n'importe qui. On va donc installer un proxy Apache devant pour ajouter une authentification de base. Du coup :

 apt install -y apache2

<VirtualHost *:443>
        ServerAdmin mon.adresse@mail.tld
 
        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined
 
        ProxyPass               / http://127.0.0.1:8088/
        ProxyPassReverse        / http://127.0.0.1:8088/
        ProxyRequests           Off
        <Location />
                AuthType Basic
                AuthName "Restricted Area"
                AuthUserFile "/etc/apache2/password.file"
                Require valid-user
        </Location>
        
        SSLEngine On
        SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem
        SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key
</VirtualHost>

htpasswd -c /etc/apache2/password.file ovpn-admin

a2enmod ssl
a2enmod proxy
a2enmod proxy_http

a2ensite ovpn-admin.conf

service apache2 restart

L'interface est maintenant disponible après authentification à l'adresse https://adresse_du_serveur :

Ajout de la double authentification Google

Pour activer le 2FA de Google, il faut au préalable ajouter le plugin dans la configuration serveur (voir plus haut). Ensuite :

apt install -y libqrencode4 libpam-google-authenticator

#!/bin/bash
 
# Variables
SCRIPT_PATH="/etc/openvpn"
CCD="$SCRIPT_PATH/ccd"
 
# Usage
get-help() {
        echo "Usage : ./create-2fa.sh user"
        echo "     user doit être un nom d'utilisateur tel qu'il apparaît dans les configurations clientes de OpenVPN, dans le dossier ccd."
        exit 1
}
 
# Test présence utilisateur
if [ -z "$1" ]; then
        get-help
elif [ ! -f $CCD/$1 ]; then
        get-help
fi
 
# Utilisateur existant, création du dossier
useradd $1 -m -s /bin/bash
 
# Création du google authenticator
su -c "google-authenticator -t -f -d -r 3 -R 30 -w 17 --label=\"OpenVPN ${1}\"" $1
 
# Mise à jour des droits
chmod 400 /home/${1}/.google_authenticator
chmod 700 /home/${1}

chmod +x /etc/openvpn/create-2fa.sh

sed -i 's/ProtectHome=true/ProtectHome=false/g' /lib/systemd/system/openvpn@.service
systemctl daemon-reload

cp /etc/pam.d/common-account /etc/pam.d/openvpn

auth required pam_google_authenticator.so authtok_prompt=pin

service openvpn@server restart

Pour créer un 2FA pour un utilisateur, il suffit de lancer le script /etc/openvpn/create-2fa.sh useruser est le nom de l'utilisateur tel qu'il apparaît dans le dossier /etc/openvpn/ccd/ et/ou dans l'interface d'administration ovpn-admin. Le script crée le compte utilisateur local affiche un QRcode qu'il faudra scanner depuis l'application mobile Google Authenticator. Il demande alors un code de validation pour terminer la création.

Pour la suite, lorsque l'utilisateur se connecte au VPN, il lui sera demander un code fourni par l'application mobile pour pouvoir se connecter.

Clients sans l'interface de gestion

Unicité

Cette procédure devra être suivie pour chaque client devant se connecter au serveur. Chaque configuration sera donc unique.

cd /usr/share/easy-rsa
./easyrsa --keysize=4096 --digest=sha512 build-client-full client nopass

client
dev tun
dev-node ClientVPN
proto udp
remote 1.2.3.4 1194
nobind
persist-key
persist-tun
comp-lzo
verb 4
# uncomment below lines for use with Google 2FA
#auth-user-pass
cipher AES-256-GCM
key-direction 1
tls-client
remote-cert-tls server
auth-nocache
explicit-exit-notify 1
reneg-sec 86400

# keys
ca chemin_vers_le_fichier_ca.crt
cert chemin_vers_le_fichier_client.crt
key chemin_vers_le_fichier_client.key
tls-auth chemin_vers_le_fichier_ta.key 1

client
dev tun
proto udp
remote 1.2.3.4 1194
nobind
persist-key
persist-tun
comp-lzo
verb 4
# uncomment below lines for use with Google 2FA
#auth-user-pass
data-cipher AES-256-GCM
key-direction 1
tls-client
remote-cert-tls server
auth-nocache
explicit-exit-notify 1
reneg-sec 86400
# uncomment below lines for use with linux
script-security 2
# if you use resolved
#up /etc/openvpn/update-resolv-conf
#down /etc/openvpn/update-resolv-conf
# if you use systemd-resolved first install openvpn-systemd-resolved package
#up /etc/openvpn/update-systemd-resolved
#down /etc/openvpn/update-systemd-resolved

# keys
ca chemin_vers_le_fichier_ca.crt
cert chemin_vers_le_fichier_client.crt
key chemin_vers_le_fichier_client.key
tls-auth chemin_vers_le_fichier_ta.key 1

nano /etc/openvpn/ccd/client

# Configuration pour le client "client"
ifconfig-push 172.16.0.5 176.16.0.6
push-reset
push "dhcp-option DNS 10.0.0.1"
push "dhcp-option DOMAIN ~."
push "route 10.0.1.1 255.255.255.255"
push "route 10.0.1.2 225.255.255.255"

...
# Autorisation pour client
-I FORWARD -i tun0 -o ens192 -s 172.16.0.5 -d 10.0.1.1 -p tcp --dport 80 -j ACCEPT
-I FORWARD -i tun0 -o ens192 -s 172.16.0.5 -d 10.0.1.1 -p tcp --dport 443 -j ACCEPT
-I FORWARD -i ens192 -o tun0 -s 10.0.1.1 -d 172.16.0.49 -j ACCEPT
-I FORWARD -i tun0 -o ens192 -s 172.16.0.5 -d 10.0.1.2 -p tcp --dport 22 -j ACCEPT
-I FORWARD -i ens192 -o tun0 -s 10.0.1.2 -d 172.16.0.49 -j ACCEPT
...

service iptables restart

Fini

Et voilà ! Vous disposez d'un serveur OpenVPN et d'un configuration cliente permettant de s'y connecter.

Clients avec l'interface de gestion