ac27db76f941435bda27900f9b44b01b860e3408
The diagnostics never got the split-DNS fix from 20097a0. That commit
taught the dial path to resolve through the tailnet's own resolver
(resolveDialAddr) but left getPeerFromRules on resolveAddr, so the two
disagreed about how to look a destination up: connecting through a
split-DNS name worked while the startup check called it unresolvable.
Both paths now share resolveHostToIP, which honors MagicDNS, split-DNS
routes and the DoH fallback.
The check also resolved once at startup and cached the result for the
process lifetime. tsnet reports Running before the netmap's DNS config
reaches its resolver, and accept-routes is only applied after Up()
returns, so a split-DNS name can fail for the first few seconds and
resolve fine after. That transient failure dropped the peer permanently
-- and when every rule failed, the goroutine returned and diagnostics
never ran at all. Peers are re-resolved every round now, with a warm-up
retry so the first report waits for DNS rather than racing it.
Destinations outside the tailnet are no longer reported as failures.
mc.lxns.net resolves fine but belongs to no peer, which is not an error,
just not something to ping. errNotTailnetPeer separates "cannot resolve"
from "resolved, not a peer"; only the former is retried or warned about.
Unresolved rules now log their tag and dst, which the old message
omitted entirely.
Also fixed:
* NormalizeDstAddrWithSuffix passed "host:port" to resolveAddr, so
every existence check failed on the stray colon. Fixing that made
the pass wait on cold-start DNS and delayed the listeners by ~5s,
so it is now bounded by normalizeDNSBudget.
* Peer lookup matched any AllowedIPs prefix containing the address.
An exit node advertises 0.0.0.0/0, which contains everything, so a
tailnet with an exit node picked the wrong peer at random depending
on map iteration order. Default routes are skipped, the most
specific route wins, ties break deterministically.
* peer.AllowedIPs is a nillable pointer, dereferenced unguarded.
tslink
基于 Tailscale tsnet 的轻量双向流量转发工具,可将本机服务暴露到 Tailnet,也可将 Tailnet 服务通过本机端口对外暴露。
特性
- 双向转发:
forward(Tailscale → 本地)与connect(本地 → Tailscale)两种模式 - TCP / UDP 全支持:透明转发 TCP 流与 UDP 数据包
- Minecraft 专用模式:支持局域网广播发现(MOTD),让本地设备发现 Tailnet 上的 Minecraft 服务器
- MagicDNS 主机名补全:
dst_addr支持按照 Tailscale 规则正确解析 Split DNS 和 Magic DNS - 连接类型识别:区分
direct直连与derp中继,便于排查延迟问题 - 对端连通性诊断:定期 ping 目标节点并报告延迟与连接路径(direct/DERP)
- Web 管理:内置 Tailscale Web Client(端口
5252),可在线管理节点配置 - 多配置源:支持本地 TOML 文件、HTTP/HTTPS URL、构建时注入默认 URL
环境要求
- Tailscale / Headscale 授权密钥
快速开始
-
前往 Releases 下载对应平台的二进制文件,赋予执行权限后放入 PATH
-
准备配置文件:
cp config.example.toml config.toml vim config.toml # 填入 auth_key 并配置转发规则 -
启动:
tslink -c config.toml
容器部署
cp config.example.toml config.toml
# 编辑 config.toml,然后对照修改 docker-compose.yml 中的端口映射
docker compose up -d
详细配置与使用说明见 USAGE.md。
工作原理
forward (你 -> 其他人):
Tailscale Client ──TCP/UDP──> [本机 Tailscale IP:port] ──转发──> [本地服务 127.0.0.1:port]
connect (其他人 -> 你):
本地/LAN 客户端 ──TCP/UDP──> [本机监听 port] ──转发──> [Tailscale 目标 host:port]
Languages
Go
100%