Créer un conteneur Docker serveur OpenVPN

On installe OpenVPN et Easy-RSA qui nous permettront de générer les certificats :

sudo apt-get install openvpn easy-rsa -y

On crée un dossier pour le projet et on s'y rend :

mkdir openvpn-server && cd openvpn-server

On crée un dossier avec Easy-RSA qu'on appelle easy-rsa et on s'y rend :

make-cadir easy-rsa && cd easy-rsa

On édite le fichier vars afin de configurer Easy-RSA de manière à accroître la sécurité :

./easyrsa init-pki

On crée une nouvelle autorité de certification (CA) qui est utilisée pour signer et valider les certificats serveur et clients de notre serveur VPN :

./easyrsa build-ca nopass

On génère une demande de signature de certificat (CSR) pour le serveur VPN, identifié par un nom qu'on donne à ce dernier, sans utiliser de mot de passe :

./easyrsa gen-req <NOM DU SERVEUR> nopass

On signe la CSR avec la CA, créant ainsi un certificat de serveur valide :

./easyrsa sign-req server <NOM DU SERVEUR>

On génère des paramètres Diffie-Hellman, utilisés pour sécuriser les échanges de clés entre les clients et le serveur VPN :

./easyrsa gen-dh

On génère une clé partagée secrète pour la fonction de sécurité supplémentaire TLS-Auth d'OpenVPN, qui ajoute une signature aux paquets de données pour vérifier leur source :

openvpn --genkey secret pki/ta.key

On crée une CSR pour un client VPN en déterminant un nom d'identification du client :

./easyrsa gen-req <NOM DU CLIENT> nopass

On signe la CSR avec la CA en tant que client, ceci indique que le certificat est destiné à être utilisé par un client VPN et permet de l'authentifier auprès du serveur VPN permettant ainsi des connexions sécurisées :

./easyrsa sign-req client <NOM DU CLIENT>

On retourne dans le dossier du projet :

cd ../

On crée le fichier de configuration client .opvn :

nano client.ovpn
client
dev tun
proto udp
remote <ADRESSE IP PUBLIQUE> 1194
resolv-retry infinite
nobind
persist-tun
persist-key
cipher AES-256-GCM
auth SHA256
remote-cert-tls server
<tls-auth>
# Certificat contenu dans le fichier ~/openvpn-server/easy-rsa/pki/ta.key
</tls-auth>
key-direction 1
<ca>
# Certificat contenu dans le fichier ~/openvpn-server/easy-rsa/pki/ca.crt
</ca>
<key>
# Certificat contenu dans le fichier ~/openvpn-server/easy-rsa/pki/private/<NOM DU CLIENT>.key
</key>
<cert>
# Certificat contenu dans le fichier ~/openvpn-server/easy-rsa/pki/issued/<NOM DU CLIENT>.crt
</cert>
verb 3

On récupère le fichier de configuration client en se connectant à notre Raspberry par SFTP.

On crée le fichier de configuration du serveur VPN :

nano server.conf
local [ADRESSE IP STATIQUE]
port 1194
proto udp
dev tun
ca /etc/openvpn/pki/ca.crt
cert /etc/openvpn/pki/issued/<NOM DU SERVEUR>.crt
key /etc/openvpn/pki/private/<NOM DU SERVEUR>.key
dh /etc/openvpn/pki/dh.pem
tls-auth /etc/openvpn/pki/ta.key 0
server <ADRESSE IP RÉSEAU POUR LE SERVEUR VPN> <MASQUE DE SOUS-RÉSEAU POUR LE SERVEUR VPN>
push "redirect-gateway def1"
push "route <ADRESSE IP DU RÉSEAU> <MASQUE DE SOUS-RÉSEAU>"
push "dhcp-option DNS <SERVEUR DNS PRIMAIRE DU SERVICE VPN>"
push "dhcp-option DNS <SERVEUR DNS SECONDAIRE DU SERVICE VPN>"
keepalive 10 120
cipher AES-256-GCM
auth SHA256
user nobody
group nogroup
persist-key
persist-tun
verb 3
explicit-exit-notify 1
duplicate-cn

On crée un script shell qui s'exécutera au lancement de notre serveur VPN :

nano up.sh
#!/bin/bash
ip rule add fwmark 1 table 100
ip route add default via <ADRESSE IP STATIQUE DU CONTENEUR OPENVPN-CLIENT> dev eth0 table 100
iptables -t mangle -A PREROUTING -i tun0 -j MARK --set-mark 1
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
iptables -A FORWARD -i tun0 -o eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A FORWARD -i eth0 -o tun0 -j ACCEPT

La première commande crée une règle de routage qui affecte les paquets marqués avec un identifiant spécifique (fwmark 1) à utiliser une table de routage spécifique (table 100). La deuxième commande définit une route par défaut dans cette table de routage, indiquant que tout le trafic qui y est dirigé doit passer par l'interface eth0 et être envoyé à la passerelle 192.168.1.2. La troisième commande utilise iptables pour marquer tous les paquets UDP entrants qui visent le port 1194 (qui est le port d'écoute de notre serveur VPN) avec ce même identifiant (--set-mark 1), les faisant ainsi passer par la table de routage spéciale que nous avons définie. La quatrième commande configure NAT sur l'interface eth0, permettant à plusieurs appareils sur un réseau local d'accéder à l'Internet en partageant une adresse IP publique. Les deux dernières règles d'iptables permettent le trafic entre l'interface VPN (tun0) et l'interface réseau locale (eth0), en autorisant seulement le trafic déjà établi ou en réponse à des connexions initiées de l'intérieur vers l'extérieur (RELATED,ESTABLISHED) et en acceptant tout trafic entrant de eth0 vers tun0.

On crée un script shell qui s'exécutera à l'interruption de notre serveur VPN :

nano down.sh
#!/bin/bash
iptables -D FORWARD -i eth0 -o tun0 -j ACCEPT
iptables -D FORWARD -i tun0 -o eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
iptables -t mangle -D PREROUTING -i tun0 -j MARK --set-mark 1
ip route del default via <ADRESSE IP STATIQUE DU CONTENEUR OPENVPN-CLIENT> dev eth0 table 100
ip rule del fwmark 1 table 100

On supprime les règles crées lors du lancement de notre VPN.

On crée un script shell qui s'exécutera à l'exécution du conteneur :

nano entrypoint.sh
#!/bin/bash
openvpn --config "/etc/openvpn/server.conf" --script-security 2 --up "/up.sh" --down "/down.sh"

On crée le Dockerfile qui contient les instructions afin de créer une image de conteneur :

nano Dockerfile
FROM debian:latest

RUN apt-get update && apt-get install -y net-tools iproute2 iptables openvpn curl iputils-ping

COPY ./easy-rsa/pki/ /etc/openvpn/pki/
RUN chmod -R 700 /etc/openvpn/pki

COPY server.conf /etc/openvpn/

COPY up.sh /up.sh
RUN chmod +x /up.sh

COPY down.sh /down.sh
RUN chmod +x /down.sh

COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

ENTRYPOINT ["/entrypoint.sh"]

On construit l'image :

docker build -t openvpn-server .

On lance un conteneur à partir de l'image créée :

docker run -d --name openvpn-server --network macvlan-network --ip <ADRESSE IP STATIQUE> --device=/dev/net/tun -v /etc/localtime:/etc/localtime:ro -v ~/openvpn-server/easy-rsa/pki:/etc/openvpn/pki --cap-add=NET_ADMIN --cap-add=NET_RAW --sysctl net.ipv4.ip_forward=1 --sysctl net.ipv6.conf.all.disable_ipv6=1 --sysctl net.ipv6.conf.default.disable_ipv6=1 --sysctl net.ipv6.conf.lo.disable_ipv6=1 --restart unless-stopped openvpn-server

On vérifie que notre conteneur soit en cours d'exécution :

docker ps

On affiche les journaux (ou logs) générés par le conteneur, particulièrement utile pour le débogage, permettant de comprendre le comportement du conteneur, de suivre son processus d'exécution ou d'identifier des problèmes et des erreurs potentielles :

docker logs openvpn-server

Commandes utiles

Arrêter le conteneur :

docker stop openvpn-server

Supprimer le conteneur :

docker rm openvpn-server

Redémarrer le conteneur :

docker restart openvpn-server

Dernière mise à jour