使用Headscale搭建开源版Tailscale服务端

相信大家对wireguard已经不陌生了。作为一种搭建虚拟局域网的工具,相比于传统VPN而言,所有节点之间都可以进行P2P连接,也就是全互联模式,效率更高。而wireguard本身只是一个内核级别的模块,只是一个数据平面,至于上层更高级的功能(比如密钥交换机制,UDP打洞,ACL等),需要通过用户空间的应用来实现。

本文主要介绍Headscale和Tailscale。

本文与2023年9月10日第二次更新。

本文与2023年10月5日第三次更新。

前言

为了基于wireguard实现更完美的VPN工具,现在已经出现了很多项目,如Netmaker,通过可视化界面来配置全互联模式,并支持UDP打洞、多租户等高端功能,几乎适配所有平台。然而现实世界是复杂的,无法保证所有的NAT都能打洞成功,而且Netmaker目前不支持fallback机制,如打洞失败,无法fallback中继节点。而Tailscale支持fallback,可以尽最大努力实现全互联模式,部分节点即使打洞不成功,也可通过中继节点在虚拟局域网内畅通无阻。

什么是Tailscale

Tailscale是一种基于Wireguard的虚拟组网工具,和Netmaker最大的区别是在用户态实现了wireguard协议,而netmaker直接实现了内核态的wireguard。想比内核态的wireguard性能会有些损失,但是功能性和易用性上完爆其他工具:

  1. 开箱即用
    • 无需配置防火墙
    • 没有额外的配置
  2. 高安全性/私密性
    • 自动密钥轮换
    • 点对点连接
    • 支持用户审查端到端的访问记录
  3. 在原有的ICE、STUN等UDP协议外,还实现了DERP TCP协议来实现NAT穿透
  4. 基于公网的控制服务器下发ACL和配置,实现了节点动态更新
  5. 通过第三方(如Google)SSO服务生成用户和私钥,实现身份认证。

我们可以将Tailscale视为更易用、功能更完善的Wireguard

安装Tailscale

  1. 打开Tailscale官网

  1. 在不付费的情况下只能通过SSO进行登陆。

Mac OS客户端接入

  1. 安装Tailscale客户端,mac系统下可能需要一个美区的ID。

  • 如果没有美区ID,还可以下载安装包直接安装,传送门

⚠️两种方式只能安装一种。

  • 还可以安装开源命令行工具 tailscaletailscaled传送门

三种安装方式的比较

应用商店 独立应用 命令行版本
是否可用 yes yes, beta yes
图形界面 yes yes no; CLI
macOS 最低版本 macOS 10.13 macOS 10.15 macOS 10.13
后台运行 no; sandboxed 理论上支持; 尚未实现 yes
使用的钥匙串🔑 用户级 系统级 直接存放在文件中
沙盒隔离 yes no no
自动更新 yes; 应用商店直接更新 yes; Sparkle no
是否开源 no no yes
MagicDNS yes yes yes
Taildrop yes yes 未实现
  1. 登陆成功的页面

  1. 查看后台

Linux 客户端接入

  1. 下载编译好的Linux客户端,传送门

  2. 解压

1
2
3
4
5
6
7
deepsoft01@deepsoft-desktop:~$ tar zxvf tailscale*.tgz
tailscale_1.26.1_amd64/
tailscale_1.26.1_amd64/tailscale
tailscale_1.26.1_amd64/tailscaled
tailscale_1.26.1_amd64/systemd/
tailscale_1.26.1_amd64/systemd/tailscaled.defaults
tailscale_1.26.1_amd64/systemd/tailscaled.service
  1. 复制二进制文件到PATH目录
1
2
sudo install tailscale*/tailscaled /usr/sbin/tailscaled
sudo install tailscale*/tailscale /usr/local/bin/tailscale
  1. 设置systemd单元文件
1
sudo cp tailscale*/systemd/tailscaled.service /lib/systemd/system/tailscaled.service
  1. 设置环境变量配置文件
1
sudo cp tailscale*/systemd/tailscaled.defaults /etc/default/tailscaled
  1. 启动tailscaled服务并设置开机自动启动
1
sudo systemctl enable --now tailscaled
  1. 查看服务启动状态
1
systemctl status tailscaled
  1. 接入网络
1
sudo tailscale up --accept-routes=true --accept-dns=false --advertise-routes=192.168.31.0/24 --authkey xxx
  • accept-routes:允许别的设备路由到此设备

  • advertise-routes:向所有设备通告本地路由

  • authkey:非交互接入的key,在控制台Setting可以添加

  1. 查看网络接入状态

Windows 客户端接入

下载地址:https://pkgs.tailscale.com/stable/#static

什么是Headscale

Tailscale是一款商用产品,对个人提供免费服务,就个人而言完全够用,但是后端服务器全在国外,而且免费用户只能使用SSO登陆,非常不方便。

有人认为Tailscale后台不开源也不透明,于是自行开发了一套后端,命名为Headscale。这套后端完全开源,可自行部署。

部署Headscale

项目地址:https://github.com/juanfont/headscale

理论上说只要你的Headscale服务可以暴露到公网出口就行,但是不要有NAT,所以推荐部署到有公网IP的云主机上。

systemd部署

  1. 下载服务端

这里请根据服务器的架构进行选择

https://github.com/juanfont/headscale/

  1. 创建配置
1
2
3
4
5
6
# 创建文件夹
mkdir -p /etc/headscale
# 存储数据与证书
touch /var/lib/headscale/db.sqlite
# 创建空的sqlite文件
touch /var/lib/headscale/db.sqlite
  1. 创建Headscale配置文件

配置文件的示例

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
---
# headscale will look for a configuration file named `config.yaml` (or `config.json`) in the following order:
#
# - `/etc/headscale`
# - `~/.headscale`
# - current working directory

# The url clients will connect to.
# Typically this will be a domain like:
#
# https://myheadscale.example.com:443
#
server_url: http://127.0.0.1:8080

# Address to listen to / bind to on the server
#
# For production:
# listen_addr: 0.0.0.0:8080
listen_addr: 127.0.0.1:8080

# Address to listen to /metrics, you may want
# to keep this endpoint private to your internal
# network
#
metrics_listen_addr: 127.0.0.1:9090

# Address to listen for gRPC.
# gRPC is used for controlling a headscale server
# remotely with the CLI
# Note: Remote access _only_ works if you have
# valid certificates.
#
# For production:
# grpc_listen_addr: 0.0.0.0:50443
grpc_listen_addr: 127.0.0.1:50443

# Allow the gRPC admin interface to run in INSECURE
# mode. This is not recommended as the traffic will
# be unencrypted. Only enable if you know what you
# are doing.
grpc_allow_insecure: false

# Private key used to encrypt the traffic between headscale
# and Tailscale clients.
# The private key file will be autogenerated if it's missing.
#
private_key_path: /var/lib/headscale/private.key

# The Noise section includes specific configuration for the
# TS2021 Noise protocol
noise:
# The Noise private key is used to encrypt the
# traffic between headscale and Tailscale clients when
# using the new Noise-based protocol. It must be different
# from the legacy private key.
private_key_path: /var/lib/headscale/noise_private.key

# List of IP prefixes to allocate tailaddresses from.
# Each prefix consists of either an IPv4 or IPv6 address,
# and the associated prefix length, delimited by a slash.
# It must be within IP ranges supported by the Tailscale
# client - i.e., subnets of 100.64.0.0/10 and fd7a:115c:a1e0::/48.
# See below:
# IPv6: https://github.com/tailscale/tailscale/blob/22ebb25e833264f58d7c3f534a8b166894a89536/net/tsaddr/tsaddr.go#LL81C52-L81C71
# IPv4: https://github.com/tailscale/tailscale/blob/22ebb25e833264f58d7c3f534a8b166894a89536/net/tsaddr/tsaddr.go#L33
# Any other range is NOT supported, and it will cause unexpected issues.
ip_prefixes:
- fd7a:115c:a1e0::/48
- 100.64.0.0/10

# DERP is a relay system that Tailscale uses when a direct
# connection cannot be established.
# https://tailscale.com/blog/how-tailscale-works/#encrypted-tcp-relays-derp
#
# headscale needs a list of DERP servers that can be presented
# to the clients.
derp:
server:
# If enabled, runs the embedded DERP server and merges it into the rest of the DERP config
# The Headscale server_url defined above MUST be using https, DERP requires TLS to be in place
enabled: false

# Region ID to use for the embedded DERP server.
# The local DERP prevails if the region ID collides with other region ID coming from
# the regular DERP config.
region_id: 999

# Region code and name are displayed in the Tailscale UI to identify a DERP region
region_code: "headscale"
region_name: "Headscale Embedded DERP"

# Listens over UDP at the configured address for STUN connections - to help with NAT traversal.
# When the embedded DERP server is enabled stun_listen_addr MUST be defined.
#
# For more details on how this works, check this great article: https://tailscale.com/blog/how-tailscale-works/
stun_listen_addr: "0.0.0.0:3478"

# List of externally available DERP maps encoded in JSON
urls:
- https://controlplane.tailscale.com/derpmap/default

# Locally available DERP map files encoded in YAML
#
# This option is mostly interesting for people hosting
# their own DERP servers:
# https://tailscale.com/kb/1118/custom-derp-servers/
#
# paths:
# - /etc/headscale/derp-example.yaml
paths: []

# If enabled, a worker will be set up to periodically
# refresh the given sources and update the derpmap
# will be set up.
auto_update_enabled: true

# How often should we check for DERP updates?
update_frequency: 24h

# Disables the automatic check for headscale updates on startup
disable_check_updates: false

# Time before an inactive ephemeral node is deleted?
ephemeral_node_inactivity_timeout: 30m

# Period to check for node updates within the tailnet. A value too low will severely affect
# CPU consumption of Headscale. A value too high (over 60s) will cause problems
# for the nodes, as they won't get updates or keep alive messages frequently enough.
# In case of doubts, do not touch the default 10s.
node_update_check_interval: 10s

# SQLite config
db_type: sqlite3

# For production:
db_path: /var/lib/headscale/db.sqlite

# # Postgres config
# If using a Unix socket to connect to Postgres, set the socket path in the 'host' field and leave 'port' blank.
# db_type: postgres
# db_host: localhost
# db_port: 5432
# db_name: headscale
# db_user: foo
# db_pass: bar

# If other 'sslmode' is required instead of 'require(true)' and 'disabled(false)', set the 'sslmode' you need
# in the 'db_ssl' field. Refers to https://www.postgresql.org/docs/current/libpq-ssl.html Table 34.1.
# db_ssl: false

### TLS configuration
#
## Let's encrypt / ACME
#
# headscale supports automatically requesting and setting up
# TLS for a domain with Let's Encrypt.
#
# URL to ACME directory
acme_url: https://acme-v02.api.letsencrypt.org/directory

# Email to register with ACME provider
acme_email: ""

# Domain name to request a TLS certificate for:
tls_letsencrypt_hostname: ""

# Path to store certificates and metadata needed by
# letsencrypt
# For production:
tls_letsencrypt_cache_dir: /var/lib/headscale/cache

# Type of ACME challenge to use, currently supported types:
# HTTP-01 or TLS-ALPN-01
# See [docs/tls.md](docs/tls.md) for more information
tls_letsencrypt_challenge_type: HTTP-01
# When HTTP-01 challenge is chosen, letsencrypt must set up a
# verification endpoint, and it will be listening on:
# :http = port 80
tls_letsencrypt_listen: ":http"

## Use already defined certificates:
tls_cert_path: ""
tls_key_path: ""

log:
# Output formatting for logs: text or json
format: text
level: info

# Path to a file containg ACL policies.
# ACLs can be defined as YAML or HUJSON.
# https://tailscale.com/kb/1018/acls/
acl_policy_path: ""

## DNS
#
# headscale supports Tailscale's DNS configuration and MagicDNS.
# Please have a look to their KB to better understand the concepts:
#
# - https://tailscale.com/kb/1054/dns/
# - https://tailscale.com/kb/1081/magicdns/
# - https://tailscale.com/blog/2021-09-private-dns-with-magicdns/
#
dns_config:
# Whether to prefer using Headscale provided DNS or use local.
override_local_dns: true

# List of DNS servers to expose to clients.
nameservers:
- 1.1.1.1

# NextDNS (see https://tailscale.com/kb/1218/nextdns/).
# "abc123" is example NextDNS ID, replace with yours.
#
# With metadata sharing:
# nameservers:
# - https://dns.nextdns.io/abc123
#
# Without metadata sharing:
# nameservers:
# - 2a07:a8c0::ab:c123
# - 2a07:a8c1::ab:c123

# Split DNS (see https://tailscale.com/kb/1054/dns/),
# list of search domains and the DNS to query for each one.
#
# restricted_nameservers:
# foo.bar.com:
# - 1.1.1.1
# darp.headscale.net:
# - 1.1.1.1
# - 8.8.8.8

# Search domains to inject.
domains: []

# Extra DNS records
# so far only A-records are supported (on the tailscale side)
# See https://github.com/juanfont/headscale/blob/main/docs/dns-records.md#Limitations
# extra_records:
# - name: "grafana.myvpn.example.com"
# type: "A"
# value: "100.64.0.3"
#
# # you can also put it in one line
# - { name: "prometheus.myvpn.example.com", type: "A", value: "100.64.0.3" }

# Whether to use [MagicDNS](https://tailscale.com/kb/1081/magicdns/).
# Only works if there is at least a nameserver defined.
magic_dns: true

# Defines the base domain to create the hostnames for MagicDNS.
# `base_domain` must be a FQDNs, without the trailing dot.
# The FQDN of the hosts will be
# `hostname.user.base_domain` (e.g., _myhost.myuser.example.com_).
base_domain: example.com

# Unix socket used for the CLI to connect without authentication
# Note: for production you will want to set this to something like:
unix_socket: /var/run/headscale/headscale.sock
unix_socket_permission: "0770"
#
# headscale supports experimental OpenID connect support,
# it is still being tested and might have some bugs, please
# help us test it.
# OpenID Connect
# oidc:
# only_start_if_oidc_is_available: true
# issuer: "https://your-oidc.issuer.com/path"
# client_id: "your-oidc-client-id"
# client_secret: "your-oidc-client-secret"
# # Alternatively, set `client_secret_path` to read the secret from the file.
# # It resolves environment variables, making integration to systemd's
# # `LoadCredential` straightforward:
# client_secret_path: "${CREDENTIALS_DIRECTORY}/oidc_client_secret"
# # client_secret and client_secret_path are mutually exclusive.
#
# # The amount of time from a node is authenticated with OpenID until it
# # expires and needs to reauthenticate.
# # Setting the value to "0" will mean no expiry.
# expiry: 180d
#
# # Use the expiry from the token received from OpenID when the user logged
# # in, this will typically lead to frequent need to reauthenticate and should
# # only been enabled if you know what you are doing.
# # Note: enabling this will cause `oidc.expiry` to be ignored.
# use_expiry_from_token: false
#
# # Customize the scopes used in the OIDC flow, defaults to "openid", "profile" and "email" and add custom query
# # parameters to the Authorize Endpoint request. Scopes default to "openid", "profile" and "email".
#
# scope: ["openid", "profile", "email", "custom"]
# extra_params:
# domain_hint: example.com
#
# # List allowed principal domains and/or users. If an authenticated user's domain is not in this list, the
# # authentication request will be rejected.
#
# allowed_domains:
# - example.com
# # Note: Groups from keycloak have a leading '/'
# allowed_groups:
# - /headscale
# allowed_users:
# - alice@example.com
#
# # If `strip_email_domain` is set to `true`, the domain part of the username email address will be removed.
# # This will transform `first-name.last-name@example.com` to the user `first-name.last-name`
# # If `strip_email_domain` is set to `false` the domain part will NOT be removed resulting to the following
# user: `first-name.last-name.example.com`
#
# strip_email_domain: true

# Logtail configuration
# Logtail is Tailscales logging and auditing infrastructure, it allows the control panel
# to instruct tailscale nodes to log their activity to a remote server.
logtail:
# Enable logtail for this headscales clients.
# As there is currently no support for overriding the log server in headscale, this is
# disabled by default. Enabling this will make your clients send logs to Tailscale Inc.
enabled: false

# Enabling this option makes devices prefer a random port for WireGuard traffic over the
# default static port 41641. This option is intended as a workaround for some buggy
# firewall devices. See https://tailscale.com/kb/1181/firewalls/ for more information.
randomize_client_port: false
  1. 下载配置文件
1
wget https://github.com/juanfont/headscale/raw/main/config-example.yaml -O /etc/headscale/config.yaml
  • 修改配置文件,将server_url改为公网域名或者IP,国内服务器域名必须要备案;

  • 如果暂时用不到DNS功能,可以将magin_dns设置为false;

  • server_url 设置为 http://<PUBLIC_IP>:8080,将 <PUBLIC_IP> 替换为公网 IP 或者域名;

  • 可自定义私有网段,也可同时开启 IPv4 和 IPv6

    1
    2
    3
    ip_prefixes:
    - fd7a:115c:a1e0::/48
    - 100.64.0.0/10
  1. 创建SystemD service配置文件
1
vim /etc/systemd/system/headscale.service
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[Unit]
Description=headscale controller
After=syslog.target
After=network.target

[Service]
Type=simple
User=headscale
Group=headscale
ExecStart=/usr/local/bin/headscale serve
Restart=always
RestartSec=5

# Optional security enhancements
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
ProtectHome=yes
ReadWritePaths=/var/lib/headscale /var/run/headscale
AmbientCapabilities=CAP_NET_BIND_SERVICE
RuntimeDirectory=headscale

[Install]
WantedBy=multi-user.target
  1. 创建headscale用户
1
useradd headscale -d /home/headscale -m
  1. 修改配置文件的owner
1
chown -R headscale:headscale /var/lib/headscale
  1. 创建必须的文件夹
1
mkdir -p /var/run/headscale/
  1. 修改配置文件unix_socket
1
unix_socket: /var/run/headscale/headscale.sock
  1. 重载新的配置文件
1
sudo systemctl daemon-reload
  1. 启动Headscale并设置开机自动启动
1
sudo systemctl enable --now headscale
  1. 查看运行状态
1
sudo systemctl status headscale
  1. 查看占用端口
1
ss -tulnp|grep headscale
  1. 创建namespace

Tailscale中有有一个概念叫做tailnet,可以理解为租户。租户和租户之间是相互隔离的,具体可查看tailscale的官方文档.

Headscale中也有类似的实现叫做namespace.

Headcale类似的实现叫做user.

需要先创建一个user,以便后续客户端接入。

1
headscale users create fly97
  1. 查看命名空间
1
headscale namespaces list

docker-compose部署

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
version: '3'
services:
headscale:
user: root
image: headscale/headscale
container_name: headscale
network_mode: host
cap_add:
- CAP_NET_BIND_SERVICE
command: headscale serve
volumes:
- ./config.yaml:/etc/headscale/config.yaml
- ./db.sqlite:/var/lib/headscale/db.sqlite
- ./ssl/221.12.170.100.crt:/221.12.170.100.crt
- ./ssl/221.12.170.100.key:/221.12.170.100.key
#- ./headscale.sock:/var/run/headscale.sock

接入Headscale

Mac OS客户端接入

首先需要在服务端创建命名空间

1
headscale users create fly97

客户端下载参考Tailscale。

安装完毕以后打开http://<headscale_ip>:<port>/apple,会出现如下页面

按照上图所说,安装描述文件,然后重新打开Tailscale APP,点击登录,会自动打开如下的页面:

在安装headscale的机器上,执行上面所示的命令,就可以把节点加入到网络中。

Linux 客户端接入

首先在headscale生成api key,-n 用于指定命名空间

1
headscale preauthkeys create -e 24h -u fly97

然后在tailscale客户端接入,将authkey替换成上文生成的key

1
2
3
4
login_server=http://xx.xx
authkey=xxxx
route=xxxx
sudo tailscale up --login-server=${login_server} --accept-routes=false --accept-dns=false --advertise-routes=$route --authkey ${authkey}

打通局域网

假设内网有一台 Linux 主机(比如 OpenWrt)安装了 Tailscale 客户端,希望其他 Tailscale 客户端可以直接通过家中的局域网 IP(例如 192.168.100.0/24) 访问内网的任何一台设备;

配置方法很简单,首先需要设置 IPv4 与 IPv6 路由转发:

1
2
3
echo 'net.ipv4.ip_forward = 1' | tee /etc/sysctl.d/ipforwarding.conf
echo 'net.ipv6.conf.all.forwarding = 1' | tee -a /etc/sysctl.d/ipforwarding.conf
sysctl -p /etc/sysctl.d/ipforwarding.conf

客户端修改注册节点的命令,在原来命令的基础上加上参数 --advertise-routes=192.168.100.0/24

1
2
3
4
login_server=http://xx.xx
authkey=xxxx
route=xxxx
sudo tailscale up --login-server=${login_server} --accept-routes=false --accept-dns=false --advertise-routes=$route --authkey ${authkey}

PS:这里不能加入accept-routes=true,这样会导致当前节点访问当前路由时,强制走derp-server的流量。

在headscale服务端启动路由,首先需要查看要启用节点的id,假设这个id是1

1
headscale routes list

根据ID启动路由

1
headscale routes enable -i 1

Windows 客户端接入

访问以下连接

1
https://<headscale_ip>:<port>/windows

一些常见的命令

查看链接状态

1
2
3
4
$tailscale status 
192.168.7.1 mac-mini-bj4beb75 default macOS -
deepsoft-ci-0cf7bgtj default linux active; direct 221.12.170.99:41642; offline, tx 24760 rx 98248
deepsoft-desktop-xdcbicir default linux active; direct 221.12.170.98:41641; offline, tx 16404 rx 21900

查看darp连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$tailscale status 
2022/06/25 14:17:34 portmap: [v1] Got PMP response; IP:xxx, epoch: 683007
2022/06/25 14:17:34 portmap: [v1] Got PCP response: epoch: 683007
2022/06/25 14:17:34 portmap: [v1] UPnP reply {Location:http://192.168.15.1:5000/rootDesc.xml Server:OpenWRT/5.10.107 UPnP/1.1 MiniUPnPd/2.2.3 USN:uuid:d38cfa50-cc98-48d8-8e37-2c97c660b8aa::urn:schemas-upnp-org:device:InternetGatewayDevice:2}, "HTTP/1.1 200 OK\r\nCACHE-CONTROL: max-age=120\r\nST: urn:schemas-upnp-org:device:InternetGatewayDevice:2\r\nUSN: uuid:d38cfa50-cc98-48d8-8e37-2c97c660b8aa::urn:schemas-upnp-org:device:InternetGatewayDevice:2\r\nEXT:\r\nSERVER: OpenWRT/5.10.107 UPnP/1.1 MiniUPnPd/2.2.3\r\nLOCATION: http://192.168.15.1:5000/rootDesc.xml\r\nOPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n01-NLS: 1655692518\r\nBOOTID.UPNP.ORG: 1655692518\r\nCONFIGID.UPNP.ORG: 1337\r\n\r\n"
2022/06/25 14:17:34 portmap: UPnP meta changed: {Location:http://192.168.15.1:5000/rootDesc.xml Server:OpenWRT/5.10.107 UPnP/1.1 MiniUPnPd/2.2.3 USN:uuid:d38cfa50-cc98-48d8-8e37-2c97c660b8aa::urn:schemas-upnp-org:device:InternetGatewayDevice:2}

Report:
* UDP: false
* IPv4: (no addr found)
* IPv6: no
* MappingVariesByDestIP:
* HairPinning:
* PortMapping: UPnP, NAT-PMP, PCP
* Nearest DERP: unknown (no response to latency probes)

由于我这里通过p2p打洞就建立了连接,同时没有导入Tailscale的中继节点,因此这里无法获得darp连接。

其他情况应该是

1
2
3
4
5
6
7
8
9
10
11
12
$ tailscale netcheck

Report:
* UDP: true
* IPv4: yes, xxxxx:57068
* IPv6: no
* MappingVariesByDestIP: false
* HairPinning: false
* PortMapping:
* Nearest DERP: Tencent Hongkong
* DERP latency:
- thk: 39.7ms (Tencent Hongkong)