马春杰杰博客
致力于深度学习经验分享!

Tailscale 从安装到自建 DERP 与出口节点:一份完整记录

#智能家居
本文于 2026-11-09 22:07 更新,部分内容具有时效性,如有失效,请留言
最新目录‹

最近把手上的几台设备都接进了Tailscale,顺便把之前零散发过的几篇东西整理到一块。

一台阿里云ECS做自建DERP中转,家里树莓派上的iStoreOS做出口节点兼子网路由,实验室的服务器也接进来。

阿里云这台用的是Ubuntu 22.04。

公网IP是xxx.xxx.xxx.xxx

家里的iStoreOS跑在树莓派5的KVM虚拟机里。

OpenWrt 23.05,旁路由模式,LAN网段是192.168.110.0/24。

1、Tailscale是干什么的

Tailscale底层是WireGuard,但省掉了最麻烦的一步:密钥交换。

自己组WireGuard的话,两台设备要互相知道对方的公钥。

IP变了还要重新配。

Tailscale引入了一个控制平面,默认是官方的controlplane.tailscale.com。

设备注册上去之后,密钥交换、节点列表、路由分发全由它完成。

平时节点之间能直连的话,走的是WireGuard的P2P,数据不经过任何第三方。

但总有些环境直连不了,比如两边都在严格NAT后面,或者运营商做了限制。这种情况下流量会被中转到DERP服务器。

DERP全称Designated Encrypted Relay for Packets,只是个加密包的中转站,它看不到你的明文流量。

后面的内容分三块:

  1. 安装Tailscale

  2. 在阿里云ECS上自建DERP

  3. 把家里的iStoreOS做成出口节点

2、安装Tailscale

2.1 Ubuntu / Debian

官方脚本最省事:

装完之后启动服务并登录:

tailscale up会打印一个登录链接,在浏览器里打开,用你的账号授权就行。

如果机器上跑着systemd-resolved,建议把它打开:

这样Tailscale的DNS规则会注入到systemd-resolved里统一调度。

不是直接覆盖resolv.conf。

IPv6 DNS不可用时会自动降级到IPv4解析。

2.2 Debian 13 trixie

上面那条命令在trixie上会报错:

Tailscale官方仓库当时还没加trixie。手动写源就行。

先清理空文件,之前失败的下载会留下0字节的key:

下载key并安装:

写入软件源:

然后:

如果下载key时卡住不动,多半是IPv6不通,强制IPv4再试:

2.3 macOS

官方App装完就行。

装完有个常见问题:命令行里敲tailscale说找不到命令。

因为App装的东西不会往PATH里加。两个办法:

直接用全路径:

或者写进shell配置:

加到~/.zshrc或~/.bash_profile里,然后source一下或者重开终端。

2.4 阿里云安全组

这台ECS只负责DERP中转,不做出口节点。

出口节点放在家里的iStoreOS上。

在ECS控制台的安全组里放行这几个端口:

用途 协议 端口
DERP HTTPS TCP 443
DERP HTTP(证书签发用) TCP 80
DERP STUN UDP 3478
SSH TCP 22

3、自建DERP

DERP跑在阿里云ECS上。

3.1 为什么DERP和出口节点不放一台

DERP需要的机器特征是公网可达、固定IP、能扛住中转流量。

出口节点需要的机器特征是离用户近、带宽够、能伪装成自己的出口IP。

我这边的分工是:

阿里云ECS做DERP。

家里iStoreOS做出口节点。

这样DERP走的是ECS的公网带宽,出口流量走的是家里的宽带。

而且iStoreOS是NAT后面的设备,本来就不满足DERP「必须公网直连」的要求,放到ECS上是唯一合理的选择。

3.2 先想清楚要不要建

大部分情况下真不需要。

DERP只在P2P直连失败时才用得上,流量走中转必然比直连慢。如果经常发现自己走DERP,正确的做法是去查为什么直连不成功,而不是自己搭DERP。

我现在建它的原因是:国内访问官方DERP延迟高,而且想把流量约束在自己机器上。

3.3 开始自建

这里参考这篇文章:

4、把iStoreOS做成出口节点

前面准备工作都做完了,这一步在家里的iStoreOS上开。

出口节点的作用是:把客户端的所有公网流量都从这个节点出去,对外显示的IP就是家里宽带的公网IP。

在iStoreOS上做出口节点和普通Linux服务器不一样。

OpenWrt有自己的防火墙和DNS管理,Tailscale装上去只是其中一环。

配置不对的话,常见现象是客户端能连上iStoreOS的Tailscale IP,但是一上网就断。

4.1 装包

iStoreOS基于OpenWrt,用opkg装:

新版本的OpenWrt已经全面转向nftables,防火墙默认由fw4管理。

但Tailscale底层还是调iptables命令来注入规则,所以要装兼容层:

ip6tables-nft也要装。

就算不用IPv6,Tailscale启动时也会调ip6tables做--cleanup。

缺了这个二进制,服务会起不来或者反复重启。

要能转发流量的话还需要NAT模块:

装完启动:

看日志如果只有Program starting... --cleanup就退出了,那是清理线程正常结束。

主进程由procd在后台跑,用这个确认:

4.2 建接口

Tailscale自己创建的tailscale0是个虚拟网卡,OpenWrt不认识它,防火墙也就没法管。

得在LuCI里给它建一个接口:

网络 → 接口 → 添加新接口

名称填tailscale,协议选「不配置协议 (Unmanaged)」,设备选tailscale0。

4.3 建防火墙区域

这是最关键的一步,OpenWrt默认是「默认拒绝」策略,tailscale0被当成不受信任的外部区域。

网络 → 防火墙 → 区域 → 添加

名称填tailscale。

输入、输出、转发都选ACCEPT。

IP动态伪装打开,MSS钳制打开,覆盖网络勾tailscale。

允许转发到目标区域里,要勾WAN。

出口节点需要流量从tailscale转到WAN出去,不勾这个就不通。

允许从源区域转发这里,勾上LAN。

这样局域网里的设备也能访问Tailscale网络。

命令行等价写法:

masq一定要开。

Tailscale自己会打MASQUERADE规则。

它靠打mark的方式匹配所有从tailscale0进来的流量,出口节点也在内。

但OpenWrt上还是建议在防火墙区域把masq打开。

一是子网路由场景本来就需要,二是有OpenWrt这层NAT在,回包路径和conntrack状态都更干净。

mtu_fix也就是MSS钳制,建议一起开。

WireGuard封装之后包会变大,不钳制的话大包容易被丢,表现是网页能开但下载卡住。

4.4 DNS不要让Tailscale管

这一步很多人出问题。

Tailscale的MagicDNS会覆盖OpenWrt的dnsmasq配置。

两边抢DNS解析权的结果是解析循环或者整个网络断掉。

如果确实要用MagicDNS解析*.ts.net,手动加到dnsmasq里:

4.5 广告成出口节点和子网路由

先确认IP转发是开的:

返回1就是开的。

OpenWrt默认就是开的,一般不用动。

--reset建议带上。

不加的话改某些参数会报参数冲突,尤其是想把出口节点关掉再打开的时候。

--advertise-routes是同时把家里局域网也广播出去。

这样在外面既能上网,也能直接访问NAS和Home Assistant。

如果只需要出口节点,不广播局域网,把这行删掉。

--snat-subnet-routes不要加false。

OpenWrt wiki上写子网路由时建议加这个参数,前提是防火墙区域的masquerading已经打开。

因为那种情况下NAT由OpenWrt做。

但出口节点不能这么设,关掉之后客户端能连上但是上网不通。

Tailscale现在会直接给警告:

4.6 后台批准

tailscale up只是广播,还要在后台勾选。

进Machines页面找到iStoreOS,点右侧三个点,选Edit route settings。

Subnet routes勾上192.168.110.0/24。

Exit node勾上Use as exit node。

保存。

这一步漏了的话,客户端能看到节点但用不了出口节点,也不会给任何提示。

4.7 客户端使用

macOS、Linux、Windows、iOS、Android都有图形界面,在Exit Node下拉框里选这台iStoreOS就行。

命令行:

或者用节点名:

关掉:

验证有没有走出口节点:

返回的应该是家里宽带的公网IP。

4.8 访问本地网络的例外

默认情况下,走了出口节点之后,本地局域网就访问不到了,比如打印机、公司内网。

需要访问的话加--exit-node-allow-lan-access:

这样只有本地网段的流量走本地,其余还是走家里宽带。

4.9 和iStoreOS上其他插件的冲突

iStoreOS上一般还装着Nikki这类透明代理。

它们和Tailscale都要改路由表和防火墙规则,容易撞车。

Nikki / OpenClash拦截Tailscale的UDP隧道

把Tailscale的虚拟网段加到「不走代理的局域网设备」里:

不排除的话,OpenClash可能把Tailscale的UDP 41641也劫持走,形成路由环路。

DNS冲突

上面已经用--accept-dns=false处理掉了大部分。

但还要确认Nikki允许处理来自tailscale0的DNS请求,否则走出口节点的客户端解析不了域名。

如果开了mwan3

mwan3会改路由表优先级,Tailscale的流量可能被主WAN抢走,出口节点就不通。

绕过办法是在/etc/config/network里加两条规则:

Tailscale默认用52号路由表,mwan3启用后这个表的优先级会变成5270,比WAN接口低,流量就路由错了。

4.10 校园网场景的一个补充

如果是在学校用家里宽带做出口节点,学校有些网站只认校园网。

这种情况在Mac上用Surge配一个绑定校园网有线网卡的直连策略,让这些域名绕过Tailscale出口节点。

规则写在最前面:

5、常见问题

客户端连不上DERP

先看tailscale netcheck。

如果UDP显示false,说明NAT穿透失败,流量全部走DERP中转。这时候延迟高是必然的,要去查本地的网络环境。

如果自己的DERP区域没出现在netcheck里,检查三样东西:

阿里云安全组有没有放行443和3478。

策略文件里的derpMap写对没有,JSON要合法。

自签证书方案的CertName和derper日志里打印的是否一致。

开了出口节点就没网

在iStoreOS上按顺序查。

先看防火墙区域的masquerading开没开:

应该看到masq='1'。

没开就补上:

再看转发规则:

再确认tailscale0到WAN的转发是通的,LuCI里「允许转发到目标区域」要勾上WAN。

看SNAT参数:

NoSNAT应该是false。

如果这里显示NoSNAT是true,说明当初tailscale up带了那个参数。

得去掉:

还不行就抓包看看包走到哪一步:

tailscale0上没流量说明包没进来。

有流量但WAN上没有,是转发规则的问题。

两边都有但客户端打不开,多半是回包被conntrack判成INVALID丢掉了。

这种情况在防火墙区域的高级设置里勾上「允许无效流量」。

能ping通内网,但打不开iStoreOS自己的后台

防火墙区域的Input链默认是REJECT,要改成ACCEPT。

另外检查/etc/config/uhttpd,监听地址不能只绑LAN IP:

要包含0.0.0.0:80。

走出口节点但IP没变

看当前是不是真的选中了出口节点。

然后在iStoreOS上确认:

再确认后台勾选了Use as exit node。

只能访问iStoreOS自己,访问不了别的

这是子网路由的问题,不是出口节点的问题。

出口节点只负责转发互联网流量。

想访问局域网其他设备(比如家里的NAS),得广播--advertise-routes,并且后台批准。

6、整套配置清单

汇总一下,方便自己核对。

阿里云ECS上(DERP):

Tailscale后台:

策略文件里加derpMap,区域ID用900。

iStoreOS上(出口节点兼子网路由):

后台:

Machines里给iStoreOS勾上Use as exit node和子网路由。

客户端:

7、提醒

自建DERP和出口节点都是要自己维护的东西。

derper要跟着客户端版本更新,不然会出兼容问题。

iStoreOS上的防火墙和DNS设置,改动前先备份/etc/config/firewall。

出口节点开着的时候,注意哪些站点需要走本地线路,别一刀切。

记录一下,方便以后遇到相同问题时查阅~

赞(0) 打赏
版权声明:本文采用知识共享 署名4.0国际许可协议 [BY-NC-SA] 进行授权
文章名称:《Tailscale 从安装到自建 DERP 与出口节点:一份完整记录》
文章链接:https://www.machunjie.com/linux/2243.html
本站资源仅供个人学习交流,请于下载后24小时内删除,不允许用于商业用途,否则法律问题自行承担。

评论 抢沙发

觉得文章有用就打赏一下文章作者

非常感谢你的打赏,我们将继续提供更多优质内容,让我们一起创建更加美好的网络世界!

支付宝扫一扫

微信扫一扫

:smile: :sad: :arrow: :cool: :confused: :cry: :eek: :evil: :exclaim: :idea: :lol: :mad: :mrgreen: :neutral: :question: :razz: :redface: :rolleyes: :surprised: :wink: :biggrin: :twisted: