Une ACL par IP du VPN derrière un split-tunnel ne voit pas l’IP du VPN : le nom public du service résout vers l’IP publique du serveur, et comme seul le /24 du mesh passe dans le tunnel, la requête sort par la box. Apache reçoit l’IP publique de la maison, l’ACL répond 403, alors que le tunnel est bien monté. Le correctif est côté DNS : faire résoudre ces noms vers l’IP mesh du serveur, et seulement eux, quand le tunnel est actif.
Le contexte : des Studios Supabase réservés au mesh
Depuis le 25 septembre 2026, les Studios Supabase auto-hébergés sur srv-a ne sont plus derrière une Basic Auth : ils sont réservés aux postes admin du mesh WireGuard. Le pourquoi (le Studio n’a pas d’authentification propre, et Kong l’expose aussi sur l’hôte API) est détaillé dans l’article sur le Studio Supabase exposé via Kong.
La règle Apache tient dans un snippet inclus par chaque vhost de Studio :
# /etc/apache2/supabase-acl-studio.conf
Require ip 10.8.0.2 10.8.0.3
10.8.0.2 est mon PC en split-tunnel (wg-srv-s), 10.8.0.3 le même PC en full-tunnel (wg-srv-f). Depuis internet : 403, c’est voulu.
Pourquoi l’ACL par IP du VPN renvoie 403 en split-tunnel ?
Un split-tunnel est un VPN qui ne route que certains sous-réseaux, ici AllowedIPs = 10.8.0.0/24. Tout le reste, y compris l’IP publique de srv-a, part par la route par défaut de la machine, donc par la box.
Le nom supabase.dev.example.com est public : il résout vers 203.0.113.10. Le trajet est alors :
- le navigateur résout le nom →
203.0.113.10; 203.0.113.10n’est pas dans10.8.0.0/24→ pas de tunnel ;- la requête sort par la box, Apache voit
198.51.100.20; 198.51.100.20n’est pas dans la liste →403.
Une ACL par IP de VPN ne contrôle pas « qui a le tunnel monté », mais « par où est arrivé le paquet » : en split-tunnel, c’est le DNS qui décide.
Le full-tunnel n’a pas le problème : tout passe dans le tunnel, y compris vers l’IP publique, et Apache voit 10.8.0.3. C’est le profil du quotidien, le split, qui coince.
Le correctif : rewrite DNS et routing domains
Deux pièces, une côté serveur, une côté poste.
Côté serveur, un AdGuard Home écoute sur l’IP mesh de srv-a (10.8.0.1, interface web sur :8080). Dans Filters → DNS rewrites, chaque nom de Studio est réécrit vers 10.8.0.1. Interrogé par le mesh, AdGuard répond l’IP mesh, et la requête HTTP passe dans le tunnel.
Côté poste, il faut que seuls ces noms soient résolus par AdGuard quand le tunnel est monté, sans faire passer toute la résolution du PC par le VPN. C’est le rôle des routing domains de systemd-resolved : un domaine préfixé par ~ envoie ses requêtes vers le DNS d’une interface donnée, sans devenir un domaine de recherche.
nmcli connection modify wg-srv-s ipv4.dns 10.8.0.1 \
ipv4.dns-search "~supabase.dev.example.com,~supabase-dev.dev.example.com,~supabase.app.example.net"
nmcli connection up wg-srv-s
resolvectl domain wg-srv-s # vérifier
Ajouter un Studio, c’est donc trois endroits : le vhost Apache avec le snippet, le rewrite AdGuard, et le routing domain de wg-srv-s.
Vérifier avant d’accuser Apache
Le premier contrôle n’est pas dans les logs Apache, il est dans la résolution :
resolvectl query supabase.dev.example.com # doit répondre 10.8.0.1
curl -so/dev/null -w '%{http_code}' https://supabase.dev.example.com/
Depuis internet, curl renvoie 403. Tunnel monté et nom résolu en 10.8.0.1, il renvoie 307 : la requête a passé l’ACL. Si resolvectl query répond l’IP publique, le 403 est normal : le routing domain manque ou le tunnel est descendu.
Ce piège est cousin de celui des deux VPN sur le même /24 : dans les deux cas, la bonne question est « par quelle interface sort ce paquet ? », pas « le VPN est-il connecté ? ».
Lessons learned
- En split-tunnel, un nom public sort par la box. Seul le sous-réseau du VPN passe dans le tunnel, l’IP publique du serveur n’en fait pas partie.
- Une ACL par IP de VPN dépend du DNS autant que du tunnel. Le serveur voit l’IP de l’interface par laquelle le paquet est sorti, pas l’état du VPN.
- Un rewrite DNS vers l’IP mesh rend le service joignable par le tunnel. AdGuard Home le fait par nom, sans toucher au DNS public.
- Les routing domains
~domainelimitent le DNS du VPN aux noms utiles.ipv4.dns-searchsur la connexion NetworkManager, contrôlé parresolvectl domain. resolvectl queryavant les logs du serveur. Si le nom ne résout pas vers l’IP mesh, le 403 est la réponse attendue.