Si l'accès de l'extérieur est nécessaire sur plusieurs machines et plusieurs protocoles, ouvrir sur la box chaque port pour chaque service est la méthode la plus basique. Cela peut vite devenir compliqué à gérer et à mémoriser, et c'est en plus une très mauvaise pratique. Il existe de nombreuses solutions à préférer, VPN en tête.

Un abonnement comme NordVPN et consorts ne sera ici d'aucune utilité. Même si le principe est identique, le but est différent : on ne veut pas anonymiser sa connexion — plus rigoureusement, changer son IP ― pour voir la F1 en Belgique, mais chiffrer la connexion établie entre l'extérieur et le réseau local interne de l'entreprise pour accéder à ses machines.

Schéma d'une connexion VPN
Schéma d'une connexion VPN. Source : std.rocks


Certaines box pro en intègrent, si c'est le cas il faut en profiter : gain de temps, d'argent et de consommation électrique évident ! Sinon, il faut se rabattre sur une installation VPN derrière la box, qu'on va rendre accessible de l'extérieur.

Pour un maximum de sécurité, on peut l'installer en appliance — un vieux PC portable fera l'affaire, il faut néanmoins qu'il soit stable et connecté au réseau local. A défaut, c'est réalisable sur une VM. Cela permet de réduire les coût à zéro mais le VPN ne sera plus fonctionnel si la machine physique tombe en panne ; c'est à garder à l'esprit.

Wireguard est actuellement parmi les VPN les plus sûrs. Il est très léger, peu gourmand en ressource CPU et RAM, et très stable. Son fonctionnement est extrêmement simple à comprendre, toutefois la mise en place des fichiers de configuration prête à confusion.

Logo Wireguard
Le beau logo de Wireguard. Source : wireguard.com


Il existe PiVPN, un script pré-mâché et bien fait, qui permet de mettre en place le serveur très rapidement, du moment qu'une distribution basée sur Debian est installée. Si on a les bases de connaissances sur Wireguard, PiVPN peut même s'installer juste en suivant les directives. Sinon, des tutos sont disponibles sur le net.

Je préfère présenter ici la méthode manuelle. Cela permet de tout comprendre, et de mieux déceler les pannes et les erreurs ultérieures.

Merci à Petitchat du forum Debian et à Florian Burnel de IT-Connect qui ont inspiré grandement le setup ci-dessous.

Prérequis

  • Connaître les commandes basiques en bash, savoir utiliser un éditeur de texte en CLI (Nano, vim, Emacs, etc.), savoir naviguer dans l'arborescence Linux en CLI
  • Distribution Debian mère ou fille (Ubuntu, LMDE, etc) sans environnement de bureau
  • IP fixe, ou faire une réservation DHCP
  • Si machine virtuelle, un hyperviseur (Hyper-V, VMware, Virtualbox, etc.)
  • Un serveur ssh
  • Un client ssh, openssh est disponible sur Windows 10/11 pro nativement, sinon PuTTY
  • Avoir un compte sudo ou un accès root

Installation de Wireguard

apt update && apt install wireguard --assume-yes
On se déplace dans le dossier Wireguard :
cd /etc/wireguard
On crée un dossier serveur et un dossier utilisateurs :
mkdir server ; mkdir users
Génération des clés avec droits restreints :
umask 077
wg genkey > wg-private.key
wg pubkey < wg-private.key > wg-public.key

Pare-feu

Activation de la redirection d'IP :
echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
Ou décommenter la ligne correspondante dans /etc/sysctl.conf.
Puis appliquer les changements :
sysctl -p

Installation de ufw, si pas déjà fait :
apt install ufw
ufw default deny incoming
ufw default allow outgoing

🚨 Indispensable pour ne pas être "enfermé dehors" (remplacer ssh par <PORT_SSH>/tcp si différent du port 22) :
ufw allow ssh

Autoriser l'entrée de Wireguard, 51820 étant le port par défaut, UDP le protocole qu'il utilise :
ufw allow 51820/udp
Exécuter ufw et l'activer au démarrage :
ufw enable

Vérifier le nom de son interface réseau, si ce n'est pas eth0, modifier le nom dans le fichier édité à l'étape d'après :
ip -o -4 route show to default | awk '{print $5}'

Edition de /etc/ufw/rules.before, à coller à la fin du fichier :

# Activer l'IP masquerading au niveau du NAT
*nat
:POSTROUTING ACCEPT [0:0]
-A POSTROUTING -o eth0 -j MASQUERADE

# Bien laisser la ligne COMMIT
COMMIT

Toujours dans /etc/ufw/rules.before , on définit les plages autorisées pour la redirection. Mettre ces règles juste après # ok icmp code for FORWARD :

# Limiter la redirection uniquement de l'interface WireGuard
# vers le réseau local pour empêcher le full-tunneling
-A ufw-before-forward -i wg0 -d 192.168.1.0/24 -j ACCEPT

Personnaliser wg0 si l'interface est nommée autrement dans les étapes ci-dessous !

Redémarrer ufw :
ufw reload

N.B. : il n'est pas nécessaire de définir une règle DROP par défaut, car cela est géré par ufw en aval. Vérifier en tapant la commande ufw status verbose qu'on a bien en "deny (routed)" qui apparaît comme ci-dessous :

Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), deny (routed)
New profiles: skip

Interface côté serveur

Il faut ici imaginer que l'interface que l'on va créer est comme un serveur physique sur lequel on va connecter plusieurs ordinateurs clients (les utilisateurs), et que cet ensemble est relié au réseau local physique de la clinique, mais qu'il communique de façon restrictive.

Cette interface prend la forme d'un simple fichier. Il doit se terminer en *.conf, le préfixe est au choix et sera automatiquement utilisé par Wireguard comme nom d'interface. Il est conventionnellement nommé wg0.conf.
Il est conseillé de créer et laisser ce fichier dans /etc/wireguard pour pouvoir l'activer facilement via la commande wg-quick. Autrement, il faudra taper le chemin absolu.

Créer le fichier de configuration : touch /etc/wireguard/wg0.conf Ouvrir ce nouveau fichier avec son éditeur préféré et coller ce paramétrage :

[Interface]
Address = 10.0.0.1/24
PrivateKey = #coller ici le contenu de "/etc/wireguard/server/wg-private.key"
ListenPort = 51820

Ici, "Address" (oui, avec deux d en anglais) définit la plage d'adresse utilisée par l'interface wg0 (ou celle du nom préalablement personnalisé). On peut mettre la plage que l'on veut mais ce doit être une plage d'IP privée et ce ne doit pas être la même plage que le réseau local physique.

Activer l'interface :
wg-quick up wg0
Vérifier son état :
wg show wg0
L'arrêter :
wg-quick down wg0
On peut rendre l'exécution permanente au démarrage :
systemctl enable wg-quick@wg0.service
Contrôler l'état du service :
systemctl status wg-quick@wg0.service

Interface côté client

Pour simplifier, je reprends le script bash de Petitchat, gain de temps considérable !
Il faut bien placer ce script dans /etc/wireguard et ne pas le déplacer, sans quoi il ne marchera pas : il est défini en chemins relatifs.

On crée d'abord le fichier nextip dans le dossier /etc/wireguard/users qui sera utile pour l'exécution du script.
echo 2 > /etc/wireguard/users/nextip
Puis on crée le fichier qui va contenir le script :
touch wgadduser
On colle le script suivant :

#!/bin/bash

# wgadduser "utilisateur" ajoute un utilisateur à wireguard
declare -i digit ;
clear ;

#teste si le nom de l'utilisateur a été précisé
if [[ -z "$1" ]]; then
    echo "syntaxe : ./wgadduser nom_utilisateur " ;
    exit 0 ;
fi

#récupération de la clé publique du serveur
pbkeysrv=$(head ./server/wg-public.key) ;

#changement de dossier courant
cd users ;

#récupération de nextIP
digit=$(head nextip) ;

#création du dossier utilisateur
#faire commencer le nom du dossier par $digit permet de lister
#les dossiers dans  l ordre de création et d adresse ip
mkdir $digit.$1 ;
cd $digit.$1 ;

#génération et récupération de la clé privée
nmprkey=wg-"$1"-private.key ;
umask 077 ;
wg genkey > $nmprkey ;
prkey=$(head $nmprkey) ;

#génération et récupération de la clé pré-partagée
nmpskey=wg-"$1"-preshared.key ;
umask 077 ;
wg genpsk > $nmpskey ;
pskey=$(head $nmpskey) ;

#génération et récupération de la clé publique
nmpbkey=wg-"$1"-public.key ;
wg pubkey < $nmprkey > $nmpbkey ;
pbkey=$(head $nmpbkey) ;

#génération du fichier wg-utilisateur.conf
confile=wg-"$1".conf ;
echo "[Interface]" > $confile ;
echo "PrivateKey = "$prkey >> $confile ;
echo "Address = 10.0.0."$digit"/32" >> $confile ;
echo "DNS = 192.168.1.1, 192.168.1.2" >> $confile ;
echo "" >> $confile ;
echo "[Peer]" >> $confile ;
echo "PublicKey = "$pbkeysrv >> $confile ;
echo "PresharedKey = "$pskey >> $confile ;
echo "AllowedIPs = 192.168.1.0/24" >> $confile ;
echo "Endpoint = vpn.mondomaine.tld:51820" >> $confile ;
echo "PersistentKeepalive = 25" >> $confile ;

#affichage du fichier
cat $confile;

#choix de validation ou d'annulation
while true ; do
        read -p "Valider l'enregistrement ? y/n : " choix
        case $choix in
                [YyOo]* ) break ;;
                [Nn]* ) cd ..; rm -R $1; exit 0 ;;
                * ) echo "Répondre oui (o) ou non (n)" ;;
        esac
done
clear ;

#ajout de l'utilisateur à /etc/wireguard/wg0.conf
config="/etc/wireguard/wg0.conf" ;
echo "" >> $config ;
echo "[Peer]" >> $config ;
echo "#"$1 >> $config ;
echo "PublicKey = "$pbkey >> $config ;
echo "PresharedKey = "$pskey >> $config ;
echo "AllowedIPs = 10.0.0."$digit"/32" >> $config ;
echo "PersistentKeepalive = 25" >> $config ;

#incrémentation de nextip
digit=$((digit+1)) ;
cd .. ;
echo $digit > nextip ;

#proposition de redémarrage du service
echo "Utilisateur "$1" bien enregistré." ;
while true ; do
        read -p "Faut-il redémarrer l'interface pour prendre en compte les changements ? (O)ui ou (N)on : " choix1 ;
        case $choix1 in
                [YyOo]* ) break;;
                [Nn]* ) exit 0 ;;
                * ) echo "Répondre oui (o) ou non (n)" ;;
        esac
done
systemctl restart wg-quick@wg0.service ;
echo "service relancé, fin du script, merci !" ;
exit 0 ;

🚨 Il faut personnaliser les zones en gras et rouge :

  • Partie "#génération du fichier wg-utilisateur.conf" :
    • "Address = " : Celle définie dans wg0.conf. Il faut bien écrire la plage sous la forme "x.x.x.", sans remplir le dernier octet
    • "DNS = " : Optionnel, à défaut ce seront ceux définis sur l'interface eth0
    • "AllowedIPs = " : Les IP que l'on veut autoriser au client, mais c'est faible sur le plan sécurité (voir plus bas § Sécurité)
    • "Endpoint = " : L'IP ou le nom de domaine + le port sur lesquels le client doit pointer
  • Partie "#ajout de l'utilisateur à /etc/wireguard/wg0.conf" :
    • "AllowedIPs =" Remplir comme "Address" de la partie ci-dessus, ne pas toucher le reste !

Le rendre exécutable :
chmod +x wgadduser
Le lancer :
./wgadduser <nom_du_client>

Si on veut ajouter un nouveau client, on peut à tout moment lancer le script wgadduser et récupérer le fichier *.conf dans le dossier client correspondant.

Redirection de port

Point le plus facile mais à ne pas oublier sinon l'accès à l'extérieur ne fonctionnera pas : activer le port forwarding sur la Box.
Se rendre sur la page de connexion de sa box et définir une règle qui redirige le protocole UDP pour les flux Internet ayant le port 51820 de la box vers le port 51820 de l'IP du serveur Wireguard.

Mise en place du client

Aucun système d'exploitation ne gère Wireguard nativement. Il faut donc télécharger et installer une appli ad hoc.

Windows

Télécharger l'appli sur le site officiel.
Attention, si le client est utilisé sur une session Windows utilisateur et non administrateur, la connexion ne pourra se faire que dans la session administrateur. Lors de mon dernier test, même en exécutant en administrateur ça ne le lance pas, il faut ouvrir une session admin. Il existe néanmoins plusieurs parades.
Une fois installé, simplement ouvrir le fichier *.conf dans le logiciel.

Ordi Mac type MacBook, MacBook Pro

Je n'ai jamais testé, mais l'appli est disponible sur le Mac Apple Store.

Smartphones

Appli téléchargeable sur Play Store ou Appstore.
Le plus pratique est de générer un QR code du fichier *.conf du client. Procéder comme suit (sur le serveur) :
apt install qrencode
qrencode -t ansiutf8 < /etc/wireguard/users/<dossier_client>/<nom_du_client>.conf
On aura ainsi un QR code qui va apparaître à l'écran, qui peut être flashé directement, imprimé, envoyé par mail, etc.

Split-tunnel vs full-tunnel

Tunnel routier
Tunnel ou pas tunnel ?


Dans cette configuration, le client est en split-tunneling, c'est-à-dire qu'il est limité à la connexion au réseau local de la clinique.
Tout le reste du trafic se fera hors tunnel, via les directives de la connexion internet du client.

En revanche, la résolution des noms de domaine suivra les règles du tunnel et non celles définies par le client hors VPN.
Cela peut très facilement se vérifier en effectuant un test DNS.
Cependant, l'utilisateur peut modifier son fichier de configuration pour personnaliser ses DNS, mais s'il le fait, les serveurs DNS de la clinique ne seront plus en mesure de résoudre les noms de domaines internes de la clinique.

Autre modification possible par l'utilisateur : remplacer 192.168.1.0/24 par 0.0.0.0/0, dans le but de passer en full-tunelling.
Dans cette configuration, le pare-feu bloquera toutes ses requêtes hors réseau local tant qu'il sera connecté au VPN.
Si on veut autoriser le full-tunneling, il faut modifier les règles iptables dans le fichier /etc/before.rules en mettant par exemple juste après # ok icmp code for FORWARD :

# autoriser le forwarding pour le réseau distant de confiance, personnaliser les plages IP
-A ufw-before-forward -s 192.168.1.0/24 -j ACCEPT
-A ufw-before-forward -d 192.168.1.0/24 -j ACCEPT
# autoriser le forwarding pour le réseau VPN, personnaliser les plages IP
-A ufw-before-forward -s 10.0.0.0/24 -j ACCEPT
-A ufw-before-forward -d 10.0.0.0/24 -j ACCEPT

Ainsi, tout le trafic du client transitera par le VPN. Dès lors, attention aux problèmes de sécurité et de consommation de bande passante que cela implique ! Surtout si l'utilisateur oublie de se déconnecter du VPN.

Sécurité

🔐 Une clé numérique, c'est comme une clé physique : si elle est perdue, quiconque trouve la serrure peut entrer. C'est donc un élément à ne surtout pas perdre, côté serveur comme côté client.

Conseils côté utilisateurs

⚠️ Le fichier *.conf côté client ne doit pas traîner dans la nature : il contient la clé privée du client, le fichier n'est par défaut pas protégé par mot de passe. S'il est dérobé, un intrus pourra se connecter au réseau de la clinique.
Si la clé est compromise, avertir l'administrateur. Il suffit de la révoquer du fichier wg0.conf.
Il convient donc de transmettre ce fichier de façon sécurisée sur le net, et d'avertir l'utilisateur final de la sensibilité de ce fichier.

L'utilisateur peut modifier beaucoup de choses sur son fichier de configuration.
    ⇨ Il faut donc avertir l'utilisateur que son fichier a été configuré pour des besoins spécifiques, définis par l'administrateur réseau, et que s'il le modifie sans accord préalable, c'est de sa propre responsabilité et qu'il peut être amené à ne plus pouvoir se connecter.
    ⇨ Sensibiliser sur le fait que cette connexion VPN permet un accès direct à tout ou une partie (selon la configuration) du réseau local de la clinique. Il faut donc qu'il se connecte uniquement lorsque cela est nécessaire, et pas en permanence.

Conseils côté serveur

⚠️ Le fichier *.conf côté serveur ne doit jamais sortir du serveur (sauf pour sauvegarde), ainsi que toutes les clés générées et stockées dans le serveur, soit tout le répertoire /etc/wireguard.

C'est de ce côté que l'on pourra au mieux réduire des risques d'intrusions malveillantes, avec entre autres :

  • Protection de l'accès physique au serveur
  • ssh hardening : HostbasedAcceptedAlogorithms, AllowUsers, connexion par clé, etc.
  • Affiner ses règles de pare-feu
  • L'accès doit-il être obligatoirement permanent et possible à tout le monde sur tout le réseau local ?

Il est par exemple possible de limiter l'accès à certaines machines juste pour certains clients. Par exemple, si je veux que le client X avec l'adresse IP 10.0.0.2 ait accès à tout le réseau local mais que le client Y avec l'adresse IP 10.0.0.3 soit limité à 192.168.1.115 et 192.168.1.234, on peut modifier les règles iptables dans /etc/ufw/before.rules comme suit :

# Accès pour X à tout le réseau local mais pas internet via le VPN
-A ufw-before-forward -s 10.0.0.2/32 -d 192.168.1.0/24 -j ACCEPT

# Accès pour Y à seulement quelques machines définies ci-dessous
-A ufw-before-forward -s 10.0.0.3/32 -d 192.168.1.115/32,192.168.1.234/32 -j ACCEPT

On peut évidemment personnaliser de pleins d'autres façons... Selon les besoins.