Tailcat – Like netcat, but over Tailscale’s data plane
Tailcat – Like netcat, but over Tailscale’s data plane
“Tailscale without Tailscale, by Tailscale” “没有 Tailscale 的 Tailscale,由 Tailscale 出品”
Tailcat is a remix of Tailscale open source pieces to act like netcat, but over Tailscale’s data plane, without Tailscale’s control plane. Tailcat 是 Tailscale 开源组件的重组,其功能类似于 netcat,但运行在 Tailscale 的数据平面之上,且无需 Tailscale 的控制平面。
Tailscale’s data plane (magicsock, internally) gives you point-to-point WireGuard®-encrypted tunnels between two machines with DERP as the NAT-hole-punching communication side channel and the ultimate relay-of-last-resort if NAT traversal fails. Tailscale 的数据平面(内部称为 magicsock)可在两台机器之间提供点对点的 WireGuard® 加密隧道,并使用 DERP 作为 NAT 穿透的通信侧信道,以及在 NAT 穿透失败时的最终中继方案。
Instead of using the Tailscale control plane, all tailcat connection metadata is exchanged out of band, however you want. Tailcat 不使用 Tailscale 控制平面,所有的连接元数据均通过带外(out-of-band)方式交换,你可以根据需要自由选择交换方式。
The tailcat CLI (in cmd/tailcat) is built on the tailcat Go library (importable as github.com/tailscale/tailcat). Whether you use tailcat as a CLI tool or library, one side runs a tailcat server (listener) and gets back a short connection token. The other side passes that token to tailcat’s client side to connect. Tailcat CLI(位于 cmd/tailcat)基于 tailcat Go 库构建(可作为 github.com/tailscale/tailcat 导入)。无论你是将 tailcat 用作命令行工具还是库,其中一方需运行 tailcat 服务器(监听端)并获取一个短连接令牌,另一方则将该令牌传递给 tailcat 客户端进行连接。
All traffic between the two is encrypted end-to-end with WireGuard. The initial connection bootstraps through a DERP server (see below), and then magicsock performs NAT traversal to upgrade to a direct peer-to-peer UDP connection when possible (usually!). 双方之间的所有流量均通过 WireGuard 进行端到端加密。初始连接通过 DERP 服务器引导(见下文),随后 magicsock 会执行 NAT 穿透,在可能的情况下(通常都可以!)升级为直接的点对点 UDP 连接。
You don’t need a Tailscale account, root/admin access on the machine (it doesn’t alter your machine’s routing tables, DNS, etc.). It’s just a userspace library and CLI tool. And it’s all open source. 你无需 Tailscale 账户,也无需机器的 root/管理员权限(它不会更改机器的路由表、DNS 等)。它只是一个用户空间库和命令行工具,且完全开源。
You can use our free rate-limited DERP relays (the default DERP map is https://tailcat.dev/derpmap.json) or you can run your own. 你可以使用我们免费且限速的 DERP 中继(默认 DERP 映射为 https://tailcat.dev/derpmap.json),也可以运行自己的中继。
Install
安装
$ go install github.com/tailscale/tailcat/cmd/tailcat@latest
Or with Nix flakes, run it directly or install it: 或者使用 Nix flakes 直接运行或安装:
$ nix run github:tailscale/tailcat
$ nix profile install github:tailscale/tailcat
Usage
使用方法
Pipe stdin/stdout between two machines. 在两台机器之间通过管道传输标准输入/输出。
Server starts, printing out its ephemeral address: 服务器启动并打印其临时地址:
$ tailcat
# Selected bootstrap relay region 302, San Francisco
# 🐈 Server listening with new address: tcomFwWCCcjS5nKNqAod034nWoJZW0LZqDhhC8U_dKdnDRYQ8uNGFpGQEu
(hangs, waiting...)
And then the client can: 随后客户端可以执行:
$ echo hello | tailcat tcomFwWCCcjS5nKNqAod034nWoJZW0LZqDhhC8U_dKdnDRYQ8uNGFpGQEu
Then the server unblocks: 此时服务器端解除阻塞:
$ tailcat
# Selected bootstrap relay region 302, San Francisco
# 🐈 Server listening with new address: tcomFwWCCcjS5nKNqAod034nWoJZW0LZqDhhC8U_dKdnDRYQ8uNGFpGQEu
hello
Expose local ports through the tunnel
通过隧道暴露本地端口
Or you can serve a local TCP port, forwarded to localhost: 或者,你可以提供一个本地 TCP 端口,并将其转发到 localhost:
$ tailcat --serve=8080,8443 # or --serve=all
# 🐈 Server listening with new address: tcXXXXXXXXX
And then the client: 随后客户端执行:
$ tailcat tcXXXXXXXXX 8080
GET / HTTP/1.1
Host: foo
HTTP/1.1 200 OK
....
Auth-free SSH server
无需认证的 SSH 服务器
On Linux and macOS, you can run an SSH server too with no auth. (If you want auth, you can just tailcat —serve=22 and proxy to your system SSH server)
在 Linux 和 macOS 上,你也可以运行一个无需认证的 SSH 服务器。(如果你需要认证,只需运行 tailcat --serve=22 并代理到你的系统 SSH 服务器即可)
$ tailcat --serve=no-auth-ssh
# 🐈 Server listening with new address: tcXXXXXXXXX
And on the client side: 在客户端:
$ tailcat ssh tcXXXXXXXXX
$ tailcat ssh tcXXXXXXXXX ls -la
Misc commands
其他命令
Ping to test connectivity; each pong reports whether it arrived via a DERP relay or a direct path. —until-direct keeps pinging (up to —timeout, default 10s) until a direct path works, exiting non-zero if one doesn’t:
Ping 用于测试连通性;每个 pong 都会报告它是通过 DERP 中继还是直接路径到达的。--until-direct 会持续 ping(直到 --timeout,默认为 10 秒),直到直接路径连通为止;如果无法连通,则以非零状态码退出:
$ tailcat ping --until-direct <token>
pong in 42.1ms via DERP(sfo)
pong in 1.2ms via 203.0.113.7:41641
Run a command through a SOCKS5 proxy routed over the tunnel: 通过隧道路由的 SOCKS5 代理运行命令:
$ tailcat socks <token> curl http://server.tailcat:8081/
Tokens also work directly as URL hostnames: the SOCKS proxy recognizes and dials them, so the token argument is optional. (Tokens are case-sensitive; this works with curl and most CLI tools, but not with browsers, which lowercase hostnames.) 令牌也可以直接作为 URL 主机名使用:SOCKS 代理会识别并拨号,因此令牌参数是可选的。(令牌区分大小写;这适用于 curl 和大多数 CLI 工具,但不适用于浏览器,因为浏览器会将主机名转换为小写。)
$ tailcat socks curl http://<token>:8081/
Act as an exit node so the client can reach the server’s network: 充当出口节点,以便客户端可以访问服务器的网络:
$ tailcat --serve=exit-node
Parse a connection token and print its contents (the server’s WireGuard public key and DERP info) as JSON, without connecting to anything: 解析连接令牌并以 JSON 格式打印其内容(服务器的 WireGuard 公钥和 DERP 信息),而不进行任何连接:
$ tailcat parse tcomFwWCCcjS5nKNqAod034nWoJZW0LZqDhhC8U_dKdnDRYQ8uNGFpGQEu
{
"ServerPublic": "nodekey:9c8d2e6728da80a1dd37e275a82595b42d9a838610bc53f74a7670d1610f2e34",
"RegionID": 302
}
Resolve a short token (which references a DERP region by ID, requiring clients to fetch the DERP map) into a longer self-contained one with the DERP server info embedded, letting clients connect more quickly: 将短令牌(通过 ID 引用 DERP 区域,需要客户端获取 DERP 映射)解析为嵌入了 DERP 服务器信息的长自包含令牌,从而让客户端连接得更快:
$ tailcat resolve tcomFwWCCcjS5nKNqAod034nWoJZW0LZqDhhC8U_dKdnDRYQ8uNGFpGQEu
tcomFwWCCcjS5nKNqAod034nWoJZW0LZqDhhC8U_dKdnDRYQ8uNGFygaFhToGjYWhudGMzMDJhLmlwbi5kZXZhNG0yMDguMTExLjM5LjM4YTZzMjYwNzpmNzQwOjA6M2Y6OjcyMA
Parsing that resolved token shows the embedded DERP info: 解析该已解析的令牌会显示嵌入的 DERP 信息:
$ tailcat parse tcomFwWCCcjS5nKNqAod034nWoJZW0LZqDhhC8U_dKdnDRYQ8uNGFygaFhToGjYWhudGMzMDJhLmlwbi5kZXZhNG0yMDguMTExLjM5LjM4YTZzMjYwNzpmNzQwOjA6M2Y6OjcyMA
{
"ServerPublic": "nodekey:9c8d2e6728da80a1dd37e275a82595b42d9a838610bc53f74a7670d1610f2e34",
"Region": [
{
"Nodes": [
{
"HostName": "tc302a.ipn.dev",
"IPv4": "208.111.39.38",
"IPv6": "2607:f740:0:3f::720"
}
]
}
]
}
A server can print the long self-contained form directly with the —full-address flag.
服务器可以使用 --full-address 标志直接打印长自包含格式。
Key Management
密钥管理
A server’s address (connection token) is derived from its WireGuard key, so the key you use determines who can reach you: 服务器的地址(连接令牌)源自其 WireGuard 密钥,因此你使用的密钥决定了谁可以连接到你:
-
Ephemeral keys (the default): each server run generates a fresh key in memory and prints an address nobody has ever seen. When the process exits, the key is discarded and the address is dead forever. This is the safe default: sharing that address only ever refers to that one run. 临时密钥(默认): 每次服务器运行都会在内存中生成一个新密钥,并打印出一个从未见过的地址。当进程退出时,密钥会被丢弃,地址也随之永久失效。这是安全的默认设置:共享该地址仅指向那一次运行。
-
Saved keys:
tailcat genkeygenerates a key saved to disk so the address stays stable across restarts. The flip side: anyone you’ve ever shared that address with can connect to any future server using that key, unless you restrict clients with —allow (see tailcat genkey —client). 保存的密钥:tailcat genkey会生成一个保存到磁盘的密钥,以便地址在重启后保持稳定。反之:任何你曾共享过该地址的人都可以使用该密钥连接到未来的任何服务器,除非你使用--allow限制客户端(参见tailcat genkey --client)。
The CLI says at startup which kind it’s using, so you know whether you’re starting a fresh single-use server or re-listening on an address you may have shared in the past. CLI 在启动时会说明它正在使用哪种类型,因此你知道自己是在启动一个全新的单次使用服务器,还是在重新监听一个过去可能共享过的地址。
$ tailcat genkey --region=nyc
# prints the token; key saved to ~/.config/tailcat/keys/default.private.json
# later; the key named "default" is used automatically