このページの目次

Tailscale アーキテクチャ

WireGuard 上にゼロコンフィグメッシュ VPN を構築する——coordination server が公開鍵と ACL を交換し、NAT 穿透で直接トンネルを確立し、不可能な場合は自動的に DERP リレーにフォールバックする。コントロールプレーンは集中型だが、データプレーンは分散型:トラフィックは中央サーバーを経由しない。

概要

Tailscale(2019)は WireGuard 上にゼロコンフィグメッシュ VPN を構築しています。coordination server が公開鍵と ACL を交換し、ノード間は NAT 穿透(STUN/UPnP/ポート予測)によって直接の WireGuard トンネルを確立します。穿透が不可能な場合は自動的に DERP リレーにフォールバックします。中核的な革新は、WireGuard の静的設定を自動管理されたメッシュオーバーレイに変えつつ、データプレーンが中央サーバーを経由しないことを維持することです。オープンソースの代替実装である headscale を用いれば、完全に自前の環境で構築することも可能です。

コントロールプレーンとデータプレーンの分離

Tailscale はピアツーピアではありません。集中型コーディネーションを伴うメッシュオーバーレイです。

コントロールプレーン (Coordination Server):
  - HTTPS (REST + long-poll): 公開鍵、エンドポイント、ACL の交換
  - ユーザーデータは転送されない
  - オープンソースの代替: headscale

データプレーン:
  - WireGuard トンネル (ユーザー空間: wireguard-go または カーネル: wireguard.ko)
  - ピア間: NAT 穿透が成功すれば直接 UDP、そうでなければ DERP リレー経由

coordination server が提供するのは、ピアリスト、公開鍵、エンドポイントのヒント、および ACL ルールです。接続が確立されると、ノード間は WireGuard を介して直接通信します。

DERP

DERP (Detoured Encrypted Routing Protocol) は、Tailscale のリレーネットワークであり、直接接続が失敗した場合に使用されます。

[ノード A] → TLS → DERP サーバー → TLS → [ノード B]

DERP プロトコル:
  1. ノード → DERP: HTTPS(TLS) で :443 またはカスタムポートに接続
     → 認証: ノードは自身の tailscale 秘密鍵で署名する — DERP は tailnet へのログインを必要としない
  2. DERP → ノード: サーバー情報(リージョン、バージョン)
  3. 以降:
     - DERP は WireGuard で暗号化されたパケットを転送する(復号は不可)
     - ピア検出(Peer Present / Peer Gone メッセージ)
     - ping/pong によるキープアライブ

自前 DERP の構築: TLS 証明書とパブリックポートが必要です。verify-clients オプションは、tailscaled が coordination server に接続してクライアントの身元を確認することに依存しています。もし中国でのデプロイメントにおいて coordination server への接続がブロックされている場合、このオプションは使用できません。

NAT 穿透

Tailscale は WireGuard トンネルを確立する前に、まず NAT 穿透を試みます。

戦略(優先度順):
  1. 両ノードが同じ LAN にいる場合 → 直接 LAN IP(ホストファイアウォールが許可している必要がある)
  2. 片方が Full Cone NAT の場合 → STUN で外部 IP:ポートを取得 → リモート側から接続
  3. 両方が Symmetric NAT の場合 → ポート予測 + バースデー攻撃:
     各ピアは同時に相手の推定ポートに向けて UDP パケットを送信(複数のポートをプローブ)
     → 何らかのペアが一致すれば直接接続可能
  4. UPnP/NAT-PMP/PCP: ルーターがサポートしていれば → ポートマッピング → 公网 IP と同等
  5. 上記すべてが失敗した場合 → DERP リレー(フォールバック)

ポート予測:
  NAT のポート割り当てパターンを観察する(インクリメント、ハッシュ、ランダム):
    インクリメント: port_N = base_port + N
    ハッシュ: port_N = f(proto, src_ip, src_port, dst_ip, dst_port)
    ランダム: 予測不可能 → 穴あけ不可能 → DERP のみ

参考

  • tailscale blog: 「How NAT traversal works」
  • headscale: github.com/juanfont/headscale
  • DERP: tailscale.com/kb/1232/derp

キーワード: Tailscale, coordination server, DERP, NAT 穿透, ポート予測, wireguard-go, headscale, ACL