
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キーが無効化されるリスクもありません。
https://login.tailscale.com/admin/settings/oauth
でOAuth Clientを作成
読み取り専用監査には以下のスコープを付与:
all:read(最も簡単)- または個別に:
policy_file:read, devices:core:read, dns:read, auth_keys:read
環境変数に設定:
1
2
| export TS_OAUTH_CLIENT_ID="..."
export TS_OAUTH_CLIENT_SECRET="tskey-client-..."
|
API Key
API Keyは作成したユーザの権限を継承します。
- https://login.tailscale.com/admin/settings/keys
でAPI Keyを作成
- 環境変数に設定:
1
| export TSKEY="tskey-api-..."
|
基本的な使い方
全項目を監査
最初の実行例:
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パイプラインから盗まれたキーで不正デバイス追加
修正方法:
- 既存のreusable keyを削除
- エフェメラル(一時的)キーに切り替え:
1
2
| # エフェメラルキーを生成(1回のみ使用可能)
tailscale up --authkey tskey-auth-xxx --ephemeral
|
- 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
| tailscale logout --device <device-id>
|
対話的修復モード(–fix)
--fixフラグを使用すると、APIで修復可能な問題を対話的に修正できます。
修復可能な項目:
| チェック | 修復内容 |
|---|
| 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は便利ですが、侵害されると広範囲のネットワークにアクセス可能になります。
対策:
- Stateful Filteringを有効化:
1
| tailscale up --advertise-routes=10.0.0.0/24 --stateful-filtering
|
- Subnet Router自体をセキュリティグループやNACLで保護
SSH autogroup:nonrootの罠
autogroup:nonrootはroot以外の全ユーザへの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を継続的に活用するための運用ガイドラインです。
週次
月次
四半期
退職時(即座)
まとめ
Tailscaleは設計レベルでは従来VPNの多くの弱点を解消していますが、設定ミスによるリスクは依然として存在します。tailsnitchを使えば、「なんとなく不安」を具体的な問題として可視化し、優先度をつけて対処できます。
導入は5分で完了し、CI/CDに組み込めばACL変更のたびに自動チェックが走ります。SOC 2監査の証跡も出力できるため、コンプライアンス対応にも役立ちます。
個人的には、Tailscaleを使っている全ての組織が最低でも月1回はtailsnitchを実行すべきだと思います。Critical/Highの問題がゼロになるまで放置せず、ACLテストを追加して再発を防ぐ——この2つを徹底すれば、VPN侵入による大規模インシデントのリスクを大幅に減らせます。
興味のある方は、まず自分のtailnetでtailsnitchを実行してみてください。思わぬ設定ミスが見つかるかもしれません。
参考リンク