Tailscaleがポート開放なしでつながるまでを、NATから順に整理する

結論

外出先から自宅のIPv4端末へTailscaleで接続するときは、NATで届きにくいことと、安全対策が済んでいることを分けて考えます。そのうえで、自宅側と通信事業者側のNAT、Tailscaleの接続調整、実際の通信経路を順に追うと、ポート開放なしで接続できる理由を整理できます。

家庭のIPv4ネットワークでは、NAT(ネットワークアドレス変換)を行うルーターが、LAN内の複数端末を一つのグローバルIPv4アドレスへ対応付けます。LAN内の端末から外へ出た通信は対応表に記録され、その返答だけが元の端末へ戻ります。

反対に、外部から突然届いた通信に対応する記録や転送規則がなければ、通常そのままLAN内へ転送されません。

ここで起きているのは、外部から自宅LAN内の端末へ届く経路が作られていないということです。端末やアプリに脆弱性がないこと、許可された人だけが使えること、データが暗号化されていることまで保証しているわけではありません。

僕は次の二つを分けて考えます。

  • 到達性:どの経路を通れば、目的の端末やサービスへ届くか
  • 安全対策:誰の通信を許可し、何を暗号化し、端末をどう更新するか

Tailscaleのような仕組みでNATを越えて到達経路を作っても、安全対策が不要になるわけではありません。反対に、NATの内側に置いただけで安全対策が完成するわけでもありません。

参考:How NAT traversal worksControl and data planes

ここまでで、自宅ルーターのNATが外から突然届く通信を端末へ渡せない理由が分かりました。ただし、回線によっては自宅ルーターの外側にもNATがあります。次はCGNATで、なぜ自宅側のポート開放だけでは届かないかを見ます。

通常の家庭内NATだけなら、自宅ルーターへ「このポートはこの端末へ渡す」という転送規則を設定できます。ところがCGNATでは、その外側に通信事業者が管理するNATがあります。

インターネット
通信事業者のCGNAT  ← 利用者は転送規則を設定できない
自宅ルーター       ← ここだけ開けても経路は未完成
自宅PC

自宅ルーターの設定を変えられても、CGNAT側で「どの契約者の、どの端末へ渡すか」が決まりません。

インターネット上で自宅回線を個別に識別できるグローバルIPアドレスを契約する方法もあります。ただし、利用できるかは回線や契約に依存します。

Tailscaleは、自宅側へ固定の受信ポートを用意する代わりに、端末側から外向きに接続を始めます。NATの内側にある端末同士で直接経路を作れるか試し、できなければ中継を使います。

参考:How NAT traversal works — Concerning CGNATsConnection types

ポートを外から開ける方法と、端末側から接続を作る方法の違いは見えました。では、外向きに接続した端末同士を誰が引き合わせ、実際のデータはどこを通るのでしょうか。次は、接続の調整とデータの経路を分けます。

端末側から外向きに接続を作るTailscaleでは、相手を見つける調整と、暗号化したデータを運ぶ経路が別の役割を持ちます。

Tailscaleへ端末を追加すると、調整サーバーは同じtailnet(Tailscaleで管理するネットワーク)内で通信を許可された端末の公開鍵、接続候補、経路、規則を配ります。端末は、その情報を使って相手との接続経路を組み立てます。

一方、アプリのデータは各端末上のデータプレーンが暗号化し、相手へ送ります。直接接続なら、暗号化されたデータは端末間を流れます。

直接接続できなければ、Tailscaleの中継サーバーであるDERPか、別のTailscale端末を中継にするPeer Relayを通ります。どちらも調整サーバーとは別の経路です。

調整サーバー:誰と、どの鍵・経路・規則でつなぐかを配る
データ経路 :暗号化した通信内容を端末間で運ぶ

「中央サーバーを使う」ことと、「すべての通信内容がそのサーバーを通る」ことは同じではありません。Tailscaleの接続を理解するときは、接続を調整する役割と、パケットを運ぶ役割を分けると追いやすくなります。

参考:Control and data planesConnection types

ここまでで、調整サーバーが接続情報を配り、データプレーンがパケットを運ぶところまで分かりました。次は、そのデータ経路が中継から直接接続へどう変わるかを追います。

実際の接続開始時は、相手が接続しているDERPサーバーを経由して接続要求を届けます。両端末は接続候補を交換します。その後、NATの内側から互いにUDPを送り、直接届く組み合わせを探します。

DERPで接続開始
直接UDP接続を試す
  ├─ 成功 → direct
  └─ 失敗 → 別のTailscale端末を中継にするPeer Relayを試し、
              使えなければDERPを継続

この順序なら、直接経路の探索を待ってから通信を始める必要がありません。ネットワーク条件が変わった後も、Tailscaleはよりよい経路を定期的に探します。

「Tailscaleは必ず端末同士で直接通信する」と考えると、接続状態の中継表示を異常だと誤解します。直接接続は性能面で望ましい経路ですが、中継は接続を保つための正規の経路です。

参考:Connection types

接続経路は、DERP、Peer Relay、直接接続の間で変わり得ます。現在どの経路を使っているかをコマンドで確認します。

速度や遅延が気になるときは、接続経路を推測せず、コマンドライン(CLI)から確認します。

tailscale ping <相手の端末名またはTailscale IP>

viaの後にIPアドレスとポートが表示されれば直接接続です。via DERP(...)ならDERP中継、via peer-relay(...)なら同じtailnet内のPeer Relayを使っています。

接続開始直後はDERPから直接接続へ切り替わることもあります。最初の一行だけで決めず、複数回の応答を見ます。

普段の接続一覧は、次のコマンドでも確認できます。

tailscale status

直接接続できない理由を調べる段階では、tailscale netcheckを使います。確認するのは次の項目です。

  • UDPを利用できるか
  • 接続先によってNATの割り当てが変わるか
  • どのポートマッピング方式が使われているか
  • どのDERPが近いか

中継であることだけを原因と決めず、「今どの経路か」と「現在のネットワーク条件」を分けて確認します。

参考:Connection typesTailscale CLI

NATで外から届きにくい理由と、Tailscaleが作る通信経路を分けると、ポート開放なしでつながる仕組みと、現在の経路を確かめる方法を同じ流れで追えます。