J’ai eu l’occasion d’expérimenter récemment avec Wireguard sous FreeBSD. Globalement, je suis plutôt satisfait du mode de fonctionnement, mais aussi de la façon dont cela a été packagé de manière générale sous FreeBSD.
Pour mon usage, j’avais besoin de créer un VPN point à point entre deux machines, une derrière une Livebox et l’autre chez un hébergeur. Je voulais que la seconde ait accès à des services hébergés sur la première, comme Postfix et nginx. Ces services sont configurés sur la première machine pour écouter sur une liste explicite d’adresses IP.
Mais au redémarrage de la machine chez l’hébergeur, Postfix et nginx
refusaient de démarrer, car l’interface wg0
créée par Wireguard n’existait
pas encore. Ce problème est dû au lancement trop tardif du script
/etc/rc.d/wireguard
à l’initialisation du système.
Comment faire, alors, pour que les VPN Wireguard soient montés plus tôt ? La
solution consiste à les démarrer en même temps que les autres interfaces
réseau du système, via le script /etc/rc.d/netif
et des éléments de
configuration dans /etc/rc.conf
. La méthode que je vais vous montrer est en
production chez moi depuis quelques mois, et je n’ai pas rencontré de problème
particulier jusqu’à maintenant.
Prérequis
Pour commencer, installez Wireguard sur les deux côtés :
# pkg install wireguard-kmod wireguard-tools
Il faudra ensuite choisir un plan d’adressage pour le VPN et générer des clefs et un secret partagé.
Adressage
Pour mon VPN, j’ai choisi d’adresser les deux extrémités en IPv6, en utilisant les adresses locales uniques (Unique Local Addresses, ULA), dans la plage fc00::/7. Pour avoir la quasi-garantie d’une plage unique, il faut utiliser un générateur pour obtenir un /48 privé. J’obtiens par exemple fdd9:835d:6e41::/48.
Puisqu’il n’y a que deux machines dans ce réseau, alors d’après la RFC 6164, il est tout à fait possible (et souhaitable) de se restreindre à un /127 pour adresser les deux côtés. Mais alors, quelles valeurs choisir pour les 64 bits les plus à droite de l’adresse ?
Au début, j’avais choisi fdd9:835d:6e41::1 et fdd9:835d:6e41::2. Mais ce choix est mauvais. Sauriez-vous pourquoi ? Rassurez-vous, il m’a fallu plusieurs semaines pour comprendre.
On pourrait alors choisir fdd9:835d:6e41:: et fdd9:835d:6e41::1 à la place. Là aussi, le choix est mauvais, car la première des deux adresses a ses 64 bits de poids faible à zéro et leur usage en tant qu’adresse unicast est déconseillé. Pour la même raison, l’intervalle fdd9:835d:6e41::ffff:ffff:ffff:ff7f à fdd9:835d:6e41::ffff:ffff:ffff:ffff est déconseillé également.
J’ai donc fini par choisir fdd9:835d:6e41::a et fdd9:835d:6e41::b. Ainsi, pour la suite, j’appellerai les deux côtés « A » et « B », d’après l’identifiant d’interface que je leur donnerai dans le /127 susmentionné.
Dans mon cas, le côté B est celui qui initie la connexion avec le côté A. Les deux côtés peuvent initier la connexion, tout comme avec IPSec, mais dans mon cas, le côté B se trouve derrière un NAT et son adresse IP publique peut changer, alors que le côté A a une adresse IPv4 publique fixe.
Génération des clefs
Ensuite, il faut créer les clefs publiques et privées de chaque côté. La commande ci-dessous génère deux fichiers contenant des clefs représentés en base64.
# umask 077
# wg genkey | tee clef_privee | wg pubkey > clef_publique
Puis, sur l’un ou l’autre des côtés, générez un secret partagé entre les deux stations, qui se trouvera également représenté en base64 dans un fichier :
root@A:~# umask 077
root@A:~# wg genpsk > secret_partage
Nous pouvons ensuite passer à la configuration proprement dite.
Configuration
La page man de rc.conf(5) explique que, pour chaque interface à
démarrer, netif
cherche un script nommé /etc/start_if.<interface>
. S’il
existe, alors il est exécuté avant de configurer les adresses. Nous allons
donc créer de chaque côté un script /etc/start_if.wg0
, destiné à l’interface
créée pour le VPN Wireguard. Cette interface est configurée en passant par
l’utilitaire wg(8).
Côté B (initiateur)
Ainsi, sur le côté B, qui initie la connexion, le script /etc/start_if.wg0
est le suivant :
#!/bin/sh /usr/local/bin/wg setconf wg0 /dev/stdin <<EOF [Interface] ListenPort = 51820 PrivateKey = clef privée de B [Peer] AllowedIPs = fdd9:835d:6e41::a/128 PublicKey = clef publique de A PreSharedKey = secret partagé Endpoint = adresse ou nom d’hôte de A:51820 PersistentKeepAlive = 30 EOF
Ici, j’ai choisi d’ajouter l’option PersistentKeepAlive
, car il s’agit du
côté qui initie la connexion et que je dois traverser des NAT IPv4. Et dans ce
script, il faut utiliser le chemin complet vers l’utilitaire wg, car
/usr/local/bin
ne se trouve pas dans le PATH
du shell qui l’exécute.
Étant donné que le fichier contient des secrets, celui-ci doit impérativement être chmod 700 ou la confidentialité de votre VPN sera compromise.
On peut ensuite ajouter les éléments de configuration suivants au rc.conf du côté B :
cloned_interfaces="wg0" # si cette variable existe déjà, ajoutez "wg0"
ifconfig_wg0_ipv6="inet6 fdd9:835d:6e41::b prefixlen 127"
C’est tout !
Côté A
Pour le côté A, l’idée est la même, mais le contenu du
script /etc/start_if.wg0
est légèrement différent :
#!/bin/sh /usr/local/bin/wg setconf wg0 /dev/stdin <<EOF [Interface] ListenPort = 51820 PrivateKey = clef privée de B [Peer] AllowedIPs = fdd9:835d:6e41::b/128 PreSharedKey = secret partagé PublicKey = clef publique de A
Là encore, pensez à faire un chmod 700
sur ce fichier pour éviter de
compromettre votre VPN.
Les éléments à ajouter dans rc.conf sont similaires :
cloned_interfaces="wg0" # si cette variable existe déjà, ajoutez "wg0"
ifconfig_wg0_ipv6="inet6 fdd9:835d:6e41::a prefixlen 127"
Démarrage du VPN
Enfin, de chaque côté, il ne reste plus qu’à démarrer l’interface wg0…
# service netif start wg0
…et de tester en « pingant » l’autre côté (ping6
sous FreeBSD jusqu’à 12,
ping
tout court à partir de FreeBSD 13) :
root@A:~# ping6 fdd9:835d:6e41::b
PING6(56=40+8+8 bytes) fdd9:835d:6e41::a --> fdd9:835d:6e41::b
16 bytes from fdd9:835d:6e41::b, icmp_seq=0 hlim=64 time=60.610 ms
16 bytes from fdd9:835d:6e41::b, icmp_seq=1 hlim=64 time=79.763 ms
16 bytes from fdd9:835d:6e41::b, icmp_seq=2 hlim=64 time=59.891 ms
16 bytes from fdd9:835d:6e41::b, icmp_seq=3 hlim=64 time=59.442 ms
^C
--- fdd9:835d:6e41::b ping6 statistics ---
4 packets transmitted, 4 packets received, 0.0% packet loss
round-trip min/avg/max/std-dev = 59.442/64.926/79.763/8.576 ms
Conclusion
Je vous ai donc présenté une méthode pour lancer un VPN Wireguard le plus tôt possible au démarrage de FreeBSD.
Cette méthode ne se limite pas qu’aux VPN point à point, mais il se trouve que dans mon cas d’usage, un lien entre deux machines suffisait. Si nous avions eu plusieurs machines initiatrices de connexions à un seul et même point central, les configurations de chaque initiateur serait identique (à la clef privée près) et le point central aurait une déclaration par initiateur autorisé.
Il y a aussi quelques opportunités de mettre les choses un peu plus au propre
dans ce système. Tout d’abord, le script /etc/start_if.wg0
contient toute la
configuration pour l’interface wg0
, dans un seul fichier. Je peux comprendre
que ce choix puisse déranger certains. Mais adapter ce script pour qu’il lise
des secrets ou un fichier de configuration complet dans
/usr/local/etc/wireguard
est tout à fait possible.
De même, le nom wg0
est codé en dur dans le fichier ; mais là encore, il est
possible de modifier le script en examinant le nom sous lequel il est appelé
(variable $0
) et d’en dériver le nom de l’interface à démarrer (par la
transformation ${0##*.}
). Ainsi, s’il faut démarrer un autre VPN Wireguard,
en imaginant que l’interface s’appelle wg1
, on pourrait finir par juste
pouvoir s’en sortir en faisant de /etc/start_if.wg1
un lien symbolique vers
/etc/start_if.wg0
.
Dans tous les cas, je vous ai montré qu’il est tout à fait possible de passer
par netif
, pour démarrer les VPN Wireguard en même temps que les autres
interfaces réseau, dès l’initialisation du système. Ainsi, les interfaces wg
se comportent beaucoup plus comme des interfaces natives du système, ce qui
est élégant et appréciable.
Commentaires
Poster un commentaire
Poster un commentaire