GL.iNetルーターでVPNクライアント(WireGuard)をポリシーモードで使いつつ、同じルーター上でTailscaleも動かしている環境で、不可解な現象に遭遇しました。
「ポリシーモードで特定のIP宛てだけVPN経由にしているはずなのに、TailscaleのP2P接続までVPN経由になってしまう」
この記事では、その原因の調査過程と、最終的な解決方法(ファイアウォールによる対処)を記録します。同じ構成(GL.iNet + VPNクライアント + Tailscale)で悩んでいる人の参考になればと思います。
環境
- ルーター:GL.iNet(GL-MT3000、OpenWrtベース)
- VPNクライアント:WireGuard(GL.iNetのポリシーモードで特定IPのみVPN経由)
- Tailscale:ルーター自身にインストール(ルーターがTailscaleノード)
現象
別PC(Tailscaleインストール済み)から、自宅のルーター(Tailscaleノード)に対して接続確認をすると、
tailscale ping <相手>
の結果が、VPN側のIP との direct 接続になっていました。ポリシーモードで特定IPだけVPNに流す設定にしているのに、Tailscaleの通信までVPNを通っているのは明らかにおかしい挙動です。
調査1:ルーティングテーブルを確認
まずルーティングを確認しました。
ip route show table all | grep -i tailscale
100.x.x.x dev tailscale0 table 52
100.100.100.100 dev tailscale0 table 52
100.64.0.0/10 dev tailscale0 scope link
local 100.x.x.x dev tailscale0 table local proto kernel scope host
ip rule show
50: from all to 100.100.100.100 lookup 52
...
5270: from all lookup 52
6000: from all fwmark 0x1000/0xf000 lookup 1001
...
10000: from <VPN内側IP> lookup 1001
20000: from all to <VPN内側IP> lookup 1001
ここで、table 1001 がGL.iNetのVPNポリシー用、table 52 がTailscale用と分かりました。VPNポリシーは fwmark(0x1000)ベース で振り分けていることも判明しました。
調査2:fwmarkを付けているルールを特定
VPN行きのマーク(0x1000)を、どこで付けているか追います。
iptables -t mangle -L -n -v | grep -B 5 -A 5 "0x1000\|MARK"
注目すべきルールが見つかりました。
Chain TUNNEL10_ROUTE_POLICY
MARK ... /* マイIP */ match-set dst_net10 dst MARK xset 0x1000/0xf000
dst_net10 というIPセットに含まれる宛先への通信に、VPN行きのマークを付けていました。これがポリシーモードの「特定IPのみVPN経由」の実体です。
中身を確認します。
ipset list dst_net10
リストには、VPN経由でアクセスしたい自分のサーバー群のIPが並んでいました。しかし、Tailscaleのピア相手のIPも、VPN内側のIPも、このリストには含まれていませんでした。つまり、ポリシーのIP指定が直接の原因ではありませんでした。
調査3:プロセス単位のポリシーも確認
GL.iNetのポリシーには、プロセスのGID単位で振り分けるルールもありました。
owner GID match 20000 MARK xset 0x1000/0xf000 (VPN行き)
owner GID match 10000 MARK xset 0x8000/0xf000 (WAN行き)
tailscaledのGIDを確認します。
cat /proc/$(pidof tailscaled)/status | grep -i gid
Gid: 0 0 0 0
tailscaledは GID 0(root) で動いており、どちらのルールにもマッチしませんでした。プロセス単位ポリシーも原因ではありません。
原因の特定:複数のグローバルIPをTailscaleが候補にしていた
ここでインターフェースを確認したのが決め手でした。
ip addr show
24: wgclient1: <POINTOPOINT,...> mtu 1350
inet <VPN内側IP>/32 scope global wgclient1
25: tailscale0: <POINTOPOINT,...> mtu 1280
inet 100.x.x.x/32 scope global tailscale0
ポイントは、VPNクライアントのインターフェース(wgclient1)に、グローバルIPが直接割り当てられていることでした。
Tailscaleは、NAT越え(P2P直結)のために、ルーター上に存在するすべての到達可能そうなIPアドレスを「自分のエンドポイント候補」として相手に伝えます。通常のWAN側IPだけでなく、VPNインターフェース(wgclient1)のグローバルIPも候補に含めてしまい、相手側がそのVPN側IPへの到達に成功した結果、それが direct 接続として採用されていたのです。
つまり、GL.iNetのポリシー設定の不備ではなく、Tailscale自身が「使えるIPはどれでも使う」という挙動を取ったことが原因でした。
実測で裏付け
VPN(wgclient1)に実際にトラフィックが流れているか、カウンタで確認しました。
cat /proc/net/dev | grep wgclient1
接続中に2回実行して差分を見ると、wgclient1の送受信バイトが増加しており、確かにVPN経由でデータが流れていることが確認できました。
(補足:WireGuardは何もしていなくても接続維持のキープアライブで微増し続けます。実データかどうかは、接続時とアイドル時の増加量を比較して判断します。)
解決方法:VPN経由のTailscale通信をファイアウォールで遮断
Tailscaleの管理画面やCLIには、「特定のローカルIPをエンドポイント候補から除外する」直接的な設定は用意されていません。エンドポイント探索はOSレベルのtailscaledの責務であり、管理画面のACLは論理的なアクセス制御を扱う別レイヤーだからです。
そこで、OS側のファイアウォールで、VPNインターフェース経由のTailscale通信(UDP 41641)を物理的に遮断します。
iptables -I OUTPUT -o wgclient1 -p udp --dport 41641 -j DROP
iptables -I INPUT -i wgclient1 -p udp --sport 41641 -j DROP
これで、TailscaleのUDP通信がwgclient1(VPN)を使えなくなり、Tailscaleは自動的に通常のWAN側へフォールバックします。適用後に tailscale ping で確認すると、VPN側IPではなく通常WAN側のIPでdirect接続になりました。
恒久化
iptables -I で入れたルールは再起動で消えるため、OpenWrt/GL.iNetでは /etc/firewall.user に追記して永続化します。
cat >> /etc/firewall.user << 'EOF'
# Tailscale通信をVPN(wgclient1)経由にさせないためのブロック
iptables -C OUTPUT -o wgclient1 -p udp --dport 41641 -j DROP 2>/dev/null || iptables -I OUTPUT -o wgclient1 -p udp --dport 41641 -j DROP
iptables -C INPUT -i wgclient1 -p udp --sport 41641 -j DROP 2>/dev/null || iptables -I INPUT -i wgclient1 -p udp --sport 41641 -j DROP
EOF
-C(チェック)で既存ルールの有無を確認してから追加するので、ファイアウォール再起動を繰り返してもルールが重複しません。
反映と確認:
/etc/init.d/firewall restart
iptables -t filter -L OUTPUT -n -v --line-numbers | head -5
DROP udp -- * wgclient1 ... dpt:41641 の行が表示されれば成功です。確実を期すなら、ルーター自体を再起動してルールが残るか確認するとよいです。
注意点:経路はキャッシュされる
一度ファイアウォールで遮断した後にルールを外しても、すぐにVPN経由に戻るとは限りません。Tailscaleは一度確立した良好な経路を記憶し、問題なく動いている限りわざわざ別経路に切り替えないためです。
しかしこれは「たまたま良い経路で安定している」だけで保証された状態ではなく、ルーターやTailscaleの再起動、ネットワークの再接続をきっかけに、再びVPN側を選んでしまう可能性があります。ファイアウォールルールは外さず、恒久化したまま残しておくのが正解です。
まとめ
- GL.iNet + VPNクライアント + Tailscaleの構成で、TailscaleがVPN経由になることがある
- 原因はポリシー設定ではなく、Tailscaleがルーター上の複数のグローバルIP(WAN側とVPN側)を両方エンドポイント候補にしてしまうこと
- Tailscale公式には特定ローカルIPを除外する設定がないため、OS側のファイアウォールでVPN経由のUDP 41641を遮断するのが現実的な解決策
/etc/firewall.userに追記して恒久化し、経路キャッシュの都合上ルールは残しておく
ニッチな構成ですが、VPNクライアントとTailscaleを同居させると起こりうる問題です。同じ症状で原因が分からず悩んでいる場合は、まず ip addr show でVPNインターフェースにグローバルIPが付いていないかを確認してみてください。

コメント