🕸️ Tailscale ACL 访问控制:织一张懂规则的虚拟内网

Tailscale ACL 访问控制

默认的 Tailscale 网络"谁都连得上",可一旦接入生产设备,你就该给虚拟内网织一张懂得"谁、能碰谁、碰哪些端口"的策略网——这就是 ACL。

Tailscale ACL 访问控制策略配置

1. 概述

ACL(Access Control List)是 Tailscale 网络的安全策略核心:决定谁(src)能访问什么目标(dst)哪些端口

  • 官方管理界面:Admin Console → Access Controls(Policies),编辑的是"HujSON"文本。
  • 格式:HuJSON(Human JSON),是 JSON 的超集:支持注释(///* */)、结尾允许多余逗号。
  • 生效:保存即实时下发到各节点(网络层过滤),改错可能导致断连,务必谨慎。
  • 版本:既有acls 语法(无限期支持),也有grants 语法(新功能只加在 grants 上)。两者可共存,新配置建议直接用 grants

策略文件顶层可包含以下 sections:

Section 用途
grants 网络层 + 应用层访问策略(新语法,优先使用)
acls 网络层访问策略(旧语法)
ssh 控制谁可以使用 Tailscale SSH
hosts 给 IP / 子网起别名
groups 定义用户组
ipsets 定义网段集合
tagOwners 定义谁有权给设备打标签
autoApprovers 定义谁可免审批宣告子网路由 / 出口节点
nodeAttrs 给设备/用户附加属性(如 app connector)
postures 设备姿态(OS、版本等)规则
tests 对策略做断言测试

2. 语法基础

2.1 源对象 src

类型 示例 含义
任意 * 所有 tailnet 设备(含已批准的共享)
用户 alice@example.com 该用户的所有设备
group:dev 组内所有用户
Tailscale IP 100.100.123.123 指定设备
子网 192.168.1.0/24 子网内的 IP(经子网路由器)
标签 tag:dev 打了该标签的所有设备
Hosts 别名 my-laptop hosts 中定义的名称
Autogroup 成员 autogroup:member tailnet 所有成员
Autogroup 管理员 autogroup:admin Tailscale 管理员

2.2 目标对象 dst(旧 acls 语法:host:ports)

类型 示例 含义
任意 *:* 无限制
用户 alice@example.com:* 该用户所有设备的全部端口
group:dev:* 组内所有设备
IP 100.100.123.123:22 单设备指定端口
子网 192.168.1.0/24:* 子网内全部
标签 tag:prod:* 标签设备的全部端口
出口上网 autogroup:internet:* 通过出口节点访问互联网
自己 autogroup:self:* 允许 src 访问自己的设备

2.3 端口写法

tag:web:80                # 单端口
tag:web:80,443            # 多端口
tag:web:80-90             # 端口范围
tag:web:*                 # 全部端口
192.168.1.0/24:0-65535    # 全端口等价写法

2.4 新 grants 语法

grants 把端口从 dst 中分离到 ip 字段(支持应用层协议,如 tcp:22):

// 旧 acls
{
  "action": "accept",
  "src": ["group:dev"],
  "dst": ["tag:web:80", "tag:web:443"]
}

// 新 grants(首选)
{
  "src": ["group:dev"],
  "dst": ["tag:web"],
  "ip": ["80", "443"]
}

IP 取值示例:"*"(全放行)、"tcp:22""udp:53""80,443"


3. 各配置块详解

3.1 hosts —— IP 别名

策略文件不认设备"名称",只认 IP。想用名字,需先在 hosts 里定义:

{
  "hosts": {
    "my-laptop": "100.64.12.34",
    "my-nas":    "100.84.25.76",
    "home-lan":  "192.168.100.0/24"
  }
}

3.2 groups —— 用户组

{
  "groups": {
    "group:admin": ["admin@example.com"],
    "group:dev":   ["alice@example.com", "bob@example.com"]
  }
}

3.3 tags 与 tagOwners —— 设备按用途分类

设备打 tag 后即与个人账号"解耦",策略按用途而非人归属来管理。tag 必须先声明在 tagOwners 才能使用。

{
  "tagOwners": {
    "tag:prod":          ["autogroup:admin"],
    "tag:dev":           ["autogroup:admin", "group:dev"],
    "tag:ci":            ["autogroup:admin"],
    "tag:subnet-router": ["autogroup:admin"]
  }
}
  • 标签规则:必须以 tag: 开头。
  • 打标签方式(服务器上以标签身份认证):
    tailscale up --auth-key=<key> --advertise-tags=tag:prod
    # 或在 Admin Console 设备菜单 "Edit tags"
    

易踩坑:打了 tag 的设备不属于其所属用户的 src/dst 匹配范围。例如给 dev1 下的 guest-2 打上 tag:test 后,src: ["dev1"] 的规则就不覆盖 guest-2 了;需要它在 dst 中用 tag:test 显式声明才能访问。

3.4 autoApprovers —— 路由/出口节点免审批

默认每次设备宣告子网路由或出口节点,都需管理员在控制台点"批准"。autoApprovers 可省去手动步骤:

{
  "autoApprovers": {
    "routes": {
      "192.168.100.0/24": ["tag:subnet-router"],
      "10.0.0.0/8":      ["group:dev", "alice@example.com"]
    },
    "exitNode": ["tag:exit"]
  }
}
  • 值可以是用户邮箱、组、autogroup 或 tag。
  • 建议用标签作为 autoApprover(避免因用户设备下线导致路由自动停止宣告)。
  • 内部路由范围(RFC1918)签署本组设备通常安全;宽范围 0.0.0.0/0 要精细限定到具体 tag。

3.5 ssh —— Tailscale SSH 授权

{
  "ssh": [
    // 所有人能 SSH 回自己
    {"action": "accept", "src": ["autogroup:member"], "dst": ["autogroup:self"], "users": ["autogroup:nonroot", "root"]},
    // 开发组能 SSH 进开发机,只用 deploy 账号
    {"action": "accept", "src": ["group:dev"], "dst": ["tag:dev"], "users": ["deploy"]}
  ]
}

注意事项:

  • 需配合目标主机执行 tailscale set --ssh
  • users 允许值:具体用户名、autogroup:nonroot(排除 root)、root
  • 仅支持连接 Linux / macOS 开源版主机,Synology / QNAP 不支持。

3.6 tests —— 策略断言(保护关键连通性)

{
  "tests": [
    {"src": "my-laptop", "proto": "tcp", "accept": ["my-nas:22"], "deny": ["my-nas:443"]}
  ]
}

保存策略时自动校验,防止误删关键放行。accepthost:port 按旧格式写,一个写错就保存失败,能及时发现。

3.7 其他 sections(了解即可)

  • ipsets:把多个子网/网段打包成一个名称。
  • nodeAttrs:给设备附加属性(App Connector 域名列表、允许 IPv4/IPv6 开关等)。
  • postures:基于 OS 版本是否最新、是否经过密码认证等做准入。
  • policy:可整体禁用 IPv4 等网络策略。

4. 完整示例

一个覆盖常见场景的策略文件(按 grants 新语法):

{
  // 1. IP 别名
  "hosts": {
    "my-laptop": "100.64.12.34",
    "my-nas":    "100.84.25.76",
    "home-lan":  "192.168.100.0/24"
  },

  // 2. 用户组
  "groups": {
    "group:admin": ["admin@example.com"],
    "group:dev":   ["alice@example.com", "bob@example.com"]
  },

  // 3. 标签申明:谁有权打
  "tagOwners": {
    "tag:dev":           ["autogroup:admin", "group:dev"],
    "tag:prod":          ["autogroup:admin"],
    "tag:ci":            ["autogroup:admin"],
    "tag:subnet-router": ["autogroup:admin"],
    "tag:exit":          ["autogroup:admin"]
  },

  // 4. 放行规则
  "grants": [
    // 每个人都能连自己的设备
    {"src": ["autogroup:member"], "dst": ["autogroup:self"], "ip": ["*"]},
    // 只有我自己的笔记本能 SSH 到 NAS
    {"src": ["my-laptop"], "dst": ["my-nas"], "ip": ["tcp:22"]},
    // 开发组能访问开发机的 SSH + Web
    {"src": ["group:dev"], "dst": ["tag:dev"], "ip": ["tcp:22", "tcp:80", "tcp:443"]},
    // 只有管理员能碰生产环境全部端口
    {"src": ["group:admin"], "dst": ["tag:prod"], "ip": ["*"]},
    // CI 机器只能部署(SSH)
    {"src": ["tag:ci"], "dst": ["tag:prod"], "ip": ["tcp:22"]},
    // 管理员可访问家中局域网段(经子网路由器)
    {"src": ["group:admin"], "dst": ["home-lan"], "ip": ["*"]},
    // 开发组可通过出口节点访问互联网
    {"src": ["group:dev"], "dst": ["autogroup:internet"], "ip": ["*"]}
  ],

  // 5. Tailscale SSH
  "ssh": [
    {"action": "accept", "src": ["autogroup:member"], "dst": ["autogroup:self"], "users": ["autogroup:nonroot", "root"]},
    {"action": "accept", "src": ["group:dev"], "dst": ["tag:dev"], "users": ["deploy"]}
  ],

  // 6. 路由 / 出口节点自动批准
  "autoApprovers": {
    "routes":   {"192.168.100.0/24": ["tag:subnet-router"]},
    "exitNode": ["tag:exit"]
  },

  // 7. 保护关键链路
  "tests": [
    {"src": "my-laptop", "proto": "tcp", "accept": ["my-nas:22"], "deny": ["my-nas:443"]}
  ]
}

5. 在 Headscale 中配置 ACL

Headscale(自建控制面)也支持同一套 ACL 语法:

# /etc/headscale/config.yaml
policy:
  mode: file            # file | database
  path: /etc/headscale/acl.hujson
  • mode: file:把规则写在 path 指向的文件(HuJSON 或 YAML 均可),改文件后重启/重载生效。
  • mode: database:规则存在数据库,用 CLI 管理:
    headscale policy set -f /tmp/policy.hujson
    headscale policy get
    
  • 验证规则:headscale configtest
  • Headscale 中用 用户(namespace) 替代官方邮箱:如 src: ["default"]

6. 常见坑与排查

现象 原因 / 处理
保存策略就报错 HuJSON 语法错、autogroup:memberautogroup:members 混用(统一单数)、tag 未在 tagOwners 声明、dst 直接写了设备名而非 hosts 别名
打 tag 的设备突然访问不了 tag 设备不属于用户名下的匹配范围,需显式用 tag:xxx
出口节点能 SSH 但上不了网 dst: ["autogroup:internet"] 的放行规则
子网路由通了但策略拦着 在 ACL 中把子网 CIDR 加入 dst(如 192.168.1.0/24:*
Tailscale SSH 连不上 目标机需 tailscale set --ssh,且 ACL 有对应 ssh 规则
验证当前生效策略 tailscale debug prefs 查看下发的 ACL;状态页 Alert 会提示 ACL 改动风险

最佳实践:策略文件纳入版本管理(Git)便于审计回滚;改动前先用 tests 钉住关键连通性;个人/家庭网络保持默认"全互访 + 自己"即可,复杂的 ACL 多用于多用户团队。

评论 0 打赏 分享

相关推荐

评论交流