Fragments of verbose memory

冗長な記憶の断片 - Web技術のメモをほぼ毎日更新

Jan 7, 2026 - 日記

Tailscaleの『なんとなく不安』を数値化する:tailsnitchが暴く50の設定ミス

Tailscaleの『なんとなく不安』を数値化する:tailsnitchが暴く50の設定ミス cover image

2025年9月、アサヒグループホールディングスがランサムウェア攻撃を受けました。侵入経路はVPN装置の脆弱性。10月にはアスクルが業務委託先のVPNアカウント経由で侵入され、ECサイトが停止しました。

どちらも「VPNさえあれば安全」という思い込みが招いた被害です。

Tailscaleを導入したあなたは、こう思っているかもしれません。「従来のVPNより安全なはずだから大丈夫」と。

確かにTailscaleには、従来のオンプレVPNゲートウェイと比べて優位性があります。WireGuardによる軽量で監査済みの暗号化、デバイス単位のゼロトラスト認証、VPN装置の脆弱性を突かれるリスクがないSaaS型アーキテクチャ——これらは従来VPNの弱点を解消する設計です。

しかし、Tailscaleも設定を誤れば危険です。デフォルトACLを放置すれば全デバイスが無制限にアクセス可能になり、再利用可能な認証キーが漏洩すれば攻撃者が不正デバイスを追加できます。tailsnitch は、そんな「なんとなく不安」を50以上のチェック項目で数値化し、Critical/High/Medium/Low/Infoの5段階で評価してくれるセキュリティ監査ツールです。

本記事では、tailsnitchの使い方と、実際に検出される危険な設定ミスについて解説します。

tailsnitchとは?

tailsnitchは、Tailscale ネットワーク(tailnet)の設定を自動監査するOSSツールです。2025年12月24日にリリースされ、わずか2週間で430以上のGitHub Starsを獲得しました。

主な特徴:

  • 50以上のセキュリティチェック: ACL、認証キー、デバイス、ネットワーク露出、SSH、ログ、DNSの7カテゴリ
  • 重大度5段階評価: Critical → High → Medium → Low → Info
  • SOC 2証跡出力: CC6.1, CC6.2などのコントロールにマッピングされたCSV/JSON出力
  • CI/CD統合: GitHub ActionsでPR時に自動チェック
  • 対話的修復モード: --fixフラグで設定ミスを修正できる

開発元はAdversis というセキュリティ企業で、Tailscale Hardening Guide も併せて公開されています。

インストール

以下のいずれかの方法でインストールできます。

プリビルドバイナリ(推奨)

GitHub Releases から最新版をダウンロード:

1
2
3
4
# macOSの場合は検疫属性を削除
sudo xattr -rd com.apple.quarantine tailsnitch
chmod +x tailsnitch
sudo mv tailsnitch /usr/local/bin/

Goでインストール

1
go install github.com/Adversis/tailsnitch@latest

ソースからビルド

1
2
3
git clone https://github.com/Adversis/tailsnitch.git
cd tailsnitch
go build -o tailsnitch .

認証設定

tailsnitchはTailscale APIを使用するため、認証情報が必要です。OAuth Client(推奨)またはAPI Keyのいずれかを使用します。

OAuth Client(推奨)

OAuth Clientは権限をスコープで制限でき、監査ログにも記録されます。従業員の退職時にAPIキーが無効化されるリスクもありません。

  1. https://login.tailscale.com/admin/settings/oauth でOAuth Clientを作成

  2. 読み取り専用監査には以下のスコープを付与:

    • all:read(最も簡単)
    • または個別に:policy_file:read, devices:core:read, dns:read, auth_keys:read
  3. 環境変数に設定:

1
2
export TS_OAUTH_CLIENT_ID="..."
export TS_OAUTH_CLIENT_SECRET="tskey-client-..."

API Key

API Keyは作成したユーザの権限を継承します。

  1. https://login.tailscale.com/admin/settings/keys でAPI Keyを作成
  2. 環境変数に設定:
1
export TSKEY="tskey-api-..."

基本的な使い方

全項目を監査

1
tailsnitch

最初の実行例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
+=====================================================================+
|                    TAILSNITCH SECURITY AUDIT                        |
|            Tailnet: example.com                                     |
|            Version: 1.4.0 (build: d717661)                          |
+=====================================================================+

=== 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/kb/1192/acl-samples
----------------------------------------------------------------------

[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.
----------------------------------------------------------------------

SUMMARY
======================================================================
  Critical: 1  High: 3  Medium: 5  Low: 2  Info: 8
  Total findings: 19  |  Passed: 33

重大度でフィルタ

1
2
3
4
5
6
7
# Critical/Highのみ表示
tailsnitch --severity high

# 特定カテゴリのみ
tailsnitch --category access   # ACL問題
tailsnitch --category auth     # 認証キー問題
tailsnitch --category device   # デバイスセキュリティ

JSON出力とjqでの集計

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# 全結果をJSON出力
tailsnitch --json > audit.json

# 失敗したチェックのみ抽出
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})
'

出力例:

1
2
3
4
5
[
  {"severity": "CRITICAL", "count": 1},
  {"severity": "HIGH", "count": 3},
  {"severity": "MEDIUM", "count": 5}
]

検出される危険な設定ミス

tailsnitchが検出する代表的な問題を紹介します。

Critical: デフォルトACLを放置

問題:ACLポリシーにaclsフィールドがない場合、Tailscaleは全デバイスに全アクセスを許可する「allow all」ポリシーを適用します。

1
[CRITICAL] ACL-001: Default 'allow all' policy active

影響

  • 開発者のラップトップから本番DBへ無制限アクセス可能
  • 侵害されたデバイス1台でtailnet全体が危険に

修正方法

最小限のACLを定義:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
  "groups": {
    "group:engineering": ["[email protected]"],
    "group:devops": ["[email protected]"]
  },
  "tagOwners": {
    "tag:dev": ["autogroup:admin"],
    "tag:prod": ["autogroup:admin"]
  },
  "acls": [
    {
      "action": "accept",
      "src": ["group:engineering"],
      "dst": ["tag:dev:443", "tag:dev:8080"]
    },
    {
      "action": "accept",
      "src": ["group:devops"],
      "dst": ["tag:prod:22", "tag:prod:443"]
    }
  ]
}

High: 再利用可能な認証キーが存在

問題:再利用可能な認証キー(reusable auth key)が漏洩すると、攻撃者が無制限にデバイスを追加できます。

1
2
3
[HIGH] AUTH-001: Reusable auth keys exist
  Found 2 reusable auth key(s):
    - Key tskey-auth-xxx (expires in 45 days)

影響

  • GitHubリポジトリにコミットされた認証キーから侵入
  • CI/CDパイプラインから盗まれたキーで不正デバイス追加

修正方法

  1. 既存のreusable keyを削除
  2. エフェメラル(一時的)キーに切り替え:
1
2
# エフェメラルキーを生成(1回のみ使用可能)
tailscale up --authkey tskey-auth-xxx --ephemeral
  1. CI/CDではOAuth Clientを使用

High: Tailnet Lockが無効

問題:Tailnet Lockが無効だと、Tailscaleコーディネーションサーバが侵害された場合、攻撃者が不正なデバイスを追加できます。

1
[HIGH] DEV-010: Tailnet Lock disabled

影響

  • コントロールプレーンへの信頼が必要
  • 高度な攻撃者による中間者攻撃のリスク

修正方法

Tailnet Lockを有効化(署名ノードが必要):

1
2
3
4
5
# 信頼できるノードでロックを初期化
tailscale lock init tlpub:<SIGNING_NODE_KEY>

# 新規デバイスは署名が必要
tailscale lock sign nodekey:<NEW_NODE_KEY>

注意:Tailnet Lockは運用負荷が高いため、防衛産業やコンプライアンス要件が厳しい企業向けです。

Medium: 古いクライアントが存在

問題:古いTailscaleクライアントには既知の脆弱性が存在する可能性があります。

1
2
[MEDIUM] DEV-003: Outdated clients detected
  Found 3 devices running Tailscale < 1.50.0

修正方法

デバイスポスチャー(Device Posture)でバージョンを強制:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
{
  "postures": {
    "posture:baseline": [
      "node:tsVersion >= '1.50.0'"
    ]
  },
  "acls": [
    {
      "action": "accept",
      "src": ["group:devops"],
      "srcPosture": ["posture:baseline"],
      "dst": ["tag:prod:22"]
    }
  ]
}

Medium: 放置されたデバイス

問題:60日以上使用されていないデバイスが残っている場合、退職した従業員のデバイスが侵害されるリスクがあります。

1
2
[MEDIUM] DEV-004: Stale devices detected
  Found 5 devices not seen in 60+ days

修正方法

--fixモードで対話的に削除:

1
tailsnitch --fix

または手動で削除:

1
tailscale logout --device <device-id>

対話的修復モード(–fix)

--fixフラグを使用すると、APIで修復可能な問題を対話的に修正できます。

1
tailsnitch --fix

修復可能な項目:

チェック修復内容
AUTH-001, AUTH-002, AUTH-003認証キーの削除
AUTH-004エフェメラルキーへの置き換え
DEV-002ユーザデバイスからタグを削除
DEV-004古いデバイスの削除
DEV-005未承認デバイスの承認

手動対応が必要な項目には管理コンソールへのリンクが表示されます。

ドライラン(変更をプレビュー):

1
tailsnitch --fix --dry-run

SOC 2監査証跡の出力

tailsnitchはSOC 2監査に必要な証跡をCSV/JSON形式で出力できます。

1
2
3
4
5
# CSV形式
tailsnitch --soc2 csv > soc2-evidence.csv

# JSON形式
tailsnitch --soc2 json > soc2-evidence.json

出力例(CSV):

1
2
3
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

各チェックは以下のSOC 2コントロール(CC)にマッピングされています:

  • CC6.1: 論理アクセス制御
  • CC6.2: アクセス権限の付与
  • CC6.3: アクセス権限の削除
  • CC6.6: ネットワークセグメンテーション
  • CC7.1: セキュリティイベントの検知
  • CC7.2: セキュリティインシデントの監視

CI/CDパイプラインでの自動監査

GitHub ActionsでACL変更時に自動チェックを実行する例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# .github/workflows/tailscale-acl.yml
name: Tailscale ACL CI
on:
  pull_request:
    paths: ['policy.hujson']
  push:
    branches: [main]
    paths: ['policy.hujson']

jobs:
  test-acl:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Run tailsnitch
        env:
          TS_OAUTH_CLIENT_ID: ${{ secrets.TS_OAUTH_CLIENT_ID }}
          TS_OAUTH_CLIENT_SECRET: ${{ secrets.TS_OAUTH_CLIENT_SECRET }}
        run: |
          curl -L https://github.com/Adversis/tailsnitch/releases/latest/download/tailsnitch-linux-amd64 -o tailsnitch
          chmod +x tailsnitch
          ./tailsnitch --severity high --json > audit.json

      - name: Fail on critical issues
        run: |
          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

この設定により、ACL変更のPRで自動的にセキュリティチェックが走り、Critical/Highの問題があればマージをブロックします。

ACLテストで設定ミスを予防

tailsnitchは検出ツールですが、そもそも設定ミスを予防するにはACL内にtestsフィールドを追加します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
{
  "acls": [
    {
      "action": "accept",
      "src": ["group:engineering"],
      "dst": ["tag:dev:443"]
    }
  ],
  "tests": [
    {
      "src": "group:engineering",
      "deny": ["tag:prod:*", "tag:prod-db:5432"]
    },
    {
      "src": "group:devops",
      "accept": ["tag:bastion:22"]
    }
  ]
}

テストが失敗するとACL変更が拒否されます。これにより、tailsnitchで検出される前に設定ミスを防げます。

よくあるハマりポイント

autogroup:memberの危険性

autogroup:memberはtailnetに参加している全ユーザを含みます。外部ユーザ(Shared Nodes)も含まれるため、意図せずアクセス権を与えてしまう可能性があります。

悪い例

1
2
3
4
5
6
7
8
9
{
  "acls": [
    {
      "action": "accept",
      "src": ["autogroup:member"],
      "dst": ["tag:staging:*"]
    }
  ]
}

良い例

1
2
3
4
5
6
7
8
9
{
  "acls": [
    {
      "action": "accept",
      "src": ["group:engineering"],
      "dst": ["tag:staging:443"]
    }
  ]
}

Subnet Routerの過信

Subnet Routerは便利ですが、侵害されると広範囲のネットワークにアクセス可能になります。

対策

  1. Stateful Filteringを有効化:
1
tailscale up --advertise-routes=10.0.0.0/24 --stateful-filtering
  1. Subnet Router自体をセキュリティグループやNACLで保護

SSH autogroup:nonrootの罠

autogroup:nonrootroot以外の全ユーザへのSSHを許可しますが、sudo権限を持つユーザも含まれます。

悪い例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
  "ssh": [
    {
      "action": "accept",
      "src": ["group:engineering"],
      "dst": ["tag:prod"],
      "users": ["autogroup:nonroot"]
    }
  ]
}

良い例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
  "ssh": [
    {
      "action": "accept",
      "src": ["group:devops"],
      "dst": ["tag:prod"],
      "users": ["deploy"]
    }
  ]
}

定期監査の運用

tailsnitchを継続的に活用するための運用ガイドラインです。

週次

  • tailsnitchを実行してCritical/High問題を確認
  • デバイス承認キューを確認
  • 未使用の認証キーを削除

月次

  • グループメンバーシップを社員名簿と照合
  • 退職者のデバイスが削除されているか確認
  • ACL変更履歴をレビュー

四半期

  • 全アクセス権を監査(誰が何にアクセスできるか)
  • サードパーティアクセスのレビュー
  • Subnet Router設定を見直し

退職時(即座)

  • Tailscaleグループから削除
  • tailnetからユーザを削除
  • 作成した認証キーを削除
  • デバイスを削除
  • 最近のACL変更を監査

まとめ

Tailscaleは設計レベルでは従来VPNの多くの弱点を解消していますが、設定ミスによるリスクは依然として存在します。tailsnitchを使えば、「なんとなく不安」を具体的な問題として可視化し、優先度をつけて対処できます。

導入は5分で完了し、CI/CDに組み込めばACL変更のたびに自動チェックが走ります。SOC 2監査の証跡も出力できるため、コンプライアンス対応にも役立ちます。

個人的には、Tailscaleを使っている全ての組織が最低でも月1回はtailsnitchを実行すべきだと思います。Critical/Highの問題がゼロになるまで放置せず、ACLテストを追加して再発を防ぐ——この2つを徹底すれば、VPN侵入による大規模インシデントのリスクを大幅に減らせます。

興味のある方は、まず自分のtailnetでtailsnitchを実行してみてください。思わぬ設定ミスが見つかるかもしれません。

参考リンク