
tailsnitch v1.7
Tailscale 配置安全审计工具。扫描你的 tailnet,查找错误配置、过度宽松的访问控制以及违反安全最佳实践的问题。
Tailsnitch
Tailscale 配置的安全审计工具。Tailsnitch 会扫描您的 tailnet,检查 57 种错误配置、过度宽松的访问控制以及违反安全最佳实践的问题。
快速开始
# 1. 设置您的 Tailscale API 凭据
export TS_API_KEY="tskey-api-..."
# 2. 运行审计
tailsnitch
# 3. 仅查看高严重性发现
tailsnitch --severity high
# 4. 修复一些问题 ~交互式~ yolo 模式
tailsnitch --fix
安装
下载预编译二进制文件
从 GitHub Releases 下载最新版本。
macOS 用户: 下载后移除隔离属性:
sudo xattr -rd com.apple.quarantine tailsnitch
通过 Go 安装
go install github.com/Adversis/tailsnitch@latest
从源码构建
git clone https://github.com/Adversis/tailsnitch.git
cd tailsnitch
go build -o tailsnitch .
身份认证
Tailsnitch 支持两种身份认证方式。当两者都配置时,优先使用 OAuth。
方式 1:OAuth 客户端(推荐)
OAuth 客户端提供有作用域限制、可审计的访问权限,员工离职时不会失效。
export TS_OAUTH_CLIENT_ID="..."
export TS_OAUTH_CLIENT_SECRET="tskey-client-..."
在此处创建 OAuth 客户端:https://login.tailscale.com/admin/settings/oauth
只读审计所需的作用域:
all:read 涵盖所有权限。也可以单独授予各作用域:
| 作用域 | 用途 |
|---|---|
policy_file:read | Tailnet 策略文件 — ACL-、NET-、SSH-* |
devices:core:read | 设备列表 — DEV-、NET-、ACL-011 |
dns:read | DNS 配置 — DNS-001、DEV-007 |
auth_keys:read | 机器认证密钥 — AUTH-*、ACL-011 |
feature_settings:read | Tailnet 设置 — DEV-008、DEV-009、DEV-014 |
logs:network:read | 网络流量日志设置 — LOG-001 |
networking_settings:read | HTTPS 证书设置 — NET-004 |
log_streaming:read | 日志流目标 — LOG-002 |
webhooks:read | Webhook 端点 — LOG-005、LOG-012 |
oauth_keys:read | OAuth 客户端 — LOG-006 |
users:read | 用户角色和状态 — USER-001、LOG-006 |
account_settings:read | 安全联系人 — LOG-011 |
devices:posture_attributes:read | 态势集成 — DEV-014 |
您未授予的任何作用域只会影响需要它的检查:这些检查会报告无法读取该设置,而不是直接通过。
AUTH-005 和 AUTH-006 读取 tailnet 的联合身份(管理控制台称之为信任凭据)。它们与认证密钥来自同一个密钥列表,因此预期 auth_keys:read 可以覆盖它们。这一点尚未在真实 tailnet 上得到确认。如果无法读取密钥列表,这两个检查都会报告"未评估"而不是通过。缺少作用域时是返回错误还是返回过滤掉身份后的列表,这一点尚未确认;如果它静默过滤,AUTH-005 将报告不存在信任凭据,AUTH-006 将找不到可检查的内容。
修复模式所需的额外作用域:
devices:core- 删除设备、修改标签(需要选择标签)auth_keys- 删除认证密钥
Tailnet Lock
DEV-010 和 DEV-012 报告 Tailnet Lock 的状态,Tailscale API 不将其作为 tailnet 设置公开。被其锁定的设备可以通过 API 看到,但确定锁是否已启用需要本地的 tailscale CLI,它会读取运行 tailsnitch 的机器上的守护进程。使用 --tailnet 审计其他 tailnet 时,请相应对待该部分结果。如果二进制文件位于非标准位置,请使用 --tailscale-path。
方式 2:API 密钥
API 密钥以创建它的用户身份运行,并继承该用户的权限。
export TS_API_KEY="tskey-api-..."
在此处创建 API 密钥:https://login.tailscale.com/admin/settings/keys
使用示例
基本审计
# 运行完整审计
tailsnitch
# 同时显示通过的检查(详细模式)
tailsnitch --verbose
# 以 JSON 格式输出以便处理
tailsnitch --json
# 审计特定 tailnet(当 OAuth 客户端可访问多个 tailnet 时)
tailsnitch --tailnet mycompany.com
过滤结果
# 仅显示严重和高严重性问题
tailsnitch --severity high
# 按类别过滤
tailsnitch --category access # ACL 问题
tailsnitch --category auth # 身份认证与密钥
tailsnitch --category device # 设备安全
tailsnitch --category network # 网络暴露
tailsnitch --category ssh # SSH 规则
tailsnitch --category log # 日志与管理
# 仅运行特定检查
tailsnitch --checks ACL-001,AUTH-001,DEV-010
tailsnitch --checks stale-devices,tailnet-lock-not-enabled
# 列出所有可用检查
tailsnitch --list-checks
交互式修复模式
修复模式允许您直接通过 Tailscale API 修复问题:
# 交互式修复模式
tailsnitch --fix
# 预览将要修复的内容(试运行)
tailsnitch --fix --dry-run
# 自动选择安全修复(仍需确认)
tailsnitch --fix --auto
# 禁用修复操作的审计日志
tailsnitch --fix --no-audit-log
可通过 API 修复的项目:
| 检查 | 操作 |
|---|---|
| AUTH-001、AUTH-002、AUTH-003 | 删除认证密钥 |
| DEV-002 | 从用户设备移除标签 |
| DEV-004 | 删除过期设备 |
| DEV-005 | 授权待处理设备 |
修复模式还为需要手动干预的问题提供管理控制台的直接链接。
SOC 2 证据导出
生成带有通用标准(CC)控制映射的 SOC 2 审计证据报告:
# 导出为 JSON
tailsnitch --soc2 json > soc2-evidence.json
# 导出为 CSV(用于电子表格)
tailsnitch --soc2 csv > soc2-evidence.csv
SOC 2 报告包括:
- 每个资源的测试结果(每台设备、每个密钥、每条 ACL 规则均单独测试)
- CC 代码映射(CC6.1、CC6.2、CC6.3、CC6.6、CC7.1、CC7.2 等)
- 每个控制测试的通过/失败/不适用状态
- 用于审计追踪的时间戳
CSV 输出示例:
resource_type,resource_id,resource_name,check_id,check_title,cc_codes,status,details,tested_at
device,node123,prod-server,DEV-001,Tagged devices with key expiry disabled,CC6.1;CC6.3,PASS,Tags: [tag:server] key expiry enabled,2025-01-05T10:30:00Z
key,tskey-auth-xxx,tskey-auth-xxx,AUTH-001,Reusable auth keys exist,CC6.1;CC6.2;CC6.3,FAIL,Reusable key expires in 45 days,2025-01-05T10:30:00Z
忽略已知风险
创建 .tailsnitch-ignore 文件以抑制已接受风险的发现:
# .tailsnitch-ignore
# 忽略信息性检查
ACL-008 # 我们有意不使用组
ACL-009 # 传统 ACL 适合我们的使用场景
# 忽略带有理由说明的特定中等级检查
DEV-006 # 外部设备是已批准的承包商
LOG-001 # 流量日志需要企业版套餐
# 忽略检查中的单个项目,而不是静默整个检查
ACL-011:tag:monitoring # 设计上范围较广;其他所有标签仍会被检查
AUTH-001:tskey-auth-xxxx # 通过 CI 自动轮换,在 TICKET-123 中跟踪
一行可以指定整个检查(ACL-011)或其中的单个项目(CHECK-ID:item,按第一个冒号分割——项目本身可能包含冒号)。按项目规则仅抑制该项目:检查仍会运行并报告其发现的其他所有内容。抑制所有被标记的项目绝不会将失败的检查变为通过——发现仍然存在,只是降级为信息级别,因此被抑制的发现永远不会被解读为已满足的控制项。
忽略文件位置(按顺序检查):
- 当前目录中的
.tailsnitch-ignore - 主目录中的
~/.tailsnitch-ignore
由于第一个位置是工作目录,忽略文件可能来自代码仓库而非您本人。每次运行都会报告使用了哪个文件以及抑制了多少发现和项目,--json 会在 ignore_file 和 ignored 字段中记录这些信息(CHECK-ID 表示整个检查,CHECK-ID:item 表示单个被抑制的项目)。使用 --no-ignore 可跳过该文件。
# 使用特定的忽略文件
tailsnitch --ignore-file /path/to/ignore
# 完全禁用忽略文件处理
tailsnitch --no-ignore
JSON 导出与处理
# 导出完整报告
tailsnitch --json > audit.json
# 提取失败的检查为 TSV
tailsnitch --json | jq -r '
.suggestions
| map(select(.pass == false))
| .[]
| [.id, .title, .severity, .remediation]
| @tsv
' > findings.tsv
# 按严重性汇总
tailsnitch --json | jq '
.suggestions
| map(select(.pass == false))
| group_by(.severity)
| map({severity: .[0].severity, count: length})
'
# 列出带有管理链接的严重/高问题
tailsnitch --json | jq -r '
.suggestions
| map(select(.pass == false and (.severity == "CRITICAL" or .severity == "HIGH")))
| .[]
| "\(.id): \(.title)\n Fix: \(.fix.admin_url // "manual")\n"
'
命令参考
| 标志 | 描述 |
|---|---|
--json | 以 JSON 格式输出 |
--severity | 按最低严重性过滤:critical、high、medium、low、info |
--category | 按类别过滤:access、auth、network、ssh、log、device、dns |
--checks | 运行特定检查(逗号分隔的 ID 或别名) |
--list-checks | 列出所有可用检查并退出 |
--tailnet | 指定要审计的 tailnet(默认:来自 API 密钥) |
--verbose | 同时显示通过的检查 |
--fix | 启用交互式修复模式 |
--auto | 自动选择安全修复(需要 --fix) |
--dry-run | 预览修复操作而不执行(需要 --fix) |
--no-audit-log | 禁用修复操作的审计日志 |
--soc2 | 导出 SOC 2 证据:json 或 csv |
--tailscale-path | tailscale CLI 的路径(用于 Tailnet Lock 检查) |
--timeout | 审计的总体时间预算(默认 2m) |
--ignore-file | 忽略文件的路径 |
--no-ignore | 禁用忽略文件处理 |
--version | 显示版本信息 |
安全检查
Tailsnitch 在 7 个类别中执行 57 项安全检查。每个检查的详细文档请参阅 docs/CHECKS.md。
严重级别
| ID | 检查 | 风险 |
|---|---|---|
| ACL-001 | 默认"允许所有"策略 | 所有设备拥有无限制访问权限 |
| ACL-002 | SSH autogroup:nonroot 配置错误 | 可以任意非 root 用户身份进行 SSH |
| ACL-006 | tagOwners 范围过宽 | 通过标签进行权限提升 |
| ACL-007 | 使用 autogroup:danger-all | 向外部用户授予访问权限 |
高级别
| ID | 检查 | 风险 |
|---|---|---|
| ACL-011 | 标签可达范围跨越信任边界 | 被盗的可重用密钥生成可访问所有内容的标签 |
| AUTH-001 | 可重用认证密钥 | 被盗后可无限添加设备 |
| AUTH-002 | 长有效期认证密钥 | 暴露窗口延长 |
| AUTH-003 | 预授权密钥 | 绕过设备审批 |
| AUTH-006 | 联合身份主体范围过宽 | 签发方担保的任何主体都可生成标签 |
| DEV-001 | 带标签的设备未设置密钥过期 | 无限期访问 |
| DEV-002 | 用户设备被添加标签 | 用户移除后仍然存在 |
| DEV-010 | Tailnet Lock 未启用 | 无法防范密钥被盗 |
| DEV-012 | 待处理的 Tailnet Lock 签名 | 未签名节点需要审查 |
| NET-001 | Funnel 暴露 | 公共互联网访问 |
| NET-003 | 子网路由器信任边界 | 本地网络上未加密的流量 |
| SSH-002 | 无检查模式的 root SSH | 无需重新认证 |
中等级别
| ID | 检查 | 风险 |
|---|---|---|
| ACL-004 | 使用 autogroup:member | 包含外部用户 |
| ACL-005 | 配置了 AutoApprovers | 绕过路由审批 |
| AUTH-004 | 非临时 CI/CD 密钥 | 过期设备不断累积 |
| AUTH-005 | 未使用工作负载身份联合 | 长期密钥仍可被盗 |
| DEV-003 | 过时的客户端 | 潜在漏洞 |
| DEV-004 | 过期设备 | 未使用的攻击面 |
| DEV-005 | 未授权设备 | 待审批队列 |
| DEV-007 | 敏感机器名称 | CT 日志暴露 |
| DEV-009 | 设备审批配置 | 可能未启用 |
| NET-004 | HTTPS CT 日志暴露 | 机器名称公开 |
| NET-005 | 出口节点流量可见性 | 操作员可看到所有流量 |
| NET-006 | Serve 暴露 | tailnet 上的本地服务 |
| SSH-003 | 录制器 UI 暴露 | 会话对网络可见 |
信息级别
用于日志配置、DNS 设置、用户角色和手动验证项目的检查。
严重性取决于发现内容
一些检查根据发现的内容进行评级,而不是携带固定的严重性。其中三个值得特别说明:
- ACL-011 以信息级别报告每个标签的可达范围。仅当认证密钥可分配的标签达到通配符目标、路由子网或出口节点出口时才会失败:如果可重用密钥分配该标签则为高,如果仅一次性密钥分配则为中。设备数量永远不会决定严重性。
- AUTH-005 在 tailnet 完全没有信任凭据时报告为中,在存在信任凭据但可重用密钥仍生成它们均未覆盖的标签时报告为低。
- AUTH-006 对仅为通配符的主体报告为高,对较窄的通配符或缺少受众的主体报告为低。
输出示例
+=====================================================================+
| TAILSNITCH SECURITY AUDIT |
| Tailnet: example.com |
| Version: 1.0.0 (build: abc123) |
+=====================================================================+
Using ignore file: .tailsnitch-ignore (3 rules)
=== ACCESS CONTROLS ===================================================
[CRITICAL] ACL-001: Default 'allow all' policy active
Your ACL policy omits the 'acls' field. Tailscale applies a
default 'allow all' policy, granting all devices full access.
Remediation:
Define explicit ACL rules following least privilege principle.
Source: https://tailscale.com/docs/reference/examples/acls
----------------------------------------------------------------------
=== AUTHENTICATION & KEYS =============================================
[HIGH] AUTH-001: Reusable auth keys exist
Found 2 reusable auth key(s). These can be reused to add
multiple devices if compromised.
Details:
- Key tskey-auth-xxx (expires in 45 days)
- Key tskey-auth-yyy (expires in 89 days)
Remediation:
Store reusable keys in a secrets manager. Prefer one-off keys.
Source: https://tailscale.com/docs/features/access-control/auth-keys
----------------------------------------------------------------------
SUMMARY
======================================================================
Critical: 1 High: 3 Medium: 5 Low: 2 Info: 8
Total findings: 19 | Passed: 33
Tailnet Lock 检查
Tailnet Lock 检查(DEV-010、DEV-012)需要本地的 tailscale CLI,并针对本地机器的守护进程运行。通过 --tailnet 审计远程 tailnet 时,这些检查反映的是本地状态,而非被审计的 tailnet。
# 如有需要,指定自定义 tailscale 二进制路径
tailsnitch --tailscale-path /opt/tailscale/bin/tailscale
CI/CD 集成
在 CI/CD 流水线中运行 Tailsnitch 以捕获安全回归:
# GitHub Actions 示例
- name: Audit Tailscale Security
env:
TS_OAUTH_CLIENT_ID: ${{ secrets.TS_OAUTH_CLIENT_ID }}
TS_OAUTH_CLIENT_SECRET: ${{ secrets.TS_OAUTH_CLIENT_SECRET }}
run: |
tailsnitch --json > audit.json
# Fail if critical or high severity issues exist
if tailsnitch --severity high --json | jq -e '.summary.critical + .summary.high > 0' > /dev/null; then
echo "Critical or high severity issues found!"
tailsnitch --severity high
exit 1
fi
参考
许可证
MIT
贡献
指南请参阅 CONTRIBUTING.md。