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(session traversal utilities for NAT)とICEの標準仕様に基づく技術を使って経路を検出します。各ノードはSTUNサーバーへ小さなUDPパケットを送信し、そのソケットに対してルーターが割り当てた公開アドレスとポートを取得します。両方のノードはこれらの候補をcoordination serverへ報告し、coordination serverが相手側へ渡します。その後、両方のノードが同時に相手へパケットを送信し始めます。各ルーターは最初に外向きのパケットを検出するため、マッピングを作成し、同じアドレスから到着した応答を受け入れます。どちらの側にも、受信用のファイアウォールルールは必要ありません。
使用するポートは決まっています。直接接続するWireGuardトンネルはUDPを使用し、送信元ポートのデフォルト値は41641です。STUNはUDP 3478でTailscaleのrelay serverと通信します。control connectionとrelay経由のデータは、TCP 443のHTTPSを使用します。通常は受信用のポートを開く必要はありません。ただし、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 パケットをいずれかのリレー経由で送信します。
パケットは暗号化されたままです。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 を開いて認証すると、ノードが管理コンソールに表示されます。その後、再起動してもデーモンが再び起動することを確認してください。この確認は省略されがちです。
sudo systemctl is-enabled tailscaled
tailscale statusis-enabled は enabled を出力し、tailscale status には新しいノードとその 100.x アドレスが表示されます。スクリプトで構築したサーバーでは、対話型 URL は使えません。管理コンソールで認証キーを生成し、このマシンの種類を示すタグとともに渡します。
sudo tailscale up --auth-key=tskey-auth-REPLACE-ME --advertise-tags=tag:serverタグ付きノードは、コマンドを実行したユーザーではなくタグによって所有されます。そのため、そのユーザーのアカウントが削除された後も動作します。先にポリシーファイルの tagOwners でタグを宣言する必要があります。宣言していない場合、コマンドは拒否されます。タグ付けによって、プランの使用量の数え方も変わります。タグ付きリソースはユーザー自身のデバイスとは別に料金が計算されます。無料プランで実際に利用できる範囲では、これらの上限について説明しています。
複数のマシンを管理する場合は、2 つの設定が重要です。ノードキーはデフォルトで 180 日後に有効期限が切れます(2026 年 8 月時点)。キーの有効期限が切れると、「指定されたエンドポイントとの接続は停止します」。再度ログインするまで復旧しません。そのため、無人運用のサーバーでは、管理コンソールで対象マシンの行を開き、Disable Key Expiry を選択してください。2022 年 10 月 20 日以降に作成された tailnet では、MagicDNS がデフォルトで有効になっています。各ノードには db-1.yak-bebop.ts.net のような名前が付けられ、100.100.100.100 の stub resolver によって名前解決されます。アドレスではなく名前を使用してください。ノードを再構築すると新しいアドレスが割り当てられますが、名前は維持されます。
apt またはリポジトリでインストールに失敗する場合は、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 内の peer です。そのため、次にアクセスできる範囲はポリシーが許可する内容になります。これが許容可能なリスクになるかは、何から防御するかによって異なります。完全な信頼モデルでは、盗まれた identity アカウントで実際に可能な操作を含め、これらのケースを詳しく説明しています。
鍵の配布リスクには、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 を最初から最後まで自分で運用できます。
初日に変更すべきデフォルト設定があります。新しい tailnet は permissive な状態で提供されます。「デフォルトの tailnet policy file では、tailnet 内のすべてのデバイス間の通信が有効になります。」acls セクションを追加すると、モデルはデフォルト拒否に切り替わり、指定したルールだけが通過します。
{
"tagOwners": {
"tag:server": ["autogroup:admin"]
},
"acls": [
{"action": "accept", "src": ["autogroup:member"], "dst": ["tag:server:22"]}
]
}このポリシーでは、tailnet のメンバーがタグ付き server の SSH にアクセスできます。それ以外へのアクセスは許可しません。wildcard を残すのではなく、サービスごとにルールを追加してください。wildcard を指定すると、盗まれた laptop の鍵だけでデータベースに到達できるためです。
障害時に発生する事象と表示される文字列
tailscale status は常に relay と表示されます。 2 つのノード間に直接経路が確立されていません。両端で tailscale netcheck を実行します。UDP: false は送信方向の UDP がブロックされていることを示します。この場合はリレーしか使用できません。MappingVariesByDestIP: true は厳しい NAT が介在していることを示します。管理下にある側で受信 UDP 41641 を許可すると、多くの場合は解決します。
数か月間動作していたノードが消えました。 ノードキーの有効期限が、デフォルトの 180 日で切れています。管理コンソールではマシンが期限切れとして表示され、マシン上で sudo tailscale up を実行すると復旧します。サーバーではキーの有効期限を無効にして、再発を防ぎます。
ピアは一覧に表示されますが、接続がタイムアウトします。 接続性は確保されていますが、ポリシーによってトラフィックが拒否されています。acls セクションで、この送信元、宛先、ポートを対象とするルールを確認します。拒否されたパケットは応答されずに破棄されるため、Connection refused ではなくタイムアウトになります。
MagicDNS の名前を解決できません。 ping db-1 は失敗しますが、ping 100.101.102.104 は動作します。何らかの処理によって /etc/resolv.conf が置き換えられているため、クエリが 100.100.100.100 の stub resolver に到達していません。cat /etc/resolv.conf で 100.100.100.100 を確認し、そのファイルを書き換える他の処理も調べます。これは WireGuard トンネル内で DNS が機能しなくなる問題 と同じ種類の問題です。
tailscale up でタグを指定できません。 ポリシーファイルの tagOwners に、そのタグが宣言されていません。そこに追加してから、コマンドを再度実行します。
FAQ
Tailscale は VPN ですか、それともメッシュネットワークですか?
どちらの表現も正しく、異なる層を表しています。トンネルには WireGuard を使用するため、VPN です。各ノードが、通信する相手の各ノードへ直接トンネルを構築し、すべてのパケットを中央サーバー経由で送信するわけではないため、トポロジーはメッシュです。調整サーバーはデータパスではなくコントロールパスに位置します。そのため、調整サーバーに到達できなくなっても、既存のトンネルはネットワークトラフィックの転送を続けます。障害中に停止するのは、新しいノードの参加と、鍵やポリシーの変更の反映です。
Tailscale は私のネットワークトラフィックを読み取れますか?
内容は読み取れません。ネットワークトラフィックは WireGuard によりノード間でエンドツーエンド暗号化され、秘密鍵がノードの外へ出ることはありません。また、DERP リレーは復号できないパケットを転送します。一方で Tailscale は、マシン名、所有者、公開鍵、エンドポイントアドレス、各ノードがオンラインになった時刻などのメタデータを確認できます。さらに鍵も配布するため、調整サーバーが侵害されると、フリートが信頼するノードを挿入しようとする可能性があります。Tailnet lock は、ユーザー自身が信頼するノードからの署名を要求することで、これを防ぎます。Headscale を使うと、ホスト型のコントロールプレーンを構成から外せます。
Tailscale のためにファイアウォールのポートを開く必要はありますか?
インバウンド通信では、ほとんどありません。Tailscale の公式ガイダンスでも、「ほとんどの場合、ファイアウォールのポートを開く必要はありません」と説明されています。アウトバウンド通信では、ノードから調整サーバーとリレーへ接続するために TCP 443 が必要です。STUN には UDP 3478 も必要です。直接トンネルでは UDP を使用し、送信元ポートのデフォルト値は 41641 です。インバウンドで UDP 41641 を許可する設定は任意です。複雑なネットワーク環境で直接接続を確立しやすくする効果があります。
他のノードからポート 3000 のサービスに接続できないのはなぜですか?
まず ss -tlnp でバインドアドレスを確認します。127.0.0.1:3000 で待ち受けるリスナーは、ノードの 100.x tailnet アドレス宛てに到着した接続を拒否します。このソケットはループバック宛ての接続だけを受け付けるため、クライアントには Connection refused が表示されます。サービスを tailnet アドレスにバインドするか、tailscale serve 3000 を実行してプロキシします。リスナーがすでに 0.0.0.0 で待ち受けていて、接続拒否ではなくタイムアウトする場合、原因はバインドアドレスではなく、ポリシールールまたはホストのファイアウォールです。
Tailscale の調整サーバーではなく Headscale を実行すべきですか?
デバイスグラフまたは鍵の配布を自分で管理するインフラ上に保持する必要がある場合や、外部サービスに依存せず tailnet を動作させる必要がある場合は、Headscale を実行します。クライアントとプロトコルは同じです。代わりに、調整サーバーを自分で運用する必要があります。また、調整サーバーが停止すると、新しいノードの参加とポリシー変更の反映も停止します。小規模なフリートでは、tailnet lock を有効にしたホスト型コントロールプレーンのほうが、通常は適切な選択です。