我的服务器系列:tailscale使用自定义derper服务器(docker部署)

使用docker部署tailscale私有中继服务器,并使用nginx四层代理与原有服务共用443端口,最后配置使用并检测。
derper-docker部署及使用较麻烦,本文分享踩坑记录及经验,建议阅读。

2023.10更新:随着网站备案注销,derper已切换到ip模式,增加对应示例及说明

一 设计及参考

设计

  1. 使用DockerHub上面唯一主流的derper镜像进行部署,保证可靠性
  2. 使用nginx四层代理转发443端口请求到derper容器
  3. 在tailscale上面配置使用derper服务器
  4. 测试derper的连通

    参考

    推荐阅读:
    官方说明( https://tailscale.com/kb/1118/custom-derp-servers/
    TAILSCALE 的一些使用心得( https://leitalk.com/12245
    Tailscale 基础教程:部署私有 DERP 中继服务器( https://icloudnative.io/posts/custom-derp-servers

二 部署derper容器(已备案域名)

参考:https://hub.docker.com/r/fredliang/derper
下面是我个人的配置
注意:

  1. ssl证书
    ssl证书可外部挂载或由容器自动申请并维护证书更新(基于LetsEncrypt)
    因为derper使用的ssl证书有格式和命名要求,挂载使用很不优雅,故放弃该方案
    而自动申请需要使用443端口,因为我443端口已经使用了,所以需要用nginx stream反向代理的形式做443端口4层转发。
    后文会进行详细说明。
  2. 3478端口不能修改,因为走udp所以也不建议转发。
  3. 如果使用DERP_VERIFY_CLIENTS则需要挂载tailscaled.sock,使容器能访问到外部机器derper进程。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    mkdir -p /data/derper/certs
    docker rm -f derper

    docker run -d \
    -p 3443:443 \
    -p 3478:3478/udp \
    --name derper \
    --restart=always \
    -v /data/derper/certs:/app/certs \
    -v /var/run/tailscale/tailscaled.sock:/var/run/tailscale/tailscaled.sock \
    -e DERP_ADDR=":443" \
    -e DERP_VERIFY_CLIENTS=true \
    -e DERP_DOMAIN="derper.linshenkx.cn" \
    fredliang/derper

    docker logs -f derper

    derper服务器

二 部署derper容器(ip)

参考 https://github.com/yangchuansheng/ip_derper

另外,反正直接走ip了,直接端口也换掉,这里用33380
因为不走域名,也不需要nginx来根据域名分流了,直接把端口暴露出来就好

1
2
3
4
5
6
7
8
9
10
11
12
13
14
docker rm -f derper

docker run -d \
-p 3478:3478/udp \
-p 33380:33380 \
--name derper \
--restart=always \
-v /var/run/tailscale/tailscaled.sock:/var/run/tailscale/tailscaled.sock \
-e DERP_ADDR=":33380" \
-e DERP_VERIFY_CLIENTS=true \
ghcr.io/yangchuansheng/ip_derper

docker logs -f derper

三 nginx代理配置(域名方式需要)

虽然有些人说derper运行一段时间就会崩溃(Derper TLS handshake error: remote error: tls: internal error ),
但我没有遇到过,按照官方issueTAILSCALE 的一些使用心得里的说法,可能和墙/备案有关。

顺带一提,如腾讯云这样的国内云服务器提供商,会审查 TLS handshake 里的 SNI 信息, 如果发现 SNI 域名未备案,会阻断 TLS 握手。 所以,我曾经也尝试过用一个境外服务器的域名,然后指向到境内服务器 IP 的形式尝试绕过备案, 然而短暂的用了几分钟后,就遇到了 tls handshake reset 的问题。 目前看来,只要你想用境内服务器,那么备案就是绕不过的问题。
Ps. 如果你有备案域名,那么可以用 nginx stream 反向代理的形式做 443 端口 4 层转发。 nginx stream 可以在四层探测 SNI 信息,然后分发到不同的后端,这样你 derper 的 443 和其他域名的 443 就可以共存在同一个服务器上了。

这里主要知识点是:

  1. TLS handshake 里的 SNI 信息
  2. nginx四层负载均衡

具体配置参考如下:
在nginx.conf添加如下配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
stream {
# 这里就是 SNI 识别,将域名映射成一个配置名
map $ssl_preread_server_name $backend_name {
# 把derper.linshenkx.cn的流量转到derper的upstream
derper.linshenkx.cn derper;
# 域名都不匹配情况下的默认值
default https_web;
}
# 监听 443 并开启 ssl_preread
server {
listen 443 reuseport;
listen [::]:443 reuseport;
proxy_pass $backend_name;
ssl_preread on;
}
upstream derper {
server 127.0.0.1:3443;
}
upstream https_web {
server 127.0.0.1:12443;
}
}

然后再把default.conf里server的443换成12443,如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
upstream xxx {
server 127.0.0.1:12345;
}
# redirect all traffic to https
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
return 301 https://$host$request_uri;
}
server {
listen 12443 ssl http2 ;
listen [::]:12443 ssl http2 ;

server_name xxx.linshenkx.cn;

include /config/nginx/proxy-confs/*.subfolder.conf;

include /config/nginx/ssl.conf;

client_max_body_size 0;

location / {
proxy_pass http://xxx;
proxy_set_header HOST $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

}

}

这样访问 https://derper.linshenkx.cn 的流程是 :

  1. 443端口根据nginx.conf转到本地的3443
  2. 由docker映射到derper容器内的443端口

而访问 https://xxx.linshenkx.cn 的流程是 :

  1. 443端口根据nginx.conf转到本地的12443
  2. 12443端口根据nginx的default.conf转发到本地的12345
  3. 12345是应用的暴露端口/docker的映射端口

对于大部分的http应用,可以通过xxx的方式配置访问,自动套上https,并且避免泄露端口。
个别更底层协议的应用,如derper、gitlfs,则需在nginx.conf配置转发

四 tailscale配置

Access Contros配置内容参考如下:
主要是添加了derpMap,其他都保持默认。
如果有多个derper服务器,建议配置为多个region而非node。
因为延迟的比较是以region为单位的,会方便测试。

一开始不确定derper是否运行正常的时候,建议先把OmitDefaultRegions设置为true,关闭默认的region。

另外,修改Access Contros后,需要重启tailscale服务才能生效!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// Example/default ACLs for unrestricted connections.
{
// Declare static groups of users beyond those in the identity service.
"groups": {
"group:example": [ "user1@example.com", "user2@example.com" ],
},
// Declare convenient hostname aliases to use in place of IP addresses.
"hosts": {
"example-host-1": "100.100.100.100",
},
// Access control lists.
"acls": [
// Match absolutely everything. Comment out this section if you want
// to define specific ACL restrictions.
{ "action": "accept", "users": ["*"], "ports": ["*:*"] },
]
,
"derpMap": {
"OmitDefaultRegions": false
,
"Regions": {
"900": {
"RegionID": 900,
"RegionCode": "lian",
"RegionName": "LIAN",
"Nodes": [{
"Name": "tx",
"RegionID": 900,
"HostName": "derper.linshenkx.cn",
"DERPPort": 443
}
]
}
}
}
}

如果是ip方式,则使用如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// Example/default ACLs for unrestricted connections.
{
// Access control lists.
"acls": [
{"action": "accept", "src": ["*"], "dst": ["*:*"]},
// Match absolutely everything. Comment out this section if you want
// to define specific ACL restrictions.
],
"derpMap": {
"OmitDefaultRegions": false,
"Regions": {
"900": {
"RegionID": 900,
"RegionCode": "lian",
"RegionName": "LIAN",
"Nodes": [
{
"Name": "tx",
"RegionID": 900,
"HostName": "你的ip",
"IPv4": "你的ip",
"DERPPort": 33380,
# 这里要去掉检测
"InsecureForTests": true,
},
],
},
},
},
}


五 测试使用derper

相关命令

1
2
3
4
5
6
# 显示集群状态
tailscale status
# 显示网络状态
tailscale netcheck
tailscale ping 节点

如下图,自定义的derper比默认的延迟要低不上。
(不过只有在极端情况下流量才会走derper,通常derper只要连得上就行,所以延迟影响不大)
tailscale netcheck

需要注意的是,这里显示一切正常,不代表derper就是在正常工作了。
只是说能访问到 https://derper.linshenkx.cn 而已。

关键还是要看ping。
tailscale ping
如图,代表了几种情况:

  1. tx.linshenkx.cn
    先通过DERP(sfo)连接,然后打洞成功,进化为ip:端口直连
  2. uc.linshenkx.cn
    识别到是本机
  3. lian.linshenkx.cn
    尝试通过DERP(lian)连接,但失败(因为对方tailscale服务异常)
  4. nas.linshenkx.cn
    先通过DERP(lian)连接,然后打洞成功,进化为ip:端口直连

derper日志类似如下
docker logs -f derper


我的服务器系列:tailscale使用自定义derper服务器(docker部署)
https://linshenkx.github.io/tailscale-derper-docker/
作者
John Doe
发布于
2022年4月16日
许可协议