BLOG

如何安全地通过 Tailscale 共享服务器代理

前言

由于持有的公网云配置太低,而 Clash CPU占用过高,故用较高配置的校园网服务器当跳板机。

开始配置中转机,会遇到一个很现实的矛盾:

一方面,我们希望利用 Clash 之类的代理服务提升远程环境的可用性;另一方面,如果直接把 7890 这类端口暴露出来,就很容易被扫描器探测到,留下不必要的安全风险。

这篇文章分享我自己的做法:利用 Tailscale + UFW 做一层物理隔离,只允许来自 Tailscale 内网的设备访问代理端口,而把公网流量彻底挡在外面。

如果你也有“想用代理,但不想把代理端口暴露给全世界”的需求,这套方案基本够用,而且配置成本很低。

核心原理

这套方案的关键点只有一句话:

先给 Tailscale 虚拟网卡开一个小窗,再把公网大门焊死。

UFW 会按规则顺序自上而下匹配,因此我们可以:

  1. 先插入一条最高优先级规则,允许来自 tailscale0 的流量访问 7890
  2. 再补一条规则,拒绝其他来源访问 7890

这样一来:

  • 你自己的电脑、手机、平板,只要加入了同一个 Tailscale 网络,就能正常使用服务器代理
  • 公网扫描器即使扫到你的服务器 IP,也无法直接打到代理端口

壹 · 操作步骤详解

一、确认 Tailscale 虚拟网卡名称

大多数情况下,Tailscale 的网卡名称都是 tailscale0,但最好还是先确认一下:

ip addr show

你会看到类似下面的网卡:

tailscale0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> ...

如果你的环境里名字不是 tailscale0,下面命令里的网卡名记得一起替换。

二、插入最高优先级的放行规则

这里最关键的是 insert 1

它会把规则直接插到最顶端,确保 Tailscale 内网访问优先于后面的拦截规则。

sudo ufw insert 1 allow in on tailscale0 to any port 7890 proto tcp

这条命令的含义是:

只要流量是tailscale0 这个虚拟网卡进入,并且目标端口是 7890/tcp,就允许通过。

三、追加全局拦截规则

接下来,把这个端口对其他来源全部封住:

sudo ufw deny 7890/tcp

由于上一条规则已经通过 insert 1 放到了最顶部,所以这里的 deny 会排在它后面。

最终效果就是:

  • 从 Tailscale 内网来的流量,先命中第一条 allow
  • 从公网或其他网卡来的流量,继续往下匹配,然后被这条 deny 拦住

四、检查规则顺序是否正确

配置完成后执行:

sudo ufw status numbered

预期输出类似这样:

[ 1] 7890/tcp on tailscale0  ALLOW IN  Anywhere
[ 2] 7890/tcp                DENY IN   Anywhere

如果 ALLOW IN on tailscale0 没有排在 DENY 前面,那这套方案就不会按预期工作,需要调整规则顺序。

贰 · 实际使用方式

配置好之后,你不需要再使用跳板机 IP 来连代理。

只要你的电脑或手机已经登录同一个 Tailscale 网络,直接在代理客户端里填写:

跳板机的 Tailscale IP:7890

例如:

100.x.y.z:7890

这样代理只会在 Tailnet 内部可达,不会暴露给公网。

如何代理跳板机本身的 Docker 容器

这里有一个很容易踩坑的点:

容器访问宿主机 7890 时,流量走的通常不是 tailscale0,而是 Docker 网桥,比如 docker0

也就是说,如果你只写了:

sudo ufw insert 1 allow in on tailscale0 to any port 7890 proto tcp
sudo ufw deny 7890/tcp

那么容器里的程序即使和宿主机在同一台机器上,也可能还是连不上,因为它的请求不会命中 tailscale0 那条规则。

如果你只用默认 Docker bridge,那补一条 docker0 规则就够了:

sudo ufw insert 2 allow in on docker0 to any port 7890 proto tcp

这样规则顺序就变成:

[ 1] 7890/tcp on tailscale0  ALLOW IN  Anywhere
[ 2] 7890/tcp on docker0     ALLOW IN  Anywhere
[ 3] 7890/tcp                DENY IN   Anywhere

但这条规则只覆盖默认的 docker0不等于跳板机上所有 Docker 网络都放行

如果你还创建了自定义 bridge 网络,宿主机上通常会出现额外的 br-xxxx 网卡。更省事的做法是直接把当前所有 Docker bridge 网卡都放行:

#!/usr/bin/env bash
set -euo pipefail

PORT="${1:-7890}"

# 1. 自动获取所有 Docker 相关网桥的名称
mapfile -t DOCKER_INTERFACES < <(
  ip -o link show \
  | awk -F': ' '{print $2}' \
  | grep -E '^(docker0|br-[a-f0-9]+)$' \
  || true
)

if [ "${#DOCKER_INTERFACES[@]}" -eq 0 ]; then
  echo "未检测到任何 Docker bridge 网卡。"
  exit 1
fi

echo "正在为端口 ${PORT} 配置精准子网放行..."

# 2. 遍历网卡并提取子网
for IFACE in "${DOCKER_INTERFACES[@]}"; do
  # 提取该网卡的子网段,例如 172.18.0.1/16
  SUBNET=$(ip -o -f inet addr show "$IFACE" | awk '{print $4}' | head -n 1)

  if [ -z "$SUBNET" ]; then
    echo "警告: 无法获取网卡 ${IFACE} 的子网信息,跳过。"
    continue
  fi

  # 检查规则是否已存在,防止重复添加
  if sudo ufw status | grep -Fq "${PORT}/tcp" | grep -Fq "$SUBNET"; then
    echo "已存在规则: 允许来自子网 ${SUBNET} 的流量访问端口 ${PORT}"
    continue
  fi

  echo "正在添加规则: 允许子网 ${SUBNET} -> 端口 ${PORT}"
  # 插入到第 1 条规则位置,确保优先级最高
  sudo ufw insert 1 allow from "$SUBNET" to any port "$PORT" proto tcp
done

# 3. 补充:始终确保本机回环地址被放行(防止本机 App 失败)
if ! sudo ufw status | grep -Fq "${PORT}/tcp" | grep -Fq "127.0.0.1"; then
  echo "正在添加规则: 允许本机回环 (127.0.0.1) -> 端口 ${PORT}"
  sudo ufw insert 1 allow from 127.0.0.1 to any port "$PORT" proto tcp
fi

echo "-----------------------------------"
sudo ufw status numbered

例如保存成 allow-docker-proxy.sh 后执行:

chmod +x allow-docker-proxy.sh
./allow-docker-proxy.sh 7890

这个脚本会自动扫描:

  • 默认 bridge:docker0
  • 自定义 bridge:br-xxxx

如果后面又新建了 Docker 网络,需要再运行一次脚本,把新出现的 bridge 网卡补进 UFW 规则。

另外,network_mode: host 的容器不走 bridge,这种模式本来就不在这个脚本的覆盖范围内。

叁 · 常见端口用途速查

在服务器运维里,下面几个端口比较常见,可以按需开放:

端口服务建议
7890Clash HTTP 代理仅对 Tailscale 开放
7891 / 7892Clash SOCKS / Mixed若使用,同样仅对 Tailscale 开放
22SSH 远程登录可公网开放,但建议配合密钥登录
80 / 443HTTP / HTTPS有网站业务时再开放
40315自定义 SSH 或其他管理口仅在你明确需要时开放

如果你还开放了 9090 之类的 Dashboard 端口,也建议照着同样思路处理,不要直接裸露在公网。

肆 · 为什么这样做更安全

一、减少暴露面

最直接的收益就是:公网扫描器打不到你的代理端口。

即使代理程序本身在监听 0.0.0.0:7890,真正起决定作用的还是防火墙规则。没有经过 Tailscale 虚拟网卡的流量,根本过不来。

二、使用体验更简单

你不需要记公网 IP,也不需要每次折腾端口转发。

只要设备已经加入 Tailscale 网络,就能直接通过内网地址访问代理,手机、笔记本、平板都一样。

三、符合最小权限原则

敏感服务不对公网开放,本质上就是一种“默认不信任”的设计。

哪怕你的代理软件没有额外做认证,这层网络访问控制本身也已经帮你挡掉了绝大多数无关访问。

伍 · 建议补充的检查项

如果你准备把这套配置长期使用,建议顺手再检查下面几项:

  1. 确认代理程序确实监听在预期端口
sudo ss -lntp | grep 7890
  1. 确认服务器已经启用 UFW
sudo ufw status
  1. 从一台 已加入 Tailscale 的设备测试连接

  2. 再从公网环境测试一下服务器公网 IP 的 7890,确认无法访问

如果你本机已经启用了 ufw default deny incoming,那很多情况下即使不额外写 deny 7890/tcp 也能挡住公网访问;但我仍然更推荐显式写出来,因为规则更直观,后续排查时也更容易看懂。

结语

安全从来不是一次性动作,而是持续贯彻“最小暴露面”和“最小权限原则”。

对代理端口来说,Tailscale + UFW 这套组合的好处就在于:既保留了远程使用体验,又避免了把服务直接摊在公网面前。

如果你的服务器上还有面板、数据库、管理接口之类的敏感服务,也完全可以照着这个思路继续加固。

商业转载请联系站长获得授权,非商业转载请注明本文出处及文章链接,您可以自由地在任何媒体以任何形式复制和分发作品,也可以修改和创作,但是分发衍生作品时必须采用相同的许可协议。本文采用 CC BY-NC-SA 4.0 - 非商业性使用 - 相同方式共享 4.0 国际 进行许可。