Skip to main content

tailscale

caution
  • 一次只能加入一个网络
    • log into multiple tailscale accounts #713
    • support connecting to multiple networks #183
  • Linux 下使用的 用户空间 wg 实现 - 性能弱于 wg
    • Kernel implementation of tstun #3264
    • Linux kernel Wireguard data plane #426
  • macOS GUI, iOS, Android 不支持修改 control server
    • 只有命令行才可以修改 control server
  • IP 一旦分配不可修改 - IP 只能自动分配 - How Tailscale assigns IP addresses
    • 移除节点 - 重新加入
    • 清除 tailscale 状态
    • 固定 100.64.0.0/10 - CGNAT - RFC6598
  • Custom records in MagicDNS #1543
  • Open source server #498
  • Tailscale in browser #3157
# macOS
brew install tailscale
# Golang
go install tailscale.com/cmd/tailscale{,d}@latest
# AlpineLinux
apk add tailscale

# Binary https://pkgs.tailscale.com/stable/#static
# 内置 systemd service, 没有 openrc
curl -O https://pkgs.tailscale.com/stable/tailscale_1.38.1_amd64.tgz
tar zxvf tailscale_*.tgz

tailscale up --login-server http://192.168.1.2:8080

tailscale down

tailscale ip
# 下次 up 需要重新授权
tailscale logout
# 网络状况
tailscale netcheck

tailscale ping host
tailscale status
tailscale web
tailscale updefaultnote
--accept-dnstrue修改本地 DNS 为 100.100.100.100
--accept-routesfalse接受服务端配置的路由
--advertise-exit-node
--advertise-routes <ip>申请路由到该节点
--advertise-tags <tags>
--authkey <key>
--exit-node <ip>指定出口节点
--force-reauth
--host-routes
--hostname <name>
--operator <user>
--qr
--reset
--shields-up

tailscaled

go install tailscale.com/cmd/tailscale{,d}@latest
# macOS
sudo tailscaled install-system-daemon
flagdefaultfor
-bird-socket stringpath of the bird unix socket
-debug [ip]:portlisten debug server
-outbound-http-proxy-listen [ip]:portoutbound HTTP proxy
-cleanupclean up system state and exit
-port value0UDP port for WireGuard and peer-to-peer traffic - 0 auto
-socket string/var/run/tailscaled.socketpath of the service unix socket
-socks5-server [ip]:portSOCK5 server
-state string<statedir>/tailscaled.statestate file
-statedir stringpath of config state, TLS certs, temporary incoming Taildrop files
-tun stringutunuserspace-networking 用户空间
-verbose int0
  • -state - $HOME/.local/share/tailscale/tailscaled.state
    • kube:<secret-name>
    • arn:aws:ssm:... - AWS SSM
    • mem:
  • -debug
    • /debug/metrics
  • -port 41641

debug

debugfor
derp-mapDERP 列表
daemon-goroutinesgoroutines
metricsmetrics
envcmd/tailscale environment
hostinfohostinfo
local-credsprint how to access Tailscale local API
restunforce a magicsock restun
rebindforce a magicsock rebind
prefsprint prefs
watch-ipnsubscribe to IPN message bus
tailscale version

tailscale netcheck
tailscale status
tailscale derp-map

Notes

  • 记录的 JSON 状态
    • /var/lib/tailscale/tailscaled.state
      • _daemon
      • _machinekey
  • DNS
    • 修改 /etc/resolv.conf
    • 备份之前配置 /etc/resolv.pre-tailscale-backup.conf
  • DERP - Encrypted TCP relays
sudo jq -r ._daemon /var/lib/tailscale/tailscaled.state | base64 -d | jq
tailscaled.state
{
"ControlURL": "",
"RouteAll": false,
"AllowSingleHosts": true,
"ExitNodeID": "",
"ExitNodeIP": "",
"ExitNodeAllowLANAccess": false,
"CorpDNS": false,
"WantRunning": true,
"LoggedOut": false,
"ShieldsUp": false,
"AdvertiseTags": ["tag:linux"],
"Hostname": "",
"NotepadURLs": false,
"AdvertiseRoutes": ["192.168.1.0/22"],
"NoSNAT": false,
"NetfilterMode": 2,
"Config": {
"PrivateMachineKey": "privkey:",
"PrivateNodeKey": "privkey:",
"OldPrivateNodeKey": "privkey:",
"Provider": "",
"LoginName": "truth"
}
}

AdvertiseRoutes

-A POSTROUTING -j ts-postrouting
-A INPUT -j ts-input
-A FORWARD -j ts-forward

-A ts-postrouting -m mark --mark 0x40000 -j MASQUERADE

-A ts-forward -i tailscale0 -j MARK --set-xmark 0x40000/0xffffffff
-A ts-forward -m mark --mark 0x40000 -j ACCEPT
# 不 forward 内部
-A ts-forward -s 100.64.0.0/10 -o tailscale0 -j DROP
-A ts-forward -o tailscale0 -j ACCEPT
# 当前节点
-A ts-input -s 100.64.0.8/32 -i lo -j ACCEPT
-A ts-input -s 100.115.92.0/23 ! -i tailscale0 -j RETURN
-A ts-input -s 100.64.0.0/10 ! -i tailscale0 -j DROP

derp

  • zouyq/derper
  • 80, 443, 3478
    • HTTPS + STUN
  • 与 tailscaled 一同部署可达到限制访问的目的 - Auth
  • Custom DERP Servers
# 默认 DERP
curl -s https://controlplane.tailscale.com/derpmap/default | jq

# 安装
go install tailscale.com/cmd/derper@main
# macOS 交叉编译
# GOOS=linux go build -trimpath -o bin/derper ./cmd/derper
# GOOS=linux go build -trimpath -o bin/tailscale ./cmd/tailscale
# GOOS=linux go build -trimpath -o bin/tailscaled ./cmd/tailscaled

# 启动
derper --hostname=your-hostname.com
# 启动 - 通过本地 tailscaled 验证客户端
sudo derper --hostname=your-hostname.com --verify-clients
# behind proxy
# 需要修改代码: 启用 tls 关闭 debug
# -a 如果指定地址会同时用于 http 和 stun-port
sudo derper --hostname derper.example.com --verify-clients -a :28443 -http-port 28080
flagdefaultfor
-a [ip]:port:443HTTPS listen address
-accept-connection-burst9223372036854775807burst limit for accepting new connection
-accept-connection-limit+Infrate limit for accepting new connection
-bootstrap-dns-names stringoptional comma-separated list of hostnames to make available at /bootstrap-dns
-c string配置目录
-certdir string$HOME/.cache/tailscale/derper-certsletsencrypt 证书目录
-certmode stringletsencryptmanual, letsencrypt
-dev开发模式
-hostname stringderp.tailscale.comLetsEncrypt host name
-http-port int80-1 禁用
-logcollection stringlogtail collection to log to
-mesh-psk-file stringmesh pre-shared key file - hex string
-mesh-with stringoptional comma-separated list of hostnames to mesh with; the server's own hostname can be in the list
-stuntrue运行 STUN server
-stun-port int3478
-verify-clientsfalse通过本地 tailscaled 验证客户端
verify-clients
  • tailscale sttaus 里的节点才能使用 derp - 否则会验证失败
  • 可以考虑 derp 节点能看到所有其他节点
    • { Action: accept, Users: [ tag:derp ], Ports: [ "*:*" ] }
  • hostname 为访问 derp 的 域名 而不是 login-server 域名
  • port 为 443 才会启用 tls - tsweb.IsProd443(*addr) || *certMode == "manual"

openrc

#!/sbin/openrc-run
supervisor=supervise-daemon

name="Tailscale Derper"
description="Derper relay."

command=/usr/local/bin/derper
# config
command_args="--hostname derp.example.com --verify-clients -a :443 -http-port 8080"

DERPER_LOGFILE="${DERPER_LOGFILE:-/var/log/${RC_SVCNAME}.log}"
output_log=${DERPER_LOGFILE}
error_log=${DERPER_LOGFILE}

depend() {
use logger dns tailscale
need net
}

Taildrop

# 发送
# tailscale file cp <files> <name-or-ip>:
# 接收
tailscale file get .

NAT

Glossary

  • Exit Node
    • 路由所有流量 - 类似传统 VPN 方式 - 0.0.0.0/0
    • tailscale up --advertise-exit-node 允许节点作为 Exit Node
    • 会添加 tag - 通过 is:exit-node 过滤
    • tailscale up --exit-node=<exit-node-ip> 客户端选择 Exit Node
  • MagicDNS
    • 可以使用 hostname 访问设备
    • 还会添加 search domain 通过 网址域名访问
    • 可通过 --accept-dns=false 关闭
  • Subnet Route
    • 作为网关桥接不同网络
    • --advertise-routes
    • ACL 控制 { "action": "accept", "users": ["*"], "ports": ["172.20.10.0/24:*",] }
echo 'net.ipv4.ip_forward = 1' | sudo tee -a /etc/sysctl.conf
echo 'net.ipv6.conf.all.forwarding = 1' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p /etc/sysctl.conf
  • IPN - Identified Private Network
  • 用户空间网络
    • 使用 socks5 或 http 代理 访问网络
    • tailscaled --tun=userspace-networking --socks5-server=localhost:1055
    • 主要用于 serverless、容器、权限不足、不支持 tun 设备 等场景
    • Userspace networking mode

FAQ

derphttp.Client.Recv connect to region 999: tls: first record does not look like a TLS handshake

检查 derper 是否启用 TLS 是否申请证书。

derper 只有在端口为 443 时才会申请 letsencrypt 证书。

not connected to home DERP region 999

自定义的 DERP 确保能被访问,HTTP、HTTPS、STUN

tsweb.AllowDebugAccess

默认允许访问 debug 的逻辑

if tsaddr.IsTailscaleIP(ip) || ip.IsLoopback() || ipStr == envknob.String("TS_ALLOW_DEBUG_IP") {
return true
}

如果使用了反向代理(例如: derper) 会导致检测失败。

not logged in, last login error=fetch control key: 530

检查 login server 是不是异常了