Le problème
Depuis peu, je fais tourner des sites web sur des ordis portables revalorisés dans mon humble demeure. Ils atteignent internet via le réseau Wi-fi de la résidence, qui utilise CGNAT, ce qui rend les IP des machines inaccessibles depuis l’extérieur. J’ai trouvé 3 options :
- Louer une box internet avec IP publique statique (ex. Freebox) et configurer une redirection de trafic via DNAT (~40€/mois)
- Migrer mon infrastructure sur AWS (~∞€/mois)
- Prendre une VM pas chère sur le cloud pour rediriger le trafic (~2€/mois)
Le choix fut simple.
Le concept
Au sein d’un réseau CGNAT (et NAPT en général, la variante NAT utilisée pour la plupart des routeurs), tous les appareils sont dans un réseau privé. Ils peuvent envoyer des requêtes à des serveurs publics et recevoir une réponse car NAPT traduit chaque connexion d’une IP privée à son IP publique + un port spécifique. Cependant, le trafic extérieur est incapable de spécifier quel serveur derrière le NAT il veut contacter, ce qui rend compliqué d’exposer des ports vers internet.
Or, si c’était le serveur derrière NAT qui initiait la connexion, le problème serait résolu grâce au mapping NAPT. C’est pourquoi on va utiliser une VM cloud (VPS) en tant que proxy. Voici le flux global :

La clef c’est le tunnel VPN. Le serveur initie la connexion VPN car il peut atteindre le VPS directement, et lorsque le tunnel est actif, les 2 machines peuvent communiquer via une interface virtuelle facilement : le VPS peut ainsi rediriger tout son trafic au serveur.
L’implémentation
Une fois qu’on s’est procuré un VPS avec une IP publique statique, on commence par lui changer un paramètre kernel pour le faire agir en proxy : net.ipv4.ip_forward à 1.
Tunnel VPN
Ensuite, on installe le VPN dessus. J’apprécie WireGuard. Le nom du paquet est “wireguard” ou “wireguard-tools” sur la plupart des Linux. L’outil fonctionne avec un fichier de conf par connexion, et une couche de cryptographie asymétrique qui ne nous est pas strictement nécessaire, mais étant donné la taille de certaines clefs TLS , trop de chiffrement ne nous fera pas de mal.
Voici notre configuration /etc/wireguard/wg0.conf :
[Interface]
PrivateKey = <vps_priv_key>
Address = 10.0.0.1/24
ListenPort = 51820
[Peer]
AllowedIPs = 10.0.0.2/32
PublicKey = <server_pub_key>
L’idée est de créer une interface réseau virtuelle, wg0, avec l’IP 10.0.0.1 côté VPS et 10.0.0.2 côté serveur. Vous pouvez modifier les adresses et le port. Pour la clef privée, lancez wg genkey pour en générer une. Sa traduction en clef publique se fait avec echo "<priv_key>" | wg pubkey ; il nous faut un couple de clefs publique/privée pour chaque côté du tunnel (VPS et serveur).
En parlant du serveur, on installe WireGuard dessus également, et voici une configuration fonctionnelle :
[Interface]
PrivateKey = <server_priv_key>
Address = 10.0.0.2/24
[Peer]
PublicKey = <vps_pub_key>
AllowedIPs = 10.0.0.1/32
Endpoint = <vps_public_ip>:51820
PersistentKeepalive = 25
Le secret réside dans le PersistentKeepalive : c’est ça qui rend le tunnel permanent. Établissons la connexion depuis le VPS puis depuis le serveur (ex. systemctl start wg-quick@wg0 sur chaque machine). On peut désormais ping 10.0.0.2 depuis le VPS, et 10.0.0.1 depuis le serveur !
Redirection de trafic
Maintenant que les machines communiquent, il reste juste à rediriger les ports. Pour une intégration simple j’utilise nftables avec une table dédiée (si besoin, mon ami iTrooz a écrit un blog post sur nft). Voilà ma conf :
table ip nat {
chain prerouting {
type nat hook prerouting priority dstnat; policy accept;
iifname "ens1" tcp dport 80 dnat to 10.0.0.2:80
iifname "ens1" tcp dport 443 dnat to 10.0.0.2:443
iifname "ens1" tcp dport 25565 dnat to 10.0.0.2:25565
}
chain postrouting {
type nat hook postrouting priority srcnat; policy accept;
oifname "wg0" ip daddr 10.0.0.2 tcp dport 80 masquerade
oifname "wg0" ip daddr 10.0.0.2 tcp dport 443 masquerade
oifname "wg0" ip daddr 10.0.0.2 tcp dport 25565 masquerade
}
}
Ces règles transfèrent les paquets TCP de port 80, 443 et Minecraft 🙂↕️ (UDP fonctionne pareil), pensez juste à changer le nom de l’interface réseau (ens1).
Après avoir appliqué ces règles, si la redirection échoue, vous pouvez inspecter le firewall côté serveur et faire des tests avec netcat. Ou bien m’envoyer un message sur Matrix !
J’ai vu des gens mettre cette conf de routage dans les IfUp et IfDown de WireGuard pour que le forwarding soit actif seulement si l’interface wg0 est montée, mais pour mon cas d’usage c’est inutile.
Fun fact : vos requêtes vers ce site sont routées par ce mécanisme !