lightscale/soudan.md

40 KiB
Raw Blame History

VPNセルフホスト設計相談

User: Soma Nakamura (namamurasub@gmail.com)
Created: 1/19/2026 21:48:40
Updated: 1/20/2026 0:41:25
Exported: 1/20/2026 0:51:17
Link: https://chatgpt.com/c/696e2828-1440-8330-b0b7-14ed22c5f05c

Prompt:

簡単にセルフホスト可能なVPNソフトウェアを作るに当たり、以下のようなものを考えている。

  1. 普通のクライアント同士は、なるべく直接p2p接続を行う。
  2. グローバルIPがある場所に「サーバー」を置く。そいつが設定の管理や鍵の管理、TURNっぽいものNAT配下から抜けるためにやTURNSファイヤウォール突破のため。この場合のみこのサーバーがリレーを行うをやったりする。NAT46みたいなことができるようになれば、サーバーはv6グローバルだけあれば良くなるので、更における場所が広がる。また、これをexit nodeにするような設定もできると良い。あと、このサーバー上の設定として、各クライアントに名前をつけてDNS広報などもできるようになると良い。 こういうものを作るにあたり、どういうふうにしたらよいか以下の質問に答えてほしい。
  3. 鍵の管理などはtincみたいに分散させるべきか、集中していたほうがグローバルがある場所にあったほうが良いか。
  4. 新しいクライアントの参加時には、どのような手法で行うべきか
  5. TURNとか、様々なNAT越えの手法があるが、どれを使うとよいかどんな環境においてもHTTPSさえ通れば絶対に疎通できることが望ましい。
  6. 各クライアントがp2p接続する際のプロトコルはwireguardでよいか適当にやるとフルメッシュにしたいわけで無数のwireguardクライアントが生えて困りそうだが、多分カスタムのインターフェースを作ってそこに集約するのか

Response:

Thought for 11m 26s

あなたが考えている構成は、方向性としてはかなり「現代的なMesh VPN= Control planeは集中、Data planeはP2P、無理ならHTTPSリレー」で、実装方針も立てやすいです。結論から言うと、

  • 鍵“そのもの”(秘密鍵)は各クライアントに保持させる(分散)
  • ただし 鍵配布・ACL・名前/DNS・到達性NAT越えの候補交換といった“調停”は中央サーバ集中
  • NAT越えは ICE/STUN/TURN の考え方を借り、最終フォールバックは 443/TLS 上のリレーDERP的
  • P2Pの暗号化・仮想L3は WireGuard が最適。ただし「WireGuard単体」ではなく、それを動的にオーケストレーションする常駐デーモンが要る

…という設計が、要件セルフホスト容易・P2P優先・HTTPSさえ通れば極力なんとかするに一番素直です。Tailscale/Headscale系がまさにこの分離で動いていて、「Control planeは鍵公開鍵とポリシーを配るだけ、Data planeはWireGuardのメッシュ」という整理が定番になっています。 Tailscale+2GitHub+2

以下、質問に順に答えます。


1. 鍵の管理は分散tinc的か集中か

まず「何の鍵をどこで管理するか」を分解した方がよい

Mesh VPNでは鍵が最低でも2種類あります。

  1. データプレーン鍵実際にトラフィックを暗号化する鍵。WireGuardなら各ードの秘密鍵
  2. コントロールプレーンの認証・承認(誰がネットワークに参加できて、誰と通信できるか、という権限)

このうち、

  • **(1) データプレーンの秘密鍵は“絶対にクライアントから出さない”**のがベストです。
    そうすると中央サーバが侵害されても「盗聴」まではされにくくなりますただし後述の“鍵すり替え”リスクは残る。Tailscaleも「公開鍵だけ交換し、秘密鍵は端末から出ない」と明言しています。 Tailscale+1
  • **(2) 参加管理/ACL/DNS名/経路広告/到達性(候補交換)**は、**集中サーバcoordination server**に置く方が「簡単にセルフホスト」要件に合います。 Tailscale+1

「分散tinc的」のメリット/デメリット(あなたの要件に照らす)

分散のメリット

  • 中央が落ちても(理想的には)参加/鍵配布が継続できる
  • 中央を完全に信頼しない設計に寄せられる

分散の現実的デメリット

  • そもそも NAT越えの“候補交換/到達性観測”のために結局ランデブーが要る(完全分散は難しい)
  • オンボーディングが複雑化(誰が新規ノードを承認するのか)
  • 「HTTPSさえ通れば」の最終フォールバックに 443リレーが必要 → 結局“中央サービス”が必要

推奨(結論)

  • **鍵管理は「秘密鍵は分散(端末保持)」「公開鍵とポリシー配布は集中(サーバ)」**が最適解です。 Tailscale+1
  • さらに“中央を信頼しない”を少しでもやるなら、オプションで
    (a) 管理者署名によるノード鍵承認、または (b) 透明性ログKey Transparency的 を入れると「サーバ侵害→鍵すり替えMITM」耐性が上がります実装コストは上がります

2. 新しいクライアント参加はどう設計すべきか

要件が「簡単にセルフホスト」なので、現実的に安全で運用しやすいブートストラップに寄せるのが良いです。

推奨フロー短命の招待トークンEnrollment / Auth Key方式

  1. 管理者がサーバで 招待トークンを発行
    • TTL例: 10分〜24時間
    • 使い捨て or N回まで
    • 付与するタグ/グループ(例: dev, admin, no-exit
  2. クライアントは初回起動で
    • **デバイス識別鍵(例: Ed25519**を生成(長期)
    • **WireGuard鍵X25519**を生成(ローテーション可能)
  3. クライアントは https://control.example.com に接続し、招待トークンを提示して登録
  4. サーバは
    • ードID発行
    • VPN内IPv4 /32 と v6 /128割当
    • ACL適用
    • “通信してよい相手の公開鍵一覧 + ルート + DNS情報” を配布以後は差分Push

この「中央は公開鍵と設定を配るだけ」の形は、Headscaleの説明WireGuard公開鍵の交換点、IP割当、ルート広告とも一致します。 GitHub

追加で入れると強い安全策

  • 承認待ちモード:トークンで「仮登録」→ 管理者がUI/CLIで承認したら有効化
  • トークンのスコープ
    • 「このトークンで参加した端末は exit node になれない」
    • 「このトークンは特定ユーザ/特定タグにのみ」
  • サーバ真正性TLSLets Encrypt等+ 可能なら 初回にサーバ公開鍵ピン留めTOFUでも可
    “社内Proxyで証明書差し替え”環境もあるので、その場合の扱い明示的にCAを追加する等は設計で決めておく。

3. TURNやNAT越えはどれを使うべきかHTTPSさえ通れば絶対が理想

結論:考え方は ICESTUN+TURNを採用、実装は「段階的フォールバック」が現実的

  • **ICERFC 8445**は「NAT越えのために候補アドレス/経路を集め、疎通チェックして最適経路を選ぶ」枠組みです。ICEはSTUNとTURNを利用する、とRFC自体が書いています。 IETF Datatracker+2IETF Datatracker+2
  • STUNRFC 8489は、NAT越しに「外から見える自分のIP:port」を知ったり、NATバインディング維持のkeepaliveにも使えます。 IETF Datatracker+1
  • TURNRFC 5766 / 更新版RFC 8656は、どうしても直接が無理なときに **中継(リレー)**を提供します。 RFC Editor+1

ただし「VPN用途」だと TURNフル実装が重い

TURNは汎用中継として標準ですが、VPNの“データプレーン”のためにTURNサーバ/クライアントを完全実装するのは工数が増えがちです。
この場合、現実解としては2つあります

A) 標準準拠でいくSTUN + TURN必要ならTCP拡張

  • UDPが通る: STUNで外部アドレス取得 → UDP hole punching
  • UDPが塞がれる: TURN over TCP/TLS を検討
    TURNのTCP割当拡張はRFC 6062が定義しています。 IETF Datatracker+1

B) “HTTPSさえ通れば”を最優先DERP的な「443/TLSリレー」を自作おすすめ

「HTTPS(443/TCP)が通ればとにかく繋ぐ」を狙うなら、TURNより **DERPTCP/TLSで暗号化済みパケットを盲目的に転送するリレー**の方が設計が単純です。

  • Tailscaleは「DERPはTLS上多くは443で動くTCPリレーで、直接繋がらないときに暗号化済みパケットを転送する」と説明しています。 Tailscale+2Tailscale+2
  • そしてファイアウォール観点でも「制御サーバ/DERPリレーはHTTPSで443を使う」とドキュメントにあります。 Tailscale
  • HeadscaleのDERP解説も、DERP relayが HTTPS tcp/443、STUNが udp/3478 という“最低限ポート”の切り方になっています。 headscale.net

あなたの要件に寄せた “段階的フォールバック” のおすすめ順はこうです:

  1. IPv6直両者がグローバルv6なら最強。NAT越え問題が激減
  2. UDP直v4/v6
    • STUNで各自の外部アドレスを把握server-reflexive candidate
    • rendezvousあなたの中央サーバで候補交換
    • 同時送信でhole punchingWireGuardのハンドシェイク自体を“疎通パケット”にしても良い
  3. 任意UPnP/PCP/NAT-PMPでポートマッピング要求(家ルータでは効くことがあるが期待しすぎない)
  4. 最後の砦443/TLSリレーDERP的
    • WebSocket/HTTP2ストリーミングなど“HTTPSっぽい見え方”にして通りやすくする

「HTTPSさえ通れば絶対」についての注意設計上の現実

“絶対”は難しいです。理由は、ネットワークによっては

  • HTTPプロキシ必須CONNECTが要る、認証が要る
  • TLSインスペクション証明書差し替え
  • SNI/宛先制限(特定ドメイン以外禁止)
  • キャプティブポータル
    などがあるためです。

ただし、**「443/TCPで外向きTLSが通る」**という前提が満たされる範囲では、DERP的リレーは非常に強いアプローチです。 Tailscale+1


4. P2PのプロトコルはWireGuardでよいかフルメッシュで無数に増えないか

結論WireGuardで良い。増えるのは「インターフェース」ではなく「peerエントリ」

WireGuardは「インターフェース例: wg01つ」に対して、複数peerをぶら下げるモデルです。
WireGuard公式のQuick Startでもインターフェースを作ってIPを付ける流れが説明されています。 WireGuard
またWireGuardはNoiseフレームワーク等の現代的暗号を使う、という説明とホワイトペーパーもあります。 WireGuard+1

フルメッシュでの基本設計(典型)

  • 各ノードに VPN内アドレスを1つだけ割り当てる
    • IPv4: /32例: 100.64.0.10/32
    • IPv6: /128例: fd7a:115c:a1e0::10/128
  • 各peerのAllowedIPsは「そのードの/32(/128)だけ」にする
    AllowedIPsが重ならないのでルーティングが破綻しない
  • WireGuardのAllowedIPsは「送信時の宛先IPからpeerを選ぶ/受信時のソースIPをフィルタする」用途を兼ねる、という説明がホワイトペーパーにあります。 WireGuard

この形だと、フルメッシュでも「wg0 1つ + peerがN-1個」です。

ただし WireGuard“単体”ではNAT越え・経路切替ができない

WireGuard自体は「UDPで相手のEndpointへ投げる」だけで、

  • STUNして外部アドレスを発見
  • 経路が変わったらEndpointを更新
  • UDPが無理なら443/TLSに逃がす
    みたいなことは“外側のソフト”がやる必要があります。

なのであなたが言っている「カスタムのインターフェースを作って集約するのか」はだいたい合っていて、実装の形は2択です

方式AカーネルWireGuard + 制御デーモンnetlinkでpeer更新

  • パフォーマンス良い
  • でも **“TCP/TLSリレーでWireGuardパケットを運ぶ”**の統合が難しくなるWGは基本UDP

方式Buserspace WireGuardwireguard-go相当を内蔵し、送受信ソケットを自前で差し替えるおすすめ

  • 「UDP直」と「443/TLSリレー」を **同一の“パケット送受信抽象”**で切り替えやすい
  • Tailscaleの magicsock はまさに「通信経路を動的に変えられるソケット」を提供する、という説明があります。 Go Packages+1
  • 443/TLSリレーは「WireGuardで暗号化済みのデータをリレーが盲目的に転送する」形にしやすいです。 Tailscale+1

あなたの「HTTPSさえ通れば」要件が強いなら、方式B寄りが実装コストと到達性のバランスが良いと思います。


追加あなたの「v6グローバルだけでサーバを置きたいNAT46的」について

ここは誤解が起きやすいので先に釘を刺します。

  • IPv4しかないクライアントは、IPv6-onlyのサーバへは基本的に到達できません間に“IPv4を受けられる翻訳/プロキシ装置”が必要)。
  • NAT46は「IPv4クライアント→IPv6サーバ」を実現する翻訳の一種ですが、翻訳ゲートウェイが“受け口としてIPv4側”を持つ必要があり、サーバが純IPv6だけだとどこか別の場所に翻訳器が要ります。 Palo Alto Networks TechDocs+1

「IPv6だけで置ける場所を増やしたい」という狙いは理解できますが、“誰でもどこでもIPv4-only含む”を満たすなら、結局どこかにIPv4の到達点が必要です。
一方で日本だとIPv6普及は高いので、実用上は「v6優先 + v4はリレーで吸う」でも十分成立するケースは多いですただし“絶対”は言えない


まとめあなたの4問への短い指針

  1. 鍵管理
    • 秘密鍵は端末保持(分散)
    • **公開鍵配布・ACL・設定・名前は中央集中**が最も簡単で強い。 Tailscale+2Tailscale+2
  2. 参加手法
    • 短命/制約付きの招待トークン + 端末生成鍵 +(任意で)管理者承認が運用しやすい。 GitHub
  3. NAT越え
  4. P2Pプロトコル
    • WireGuardでOK。インターフェースは基本1つでpeerを増やす。 WireGuard+1
    • “経路切替・NAT越え・リレー”のために WireGuardの外側に常駐オーケストレータが必須Go Packages+1

必要なら次に、あなたの要件に合わせて **最小MVPサーバ1台・ポート最小・クライアント実装の部品分割**を、

  • プロトコル境界Control API、NetMap差分、Relay framing
  • データ構造ードID、鍵、ACL、DNS名、経路広告
  • 推奨ポート設計443/tcp、3478/udp 等)
    まで具体化して提案できます。

Prompt:

あと、L2的にするか、L3的にするかという問題tailscaleのSubnet Routingみたいなものをやるかどうかもあります。

Response:

Thought for 13m 4s

L2/L3は「見た目がそれっぽい」以上に、アーキテクチャ全体(到達性・スケール・運用・セキュリティ)を決める分岐なので、ここは最初に方針を固めた方がいいです。結論としては、あなたの要件(セルフホスト容易 / P2P優先 / 最悪はHTTPSで通すだと、

  • 基本は L3TUNで作る
  • “LANをまるごと繋ぐ”は L3の「Subnet Routing経路広告」で実現
  • L2TAP/ブリッジ)は「どうしても必要なユースケースだけ」のオプションMVPでは避ける

が一番きれいです。


  1. まず整理Subnet Routingは「L2にするか」とは別問題L3の拡張

TailscaleのSubnet routersは、やっていることは L3の経路広告で、L2ブリッジではありません。

  • Subnet router側で IP forwardingを有効化して、--advertise-routes=192.0.2.0/24,... みたいに 到達させたいプレフィックスを広告するControl planeに「このサブネットはこのード経由で届く」と登録Tailscale
  • 管理コンソールまたはautoApproversその経路を承認して有効化する。 Tailscale
  • 複数広告がある場合は Longest Prefix Matchで選ぶ/16より/24が優先、みたいなTailscale+1
  • デフォルトルート 0.0.0.0/0 / ::/0 を流すなら “exit node” にせよ、という整理。 Tailscale+1

つまり **Subnet Routingを実装するかどうかは「L3でルーティング機能を足すか」**の話で、L2ブリッジ同一ブロードキャストドメイン化とは別です。


  1. L3TUNで作る場合の性質WireGuardと相性が良い

あなたが前に挙げたWireGuardを採るなら、L3ベースは自然です。

  • WireGuard自体が Layer 3のトンネルとして設計されている公式ペーパーのAbstractで明言WireGuard
  • Tailscaleも「主としてネットワーク層のソリューションで、WireGuardを使う」と整理しています。 Tailscale

L3TUNMesh + Subnet Routingのメリット

  • スケールが楽:各ノード/32(/128)で、ルーティングは「宛先IP→次ホップpeer」になり、ブロードキャストの洪水が起きにくい
  • フルメッシュでも管理可能WireGuard peerは増えるが、基本は wg0 1つにpeerをぶら下げる設計で済むあなたの前提とも整合
  • セキュリティ境界が作りやすいACLはL3/4IP/Portで表現しやすい
  • “HTTPSさえ通れば”の最終フォールバック443/TLSリレーも、L3パケット単位で運びやすい
    ※Tailscaleは「UDPが無理ならTCP(HTTPS)で暗号化トラフィックをリレーする」と説明しています。 Tailscale

L3のデメリットここが「L2が欲しくなる」ポイント

  • L2ブロードキャスト/マルチキャストに依存する発見系が素直に動かない
    mDNS/Bonjour、SSDP、NetBIOS Name Service、ゲームのLAN探索、など。
    これはTailscaleでも課題として継続的に話題になります。 GitHub+1

  1. L2TAP/仮想Ethernetにすると何が起きるか

L2にする「全ードが同一のEthernetセグメントに繋がっている」ように見せるです。

L2のメリット

  • DHCP・ARP・mDNS・SSDPなど、L2に依存するプロトコルが“そのまま”動く可能性が高い
  • “VPN越しに同一LANっぽく”という体験は作りやすい

ただし、あなたの狙い簡単にセルフホスト、P2P優先、HTTPSフォールバックと衝突しやすい

L2はインターネット越しにやると、現実のつらさが出ます。

  • ブロードキャスト/ARPの扱い
    フルメッシュで“同一セグメント”をやると、結局「ブロードキャストを全peerへ複製」する必要がある
    ードが増えるほど雑に増えるO(N²)寄りになりがち)
    → P2P優先のメリットを削りやすい
  • ループ/ストーム/MTU問題
    L2はSTP相当の設計や、フレームの洪水対策をちゃんとやらないと事故りやすい
  • アドレス空間の衝突が地雷
    “同一L2”を複数拠点の既存LANに伸ばすと、同じRFC1918が被って終わる、が典型
  • セキュリティ境界が曖昧になりやすいL2は攻撃面が増えやすい

参考として、ZeroTierは「Virtual Layer 2をVXLAN的に作る」と説明していますが、これはそれ相応の設計領域を背負う、ということでもあります。 Tailscale


  1. あなたのプロダクトとしての推奨方針

推奨L3をコアにして、Subnet Routingを“必須級のオプション”にする

MVPとして一番強いのはこれです。

  • 通常ノード:/32(/128)だけを持つ
  • Subnet routerード
    • 「このノードが 192.168.1.0/24 を背負う」みたいな経路広告
    • 管理者承認勝手にLANが公開されないように Tailscale
    • ルート衝突はLPMで選択 Tailscale
  • Exit nodeード
    • 0.0.0.0/0, ::/0 を背負う(全トラフィック経由) Tailscale
    • 利用側/提供側/管理者の“オプトイン”を要求する(勝手に出口化しない) Tailscale

Subnet Routing実装でハマる要点ここを仕様に落とすと良い

Tailscaleのドキュメントが現実に寄り添っていて、あなたの実装にもそのまま使えます

  • ルータ側は IP forwarding必須、firewallは転送拒否がデフォルトの前提で説明されている Tailscale
  • SNATの扱いが超重要
    • デフォルトSNATmasqueradingLAN側から見ると送信元がルータになる → 設定が簡単 Tailscale
    • SNAT無効送信元がVPN内IPのまま → 戻り経路return routeが必要例としてTailscaleは 100.64.0.0/10 をサブネットルータへ向ける静的経路が要る、と明記) Tailscale
      → あなたの実装でも同じで、**「簡単モードはSNATあり」「正確モードはSNATなしreturn route必須」**が王道になります。

  1. 「L2っぽさ」が欲しい時の現実的な落としどころ

L2に振り切る前に、L3のまま“困りがちポイントだけ”解決する手があります。

A. 発見系mDNS/SSDPなどだけを中継する

  • いわゆる mDNSリフレクタ/ゲートウェイ224.0.0.251)や SSDPプロキシ239.255.255.250)を、サブネットルータ上で動かす
  • VPN全体をL2にせず、必要なプロトコルだけを中継
  • セキュリティポリシー(誰がどの発見情報を見ていいか)が書きやすい

B. “名前で解決”に寄せるDNSの作り込み

あなたが元々言っていた「名前つけてDNS広報」は、L3モデルの弱点をかなり潰します。
Subnet Routingの文脈でも「内部DNSへのルーティング」やsplit DNSの話が出ます。 Tailscale+1


  1. それでもL2が必要ならどう設計するのがマシか

もし「L2がないと成立しない」ユースケースが明確にあるなら、僕ならこうします

  • コアはL3のままWireGuard/TUN
  • L2は “L2セグメント”という別機能として提供デフォルトOFF
    • 特定グループだけが参加する“仮想スイッチVNI”を作り、そのVNI参加ード間だけEthernetフレームを運ぶ
    • 実装は VXLAN/Geneve 的な発想ただしメンバ管理・フラッディング制御・MTU・暗黙のブロードキャスト複製など、考えることは増える
  • さらに、HTTPSフォールバック上でL2を流すなら、TCP-over-TCPの性能劣化やフレーム断片化に注意が必要(ここは設計で割り切る)

これを最初からやると重いので、MVPでは避けるのが無難です。


まとめ:あなたのプロジェクトに対する実装優先度

  1. L3 MeshWireGuardをコアにする(最も自然・簡単・堅い) WireGuard+1
  2. Subnet Routing経路広告承認SNAT制御を実装“VPNを実用にする”機能 Tailscale+1
  3. Exit nodeデフォルトルートを実装(必要なら) Tailscale
  4. それでも足りないときだけ
    • 発見系の中継mDNS/SSDP
    • さらに必要なら L2セグメントオプション

必要なら次は、あなたのControl plane設計に合わせて、

  • 「経路広告subnet/exitをnetmapにどう持つか」
  • 「クライアント側でAllowedIPsとOSルートをどう同期するか」
  • 「SNATあり/なしの両モードのユーザ体験(設定手順)をどう分岐させるか」

を、擬似コード/データ構造まで落として提案します。

Prompt:

L3にしようと思います。もう一つの問題は、クライアントソフトウェアの設計です。個人的には、一つのクライアントのマシンがいくつかのネットワークに接続できたほうが望ましいと思っているtailscaleはこれができないのですが、どうでしょうか。ただ、問題は適当にやるとIP帯がかぶるところですね。

Response:

Thought for 14m 19s

L3で行くなら、「1台の端末が複数ネットワークに同時参加できる」はかなり価値のある機能です仕事用・個人用・検証用・顧客環境など。実際、Tailscaleも“複数アカウントを素早く切り替える”機能はありますが、同時に複数tailnetへパケット送信はできないと明記しています。 Tailscale
なお組織が複数tailnetを持てる機能も出ていますが、これは主に管理側の話で、クライアントが同時接続できる話とは別です。 Tailscale+1

あなたが作るなら、同時参加を最初から設計に入れるのはアリです。ただし、言う通り「IP帯がかぶる」問題が出るので、そこを仕様で潰すのがポイントになります。

以下、設計のおすすめを「現実に運用できる線」でまとめます。


  1. まず分けて考えるべき「かぶるIP」の種類

A. オーバレイ内VPN端末同士のIP

これはあなたのVPNが配るアドレスなので、設計でほぼ完全に回避できます。

B. Subnet Routingで流す“向こう側LAN”のIP

こっちは現実世界のLANが 192.168.0.0/16 とかで乱立するので、高確率で衝突します。
同時に2つ以上のネットワークへ参加するなら、衝突が起きたときの挙動が実質“仕様の核”になります。


  1. 「複数ネットワーク同時参加」を成立させるクライアント設計の基本方針

結論(おすすめ)

  • ネットワークごとに“独立したルーティングドメイン”を持つ発想で作る
  • ただしOSの通常ルーティングテーブルは“宛先IPだけ”で決めるので、
    衝突する経路を“そのまま”同居はできない
    → だから、衝突を避ける(オーバレイ)+衝突したら方針を決める(サブネット) が必要

  1. 実装アーキテクチャ:一番ラクなのは「マルチインスタンス」

あなたのソフトを「同時に複数ネットワーク参加可能」にする最短ルートはこれです。

方式A1ネットワーク = 1デーモン推奨

  • clientd --profile netAtun-netA を作る
  • clientd --profile netBtun-netB を作る
  • UI/CLIだけ “1つのクライアント” に見せれば良い

メリット

  • 実装が単純状態・鍵・netmap・ルート注入が分離
  • バグっても片方に閉じ込めやすい
  • “このネットワークだけ落とす/再接続” が容易

デメリット

  • DNS統合後述が少し面倒
  • OSごとにTUN複数の扱いが違うWindows/macOS周りは要検証

方式B1デーモンで複数ネットワーク管理

  • 中長期ではUXが良いけど、最初は事故りやすい状態機械が重なる

MVPは方式Aが堅いです。Tailscaleでも“複数同時接続したい”需要は昔からIssueになっていて、回避として複数デーモンを立てる話が出ます。 GitHub


  1. オーバレイIPの衝突を「設計で潰す」方法

ここはあなたが自由に決められるので、衝突が起きない割当にしてしまうのが最強です。

推奨ネットワークIDからプレフィックスを決定する

ネットワークには必ず一意な NetworkIDコントロールサーバの公開鍵、またはネットワーク作成時に生成するUUID相当を持たせて、そこから

決定的deterministicに生成します。

例(イメージ)

  • overlay_v6_prefix = fd00::/8 + H(NetworkID)の先頭40bit → /48
  • overlay_v4_prefix = 100.64.0.0/10 の中から H(NetworkID)で /24 を選ぶ

これにより、別ネットワーク同士でもオーバレイがかぶりにくい(実質“ほぼ起きない”)状態を作れます。

補足IPv6 ULAは「ローカルで使うがグローバル一意性を期待できる」用途として定義されています。 IETF Datatracker
IPv4の 100.64/10 はCGN向けの共有アドレス空間としてIETFが予約したブロックです。 IETF Datatracker

ルート注入も衝突しにくくする

  • 各ネットワークのオーバレイは そのネットワーク固有のプレフィックスだけtun-netX に向ける
  • 端末同士は /32(/128)でもいいし、プレフィックスまとめても良い(規模次第)

WireGuard自体もL3トンネルとして整理されているので、この方向は素直です。 WireGuard+1


  1. 本題Subnet Routingがあると“衝突が必ず起きる”問題

ここは「何を保証するか」を決めないといけません。おすすめは段階的です。

レベル1推奨デフォルト衝突したサブネット経路は“入れない”

クライアントが受け取った advertised routes を、OSのルートテーブルへ入れる前に

  • 既存ルートローカルLAN含む
  • 他のVPNネットワークから入っているルート

と照合して、プレフィックスが重なるものは拒否します。

ユーザ体験としては:

  • “netB の 192.168.1.0/24 は netAまたはローカルと衝突するので無効化しました”
  • どのルートが衝突してるか、解決策(後述)を提示

これで **「同時参加はできるが、衝突サブネットは使えない」**という一貫した挙動になります。
MVPとしては非常に現実的です。

レベル2手動優先度メトリクスでどちらか一方を採用

  • 衝突した2つの 192.168.1.0/24 のうち、片方だけを採用
  • UIで “この経路はnetA優先/netB優先” を選ばせる

ただし、同時に両方は使えませんOSルーティングは宛先IPで決まるため

レベル3上級者向けサブネットのアドレス変換route translation / netmap

「同時に両方の 192.168.1.0/24 を使いたい」を満たす現実解はこれです。

発想

  • netBの 192.168.1.0/24 を、そのネットワーク内では 仮想プレフィックス 172.31.200.0/24 にマッピングして提供する
  • クライアントは 172.31.200.0/24 宛に送る
  • Subnet RouternetB側1:1のプレフィックス変換172.31.200.x ⇔ 192.168.1.x を行う

こうすると、

  • ローカルLANの 192.168.1.0/24
  • netAの 192.168.1.0/24
  • netBの変換後172.31.200.0/24

同時に共存できます。

ただし仕様の重さ

  • 変換後のアドレスをユーザにどう見せるかDNSで変換後を返す、などまで決める必要がある
  • ルータだけでなく、クライアント側も「この経路は変換モード」を理解する必要がある(実装は可能)

MVPではレベル1拒否で十分、要望が出たらレベル3を足すのが良いです。

レベル4Linux限定の最終手段VRF / network namespace

  • OS側でルーティングテーブルを分離して“同じ宛先プレフィックス”を共存させる
  • ただし「そのネットワークを使うアプリ」を名前空間に入れる必要が出て、透明性が落ちます
  • クロスプラットフォームでは厳しい

  1. “同時に複数ネットワーク参加”を入れるなら、追加で決めるべき仕様

(a) Exit nodeデフォルトルートは同時に1つ、が基本

複数ネットワークで同時に 0.0.0.0/0 を入れるのは混乱のもとです。

  • 「どのネットワークのexitを使うか」を明示選択
  • もう一方はexit機能をOFFにするまたはアプリ単位の選択にする

(b) DNS名の衝突

netAにもnetBにも db みたいな名前があると衝突します。
解決策は素直に

  • ネットワークごとにDNSサフィックスを持つdb.netA.vpn, db.netB.vpn
  • OSへはsplit DNSor ローカルスタブDNSで集約
    が堅いです。

(c) ネットワーク間の“意図しない中継”を防ぐ

同じホストが2ネットワークに入ると、事故でルーティング転送してしまう可能性があります。

  • デフォルトで IP forwardを有効化しない
  • 「サブネットルータ/exit node」は明示的に有効化承認制

まとめ:あなたの状況へのおすすめ

あなたが「複数ネットワーク同時接続」を売りにしたいなら、こう決めると破綻しにくいです。

  1. クライアントはマルチインスタンス1ネットワーク=1デーモンでMVP
  2. オーバレイIPは “NetworkIDから決定的に生成”
  3. Subnet Routingは衝突検出して、衝突した経路はデフォルト拒否レベル1
  4. どうしても必要になったら **“サブネット変換レベル3”**を追加

もしよければ、次は「衝突検出ロジック」と「ネットワークIDからのプレフィックス生成」の具体案ビット割り当て、衝突時のリトライ規則、サーバ側のネットワーク作成API仕様、クライアントのルート適用順序を、実装に落とせる粒度で提案できます。