Skip to main content

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.

SCR-20230425-w22.jpeg

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 :

SCR-20230426-uih.png

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 personnaliser dev : on va appeler notre interface out-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 et down-out-tor_fi.sh avec up et down

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