今天在 GitHub Trending 上发现了一个来自 Tailscale 团队的创新项目:Tailcat,它重新定义了如何在两台机器间建立安全连接——不需要账号、不需要 root、不需要网络配置,只需一行命令。

一、项目概述

1.1 核心定位

Tailcat 的口号是 “Tailscale without Tailscale, by Tailscale”。它巧妙地将 Tailscale 的数据平面(WireGuard 加密隧道 + NAT 穿透)与控制平面(账号系统、协调服务器)完全解耦,打造出一个类似 netcat 的点对点工具。

核心特性:

  • 零账号要求:无需注册 Tailscale 账号,无需登录
  • 零权限要求:纯用户态实现,无需 root/admin 权限
  • 零配置网络:不修改路由表、DNS 或防火墙规则
  • 端到端加密:基于 WireGuard 的军事级加密
  • NAT 穿透:自动进行 UDP 打洞,失败时回退到 DERP 中继
  • 开源自研:Apache 2.0 许可,可自建 DERP 中继服务器

1.2 适用场景

场景传统方案Tailcat 方案
临时文件传输scp + 密钥配置一行命令,临时 token
内网 SSH 访问端口转发/内网穿透无需公网 IP,无需开放端口
快速调试端口nc/telnet加密隧道 + P2P 直连
无 root 环境无法操作用户态运行,完全可行

二、技术原理

2.1 架构设计

Tailcat 的核心创新在于复用 Tailscale 的网络栈但移除控制平面依赖

┌─────────────────────────────────────────┐
│           Tailcat 架构                  │
├─────────────────────────────────────────┤
│  应用层:SSH / 端口转发 / stdin-stdout   │
├─────────────────────────────────────────┤
│  传输层:gVisor Netstack(用户态 TCP/IP)│
├─────────────────────────────────────────┤
│  加密层:WireGuard(用户态实现)         │
├─────────────────────────────────────────┤
│  穿透层:magicsock + DERP 中继          │
└─────────────────────────────────────────┘

关键技术栈:

  1. WireGuard 用户态实现:无需内核模块,不创建 TUN/TAP 设备
  2. magicsock:Tailscale 的传输层,处理 STUN 探测和 UDP 打洞
  3. gVisor Netstack:用户态 TCP/IP 协议栈,避免操作系统网络配置
  4. DERP 协议:加密中继协议,用于初始握手和 NAT 穿透失败时的兜底

2.2 连接令牌机制

Tailcat 用 连接令牌(Connection Token) 替代传统的公钥基础设施:

令牌格式:

tc<base64编码的CBOR数据>

CBOR 数据包含:

  • 服务器的 WireGuard 公钥(Curve25519,32 字节)
  • DERP 区域 ID 或完整 DERP 服务器元数据

令牌示例:

# 短令牌(引用 DERP 区域 ID,约 50 字节)
tcomFwWCCcjS5nKNqAod034nWoJZW0LZqDhhC8U_dKdnDRYQ8uNGFpGQEu

# 长令牌(嵌入 DERP 服务器详情,自包含)
tcomFwWCCcjS5nKNqAod034nWoJZW0LZqDhhC8U_dKdnDRYQ8uNGFygaFhToGjYWhudGMzMDJhLmlwbi5kZXZhNG0yMDguMTExLjM5LjM4YTZzMjYwNzpmNzQwOjA6M2Y6OjcyMA

2.3 连接建立流程

从源码中可以看到完整的握手协议(disco.go):

Step 1: 服务器启动

server$ tailcat
# Selected bootstrap relay region 302, San Francisco
# 🐈 Server listening with new address: tcXXXXXXXXX

服务器生成临时 WireGuard 密钥对,连接到最近的 DERP 中继,打印令牌。

Step 2: 客户端解析令牌

client$ tailcat parse tcXXXXXXXXX
{
    "ServerPublic": "nodekey:9c8d2e67...",
    "RegionID": 302
}

客户端从令牌中提取服务器公钥和 DERP 区域,生成自己的临时密钥对。

Step 3: Meow 握手协议

源码中定义了自定义的握手消息(disco.go):

// meowMagic 是所有 meow DERP 数据包的 4 字节前缀
var meowMagic = [4]byte{'m', 'e', 'o', 'w'}

const (
    meowTypePing = 0x01 // client → server
    meowTypePong = 0x02 // server → client ("meowed")
)

// 客户端发送 Meow Ping,携带自己的公钥
func EncodeMeowPing(nodeKey key.NodePublic, discoKey key.DiscoPublic) []byte {
    b := make([]byte, 0, 4+1+key.NodePublicRawLen+key.DiscoPublicRawLen)
    b = append(b, meowMagic[:]...)
    b = append(b, meowTypePing)
    b = nodeKey.AppendTo(b)
    b = discoKey.AppendTo(b)
    return b
}

完整流程:

  1. 客户端通过 DERP 中继发送 Meow Ping(包含客户端公钥)
  2. 服务器收到后添加客户端到 WireGuard peer 列表,回复 Meowed
  3. 双方开始 WireGuard 握手(通过 DERP 路由)
  4. 握手完成后,magicsock 尝试 UDP 打洞升级到直连
  5. 打洞失败时保持 DERP 中继连接

2.4 密钥管理策略

源码支持两种密钥模式:

临时密钥(默认):

  • 每次运行生成新密钥,进程退出后销毁
  • 令牌生命周期 = 进程生命周期
  • 最安全:即使令牌泄露,进程结束后也无法重用

持久密钥:

# 生成持久密钥
tailcat genkey --region=nyc
# 密钥保存到 ~/.config/tailcat/keys/default.private.json

# 后续运行自动使用持久密钥
tailcat --serve=8080
# 🐈 Server listening with saved key "default": tcXXXXXXXXX

安全提示: 持久密钥意味着令牌长期有效,建议配合 --allow 白名单:

# 只允许特定客户端公钥连接
tailcat --serve=22 --allow=nodekey:cfb6bfa77a0654d7450947fd6acef17d...

三、安装与快速开始

3.1 安装方式

方式一:Go install(推荐)

go install github.com/tailscale/tailcat/cmd/tailcat@latest

方式二:Nix flakes

# 直接运行
nix run github:tailscale/tailcat

# 安装到系统
nix profile install github:tailscale/tailcat

3.2 最简示例:管道传输

服务器端:

server$ tailcat
# Selected bootstrap relay region 302, San Francisco
# 🐈 Server listening with new address: tcomFwWCCcjS5nKNqAod034nWoJZW0LZqDhhC8U_dKdnDRYQ8uNGFpGQEu
(等待客户端连接...)

客户端:

client$ echo "hello from client" | tailcat tcomFwWCCcjS5nKNqAod034nWoJZW0LZqDhhC8U_dKdnDRYQ8uNGFpGQEu

服务器收到:

server$ tailcat
hello from client
$

3.3 浏览器版本

官方提供了 WebAssembly 编译版本,可在浏览器中与 CLI 互操作:

👉 https://tailscale.github.io/tailcat/

支持发送/接收文件或文本,适合无法安装 CLI 的场景(浏览器版本仅支持 DERP 中继,暂不支持 WebRTC 直连)。

四、使用方法与实战

4.1 端口转发

服务器暴露本地端口:

server$ tailcat --serve=8080,8443  # 可指定多个端口,或 --serve=all
# 🐈 Server listening with new address: tcXXXXXXXXX

客户端连接:

client$ tailcat tcXXXXXXXXX 8080
GET / HTTP/1.1
Host: foo

HTTP/1.1 200 OK
....

4.2 无认证 SSH 服务器

服务器启动 SSH(Linux/macOS):

server$ tailcat --serve=no-auth-ssh
# 🐈 Server listening with new address: tcXXXXXXXXX

客户端连接:

# 交互式 SSH
client$ tailcat ssh tcXXXXXXXXX

# 执行远程命令
client$ tailcat ssh tcXXXXXXXXX ls -la

安全建议: 如需认证,可用 --serve=22 转发到系统 SSH 服务器。

4.3 测试连通性

Ping 测试:

client$ tailcat ping --until-direct tcXXXXXXXXX
pong in 42.1ms via DERP(sfo)
pong in 1.2ms via 203.0.113.7:41641  # 成功升级到直连

--until-direct 会持续 ping 直到建立直连路径(最多 10 秒),如果始终走 DERP 中继,说明 NAT 穿透失败。

4.4 SOCKS5 代理

# 通过隧道访问服务器网络
client$ tailcat socks tcXXXXXXXXX curl http://server.tailcat:8081/

# 令牌可直接作为 URL 主机名(大多数 CLI 工具支持)
client$ tailcat socks curl http://tcXXXXXXXXX:8081/

4.5 出口节点

让客户端通过服务器访问外部网络:

server$ tailcat --serve=exit-node
# 🐈 Server listening with new address: tcXXXXXXXXX

4.6 Go 库集成

源码提供了简洁的 Go API:

服务器端:

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)
}

五、常见问题与解决方案

5.1 连接建立慢

现象: 首次连接需要 5-10 秒。

原因:

  • DERP 区域选择需要网络探测
  • NAT 穿透需要多次尝试

解决方案:

# 生成持久密钥时固定区域
tailcat genkey --fixed-region

# 或明确指定区域
tailcat genkey --region=nyc

5.2 始终走 DERP 中继,无法直连

现象: tailcat ping 显示始终 via DERP(sfo),延迟较高。

原因:

  • 双方都在对称型 NAT 后(企业网络、移动网络)
  • 防火墙屏蔽 UDP

解决方案:

  1. 使用自建 DERP 服务器(延迟更低)
  2. 一方切换到非对称型 NAT 网络(家庭宽带通常可行)

5.3 公共 DERP 速率限制

现象: 传输速度受限,官方 DERP 有速率限制。

解决方案:自建 DERP 中继

# 1. 部署 DERP 服务器(需要域名 + TLS 证书)
# 参考:https://github.com/tailscale/tailscale/tree/main/cmd/derper

# 2. 生成使用自建 DERP 的令牌
server$ tailcat genkey --region=derp.example.com
tcomFwWCCAIsKOqPUux6ClG2RM4A_vOq4VBzGgHGGjq9OsJuFKSWFygaFhToGhYWhwZGVycC5leGFtcGxlLmNvbQ

# 3. 验证令牌包含自建 DERP 信息
client$ tailcat parse tcomFwWCCAIsKOqPUux6ClG2RM4A...
{
    "ServerPublic": "nodekey:8022c28e...",
    "Region": [
        {
            "Nodes": [
                {
                    "HostName": "derp.example.com"
                }
            ]
        }
    ]
}

5.4 令牌安全存储

问题: 持久密钥保存在本地文件,如何保护?

解决方案:

# 查看已保存的密钥
tailcat genkey --list

# 删除默认密钥(下次运行会生成临时密钥)
tailcat genkey --delete --key=default

# 为特定用途创建命名密钥
tailcat genkey --key=production --region=nyc

5.5 浏览器版本限制

限制:

  • 仅支持 DERP 中继,无法 WebRTC 直连(Issue #4)
  • 文件传输功能有限

适用场景: 快速临时访问、演示、无 CLI 权限的环境

六、进阶场景

6.1 通过 DNS 发布令牌

将令牌发布为 DNS TXT 记录,即可通过域名访问:

# DNS 配置
my-server.example.com. 300 IN TXT "tailcat=tcXXXXXXXXX"

# 客户端直接用域名
client$ tailcat ssh my-server.example.com
client$ tailcat my-server.example.com 8080

优势: 无需记忆或传递长令牌,域名即地址。

6.2 客户端身份验证

为 SSH 等敏感服务添加客户端身份验证:

客户端生成身份密钥:

client$ tailcat genkey --client
# wrote file to ~/.config/tailcat/keys/client-default.private.json
nodekey:cfb6bfa77a0654d7450947fd6acef17d2cd848da1d30b2540b13dac272ddfd16

服务器限制只允许该客户端:

server$ tailcat --serve=22 --allow=nodekey:cfb6bf...ddfd16
# 🐈 Server listening with saved key "default": tcXXXXXXXXX

效果: 只有拥有对应私钥的客户端能建立握手,其他人甚至无法探测到服务存在。

6.3 性能优化建议

从源码和架构分析:

  1. 选择最近的 DERP 区域:

    # 查看可用区域
    curl https://tailcat.dev/derpmap.json | jq '.regions[].regionName'
    
    # 固定区域避免每次探测
    tailcat genkey --region=region-name
    
  2. 长令牌减少 DERP map 请求:

    # 生成自包含令牌
    tailcat genkey --full-address
    
  3. 批量文件传输: 使用 --serve=all 一次暴露多个端口,减少连接建立次数。

七、总结

Tailcat 代表了网络工具设计的一种新范式:将复杂的基础设施能力封装到极简的命令行界面中。它不需要你理解 WireGuard 的握手流程、不需要配置 iptables 规则、不需要申请公网 IP,只需要:

# 一行启动服务器
tailcat

# 一行连接客户端
tailcat <token>

这种"开箱即用"的体验,得益于 Tailscale 团队在用户态网络栈、NAT 穿透、协议设计上的深厚积累。从源码中可以看到,它复用了 Tailscale 的核心组件(magicsocknetstackderp),但通过 Meow 握手协议连接令牌 机制,完全解耦了对控制平面的依赖。

适用人群:

  • 开发者:快速调试远程服务、临时端口转发
  • 运维:无需 SSH 配置的临时访问通道
  • 安全研究员:研究 WireGuard、NAT 穿透、去中心化网络
  • 普通用户:在严格网络环境下传输文件

限制:

  • 公共 DERP 有速率限制,生产环境建议自建
  • 对称型 NAT 下可能无法建立直连
  • 尚无 API 稳定性承诺(处于早期开发阶段)

对于 Tailscale 用户,Tailcat 是一个有趣的"侧信道"——当你不想或无法使用完整的 Tailscale 网络时,它提供了同样安全的点对点能力。对于非 Tailscale 用户,它是体验 WireGuard + NAT 穿透威力的最佳入门工具。


项目地址: https://github.com/tailscale/tailcat
在线体验: https://tailscale.github.io/tailcat/
开源协议: BSD-3-Clause