返回更新列表
新发布Sep 4, 2026

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:readTailnet 策略文件 — ACL-、NET-、SSH-*
devices:core:read设备列表 — DEV-、NET-、ACL-011
dns:readDNS 配置 — DNS-001、DEV-007
auth_keys:read机器认证密钥 — AUTH-*、ACL-011
feature_settings:readTailnet 设置 — DEV-008、DEV-009、DEV-014
logs:network:read网络流量日志设置 — LOG-001
networking_settings:readHTTPS 证书设置 — NET-004
log_streaming:read日志流目标 — LOG-002
webhooks:readWebhook 端点 — LOG-005、LOG-012
oauth_keys:readOAuth 客户端 — 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,按第一个冒号分割——项目本身可能包含冒号)。按项目规则仅抑制该项目:检查仍会运行并报告其发现的其他所有内容。抑制所有被标记的项目绝不会将失败的检查变为通过——发现仍然存在,只是降级为信息级别,因此被抑制的发现永远不会被解读为已满足的控制项。

忽略文件位置(按顺序检查):

  1. 当前目录中的 .tailsnitch-ignore
  2. 主目录中的 ~/.tailsnitch-ignore

由于第一个位置是工作目录,忽略文件可能来自代码仓库而非您本人。每次运行都会报告使用了哪个文件以及抑制了多少发现和项目,--json 会在 ignore_fileignored 字段中记录这些信息(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按最低严重性过滤:criticalhighmediumlowinfo
--category按类别过滤:accessauthnetworksshlogdevicedns
--checks运行特定检查(逗号分隔的 ID 或别名)
--list-checks列出所有可用检查并退出
--tailnet指定要审计的 tailnet(默认:来自 API 密钥)
--verbose同时显示通过的检查
--fix启用交互式修复模式
--auto自动选择安全修复(需要 --fix
--dry-run预览修复操作而不执行(需要 --fix
--no-audit-log禁用修复操作的审计日志
--soc2导出 SOC 2 证据:jsoncsv
--tailscale-pathtailscale CLI 的路径(用于 Tailnet Lock 检查)
--timeout审计的总体时间预算(默认 2m
--ignore-file忽略文件的路径
--no-ignore禁用忽略文件处理
--version显示版本信息

安全检查

Tailsnitch 在 7 个类别中执行 57 项安全检查。每个检查的详细文档请参阅 docs/CHECKS.md

严重级别

ID检查风险
ACL-001默认"允许所有"策略所有设备拥有无限制访问权限
ACL-002SSH autogroup:nonroot 配置错误可以任意非 root 用户身份进行 SSH
ACL-006tagOwners 范围过宽通过标签进行权限提升
ACL-007使用 autogroup:danger-all向外部用户授予访问权限

高级别

ID检查风险
ACL-011标签可达范围跨越信任边界被盗的可重用密钥生成可访问所有内容的标签
AUTH-001可重用认证密钥被盗后可无限添加设备
AUTH-002长有效期认证密钥暴露窗口延长
AUTH-003预授权密钥绕过设备审批
AUTH-006联合身份主体范围过宽签发方担保的任何主体都可生成标签
DEV-001带标签的设备未设置密钥过期无限期访问
DEV-002用户设备被添加标签用户移除后仍然存在
DEV-010Tailnet Lock 未启用无法防范密钥被盗
DEV-012待处理的 Tailnet Lock 签名未签名节点需要审查
NET-001Funnel 暴露公共互联网访问
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-004HTTPS CT 日志暴露机器名称公开
NET-005出口节点流量可见性操作员可看到所有流量
NET-006Serve 暴露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

分类