Tailscaleとは?WireGuard VPNの仕組みを解説
TailscaleはWireGuardでノード間を暗号化し、調整サーバーが鍵とACLを配布します。NAT越え、直接接続できない場合のDERPリレー、信頼モデルまで解説します。
Tailscale とは
Tailscale は、すべてのトラフィックを自分で運用する 1 台のゲートウェイ経由でルーティングするのではなく、マシン同士を直接接続する VPN です。各ノードで WireGuard が動作するため、パケットはサーバー間を暗号化された状態で移動し、経路上の何者も内容を読み取れません。ホスト型の調整サーバーが接続先の紹介を処理します。このサーバーは公開鍵を保存して配布し、各ノードに他のノードの接続先を通知します。また、作成したアクセスルールも各ノードに適用します。
この分離が設計のすべてです。データプレーンはノード間で直接通信し、ノード間で暗号化されます。コントロールプレーンは、Tailscale が代わりに運用するサービスです。信頼に関する難しい問題も含め、Tailscale に関する重要な問題は、すべてこの 2 つの事実から生じます。すでに VPS 上で WireGuard VPN を手動構築している場合、Tailscale は同じトンネルに、鍵の配布とファイアウォール越えを組み込んだものです。
Tailscale はどのように動作しますか?
ノードで構成するプライベートネットワークを tailnet と呼びます。マシンが tailnet に参加すると、4 つのことが起こります。
tailscaledデーモンが起動し、WireGuard の鍵ペアを生成して、状態を/var/lib/tailscale/tailscaled.stateに保持します。秘密鍵はそのマシンから外へ出ません。Tailscale 自身も「秘密鍵は決して、そのノードから外へ出ない」と明言しています。- ノードは調整サーバーにログインし、自身の公開鍵と、到達可能だと判断したアドレスをアップロードします。Tailscale はこのサーバーを「公開鍵を共有するドロップボックス」と説明しています。
- 調整サーバーはネットワークマップを返します。このマップには、ノードごとに公開鍵、tailnet アドレス、マシン名、このノードが接続を許可されている各ノードの候補エンドポイントが含まれます。
- 各ノードのペアは、相互間に直接 WireGuard トンネルを構築しようとします。失敗した場合は、代わりにリレーを介してパケットを転送します。
各ノードには、100.64.0.0 から 100.127.255.255 までのキャリアグレード NAT 範囲である 100.64.0.0/10 から、固定アドレスが割り当てられます。Tailscale がこの範囲を使用するのは、プロバイダーのインフラストラクチャ用に予約されており、サーバーですでに使用しているプライベートアドレスと競合することがほとんどないためです。Linux では、トンネルは tailscale0 という名前のインターフェースとして表示されます。
WireGuard の実装はカーネルモジュールではなく、ユーザー空間の tailscaled 内にあります。そのため、sudo modprobe wireguard が Operation not supported で失敗するコンテナ仮想化環境でも Tailscale を起動できます。一方、特定のホストでのスループット上限はカーネル版 WireGuard より低くなります。これは、Tailscale と通常の WireGuard の比較で説明するトレードオフの 1 つです。
現在の状態は、2 つのコマンドで確認できます。
tailscale ip -4
tailscale statustailscale status はノードごとに 1 行を出力し、最後の列が重要です。
100.101.102.103 web-1 you@ linux -
100.101.102.104 db-1 you@ linux active; direct 198.51.100.24:41641
100.101.102.105 ci-runner you@ linux active; relay "fra"direct の後にアドレスとポートが表示される場合、2 台のマシン間の経路が確立され、ネットワークトラフィックがピアツーピアで流れていることを示します。relay "fra" は、トラフィックが Frankfurt の Tailscale リレーを経由していることを示します。- は、そのノードとのアクティブなセッションが現在存在しないことを示します。これは正常です。
協調サーバーから見える情報と見えない情報
協調サーバーは公開鍵とメタデータを保持します。マシン名、各ノードを所有するユーザーまたはタグ、各ノードの tailnet アドレス、ノードに到達可能な公開アドレス、各ノードが最後にオンラインだった時刻、作成したポリシーファイルを把握しています。これは、フリート全体の完全な構成図に相当します。
秘密鍵は保持しないため、2 台のノード間のトラフィックを復号できません。暗号化は WireGuard ピア間でエンドツーエンドに行われ、協調サーバーはピアではありません。
協調サーバーが実行できるのは、鍵を配布することです。ホスト型でもセルフホスト型でも、協調サーバーは tailnet に登録する公開鍵をノードへ通知する役割を信頼されています。これが後述する脅威モデルの中心であり、自分でホストするオープンソースの協調サーバー Headscale が存在する理由です。
異なるファイアウォールの背後にある2台のサーバーが直接通信する仕組み
NAT(network address translation)は、多数のマシンで1つのパブリックアドレスを共有できるようにする仕組みです。通常、VPSには固有のパブリックアドレスがあります。しかし、tailnet に追加したい他のマシンには、固有のパブリックアドレスがないことがよくあります。たとえば、自宅のサーバー、オフィスネットワーク上のビルドランナー、変更できないプロバイダーのファイアウォールの背後にあるマシンなどです。
Tailscale は、STUN(NAT のセッション走査ユーティリティ)および ICE 標準に基づく技術を使用して経路を見つけます。各ノードは STUN サーバーに小さな UDP パケットを送信し、そのソケットに対してルーターが割り当てた公開アドレスとポートを取得します。両方のノードは取得した候補を coordination server に報告し、coordination server はそれを相手側へ渡します。続いて、両方のノードが同時に相互へのパケット送信を開始します。各ルーターは最初に送信パケットを確認するため、マッピングを作成し、同じアドレスから返ってくる応答を受け入れます。どちらの側にも受信 firewall ルールは必要ありません。このため、自宅の NAS や Pi も、ルーターでポート転送を設定せずに到達可能な状態を維持できます。ポート転送の代わりに Tailscale を使用するでは、この構成と、Tailscale ではできないことを含めて説明しています。
使用するポートは決まっています。直接のWireGuardトンネルではUDPを使用し、送信元ポートのデフォルト値は41641です。STUNはUDP 3478でTailscaleのrelay serversと通信します。control connectionは、Tailscale独自の暗号化トランスポートで保護した通常のHTTP over TCP 80を優先します。ポート80から応答がない場合は、HTTPS over TCP 443にフォールバックします。TailscaleのDERP serversを経由するデータはHTTPS over TCP 443を使用します。一方、独自のtailnet内で運用するpeer relayは、UDPでデータを転送します。通常はインバウンドポートを開く必要はありません。ただし、NATの制約が厳しいネットワークでは、UDP 41641のインバウンド通信を許可すると直接接続しやすくなります。
tailscale netcheckそのレポートの2行を確認してください。UDP: trueは、このマシンがインターネットへUDPを送信できることを示します。UDP: falseは送信できないことを示すため、このノードからのすべての接続がrelay経由になります。MappingVariesByDestIP: trueは、ルーターが宛先ごとに異なるパブリックポートを割り当てることを示します。この場合、上記のアドレス予測は機能せず、そのノードは通常relay経由のままになります。
Tailscale が DERP リレーを使用する場合
DERP(designated encrypted relay for packets)はフォールバックです。Tailscale は複数のリージョンでリレーを運用し、TCP 443 経由で到達できるようにしています。直接経路を確立できないノードは、WireGuard パケットをいずれかのリレー経由で送信します。例外は、指定したピアリレーを使用する tailnet です。ピアリレーは、UDP 経由でトラフィックを転送する自分のデバイスです。Tailscale はまずピアリレーを試し、利用可能なものがない場合にだけ DERP へフォールバックします。その場合、tailscale status は接続を peer-relay として表示します。
パケットは暗号化されたままです。Tailscale は次のように明記しています。「DERP サーバーがトラフィックを復号する方法は決してありません。DERP サーバーは、すでに暗号化されたトラフィックを一方のノードから別のノードへ、そのまま転送するだけです」。リレーが確認できるのは暗号文と、どのノードがどのノードと通信しているかです。
ほとんどの接続では、最初のパケットもリレーが転送します。直接経路の探索には少し時間がかかるため、2 つのノードが互いの経路を見つけるまで、セッションはリレー経由で開始されることがよくあります。その切り替えを確認できます。
tailscale ping db-1最初の応答は via DERP(fra) で返り、その後の行で via 198.51.100.24:41641 のような表示が出ます。この変化は、直接トンネルへの切り替えを示します。変化しない場合は、両端で tailscale netcheck を実行します。リレー経由の経路でも通信は動作します。ただし、すべてのパケットが別のマシンを経由するため、遅延が増えます。気付いた問題が接続障害ではなく転送速度の低下であれば、リレー経由の Tailscale 接続が直接接続より大幅に遅い理由で差を測定し、VPS を直接経路に接続する方法を確認できます。
VPS を tailnet に参加させる
インストールスクリプトは Ubuntu と Debian に対応しています。
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale upsudo tailscale up は URL を表示します。その URL を開いて認証すると、ノードが管理コンソールに表示されます。その後、再起動しても daemon が起動することを確認します。この手順は省略されがちです。
sudo systemctl is-enabled tailscaled
tailscale statusis-enabled は enabled を表示し、tailscale status には新しいノードとその 100.x アドレスが表示されます。スクリプトで構築したサーバーでは、対話型 URL は使用できません。管理コンソールで auth key を生成し、このマシンの種類を示す tag とともに指定します。
sudo tailscale up --auth-key=tskey-auth-REPLACE-ME --advertise-tags=tag:servertag 付きのノードは、コマンドを実行したユーザーではなく tag によって所有されます。そのため、そのユーザーのアカウントが削除された後も動作します。まず、ポリシーファイルの tagOwners に tag を宣言する必要があります。宣言していない場合、コマンドは拒否されます。tag を付けると、プラン上でのマシンの扱いも変わります。tag 付きリソースはユーザー自身のデバイスとは別に料金が計算されます。無料枠の実際の適用範囲で、その制限を確認できます。
フリートでは、2 つの設定が重要です。ノードキーはデフォルトで 180 日後に期限切れになります(2026 年 10 月時点)。キーが期限切れになると、デバイスが再認証するまで「指定されたエンドポイントとの間の接続は動作しなくなります」。管理者は、管理コンソールで期限切れのマシンに対して Temporarily extend key を選択できます。これにより、再度ログインするか、期限切れを無効にするための 30 分が与えられます。上記のように tag で認証されたノードでは、デフォルトでキーの期限切れが無効になっています。それ以外の無人運用サーバーでは、管理コンソールでそのマシンの行を開き、Disable Key Expiry を選択します。2022 年 10 月 20 日以降に作成された tailnet では、MagicDNS がデフォルトで有効です。各ノードには db-1.yak-bebop.ts.net のような名前が割り当てられ、100.100.100.100 の stub resolver によって名前解決されます。アドレスではなく名前を使用してください。再構築したノードには新しいアドレスが割り当てられますが、名前は維持されます。
apt または repository でインストール自体に失敗する場合は、Ubuntu でよくある Tailscale のインストールエラーで対処方法を確認してください。
localhost にバインドされたサービスへの接続
ここで tailnet が役立ちます。一方で、ここでつまずく人も多くいます。tailnet に参加しても、loopback で待ち受けるサービスに接続できるようにはなりません。
ss -tlnp | grep 3000これが 127.0.0.1:3000 を出力する場合、ソケットは宛先が 127.0.0.1 のパケットだけを受け付けます。別のノードからのリクエストは、このノードの 100.x アドレス宛てに到着します。そのため、kernel はリクエストを受け付ける listener を見つけられず、TCP reset を返します。クライアントには Connection refused と表示されます。トンネルには問題ありません。問題は listener です。
対処方法は2つです。1つ目は、サービスをノードの tailnet アドレスにバインドする方法です。これにより、proxy を介さずに public interface からサービスを隠せます。--bind 100.101.102.104 または設定内の同等のオプションを指定し、container の場合はポートを -p 100.101.102.104:3000:3000 として公開します。2つ目は、サービスを loopback で待ち受けさせたまま、Tailscale をその前段に置く方法です。
tailscale serve 3000これによりリクエストは http://127.0.0.1:3000 に proxy され、tailnet で HTTPS 経由の ts.net という名前で提供されます。tailnet の HTTPS 証明書を有効にしておく必要があります。サービスは自分のノードからのみ利用できます。同じ構成を public に提供する機能が Funnel です。どちらを使うべきかは Tailscale serve と Funnel の比較 を参照してください。
関連する2つの用途には、それぞれ専用のページがあります。Tailscale がインストールされていない private network 全体に接続するには、VPS 上の subnet router が必要です。あるノードの outbound internet traffic を別のノード経由にするには、exit node を使用します。
不要になったポートを閉じる
すべての管理者が tailnet 経由でサーバーへアクセスできるようになったら、公開ポート 22 は不要です。これが実際の効果です。閉じたポートにはブルートフォース攻撃を仕掛けられず、ログに攻撃の試行が記録され続けることもありません。
適用する順序が重要です。まず tailnet 経由のアクセスを追加します。次に、2 つ目のセッションから tailnet 経由でログインできることを確認します。その後で、公開ルールだけを削除します。
sudo ufw allow in on tailscale0
sudo ufw status verboseその後、公開 SSH ルールを削除し、MagicDNS 名を使用して再接続します。ufw allow in on tailscale0 が実際に行う処理に注意してください。これはトンネルから到着するすべての通信を信頼します。そのため、ufw の代わりに Tailscale のポリシーファイルがアクセス制御を担います。この点を踏まえてポリシーを作成してください。
コンテナを実行している場合は注意が必要です。公開された Docker ポートは独自の NAT ルールを設定し、ufw を迂回します。そのため、ufw deny ではそのポートを閉じられません。Docker の公開ポートが ufw を迂回する仕組みで、その動作を説明しています。上記のように tailnet アドレスへバインドすれば、この問題を回避できます。アプリを Docker Compose で実行している場合は、Compose スタックに Tailscale sidecar を追加する方法によって、さらに一歩進められます。公開ポート自体を削除し、コンテナを tailnet 名からのみ到達可能にできます。
Tailscale が保護するものと、保護しないもの
宣伝で境界が曖昧にされているため、明確にしておく価値があります。
保護されるもの: 2 つのノード間のトラフィックは WireGuard によってエンドツーエンドで暗号化され、途中のリレーはその内容を読み取れません。秘密鍵が、生成したマシンの外へ出ることもありません。ノードへのインバウンドの公開ポートは不要なため、インターネットから 22 や 5432 をスキャンされることもありません。ノード間のアクセスは、アドレスを知っている人ではなく、ポリシーファイルによって決まります。
保護されないもの: coordination server からはデバイスグラフが見えます。このメタデータ自体が機微情報です。マシン名、所有者、アドレス、オンラインになった時刻から、インフラの構成が分かるためです。さらに、coordination server は鍵も配布します。こちらのほうが重大なリスクです。Tailscale は次のように明言しています。「Tailscale が悪意を持ち、ネットワークに新しいノードをひそかに追加した場合、Tailscale は既存のノードとの間で平文のトラフィックを送受信できます。」シングルサインオンプロバイダーも同じ信頼経路にあります。そこで identity を発行できる人は、ノードを追加できるためです。また、侵害されたノードは tailnet 内のピアです。そのため、次に到達できる範囲はポリシーで許可されている内容になります。これが許容できるリスクかどうかは、誰から防御するかによって異なります。完全な信頼モデルでは、盗まれた identity アカウントで実際に可能な操作を含め、これらのケースを 1 つずつ説明しています。
鍵の配布リスクへの対策は 2 つあります。1 つ目は tailnet lock です。これを有効にすると、既存の信頼済みノードが新しいノードに暗号学的な署名を付けるまで、他のノードはその新しいノードを受け入れません。有効な署名なしでノードを追加する control plane の操作は無視されます。管理コンソールは署名ノード用の正確な tailscale lock init 行を生成し、各ノードで受け取った内容を確認できます。
tailscale lock statusすべてのノードが同じ信頼済み署名鍵のセットを報告することが望まれます。2 つ目の対策は、control plane を自分で運用することです。セルフホストの Headscale coordination server は同じクライアントと同じプロトコルで通信します。これにより、デバイスグラフと鍵の配布先を自分が所有するハードウェアへ移せます。その server の稼働時間も自分で管理することになります。まだこの構成に決めず、セルフホストの control plane を比較している段階なら、NetBird は別の mesh VPN で、単一の VPS 上で server をエンドツーエンドに運用します。
初日に変更すべきデフォルト設定が 1 つあります。新しい tailnet は、許可が広い状態で提供されます。「デフォルトの tailnet policy file では、tailnet 内のすべてのデバイス間の通信が有効になります。」acls セクションを追加すると、モデルはデフォルト拒否に切り替わり、自分のルールだけが通信を許可します。
{
"tagOwners": {
"tag:server": ["autogroup:admin"]
},
"acls": [
{"action": "accept", "src": ["autogroup:member"], "dst": ["tag:server:22"]}
]
}このポリシーでは、tailnet メンバーがタグ付き server の SSH にアクセスでき、それ以外にはアクセスできません。ワイルドカードを残さず、サービスごとにルールを追加してください。ワイルドカードを使うと、盗まれたラップトップの鍵でデータベースに到達できるためです。
障害パターンと表示される文字列
tailscale status は常に relay と表示されます。 2 つのノード間に直接経路が確立されていません。両端で tailscale netcheck を実行します。UDP: false は送信方向の UDP がブロックされていることを示すため、リレー経由でしか通信できません。MappingVariesByDestIP: true はハード NAT が存在することを示します。管理できる側で UDP 41641 の受信を許可すると、多くの場合は解消します。
数か月間動作していたノードが表示されなくなりました。 よくある原因は、デフォルトの 180 日でノードキーの有効期限が切れたことです。まず管理コンソールでそのマシンを確認します。期限切れと表示される場合は、そのマシンで sudo tailscale up を実行すると復旧します。サーバーでキーの有効期限を無効にすると、同じ問題の再発を防げます。期限が切れていない場合は、ポリシーファイルを確認します。Tailscale は、tailscale status にデバイスが表示されない場合、接続を妨げるアクセスルールを確認するよう案内しています。
ピアは一覧に表示されますが、接続がタイムアウトします。 tailscale ping が応答を受け取る場合、接続性は確保されています。考えられる原因は、ポリシーによってトラフィックが拒否されていることです。acls セクションに、この送信元、宛先、ポートを対象とするルールがあるか確認します。拒否されたパケットは応答されずに破棄されるため、Connection refused ではなくタイムアウトになります。
MagicDNS の名前を解決できません。 ping db-1 は失敗しますが、ping 100.101.102.104 は動作します。まず、クライアントが Tailscale の DNS 設定を受け入れていることを確認します。--accept-dns=false で起動したノードは、100.100.100.100 のスタブリゾルバーにクエリを送信しません。次に、cat /etc/resolv.conf と、そのファイルを書き換える他の処理を確認します。systemd-resolved を使用するシステムでは、通常このファイルは /run/systemd/resolve/stub-resolv.conf へのシンボリックリンクです。そこに 100.100.100.100 が記載されていなくても問題ありません。tailscaled がファイルを直接書き込む環境では、DHCP クライアントなどがファイルを上書きすると MagicDNS が動作しなくなります。これらに該当しない場合は、ローカル DNS またはネットワーク DNS の設定に問題がある可能性もあります。WireGuard トンネル内で DNS が機能しない場合と同じ種類の問題です。
tailscale up はタグを受け付けません。 ポリシーファイルの tagOwners にそのタグが宣言されていません。そこに追加してから、コマンドをもう一度実行します。
FAQ
Tailscale は VPN ですか、それともメッシュネットワークですか?
どちらの表現も正確で、異なる層を説明しています。トンネルには WireGuard を使用するため、これは VPN です。トポロジーはメッシュです。各ノードが通信する相手の各ノードに直接トンネルを構築し、すべてのパケットを 1 台の中央サーバー経由で送信するわけではないためです。Coordination server はコントロールパスにあり、データパスにはありません。そのため、Coordination server に到達できなくなっても、既存のトンネルは引き続き通信を運びます。障害中に停止するのは、新しいノードの参加と、鍵やポリシーの変更の反映です。これはデフォルトではプライバシー VPN ではありません。Exit node 経由でトラフィックを送信するまでは、Web サイトから自分のパブリックアドレスが見えるためです。また、Tailscale が Web サイトと ISP から隠す情報は、構成ごとに異なります。
Tailscale は私のネットワークトラフィックを読み取れますか?
内容は読み取れません。ネットワークトラフィックは WireGuard によりノード間でエンドツーエンド暗号化され、秘密鍵がノードの外へ出ることはありません。DERP リレーも、復号できないパケットを転送するだけです。一方、Tailscale はメタデータを認識します。これには、マシン名、所有者、公開鍵、エンドポイントアドレス、各ノードがオンラインになった時刻が含まれます。また、Tailscale は鍵も配布するため、侵害された調整サーバーが、フリートから信頼されるノードを追加しようとする可能性があります。Tailnet lock は、信頼済みの自分のノードによる署名を要求することで、これを防ぎます。Headscale を使用すると、ホスト型の制御プレーンを構成から外せます。
Tailscale 用にファイアウォールのポートを開く必要はありますか?
インバウンドでは、ほとんどありません。Tailscale の公式ガイダンスでも、「ほとんどの場合、ファイアウォールのポートを開く必要はありません」とされています。アウトバウンドでは、ノードから調整サーバーとリレーへ TCP 443 が必要です。また、STUN 用に UDP 3478 も必要です。直接トンネルでは、デフォルトで 41641 の送信元ポートを使用する UDP を使います。インバウンドで UDP 41641 を許可するかどうかは任意です。許可すると、接続が確立しにくいネットワークで直接接続が成功しやすくなります。
他のノードから port 3000 のサービスに接続できないのはなぜですか?
まず ss -tlnp で bind アドレスを確認します。127.0.0.1:3000 のリスナーは、ノードの 100.x tailnet アドレス宛ての接続を拒否します。このソケットはループバック宛ての接続だけを受け付けるため、クライアントには Connection refused が表示されます。サービスを tailnet アドレスに bind するか、tailscale serve 3000 を実行してプロキシします。リスナーがすでに 0.0.0.0 で待ち受けており、接続が拒否されずタイムアウトする場合、bind アドレスは原因ではありません。まずポリシールールとホストのファイアウォールを確認し、次にネットワーク側のファイアウォールと、いずれかのデバイスの認証が期限切れになっていないかを確認します。
Tailscale の調整サーバーではなく Headscale を実行すべきですか?
デバイスグラフや鍵の配布を、自分で管理するインフラ上に置く必要がある場合は Headscale を実行します。また、外部サービスに依存せず tailnet を動作させる必要がある場合にも適しています。クライアントとプロトコルは同じです。一方で、調整サーバーを自分で運用する必要があります。調整サーバーが停止すると、新しいノードが参加できなくなり、ポリシー変更も適用されません。小規模なフリートでは、tailnet lock を有効にしたホスト型の制御プレーンのほうが、通常は運用上のバランスに優れています。