Un VPN WireGuard qui route vers d'autres VPN + VPN over Tor
L'objectif de la manipulation est de pouvoir se connecter à un et un seul VPN Wireguard auto-hébergé et de choisir, à la demande, vers quel VPN (commercial) aboutira la connexion.
Dans ce tutoriel sont proposées des commandes iptables. Si vous utilisez Docker sur cette machine, remplacer la chaîne FORWARD
par la chaine DOCKER-USER
Installation et configuration de WireGuard
Sous debian, il faut faire
apt install wireguard-tools
Côté serveur
Configuration du système
On va être amenés à transférer des paquets IPv4 et IPv6, donc :
sudo nano /etc/sysctl.conf
Dans lequel les options suivantes doivent être activées :
net.ipv4.ip_forward=1
net.ipv6.conf.all.forwarding=1
Et on applique les modifications de suite :
sudo sysctl -p
Génération des clés
On génère la clé privée :
wg genkey | sudo tee /etc/wireguard/private.key
sudo chmod go= /etc/wireguard/private.key
Et on génère la clé publique :
sudo cat /etc/wireguard/private.key | wg pubkey | sudo tee /etc/wireguard/public.key
Création d'une configuration
Et on crée l'interface (récupérez votre clé privée qui se trouve dans /etc/wireguard/private.key
) :
sudo nano /etc/wireguard/wg0.conf
Et on y écrit :
[Interface]
PrivateKey = < contenu de /etc/wireguard/private.key >
Address = 10.42.0.1/24, fd42:42:42::1/64
ListenPort = 51820
PostUp = iptables -A FORWARD -o %i -m state --state RELATED,ESTABLISHED -j ACCEPT
PreDown = iptables -D FORWARD -o %i -m state --state RELATED,ESTABLISHED -j ACCEPT
On ouvre le port 51820 selon vos habitudes (NAT, iptables, nftables, ufw...)
Création d'une clé pré-partagée
On génère une clé pré-partagée (utile pour le paragraphe suivant), et on la met de côté :
wg genpsk
Côté client
C'est à peu près la même chose, on fait une configuration Wireguard qui ressemble à ça :
[Interface]
PrivateKey = < clé privée du client >
Address = 10.42.0.2/24, fd42:42:42::2/64
DNS = < un DNS ou des DNS séparés par une virgule, qui peuvent être hébergés sur le serveur ou non, par exemple 1.1.1.1 >
[Peer]
PublicKey = < clé publique à récupérer du côté du serveur dans /etc/wireguard/public.key >
PresharedKey = < la clé prépartagée que l'on vient d'obtenir avec wg genpsk >
AllowedIPs = 0.0.0.0/0, ::/0
Endpoint = < ip du serveur >:51820
La clé privée du client peut être générée en fonction de la méthode d'installation de Wireguard. Sur l'interface Mac ou Windows, cliquez sur le "+" en bas à gauche, et créez un tunnel vide. La première partie de la section [Interface]
sera préremplie avec la clé privée du client, et sa clé publique se trouvera en haut. Elle sera utile pour la suite du tutoriel.
On ajoute autant de clients que nécessaire.
Côté serveur à nouveau
On revient dans notre fichier /etc/wireguard/wg0.conf
et on rajoute :
[Peer]
PublicKey = < clé publique du client, à récupérer sur son interface wireguard >
PresharedKey = < la clé prépartagée que l'on a obtenue avec wg genpsk, qui doit être la même que dans la configuration du client >
AllowedIPs = 10.42.0.2/32, fd42:42:42::2/128
On fait ça pour tous les clients qu'on a à ajouter.
On peut désormais démarrer le tunnel et l'activer au démarrage, avec :
sudo systemctl enable --now wg-quick@wg0
Connexion à un VPN commercial
Toujours côté serveur
Cloudflare propose un VPN gratuit, Cloudflare Warp, dont j'explique la configuration pour Wireguard sur cette page. Vous pouvez tout à fait utiliser Cloudflare Warp dans le cadre de ce tutoriel. Remplacez systématiquement "germany" par "cloudflare" et "out-germany" par "out-cloudflare", par exemple.
On part du principe ici que le VPN commercial propose Wireguard.
D'abord, on va rajouter une table de routage qui nous permettra par le suite de "transférer" les paquets arrivant sur notre interface vers le VPN commercial. On crée pour cela ce fichier :
sudo nano /etc/iproute2/rt_tables.d/vpn.conf
Et on écrit :
30001 germany
Ce n'est qu'un exemple, si votre VPN est en Allemagne. Vous pouvez écrire ce que vous voulez.
On génère une configuration Wireguard chez le prestataire de notre choix, ici Mullvad, qui nous donne :
[Interface]
PrivateKey = < clé privée générée par mullvad, à ne pas toucher >
Address = < ip attribuées par mullvad, à ne pas toucher >
DNS = < dns de Mullvad, on peut carrément supprimer cette ligne ou ne rien toucher, elle ne nous servira pas >
[Peer]
PublicKey = < clé publique du serveur mullvad, à ne pas toucher >
AllowedIPs = 0.0.0.0/0,::0/0
Endpoint = < ip de Mullvad >:51820
On va modifier cette configuration et ajouter "Table = germany"
dans la section [Interface]
, et quelques règles iptables
qui nous permettront de router le trafic :
[Interface]
PrivateKey = < clé privée générée par mullvad, à ne pas toucher >
Address = < ip attribuées par mullvad, à ne pas toucher >
DNS = < dns de Mullvad, on peut carrément supprimer cette ligne ou ne rien toucher, elle ne nous servira pas >
Table = germany
PostUp = iptables -t nat -A POSTROUTING -o %i -j MASQUERADE
PostUp = ip6tables -t nat -A POSTROUTING -o %i -j MASQUERADE
PreDown = iptables -t nat -D POSTROUTING -o %i -j MASQUERADE
PreDown = ip6tables -t nat -D POSTROUTING -o %i -j MASQUERADE
[Peer]
PublicKey = < clé publique du serveur mullvad, à ne pas toucher >
AllowedIPs = 0.0.0.0/0,::0/0
Endpoint = < ip de Mullvad >:51820
On enregistre sous /etc/wireguard/out-germany.conf
, ça nous sera utile par la suite. En écrivant sous ce nom de fichier, l'interface wireguard s'appellera out-germany
On active et on lance cette interface :
sudo systemctl enable --now wg-quick@out-germany
On répète l'opération autant de fois qu'on a de VPN commerciaux à ajouter, en n'oubliant pas la table dans la configuration de Wireguard sur le serveur et son ajout, sous un numéro différent, dans /etc/iproute2/rt_tables.d/vpn.conf
.
Routage des différents clients
Toujours côté serveur
Dans notre exemple, nous avions créé un client aux IP 10.42.0.2
et fd42:42:42::2
. Pour router son trafic vers le VPN allemand, on fait :
sudo ip rule add from 10.42.0.2 lookup germany
sudo ip -6 rule add from fd42:42:42::2 lookup germany
On peut faire des "exceptions" pour que le trafic, par exemple vers le réseau local, soit accepté. Contrairement aux autres commandes, elle s'applique à tous les clients et pas uniquement à 10.42.0.2
:
ip route add 192.168.1.0/24 dev enp0s3 table germany
La commande suivante devrait marcher aussi, et n'est valable que pour 10.42.0.2
:
ip rule add from 10.42.0.2 to 192.168.1.0/24 lookup main
On peut rajouter les règles iptables
suivantes pour s'assurer que le trafic ne fuite pas :
iptables -A FORWARD -i wg0 -s 10.42.0.2 -o out-germany -j ACCEPT
ip6tables -A FORWARD -i wg0 -s fd42:42:42::2 -o out-germany -j ACCEPT
iptables -A FORWARD -i wg0 -s 10.42.0.2 -d 192.168.1.0/24 -o enp0s3 -j ACCEPT
iptables -A FORWARD -i wg0 -s 10.42.0.2 -j DROP
ip6tables -A FORWARD -i wg0 -s fd42:42:42::2 -j DROP
On peut répéter l'opération avec chaque IP de chaque client. Mais ces règles ne persistent pas après redémarrage.
Persistance des règles et changement de route
Toujours côté serveur
Pour faciliter la gestion des règles iptables par client, on peut créer script pour créer une chaîne iptables dédiée :
iptables -N MAC_COCO
ip6tables -N MAC_COCO
iptables -I FORWARD -i clients -s 10.42.0.2 -j MAC_COCO
ip6tables -I FORWARD -i clients -s fd42:42:42::2 -j MAC_COCO
Remplacez MAC_COCO
par le nom de la chaîne que vous souhaitez créer pour ce client.
Une fois enregistré quelque part, on instruit à Wireguard de lancer ce script au démarrage de l'interface, en rajoutant un PostUp
par exemple, notre configuration ressemble alors à ceci :
[Interface]
PrivateKey = < contenu de /etc/wireguard/private.key >
Address = 10.42.0.1/24, fd42:42:42::1/64
ListenPort = 51820
PostUp = iptables -A FORWARD -o %i -m state --state RELATED,ESTABLISHED -j ACCEPT
PreDown = iptables -D FORWARD -o %i -m state --state RELATED,ESTABLISHED -j ACCEPT
PostUp = < chemin du script >
Pour facilier le changement entre les VPN, on peut imaginer un script comme ceci, qu'on appellera changevpn.sh et qui prendra deux arguments : le nom de la table de routage (attention pour ce script l'interface WireGuard devra être de la forme out-<nom de la table>
), et le dernier chiffre de l'adresse IP du client (dans notre exemple pour 10.42.0.2, "2"). Il sera appelé comme ça : bash changevpn.sh 2 germany
:
# flush règles iptables
iptables -F <chaîne iptables du client>
# supprime les réglages précédents
ip rule del from 10.42.0.$1
ip -6 rule del from fd42:42:42::$1
# route vers le vpn désiré
ip rule add from 10.42.0.$1 lookup $2
ip -6 rule add from 2a01:e0a:27a:c4d1::$1 lookup $2
iptables -A <chaîne iptables du client> -o out-$2 -j ACCEPT
ip6tables -A <chaîne iptables du client> -o out-$2 -j ACCEPT
iptables -A <chaîne iptables du client> -j DROP
ip6tables -A <chaîne iptables du client> -j DROP
Par la suite, on peut rendre ces changements plus simples en lançant ces commande en interface web, par exemple avec OliveTin, assez simple à prendre en main.
Bonus : router un VPN dans Tor
Toujours côté serveur
Ici, nous n'utiliserons pas Wireguard pour se connecter au VPN commercial via Tor, car Tor n'est pas compatible avec UDP. Nous utiliserons OpenVPN.
Lecture recommandée : VPN + Tor: Not Necessarily a Net Gain
On va créer un conteneur Docker pour installer Tor dedans, avec docker-compose. Si vous ne voulez pas utiliser Docker, installez simplement le paquet tor et laissez les paramètres par défaut.
sudo mkdir /home/tor
Dedans, on édite le docker-compose.yml
sudo nano /home/tor/docker-compose.yml
On le remplit de cette configuration :
version: "2.3"
services:
tor-proxy:
image: dperson/torproxy
ports:
- 127.0.0.1:9050:9050
restart: unless-stopped
Et on lance tout ça :
cd /home/tor && sudo docker-compose up -d
On va rajouter une table de routage pour ce VPN :
sudo nano /etc/iproute2/rt_tables.d/vpn.conf
Et on ajoute la ligne (ici, ma table s'appelle tor_fi, "fi" pour Finlande) :
30002 tor_fi
On installe OpenVPN :
sudo apt update && sudo apt install openvpn
On va créer, dans /etc/openvpn/client
, des fichiers que OpenVPN enclenchera lors de sa connexion (par exemple up-out-tor_fi.conf
). Ils nous servent à bien router le trafic à nos autres clients VPN. Ici, la table de routage s'appelle tor_fi
et l'interface out-tor_fi
.
ip route add default dev out-tor_fi table tor_fi
ip -6 route add default dev out-tor_fi table tor_fi
iptables -t nat -A POSTROUTING -o out-tor_fi -j MASQUERADE
ip6tables -t nat -A POSTROUTING -o out-tor_fi -j MASQUERADE
Et un scipt quand l'interface se ferme. Je l'enregistre dans /etc/openvpn/clientdown-out-tor_fi.sh
:
ip route del default dev out-tor_fi table tor_fi
ip -6 route del default dev out-tor_fi table tor_fi
iptables -t nat -D POSTROUTING -o out-tor_fi -j MASQUERADE
ip6tables -t nat -D POSTROUTING -o out-tor_fi -j MASQUERADE
Obtenons une configuration OpenVPN de, disons, Mullvad. Dans le générateur, on sélectionne bien TCP :
Ce qui chez Mullvad, nous donne :
client
dev tun
resolv-retry infinite
nobind
persist-key
persist-tun
verb 3
remote-cert-tls server
ping 10
ping-restart 60
sndbuf 524288
rcvbuf 524288
cipher AES-256-CBC
tls-cipher TLS-DHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-RSA-WITH-AES-256-CBC-SHA
proto tcp
auth-user-pass mullvad_userpass.txt
ca mullvad_ca.crt
tun-ipv6
script-security 2
up /etc/openvpn/update-resolv-conf
down /etc/openvpn/update-resolv-conf
remote-random
remote 185.204.1.174 443 # fi-hel-ovpn-004
remote 193.138.7.237 443 # fi-hel-ovpn-102
remote 185.204.1.176 443 # fi-hel-ovpn-006
remote 185.204.1.175 443 # fi-hel-ovpn-005
remote 185.212.149.201 443 # fi-hel-ovpn-007
remote 193.138.7.217 443 # fi-hel-ovpn-101
remote 185.204.1.173 443 # fi-hel-ovpn-003
remote 185.204.1.172 443 # fi-hel-ovpn-002
remote 185.204.1.171 443 # fi-hel-ovpn-001
Transférez tous ces fichiers sur votre serveur dans /etc/openvpn/client/.
Renommez le fichier .conf (ou pas) en un nom qui vous convient, dans mon cas ce sera out-tor_fi.conf
. Ce nom est à retenir car c'est grâce à lui que nous lancerons le VPN plus tard.
On va éditer ce fichier conf pour :
- se connecter au proxy tor avec la directive
socks-proxy 127.0.0.1 9050
- y ajouter
dev-type
et personnaliserdev
: on va appeler notre interfaceout-tor_fi
- ne pas récupérer les routes par défaut du VPN, parce qu'on veut faire notre propre routage, avec
route-nopull
- lancer nos scripts
up-out-tor_fi.sh
etdown-out-tor_fi.sh
avecup
etdown
Ce qui nous donne :
client
dev out-tor_fi
dev-type tun
resolv-retry infinite
nobind
persist-key
persist-tun
verb 3
remote-cert-tls server
ping 10
ping-restart 60
sndbuf 524288
rcvbuf 524288
cipher AES-256-CBC
tls-cipher TLS-DHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-RSA-WITH-AES-256-CBC-SHA
proto tcp
auth-user-pass mullvad_userpass.txt
ca mullvad_ca.crt
tun-ipv6
script-security 2
up up-out-tor_fi.sh
down down-out-tor_fi.sh
socks-proxy 127.0.0.1 9050
route-nopull
remote-random
remote 185.204.1.174 443 # fi-hel-ovpn-004
remote 193.138.7.237 443 # fi-hel-ovpn-102
remote 185.204.1.176 443 # fi-hel-ovpn-006
remote 185.204.1.175 443 # fi-hel-ovpn-005
remote 185.212.149.201 443 # fi-hel-ovpn-007
remote 193.138.7.217 443 # fi-hel-ovpn-101
remote 185.204.1.173 443 # fi-hel-ovpn-003
remote 185.204.1.172 443 # fi-hel-ovpn-002
remote 185.204.1.171 443 # fi-hel-ovpn-001
Suite à quoi on devrait pouvoir lancer et activer le VPN :
sudo systemctl enable --now openvpn-client@out-tor_fi
Ensuite, on peut "router" nos clients vers ce VPN, comme tout à l'heure, comme ceci :
sudo ip rule add from 10.42.0.2 lookup tor_fi
sudo ip -6 rule add from fd42:42:42::2 lookup tor_fi