Skip to content

WireGuard

WireGuard est un protocle de réseau privé virtuel (VPN) libre et open source conçu pour établir des communications sécurisées entre des équipements connectés à un réseau IP. Développé par Jason A. Donenfeld et publié pour la première fois en 2015, il a été conçu pour offrir une alternative moderne aux protocoles VPN traditionnels tels qu'OpenVPN et IPsec, en mettant l'accent sur la simplicité, les performances et la sécurité.

Source : Wikipedia

Principes

WireGuard fonctionne au niveau de la couche réseau et encapsule les paquets IP dans des datagrammes UDP. Chaque pair possède une paire de clés cryptographiques composée d'une clé privée et d'une clé publique.

Contrairement à de nombreux protocoles VPN traditionnels, WireGuard ne repose pas sur une infrastructure de certificats x.509 ni sur une autorité de certification. L'authentification des pairs est réalisée à l'aide de leurs clés publiques, qui sont configurés manuellement ou distribués par un système de gestion externe.

Chaque pair est également associé à une liste de préfixes IP autorisées (AllowedIPs). Cette information sert à la fois de politique de routage et de mécanisme de contrôle d'accès, en déterminant quelles addresses IP peuvent être atteintes via chaque tunnel.

Créer un tunnel entre deux réseaux distants

Contexte et architecture

  • Serveur VPN (VPS OVH qui s'appellera DEBIAN13-OVH) : possède une IP publique fixe, c'est lui qui écoute sur le port UDP ouvert. C'est le point central du tunnel.
  • Client (machine Debian à la maison qui s'appellera DEBIAN13-LAB) : se connecte au VPS, pas d'ouverture de port nécessaire côté box/routeur.
  • Réseau overlay WireGuard : 10.10.0.0/24 (à adapter selon vos plages déjà utilisées, ex. Tailscale en 100.64.0.0/10).

Le serveur WireGuard (DEBIAN13-OVH) ouvrira le port 51820 en UDP (règles de pare-feu à faire).

RôleMachineIP publiqueIP WireGuard
ServeurVPS OVH11.22.33.4410.10.0.1/24
Client 1Debian maisondynamique/fixe10.10.0.2/24

Installer WireGuard sur nos deux machines

bash
sudo apt install wireguard

Générer les paires de clés

Sur DEBIAN13-OVH et DEBIAN13-LAB, on génère une paire clé privée / clé publique. Le dossier /etc/wireguard doit rester en 600.

bash
cd /etc/wireguard
umask 077
wg genkey | tee privatekey | wg pubkey > publickey

La clé publique de chaque machine sera à communiquer à l'autre pour établir la relation de confiance. La clé privée, elle, ne quitte jamais sa machine.

Configurer le serveur DEBIAN13-OVH

/etc/wireguard/wg0.conf :

ini
[Interface]
Address = 10.10.0.1/24
ListenPort = 51820
PrivateKey = <clé_privée_DEBIAN13-OVH>
SaveConfig = false

PostUp = sysctl -w net.ipv4.ip_forward=1
PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

[Peer]
# DEBIAN13-LAB
PublicKey = <clé_publique_DEBIAN13-LAB>
AllowedIPs = 10.10.0.2/32

Remplacer eth0 par le nom réel de l'interface publique (ip a).

Ouvrir le port côté OVH

Deux niveaux de pare-feu à traiter :

  1. Firewall réseau OVH (manager OVH) : ouvrir le port UDP 51820 en entrée sur 11.22.33.44.
  2. Pare-feu local :
bash
sudo ufw allow 51820/udp

Activer le forwarding IP de façon persistante

bash
echo 'net.ipv4.ip_forward=1' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

Configurer le client DEBIAN13-LAB

/etc/wireguard/wg0.conf :

ini
[Interface]
Address = 10.10.0.2/24
PrivateKey = <clé_privée_DEBIAN13-LAB>

[Peer]
# DEBIAN13-OVH
PublicKey = <clé_publique_DEBIAN13-OVH>
Endpoint = 11.22.33.44:51820
AllowedIPs = 10.10.0.0/24
PersistentKeepalive = 25

PersistentKeepalive est indispensable ici : DEBIAN13-LAB est derrière un NAT/box, ce paramètre maintient le mapping NAT ouvert vis-à-vis du serveur.

Démarrer le tunnel

Sur les deux machines :

bash
sudo systemctl enable --now wg-quick@wg0
sudo wg show

Vérifier la connectivité

bash
# Depuis DEBIAN13-LAB
ping 10.10.0.1

# Depuis DEBIAN13-OVH
ping 10.10.0.2

Un ping réussi dans les deux sens confirme que le tunnel est opérationnel.

Ajouter un noeud au tunnel

Exemple : intégration d'un troisième noeud, DEBIAN13-PI (Raspberry Pi), en 10.10.0.3.

Sur le nouveau noeud

bash
cd /etc/wireguard
umask 077
wg genkey | tee privatekey | wg pubkey > publickey

/etc/wireguard/wg0.conf :

ini
[Interface]
Address = 10.10.0.3/24
PrivateKey = <clé_privée_DEBIAN13-PI>

[Peer]
# DEBIAN13-OVH
PublicKey = <clé_publique_DEBIAN13-OVH>
Endpoint = 11.22.33.44:51820
AllowedIPs = 10.10.0.0/24
PersistentKeepalive = 25

Sur DEBIAN13-OVH

Ajouter un bloc [Peer] dans wg0.conf :

ini
[Peer]
# DEBIAN13-PI
PublicKey = <clé_publique_DEBIAN13-PI>
AllowedIPs = 10.10.0.3/32

Recharger la config sans couper le tunnel existant :

bash
sudo wg syncconf wg0 <(wg-quick strip wg0)

Alternative à chaud, sans toucher au fichier dans l'immédiat (à reporter ensuite dans wg0.conf pour survivre à un redémarrage) :

bash
sudo wg set wg0 peer <clé_publique_DEBIAN13-PI> allowed-ips 10.10.0.3/32

Démarrer le tunnel sur le nouveau noeud

bash
sudo systemctl enable --now wg-quick@wg0

Supprimer un noeud du tunnel

Sur DEBIAN13-OVH

À chaud, sans redémarrer l'interface :

bash
sudo wg set wg0 peer <clé_publique_du_noeud_à_retirer> remove

De façon persistante, supprimer le bloc [Peer] correspondant dans wg0.conf, puis :

bash
sudo wg syncconf wg0 <(wg-quick strip wg0)

Sur le noeud retiré

bash
sudo systemctl disable --now wg-quick@wg0
sudo rm /etc/wireguard/wg0.conf

Si le noeud ne doit plus jamais rejoindre le tunnel, supprimer également privatekey et publickey.

Le ER605 intègre un client/serveur WireGuard natif, mais uniquement à partir des hardware versions V2 et V2.60 — la V1 ne le supporte pas et n'affichera même pas le menu correspondant. Vérifier sa version sur l'étiquette sous le boîtier ou dans Overview de l'interface web avant de commencer.

Contrairement aux noeuds précédents, le ER605 est un routeur passerelle : il ne rejoint pas le tunnel comme un simple hôte, mais comme une porte d'entrée vers tout un réseau LAN placé derrière lui (192.168.50.0/24 dans cet exemple). Il sera nommé ER605-LAB et prendra l'IP 10.10.0.4/32 sur l'overlay.

RôleMachineIP publiqueIP WireGuardRéseau annexe
RouteurER605-LABdynamique/fixe10.10.0.4/32192.168.50.0/24

Configurer l'interface WireGuard sur le ER605 (mode standalone)

Dans l'interface web du routeur :

  1. Aller dans VPN > WireGuard, cliquer sur Add pour créer l'interface locale.
  2. Renseigner :
    • Name : ER605-LAB
    • MTU : 1420 (valeur par défaut recommandée)
    • Local IP Address : 10.10.0.4/32
    • Listen Port : laisser vide ou par défaut, ce champ ne sert que si le ER605 doit lui-même écouter des connexions entrantes, ce qui n'est pas le cas ici (il est client de DEBIAN13-OVH).
    • Private Key / Public Key : générées automatiquement par l'interface — copier la clé publique affichée, elle sera nécessaire côté serveur.

Ajouter le peer DEBIAN13-OVH côté ER605

Toujours dans VPN > WireGuard, section Peers, cliquer sur Add :

  • Interface : ER605-LAB (celle créée à l'étape précédente)
  • Public Key : clé publique de DEBIAN13-OVH
  • Endpoint : 11.22.33.44
  • Endpoint Port : 51820
  • Allowed Address : 10.10.0.0/24 (accès à l'ensemble de l'overlay)
  • Persistent Keepalive : 25

Ajouter le peer ER605-LAB côté DEBIAN13-OVH

Dans /etc/wireguard/wg0.conf sur le serveur :

ini
[Peer]
# ER605-LAB
PublicKey = <clé_publique_ER605-LAB>
AllowedIPs = 10.10.0.4/32, 192.168.50.0/24

L'ajout de 192.168.50.0/24 dans AllowedIPs est ce qui permet à DEBIAN13-OVH (et donc, si le routage est autorisé, aux autres noeuds du tunnel) de joindre les machines du réseau local derrière le ER605, et pas uniquement le routeur lui-même.

Recharger la configuration sans couper le tunnel :

bash
sudo wg syncconf wg0 <(wg-quick strip wg0)

Activer le routage côté ER605

Pour que le LAN 192.168.50.0/24 soit effectivement accessible via le tunnel, vérifier que :

  • L'interface WireGuard est bien activée (Status: Enable).
  • Une route est présente (automatiquement ajoutée par Omada dans la majorité des cas) faisant passer le trafic vers 10.10.0.0/24 par l'interface ER605-LAB.
  • Si des ACL inter-VLAN sont en place sur le ER605, s'assurer qu'elles n'interceptent pas le trafic destiné au tunnel avant qu'il n'atteigne l'interface WireGuard.

Vérification

bash
# Depuis DEBIAN13-OVH
ping 10.10.0.4
ping 192.168.50.1   # ex. IP LAN du ER605

Depuis l'interface web du ER605, VPN > WireGuard affiche l'état du handshake et le volume de données échangées avec DEBIAN13-OVH, équivalent à wg show côté Linux.

Si l'infrastructure évolue vers plusieurs sites Omada, la configuration Site-to-Site WireGuard via le contrôleur Omada SDN (Settings > VPN > WireGuard) permet de gérer les peers de façon centralisée plutôt que routeur par routeur — à envisager si un deuxième ER605 ou une autre passerelle Omada rejoint le tunnel.

Automatiser la rotation des clés (optionnel)

Faire tourner régulièrement les paires de clés réduit la fenêtre d'exposition en cas de compromission silencieuse d'un noeud. Le principe : régénérer une nouvelle paire de clés sur le noeud concerné, puis mettre à jour uniquement son bloc [Peer] côté serveur, sans toucher aux autres pairs ni couper le tunnel global.

Exemple de script rotate-wg-key.sh à exécuter sur un noeud (ex. DEBIAN13-LAB) :

bash
#!/usr/bin/env bash
set -euo pipefail

WG_DIR="/etc/wireguard"
OLD_PUBKEY=$(cat "$WG_DIR/publickey")

cd "$WG_DIR"
umask 077
wg genkey | tee privatekey.new | wg pubkey > publickey.new

NEW_PUBKEY=$(cat publickey.new)

# Met à jour l'interface locale à chaud
sudo wg set wg0 private-key <(cat privatekey.new)

mv privatekey.new privatekey
mv publickey.new publickey

echo "Ancienne clé publique : $OLD_PUBKEY"
echo "Nouvelle clé publique : $NEW_PUBKEY"
echo "-> à reporter manuellement (ou via Ansible) sur DEBIAN13-OVH"

Ce script peut être déclenché via un timer systemd (rotate-wg-key.timer, par exemple tous les 90 jours). La contrainte principale reste la synchronisation : le serveur doit connaître la nouvelle clé publique avant que l'ancienne ne soit invalidée localement, sous peine de coupure du tunnel pour ce noeud. C'est cette contrainte qui rend l'intégration Ansible (section suivante) particulièrement pertinente : elle permet de pousser la nouvelle clé publique sur DEBIAN13-OVH de façon coordonnée et traçable.

Intégration dans un playbook Ansible

L'objectif est de gérer la configuration WireGuard de façon déclarative : génération des clés si absentes, templating du fichier wg0.conf, et rechargement du service — le tout de manière idempotente et reproductible sur l'ensemble des noeuds.

Arborescence du rôle

roles/wireguard/
├── defaults/
│   └── main.yml
├── tasks/
│   └── main.yml
├── templates/
│   └── wg0.conf.j2
└── handlers/
    └── main.yml

defaults/main.yml

yaml
wg_interface: wg0
wg_port: 51820
wg_address: "10.10.0.2/24"
wg_peers: []   # liste des peers à injecter, définie par host_vars

tasks/main.yml

yaml
---
- name: Installer WireGuard
  apt:
    name: wireguard
    state: present
    update_cache: true

- name: Vérifier la présence d'une clé privée existante
  stat:
    path: /etc/wireguard/privatekey
  register: wg_privkey_stat

- name: Générer la paire de clés si absente
  shell: |
    umask 077
    wg genkey | tee /etc/wireguard/privatekey | wg pubkey > /etc/wireguard/publickey
  when: not wg_privkey_stat.stat.exists

- name: Lire la clé privée générée
  slurp:
    src: /etc/wireguard/privatekey
  register: wg_privkey_raw

- name: Lire la clé publique générée
  slurp:
    src: /etc/wireguard/publickey
  register: wg_pubkey_raw

- name: Déployer la configuration wg0.conf
  template:
    src: wg0.conf.j2
    dest: "/etc/wireguard/{{ wg_interface }}.conf"
    owner: root
    group: root
    mode: "0600"
  notify: Recharger WireGuard

- name: Activer et démarrer le service
  systemd:
    name: "wg-quick@{{ wg_interface }}"
    enabled: true
    state: started

templates/wg0.conf.j2

jinja
[Interface]
Address = {{ wg_address }}
{% if wg_port is defined %}
ListenPort = {{ wg_port }}
{% endif %}
PrivateKey = {{ wg_privkey_raw.content | b64decode | trim }}

{% for peer in wg_peers %}
[Peer]
# {{ peer.name }}
PublicKey = {{ peer.public_key }}
AllowedIPs = {{ peer.allowed_ips }}
{% if peer.endpoint is defined %}
Endpoint = {{ peer.endpoint }}
{% endif %}
{% if peer.persistent_keepalive is defined %}
PersistentKeepalive = {{ peer.persistent_keepalive }}
{% endif %}

{% endfor %}

handlers/main.yml

yaml
---
- name: Recharger WireGuard
  shell: "wg syncconf {{ wg_interface }} <(wg-quick strip {{ wg_interface }})"
  args:
    executable: /bin/bash

Déclaration des peers par hôte (host_vars/debian13-ovh.yml)

yaml
wg_address: "10.10.0.1/24"
wg_peers:
  - name: DEBIAN13-LAB
    public_key: "<clé_publique_DEBIAN13-LAB>"
    allowed_ips: "10.10.0.2/32"
  - name: DEBIAN13-PI
    public_key: "<clé_publique_DEBIAN13-PI>"
    allowed_ips: "10.10.0.3/32"

L'ajout ou la suppression d'un noeud revient alors à modifier wg_peers dans le host_vars du serveur puis à relancer le playbook — le handler Recharger WireGuard se charge d'appliquer la nouvelle configuration sans interrompre les pairs déjà connectés.

Publié sous lience MIT.