
2026/08/27 2:42
Tailcat – Netcat のような動作だが、Tailscale データプレーン上で行うツールです。
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
Tailcat は、netcat のような動作をするオープンソースユーティリティで、Tailscale のインフラストラクチャを利用して安全なリモート接続を可能にし、Tailscale アカウントや root アクセスを必要としません。既存のユーザースペースコンポーネント(WireGuard®、NAT 越え用の magicsock、Netstack(gVisor)など)を活用し、UDP を介して暗号化トンネルを構築します。接続は DERP サーバー(短時間の接続トークンを使用)を経由して初期化され、可能であれば直接のピアツーピアリンクにアップグレードされ、システムルーティングテーブルや DNS 設定の変更が一切不要になります。本ツールは完全にユーザースペースで動作し、管理権限は必要ありません。もともとは「derpcat」と名付けられ、TailscaleUpカンファレンスにてオープンソース化されました。各種ネーティングタスク(stdin/stdoutのパイピング、ローカルポートの公開、認証不要なSSHサーバーの実行、SOCKS5プロキシとしての機能など)に対応しています。鍵管理では、一時鍵(デフォルト)と生成された鍵による安定アドレス(
tailcat genkey)の両方がサポートされており、トークン解決には DNS TXT レコードまたは parse・resolve コマンドを利用できます。go install または Nix flakes 経由で入手可能な Tailcat は、コントロールプレーンの依存関係を排除しつつ、多様な環境間で認証された安全な接続を提供することで、複雑なネットワークセットアップを簡素化します。本文
Tailcat:コントロールプレーン不使用の Tailscale データプレーン利用ツール
概要
Tailcat は、Tailscale のコントロールプレーンを使用せず、データプレーン(magicsock)のみを利用して Netcat と同様の動作を実現するオープンソースツールです。
- 通信経路: DERP(Direct Endpoint Routing Protocol)を介して点対点の WireGuard® 暗号化トンネルを構築します。
- 構成要件: Tailscale アカウントの所持不要。ルート/管理者権限も不要(システム設定変更なし)。
- 実装ベース:
Go ライブラリと CLI ツール。github.com/tailscale/tailcat
動作原理
- ブートストラップ: 初期接続は DERP サーバーを経由して確立されます。
- 接続アップグレード: magicsock が NAT 経路設定を行い、可能であれば直結のピアツーピア(P2P)UDP 接続へアップグレードします。
- 暗号化: すべてのトラフィックが WireGuard でエンドツーエンド暗号化されます。
利用可能な DERP リレー
- デフォルト:
を参照する無料リレー。https://tailcat.dev/derpmap.json - 自管運用: ご自身の DERP サーバーも利用可能です。
インストール方法
Go や Nix flake を使用してインストールできます。
# Go モジュールを使用 go install github.com/tailscale/tailcat/cmd/tailcat@latest # または Nix flakes で実行・インストール nix run github:tailscale/tailcat nix profile install github:tailscale/tailcat
使用方法
1. スタンダードな stdin/stdout パイプ
サーバー側を起動し、生成されたトークンをクライアントに渡します。
サーバー起動(リスナー開始)
$ tailcat # Selected bootstrap relay region 302, San Francisco # 🐈 サーバーは新しいアドレスでリスニング中:tcomFwWCCcjS5nKNqAod034nWoJZW0LZqDhhC8U_dKdnDRYQ8uNGFpGQEu (ハングして待機中...)
クライアント側(コマンド実行)
$ echo hello | tailcat tcomFwWCCcjS5nKNqAod034nWoJZW0LZqDhhC8U_dKdnDRYQ8uNGFpGQEu hello $
サーバー側で解放される状態:
$ tailcat # 入力された 'hello' が出力されます hello $
2. ローカルポートのトンネリング(プロキシ)
ローカルの TCP ポートをリモートからアクセス可能にします。
サーバー設定
# ポート指定:8080 と 8443 を公開(または --serve=all ですべて) $ tailcat --serve=8080,8443 # 🐈 サーバーは新しいアドレスでリスニング中:tcXXXXXXXXX
クライアントアクセス
# トークンにポート番号を指定して接続 $ tailcat tcXXXXXXXXX 8080 # Web サーバーへのリクエストが可能になる GET / HTTP/1.1 Host: foo HTTP/1.1 200 OK ....
3. 認証なし SSH サーバー
Linux および macOS で、認証を省略した簡易 SSH サーバーとしても動作します。
サーバー設定
# no-auth-ssh モードで起動 $ tailcat --serve=no-auth-ssh # 🐈 サーバーは新しいアドレスでリスニング中:tcXXXXXXXXX
クライアント接続
$ tailcat ssh tcXXXXXXXXX $ tailcat ssh tcXXXXXXXXX ls -la # 標準的な SSH コマンドが実行可能
4. Miscellaneous コマンド
ピングによる接続性テスト
DERP リレー経由か直結(ダイレクトパス)かをレポートします。
# --until-direct: タイムアウト(デフォルト 10 秒)まで ping を続け、直結まで待機する $ tailcat ping --until-direct <トークン> pong in 42.1ms via DERP(sfo) pong in 1.2ms via 203.0.113.7:41641 # 直結成功
SOCKS5 プロキシ経由の実行
トークンを URL ホスト名として認識します(ブラウザのホスト名小文字化処理に注意)。
# トークン引数または --serve 形式 $ tailcat socks <トークン> curl http://server.tailcat:8081/ $ tailcat socks curl http://<トークン>:8081/
エグジットノードとして動作
クライアントがサーバーのネットワークに到達できるようにします。
$ tailcat --serve=exit-node
トークンの解析(JSON 出力)
接続を試行せず、WireGuard 公開鍵や DERP 情報を確認できます。
$ tailcat parse tcomFwWCCcjS5nKNqAod034nWoJZW0LZqDhhC8U_dKdnDRYQ8uNGFpGQEu { "ServerPublic": "nodekey:9c8d2e6728da80a1dd37e275a82595b42d9a838610bc53f74a7670d1610f2e34", "RegionID": 302 }
トークンの展開(短縮版 → 自己完結型)
DERP リージョン ID を含んだ短縮トークンを、ホスト名や IP が埋め込まれた完全な形式に変換します。これによりクライアントはより迅速に接続できます。
# 短縮トークンの解決 $ tailcat resolve tcomFwWCCcjS5nKNqAod034nWoJZW0LZqDhhC8U_dKdnDRYQ8uNGFpGQEu tcomFwWCCcjS5nKNqAod034nWoJZW0LZqDhhC8U_dKdnDRYQ8UdGjYWhudGMzMDJhLmlwbi5kZXZhNG0yMDguMTExLjM5LjM4YTZzMjYwNzpmNzQwOjA6M2Y6OjcyMA # 展開後の解析(DERP ノード情報を含む) $ tailcat parse <展開されたトークン> { "ServerPublic": "...", "Region": [ { "Nodes": [ { "HostName": "tc302a.ipn.dev", "IPv4": "208.111.39.38", "IPv6": "..." } ] } ] }
サーバー側でも
--full-address フラグで完全な形式を出力できます。
キーマネジメント(鍵の管理)
接続トークンは派生する WireGuard 鍵により、アクセス権限が決定されます。
| キータイプ | 特徴 |
|---|---|
| 一時的鍵 (Ephemeral keys) | デフォルト。 起動時に新しい鍵を生成し、プロセス終了時に破棄されます。 安全なデフォルト。共有してもそのセッションのみ有効です。 |
| 保存された鍵 (Saved keys) | でディスクに保存。再起動後もアドレスが安定します。 注意: 以前共有したすべてのアドレスで、将来の接続が可能になる可能性があります( で制限しない限り)。 |
操作例
-
永続鍵の生成と保存:
$ tailcat genkey --region=nyc # トークン出力。キーは ~/.config/tailcat/keys/default.private.json に保存されます。 -
保存された鍵を自動使用:
# 'default' という名前の鍵が存在すると自動的に使用されます $ tailcat --serve=8080 # 🐈 サーバーは保存されたキー "default" でリスニング中:tcXXXXXXXXX -
新しい一時的鍵を強制的に生成:
$ tailcat --serve=8080 --key=new # 🐈 サーバーは新しいアドレスでリスニング中:tcXXXXXXXXX -
保存済みデフォルト鍵の削除:
tailcat genkey --delete --key=default
DNS によるトークン解決 トークンを DNS TXT レコードとして公開し、ドメイン名で接続できます。
# DNS 設定例 my-server.example.com. 300 IN TXT "tailcat=tcXXXXXXXXX"
# ドメイン名を直接指定(自動解決) $ tailcat example.com 8080 $ tailcat ssh example.com $ tailcat ping example.com
例:DNS を介した保護された SSH サーバー
ポートフォワーディングやノッキングなしに、どこからでも名前でアクセス可能な SSH サーバーを設定できます。WireGuard がパケットを見る前にクライアント認証を行います。
-
クライアント側: アイデンティティ鍵ペアを生成し、公開鍵のみをサーバーに渡す必要はありません(CLI 上で処理可能)。
client$ tailcat genkey --client # ファイル:~/.config/tailcat/keys/client-default.private.json に保存されます。 nodekey:cfb6bfa77a0654d7450947fd6acef17d2cd848da1d30b2540b13dac272ddfd16 -
サーバー側: ネイレスト DERP リージョンに固定された鍵ペアを生成し、特定のクライアントに対してのみ SSH サーバーを提供します。
server$ tailcat genkey --fixed-region # ファイル:~/.config/tailcat/keys/default.private.json に保存されます。 tcXXXXXXXXX server$ tailcat --serve=22 --allow=nodekey:cfb6bf...ddfd16 # 🐈 サーバーは保存されたキー "default" でリスニング中:tcXXXXXXXXX -
DNS レコード設定:
my-server.example.com. 300 IN TXT "tailcat=tcXXXXXXXXX" -
接続:
client$ tailcat ssh my-server.example.com自動保存された
キーが使用されるため、追加のフラグは不要です。client-default
なぜ --fixed-region
を使うのか?
--fixed-region通常
tailcat genkey は --region=auto(起動時選択)を使用しますが、DNS に公開されたトークンはクライアントとサーバー再起動後も同じ場所で集合できるようにするため、固定されたリージョンを指定する必要があります。
- 固定:
または--fixed-region--region=<名前> - リージョン選択可:
--region=list
TODO: DERP マップが変化した場合のクライアント側強化が必要(Issue #7)
独自 DERP リレーの導入
Tailscale のリレーを使用せず、独自の DERP サーバーを運用可能です。 DERP は hostname と TLS 証明書が必要です(derper により Let's Encrypt で自動取得可能)。
サーバー設定例:
server$ tailcat genkey --region=derp.example.com tcomFwWCCAIsKOqPUux6ClG2RM4A_vOq4VBzGgHGGjq9OsJuFKSWFygaFhToGhYWhwZGVycC5leGFtcGxlLmNvbQ server$ tailcat --serve=22
自己完結型トークンの確認: トークンにはリレーのホスト名が埋め込まれており、Tailscale マップサーバーへの依存はありません。
$ tailcat parse tcomFwWCCAIsKOqPUux6ClG2RM4A_vOq4VBzGgHGGjq9OsJuFKSWFygaFhToGhYWhwZGVycC5leGFtcGxlLmNvbQ { "ServerPublic": "...", "Region": [ { "Nodes": [ { "HostName": "derp.example.com" } ] } ] }
複数のリレーを運用している場合は、独自の DERP マップ JSON を
--derpmap-url で渡すことも可能です。
Go ライブラリによる利用
TCP ポートを回答する最小限のサーバーとクライアントを実装できます。
サーバー側コード例
package main import ( "fmt" "log" "net" "github.com/tailscale/tailcat" ) func main() { s := &tailcat.Server{ OnTCP: func(port uint16) func(net.Conn) { return func(c net.Conn) { fmt.Fprintf(c, "hello from port %v\n", port) c.Close() } }, } if err := s.Start(); err != nil { log.Fatal(err) } fmt.Println(s.ConnBlob()) // トークンを出力 select {} // 待機 }
クライアント側コード例
package main import ( "context" "io" "log" "os" "github.com/tailscale/tailcat" ) func main() { // サーバーから取得したトークンを引数に指定 cl := tailcat.NewClient(tailcat.ConnBlob(os.Args[1])) defer cl.Close() c, err := cl.DialTCPPort(context.Background(), 80) if err != nil { log.Fatal(err) } io.Copy(os.Stdout, c) // 結果を表示 }
実行例:
$ ./client tcomFwWCAWf933BLELdzd3RkHiOufJ... hello from port 80
仕組みの詳細
接続トークン (ConnBlob)
tcXYZ... の形式で、Base64 エンコードされた CBOR を含む。
- WireGuard 公開鍵: Curve25519(32 バイト)。
- DERP 情報:
- 整数のリージョン ID(デフォルト Tailscale リレー参照)。
- または、完全な DERP サーバーメタデータ(
や--full-address
で生成)。自己完結型。tailcat resolve
ネットワークスタック
コントロールプレーンを使用せず、以下のコンポーネントを再利用します。
- WireGuard: ユーザースペース実装。カーネル TUN/TAP デバイスやルート権限は不要。
- magicsock: UDP と DERP リレーを超えてマルチプレックスするトランスポートレイヤー。NAT 経路設定(ホールパンチ)も処理。
- Netstack (gVisor): ユーザースペースの TCP/IP スタック。OS のネットワーク構成なしに入出力を処理。
- DERP リレー: フォールバック用および集合チャネルとしての暗号化プロトコル。
接続フロー
- サーバー起動: 鍵ペア生成 → DERP に接続 → トークン出力 → クライアント待機。
- クライアント解析: トークン学習 → 独自鍵ペア生成 → DERP 接続。
- ハンドシェイク: クライアントが "Meow" を送信 → サーバーがピアリストに追加 → "Meowed" 確認。
- WireGuard トンネル: WireGuard ハンドシェイク完了。暗号化トンネル確立。
- NAT 経路設定: STUN ベースのホールパンチを試行。成功すれば DERP から直結 P2P にアップグレード。失敗しても DERP で動作(スループット制限あり)。
- データ転送: gVisor タックで TCP コネクション処理 → サーバー側でのハンドリング(ポートディスパッチ、SSH など)。
アドレス指定
ピアは WireGuard 公開鍵から派生する決定的な IPv6 アドレスを割り当てられますが、これは実装詳細であり変更可能な可能性があります。
安定性と保証
- 無償: 利用は無料です。
- 非保証: API や CLI の挙動、出力形式は予告なく変更される可能性があります。
- SLA なし: アップタイムやスループットについて何らかの保証はありません。
- サービス停止: 必要な場合、理由を問わずアクセスを停止する可能性があります。
サポートと歴史
サポート
設定やサポートで困った場合は、販売担当者に連絡してください。 お金のやり取り(製品・サービス交換)も可能です。
歴史
- 2023 年 9 月: Tailscale コミュニティメンバーによって「derpcat」として誕生。長距離フライト中に作成されました。
- 初期コミット:
(開始)、9e4d925cc
(動作開始:"UA 605 PDX-ORD en route to Ireland. yay not buying the wifi.")。911915fbb - 進化: tailscale.com リポジトリのフォークから、通常の Go モジュールとして再ファクタリング。
- 2026 年 8 月: TailscaleUp カンファレンスでオープンソース化されました。