Douze constructions

Un serveur WireGuard durci avec IPv6 qui route réellement

Construisez un point de terminaison WireGuard qui attribue aux clients une adresse IPv4 privée et un préfixe IPv6 réellement routé, derrière une politique nftables qui rejette tout ce qui n'est pas demandé.

Ce que cela construit

Une instance exécutant WireGuard, attribuant à chaque client une adresse IPv4 privée, une adresse IPv6 routée issue de votre propre /48, et un résolveur qui vit à l'intérieur du tunnel afin que les requêtes DNS ne suivent jamais un chemin différent du reste du trafic. Le pare-feu est en refus par défaut dans les deux sens. Le forwarding n'est activé que pour l'interface du tunnel, et uniquement en sortie.

Testé sur un R-4 dans AMS-01 avec Debian 13. WireGuard est à flux unique et s'exécute dans le noyau, donc plus de cœurs ne vous apportent rien jusqu'à ce que vous poussiez plusieurs gigabits à travers. Ce qui compte bien plus, c'est le site : choisissez celui qui est le plus proche de votre position réelle, car chaque milliseconde ajoutée ici s'ajoute à chaque paquet.

Avant de commencer

  • Une instance déployée et un accès root en SSH.
  • L'option IPv6 /48 activée dans le panneau. Le /64 inclus fonctionne pour un seul client, mais des sous-réseaux par client dans un seul /64 impliquent de proxifier la découverte de voisins, et cela relève du loisir plutôt que de la configuration.
  • Un nom d'hôte pointant vers l'instance. Tout ce qui suit utilise vpn.example.com.
  • Chaque adresse 2001:db8: ici est de l'espace documentaire. Remplacez le préfixe par celui affiché sur la page de votre instance.

1. Paquets et paramètres du noyau

apt update && apt full-upgrade -y
apt install -y wireguard-tools nftables unbound qrencode
ip -br link

Cette dernière commande affiche le nom de votre interface sortante. Sur nos images KVM, c'est ens3 ; si la vôtre diffère, substituez-la partout ci-dessous. Maintenant le noyau :

cat > /etc/sysctl.d/99-wireguard.conf <<EOF
net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1
net.ipv6.conf.ens3.accept_ra = 2
net.ipv4.conf.all.rp_filter = 1
EOF
sysctl --system

La ligne accept_ra = 2 est celle que l'on oublie. Activer le forwarding IPv6 fait que le noyau cesse d'accepter les annonces de routeur, la route par défaut disparaît en dix minutes, et la machine devient invisible en v6 tout en répondant toujours en v4. La définir sur deux permet de continuer à accepter les annonces de routeur sur un hôte faisant du forwarding.

2. Clés

install -d -m 700 /etc/wireguard
umask 077
wg genkey | tee /etc/wireguard/server.key | wg pubkey > /etc/wireguard/server.pub
wg genkey | tee /etc/wireguard/laptop.key | wg pubkey > /etc/wireguard/laptop.pub
wg genpsk > /etc/wireguard/laptop.psk

La clé pré-partagée n'est pas facultative dans cette construction. Elle ne coûte rien, c'est une ligne dans chaque configuration, et c'est la différence entre un tunnel simplement sécurisé aujourd'hui et un tunnel qui reste sécurisé contre un adversaire enregistrant le trafic maintenant pour le déchiffrer plus tard.

3. L'interface serveur

Écrivez /etc/wireguard/wg0.conf, en collant le contenu des clés aux endroits indiqués :

[Interface]
Address = 10.7.0.1/24, 2001:db8:1a2b:7::1/64
ListenPort = 51820
PrivateKey = <contents of /etc/wireguard/server.key>
MTU = 1420

[Peer]
# laptop
PublicKey = <contents of /etc/wireguard/laptop.pub>
PresharedKey = <contents of /etc/wireguard/laptop.psk>
AllowedIPs = 10.7.0.2/32, 2001:db8:1a2b:7::2/128

Côté serveur, AllowedIPs est une table de routage, pas une liste de permissions. Donnez à chaque pair exactement les adresses qu'il possède et rien de plus large, sinon deux clients se battront pour la même route et le perdant cessera simplement de recevoir des paquets.

chmod 600 /etc/wireguard/wg0.conf

4. Un résolveur dans le tunnel

Pointer les clients vers un résolveur public annule une bonne partie de l'intérêt. Unbound est déjà installé ; donnez-lui /etc/unbound/unbound.conf.d/tunnel.conf :

server:
  interface: 10.7.0.1
  interface: 2001:db8:1a2b:7::1
  access-control: 10.7.0.0/24 allow
  access-control: 2001:db8:1a2b:7::/64 allow
  do-ip6: yes
  prefetch: yes
  hide-identity: yes
  hide-version: yes
  qname-minimisation: yes

Unbound ne démarrera pas avant que le tunnel n'existe, car ces adresses ne sont pas encore actives. Séquencez cela correctement plutôt que de lutter contre :

systemctl edit unbound
[Unit]
[email protected]
[email protected]

5. Le pare-feu

Remplacez /etc/nftables.conf entièrement :

flush ruleset

table inet filter {
  chain input {
    type filter hook input priority filter; policy drop;
    ct state established,related accept
    ct state invalid drop
    iif lo accept
    ip protocol icmp accept
    ip6 nexthdr icmpv6 accept
    tcp dport 22 accept
    udp dport 51820 accept
    iifname "wg0" udp dport 53 accept
    iifname "wg0" tcp dport 53 accept
  }
  chain forward {
    type filter hook forward priority filter; policy drop;
    ct state established,related accept
    iifname "wg0" oifname "ens3" accept
    iifname "wg0" oifname "wg0" drop
  }
  chain output {
    type filter hook output priority filter; policy accept;
  }
}

table ip nat {
  chain postrouting {
    type nat hook postrouting priority srcnat; policy accept;
    ip saddr 10.7.0.0/24 oifname "ens3" masquerade
  }
}

Notez ce qui est absent : il n'y a pas de table NAT pour IPv6. Les clients sortent avec leur propre adresse depuis votre /48, ce qui est précisément la raison de prendre le /48 en premier lieu. La ligne iifname "wg0" oifname "wg0" drop empêche les pairs de se joindre, ce que vous voulez sauf si vous construisez délibérément un maillage.

systemctl enable --now nftables
nft list ruleset | head -20

6. Démarrage

systemctl enable --now wg-quick@wg0
systemctl restart unbound
wg show wg0

7. Le client

[Interface]
PrivateKey = <contents of /etc/wireguard/laptop.key>
Address = 10.7.0.2/24, 2001:db8:1a2b:7::2/64
DNS = 10.7.0.1
MTU = 1420

[Peer]
PublicKey = <contents of /etc/wireguard/server.pub>
PresharedKey = <contents of /etc/wireguard/laptop.psk>
Endpoint = vpn.example.com:51820
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25

Pour un téléphone, transformez cela en code QR sur le serveur et photographiez le terminal :

qrencode -t ansiutf8 < /root/laptop.conf

Sur un client Debian ou Ubuntu, la ligne DNS = nécessite openresolv installé, sinon wg-quick affiche un avertissement et laisse silencieusement votre ancien résolveur en place. C'est la cause la plus courante d'un tunnel qui fonctionne parfaitement et qui fuit chaque requête.

Vérification

Quatre vérifications, dans l'ordre. Toute défaillance vous indique l'étape à laquelle revenir.

# on the server, after connecting the client
wg show wg0 latest-handshakes
wg show wg0 transfer

Un horodatage de négociation datant de moins de deux minutes et des compteurs non nuls dans les deux sens signifient que la cryptographie et le routage sont tous deux corrects.

# on the client
ping -c3 10.7.0.1
ping -c3 2001:db8:1a2b:7::1
dig @10.7.0.1 paragonvps.com AAAA +short

La troisième commande prouve que le résolveur du tunnel répond. En cas de délai d'attente, unbound a démarré avant que wg0 n'existe ; redémarrez-le.

La dernière vérification est la seule qui prouve que votre trafic sort là où vous le pensez, et elle nécessite une seconde machine. Toute autre instance de la flotte fera l'affaire, dans n'importe quelle ville :

# on the second instance
nc -lv 9101

# on the client, tunnel up
curl -m5 -4 http://second.example.com:9101/
curl -m5 -6 http://second.example.com:9101/

L'écouteur affiche l'adresse source de chaque connexion. Votre tentative v4 devrait arriver depuis le serveur WireGuard, car elle a été masquée. La tentative v6 devrait arriver depuis l'adresse de tunnel propre au client dans votre /48, intacte, ce qui est ce que signifie "routé" et non "proxifié".

En cas de problème

SymptômeCauseCorrectif
La négociation réussit, aucun trafic ne passeForwarding désactivé, ou la chaîne forward bloquesysctl net.ipv4.ip_forward, puis relisez la chaîne forward
Les petites requêtes passent, les gros téléchargements se bloquentMTU et découverte de cheminPassez les deux configurations à MTU = 1380 et réessayez
IPv6 meurt environ dix minutes après le démarrageLe forwarding a tué les annonces de routeuraccept_ra = 2 sur l'interface sortante
Le DNS résout en dehors du tunnelLe client a ignoré DNS =Installez openresolv sur le client
Tout disparaît après un redémarrageUnités jamais activéessystemctl enable wg-quick@wg0 nftables unbound

À partir de là, les prochaines étapes logiques sont un second point de terminaison dans une autre région et une pile de surveillance qui vous indique quand l'un d'eux cesse de répondre. Les deux sont couverts ailleurs dans les guides; la page réseau explique ce que la périphérie fait de votre trafic avant qu'il ne vous atteigne.

Prêt quand vous l'êtes

Choisissez une ville. Choisissez une taille. Payez en crypto.

Aucun formulaire sur votre identité, pas d'attente d'approbation humaine, pas d'appel téléphonique pour vérifier quoi que ce soit. La facture est réglée et les identifiants arrivent dans votre boîte mail.