シリーズ: ホームラボ AI SOC 構築記
前回: AIにホームラボのセキュリティ監視を任せてみた — Sysdig × MCP × n8n で作る個人SOC
はじめに
前回の記事では、Sysdig Secure × MCP × n8n を使って、ホームラボにAI駆動のSOC(Security Operations Center)を構築した話を書きました。脅威検知の自動化は実現できた。では、次のステップは?
脆弱性は検知できる。でも、修正は?
Sysdig Secure が「Critical な脆弱性があります」と教えてくれるのは素晴らしい。でも、通知を受け取って「ふーん」で終わっていたら意味がない。検知 → トリアージ → 優先度判定 → 修正計画 → 承認 → 実行 → 検証。このライフサイクル全体を回す仕組みが必要だった。
結論から言うと、6フェーズ・59タスク・4つの n8n ワークフローを構築し、脆弱性の検知から修正まで一気通貫で自動化した。そして、その過程で「AIに承認ゲートを任せてはいけない」という、身をもって学んだ教訓がある。
「OK やって」 — この一言が、本番環境に未検証のイメージをデプロイしかけた事件の始まりだった。
Before / After — 何が変わったのか
Before: 通知が来ても「あとで見る」
脆弱性管理の "Before" は、多くの組織で見覚えのある光景だろう。
- Sysdig Secure が脆弱性を検出する
- Slack に通知が飛ぶ
- 「あとで確認しよう」と思う
- 他のタスクに追われて忘れる
- 1ヶ月後、同じ脆弱性がまだそこにいる
- 「あれ、これ前も見たような…」
特にホームラボでは、専任のセキュリティチームなどいない。全部一人でやっている。通知は来るが、トリアージする時間も、修正を計画する時間も、実行する時間も限られている。結果として、見逃し・対応漏れ・優先度の判断ミスが常態化していた。
After: 検知から修正まで一気通貫
構築後のフローは、こうなった。
| フェーズ | 処理 | 担当 |
|---|---|---|
| 検知 | Sysdig Automations が Critical + Exploit + Fix ありの脆弱性を検出 | Sysdig Secure |
| 通知 | Slack #security-alerts に構造化通知 |
Sysdig Automations |
| AIトリアージ | OpenClaw が CVSS/EPSS/Exploit/In Use を分析し、優先度を判定 | OpenClaw (AI) |
| 修正計画 | AI が修正内容・リスク評価・ロールバック手順を生成 | OpenClaw (AI) |
| 承認 | Telegram で構造化フォーマットによる承認(正規表現で厳密検証) | n8n MCP ツール |
| 実行 | K8s REST API でイメージ更新(dry-run → 実行) | n8n MCP ツール |
| 検証 | 60秒間のヘルスチェック、異常時は自動ロールバック | n8n MCP ツール |
人間が関与するのは、修正計画の確認と承認の1ステップだけ。しかもその承認は、AIの「なんとなく OK」ではなく、正規表現による厳密なフォーマット検証を通過する必要がある。
アーキテクチャ全体像
構築したシステムは4つのレイヤーで構成されている。上の図の通り、3つのデータフロー(リアルタイム検知・定期レポート/監視・半自動修正)がそれぞれ独立して動作する。
エンタープライズとの対比
「ホームラボでそこまでやるの?」と思うかもしれない。でも、やっていることの骨格はエンタープライズと同じだ。
| 機能 | エンタープライズ | ホームラボ (本構成) |
|---|---|---|
| 脆弱性スキャン | CNAPP / 脆弱性スキャナー | Sysdig Secure |
| トリアージ | セキュリティチーム | OpenClaw AI |
| ワークフロー | ITSM / チケット管理ツール | n8n (OSS) |
| 承認ゲート | 変更管理プロセス (CAB) | n8n MCP ツール (正規表現) |
| パッチ適用 | 構成管理 / IaC ツール | K8s REST API 直接呼び出し |
| SLA 監視 | 専用ダッシュボード | n8n 自動監視 + Telegram |
| レポーティング | SIEM / BI ツール | n8n 週次レポート + Sysdig Reporting |
エンタープライズでは専任チームが回すフローを、Sysdig Secure + OSS の n8n + AI の OpenClaw を組み合わせることで、一人でも同等の脆弱性管理フローが構築できる。これがホームラボの醍醐味だ。
設計プロセス — 112件の指摘と5ラウンドのレビュー
この脆弱性管理フローの要件定義書は、v1.0 から v2.6 まで 16回の改訂 を重ねた。その中で特に効果的だったのが、5ラウンドのレビュープロセスだ。
レビューラウンドと発見件数
| ラウンド | レビュー種別 | 指摘件数 | 主な発見 |
|---|---|---|---|
| Round 1 | 初回レビュー | 17件 | ファイル命名規則違反、セクション構造の不整合 |
| Round 2 | v2.0 レビュー | 21件 | Runtime/Pipeline トリアージ差分の未定義、AI プロンプト未更新 |
| Round 3 | クロスレビュー | 21件 | 依存関係の誤り、変更履歴の順序ミス |
| Round 4 | 再レビュー | 7件 | テスト手順の分岐条件不足、MCP ツール名不統一 |
| Round 5 | 実装リハーサル | 46件 | テスト用脆弱イメージ手順なし、SysQL 構文ミス、ヘルスチェック設計不備 |
| 合計 | 112件 |
「実装リハーサル」という手法
注目すべきは Round 5 の実装リハーサルだ。これは、要件定義書を「実際にこの手順で実装する」という視点で読み直すレビュー手法で、全体の 41%(46件/112件) の指摘がこのラウンドで発見された。
たとえば:
- 「テスト用の脆弱なイメージをデプロイする手順が書かれていない」— テストは書いてあるが、テスト環境の準備手順がない
- 「SysQL の構文が SQL 風になっている」— Sysdig の SysQL は MATCH 構文を使うグラフクエリ言語。SQL じゃない
- 「ヘルスチェックの Phase 2(10分間の安定期監視)の設計が曖昧」— "簡略版" とだけ書いてあって、具体的な実装方式が不明
通常のレビューでは「論理的に正しいか」を見る。実装リハーサルでは「これで本当に手が動くか」を見る。この差分が 46件という数字に表れている。
教訓: 要件定義のレビューは "実装者の目線" で最低1ラウンド入れるべき。
検知から監視まで — 基盤の構築
59タスクのうち44タスクで、検知・レポーティング・SLA監視の基盤を構築した。ここではポイントを絞って紹介する。
Sysdig Automations + AI トリアージ
基盤の核心は、Sysdig Secure の Automations 機能だ。
Automations は、Sysdig Secure 上で脆弱性の検出条件を定義し、条件に一致した場合に自動でアクションを実行する機能。ここでは以下の条件で Slack 通知を設定した:
- Severity: Critical
- Exploit: Public exploit available
- Fix: Fix version available
- 対象: nuc-home クラスタの Runtime 脆弱性
通知を受けた OpenClaw は、AI トリアージを実施する。ここで重要なのが Runtime と Pipeline の判定基準の違い だ:
| 判定要素 | Runtime(nuc-home) | Pipeline(tk-mini1) |
|---|---|---|
| In Use (Risk Spotlight) | ✅ 最優先フィルタ | ❌ 判定不可 |
| CVSS | ○ | ○ |
| EPSS | ○ | ○ |
| Exploit 有無 | ○ | ○ |
| Fix 有無 | ○ | ○ |
Runtime の脆弱性には Sysdig の Risk Spotlight 機能で「In Use」(実際にメモリにロードされているパッケージ)のフィルタが効く。これにより、インストールされているだけで使われていない脆弱なパッケージを除外できる。一方、Pipeline スキャンはポイントインタイムの静的解析なので、In Use の判定ができない。この差分を AGENTS.md のプロンプトに明記することで、OpenClaw が適切にトリアージできるようにした。
週次脆弱性レポート
週次レポートは n8n の独立 Cron ワークフローとして構築した。毎週月曜 10:00 JST に自動実行され、Sysdig REST API から脆弱性データを取得、集計し、Slack と Telegram に通知する。
設計のポイント: 脆弱性データの情報源は Sysdig Secure に一元化(Single Source of Truth)。n8n にはデータを保持しない。前週比較のためのサマリーだけ n8n の Static Data に保存するが、これは脆弱性ステータスではなくメタデータの例外的な保持にとどめている。
CLI Scanner + 定期スキャン
Mac Mini には Sysdig Agent(Linux カーネルモジュール)がインストールできない。代わりに Sysdig CLI Scanner を導入し、ローカルのコンテナイメージを Pipeline スキャンする。
# スキャン実行例
sysdig-cli-scanner --apiurl https://app.sysdigcloud.com \
--json-scan-result /tmp/scan-result.json \
docker.io/library/nginx:1.19.0
スキャン結果は Sysdig Secure にアップロードされ、Pipeline 脆弱性として管理される。日次の定期スキャンは launchd で自動化し、差分があればスキャン結果処理ワークフロー経由で Slack/Telegram に通知する。
SLA 監視
脆弱性には「いつまでに対応すべきか」という SLA(Service Level Agreement)がある。
| Severity | SLA |
|---|---|
| Critical | 7日 |
| High | 30日 |
| Medium | 90日 |
SLA 監視ワークフローは毎日 09:00 JST に、Sysdig REST API で Runtime + Pipeline 両方の脆弱性をチェックし、SLA 超過があれば通知する。Critical の SLA 超過は Telegram にもエスカレーションされる。
構築した n8n ワークフローの全体像がこちらだ。11のワークフローが稼働している。
"OKやって" 事件 — AI承認ゲートの落とし穴
ここからが、この記事のハイライトだ。
半自動修正フローの設計思想
最後の仕上げは、脆弱性の修正を半自動化すること。完全自動ではない。人間が承認するステップを必ず残す。
フローの全体像はこうだ:
Step 1: 修正計画の AI 自動生成
OpenClaw がトリアージ済みの脆弱性情報(CVE ID、影響パッケージ、修正バージョン、CVSS/EPSS スコア)を分析し、修正計画を自動生成する。修正計画には以下が含まれる:
- 修正対象(パッケージ名、現バージョン → 修正バージョン)
- 修正カテゴリ(低リスク: パッチ更新 / 中リスク: マイナー更新 / 高リスク: メジャー更新)
- 影響範囲(ワークロード名、Pod 数、ネームスペース)
- ロールバック手順
Step 2: Telegram で承認を要求
生成した修正計画を Telegram に送信する。高リスクカテゴリ(メジャーバージョン更新)の場合は自動実行せず、手動対応を推奨する。
Step 3: 構造化フォーマットによる承認
ユーザーは 承認: CVE-XXXX-XXXXX / deployment/<name> の構造化フォーマットで返信する。正規表現で厳密に検証し、CVE-ID とワークロード名が修正計画と一致するかも確認する。
Step 4: Dry-run → 本番実行
承認後、まず K8s REST API の ?dryRun=All パラメータで変更内容をプレビューする。問題がなければ本番実行(イメージの PATCH)に進む。同時に修正可能なワークロードは 1 つに制限し、前の修正の検証完了後に次を実行する。
Step 5: ヘルスチェック(60秒間)
修正適用後、5秒間隔で Pod ステータスを K8s REST API で監視する。CrashLoopBackOff、ErrImagePull、ImagePullBackOff などの異常を検出する。全 Pod が Ready になれば成功。
Step 6: 自動ロールバック
ヘルスチェックで異常を検出した場合、またはタイムアウト(60秒)した場合、K8s REST API で元のイメージに自動ロールバックする。ロールバック自体が失敗した場合は緊急通知を送信する。
さらに、修正対象はホワイトリストで制限している。ネームスペースとワークロード名を事前に登録しておかないと、いくら正しいフォーマットで承認しても not_whitelisted で拒否される。意図しないワークロードへの修正を防ぐ安全装置だ。
設計段階では「AIに承認テキストを解釈させれば、柔軟に対応できるだろう」と考えていた。AGENTS.md(AI のプロンプト)に承認フォーマットを定義し、フォーマットに一致しない入力は拒否するよう指示する。合理的に聞こえる。
これが間違いだった。
"OKやって" — 何が起きたか
E2E テストで、以下のシナリオをテストした:
- テスト用の脆弱なイメージ(
nginx:1.19.0)をデプロイ - OpenClaw に「nginx の脆弱性を修正して」と依頼
- OpenClaw が修正計画を生成し、Telegram に送信
- ユーザーが 「OKやって」 と返信
- 期待する動作: 「正しいフォーマットで承認してください」とガイダンスを表示
実際に起きたこと: OpenClaw は「OK やって」を承認と解釈し、kubectl で nginx:1.19.10 へのイメージ更新を実行した。
構造化された承認フォーマット(承認: CVE-2021-23017 / deployment/vuln-test-nginx)を経由せずに、曖昧な日本語の一言で本番相当の操作が実行された。
なぜ起きたのか — AIの会話的性質 vs 構造化検証
根本原因はシンプルだ。AIは会話的な存在であり、人間の意図を "いい感じに" 解釈するのが仕事 だからだ。
AGENTS.md に「承認は 承認: CVE-XXXX / deployment/<name> のフォーマットで受け付けること」と書いても、AIにとってそれは「参考情報」に過ぎない。「OK やって」という入力を受け取ったとき、AIは以下のように推論する:
- ユーザーは直前の修正計画に対して返信している
- 「OK」は肯定の意思表示
- 「やって」は実行の依頼
- したがって、修正を承認している
論理的に正しい。しかし、セキュリティの観点では致命的に間違っている。
承認ゲートに必要なのは、「ユーザーの意図を解釈する」ことではない。「決められたフォーマットに厳密に一致するかを検証する」 ことだ。これは AI の得意分野ではなく、正規表現の得意分野だ。
解決策 — n8n MCP ツールへの移管
解決策は明確だった。承認フォーマットの検証と K8s の修正実行を、AI から n8n の MCP ツールに移管する。
具体的な変更内容:
| 機能 | 変更前 (AI ベース) | 変更後 (コードベース) |
|---|---|---|
| 承認フォーマット検証 |
AGENTS.md のプロンプト |
正規表現(n8n MCP ツール) |
| K8s 操作 | kubectl 直接実行 | K8s REST API(n8n MCP ツール) |
| ヘルスチェック | kubectl get pods | K8s REST API(Pod ステータス監視) |
| ロールバック | kubectl rollout undo | K8s REST API(イメージ PATCH) |
正規表現による厳密なフォーマット検証
const approvalRegex = /^承認:\s*(CVE-\d{4}-\d+)\s*\/\s*deployment\/([\w.-]+)$/;
const rejectionRegex = /^(拒否|却下)$/;
const text = input.approval_text.trim();
if (approvalRegex.test(text)) {
// CVE-ID と deployment 名がリクエストと一致するかも検証
const match = text.match(/承認:\s*(CVE-\d{4}-\d+)\s*\/\s*deployment\/([\w.-]+)/);
if (match[1] !== input.cve_id || match[2] !== input.deployment) {
return { status: "format_error", reason: "CVE-ID or deployment name mismatch" };
}
return { status: "approved" };
} else if (rejectionRegex.test(text)) {
return { status: "rejected" };
} else {
return { status: "format_error", reason: "invalid_format" };
}
この正規表現は曖昧さがゼロだ。「OK やって」「承認します」「いいよ」— どれも format_error として拒否される。承認するには、CVE-ID と deployment 名を含む完全なフォーマットを入力する必要がある。
kubectl 除去 → K8s REST API 直接呼び出し
もう一つの重要な変更が、OpenClaw から kubectl の実行権限を完全に除去したことだ。
変更前は、OpenClaw が kubectl set image で直接イメージを更新していた。つまり、AI がプロンプトのルールを無視すれば(「OK やって」事件のように)、何でも実行できてしまう状態だった。
変更後は:
- OpenClaw はユーザーの返信テキストを n8n MCP ツール
execute_remediationに渡すだけ - 実際の K8s 操作は n8n のコード内で実行(AI は関与しない)
- K8s API への認証は ServiceAccount
n8n-remediationの最小権限 RBAC
ユーザー → Telegram → OpenClaw (AI) → n8n execute_remediation (コード)
│
├── 正規表現で承認検証
├── ホワイトリストで対象検証
├── K8s REST API: dry-run
├── K8s REST API: 実行
├── K8s REST API: ヘルスチェック (60s)
└── 異常時: K8s REST API: ロールバック
AI の役割は「修正計画を生成する」ことと「ユーザーの返信を受け取って n8n に渡す」こと。判断と実行はすべてコードが担う。
テスト結果
n8n MCP ツールへの移管後、以下の 7つのテストシナリオを全て Pass:
| テスト | 入力 | 期待結果 | 結果 |
|---|---|---|---|
| 曖昧な承認 | 「OK やって」 | format_error | ✅ Pass |
| 不完全な承認 | 「承認します」 | format_error | ✅ Pass |
| CVE-ID 不一致 | 「承認: CVE-9999-9999 / deployment/vuln-test-nginx」 | format_error | ✅ Pass |
| 正しい拒否 | 「拒否」 | rejected | ✅ Pass |
| ホワイトリスト外 | 正しいフォーマットだが対象外 NS | not_whitelisted | ✅ Pass |
| 正常修正 |
nginx:1.19.0 → 1.19.10
|
success | ✅ Pass |
| ロールバック | 存在しないイメージへの更新 | rollback (ErrImagePull) | ✅ Pass |
「OK やって」は、もう二度と通らない。
技術的深掘り — 構築で直面した壁
AGENTS.md 20,000バイト制限との戦い
OpenClaw の AGENTS.md(AI への指示プロンプト)には 20,000バイトの上限がある。SOC 運用のプロンプトで既に 11,000バイトを使っていたため、脆弱性管理で使える予算は約 9,000バイトだった。
59タスク分のルールと手順を 9,000バイトに収めるのは至難の業だ。解決策として:
-
AGENTS.mdはルールの要約のみ(〜2,000バイト) -
SOC-REFERENCE.mdに詳細テンプレートを分離(制限なし) - OpenClaw は必要に応じて
SOC-REFERENCE.mdを参照する
最終的に AGENTS.md は 19,607バイト(残り 393バイト)で収まった。ギリギリだ。
n8n CE の制限とワークアラウンド
n8n Community Edition (CE) 2.10.4 にはいくつかの制限がある。当初、承認ゲートを Webhook トリガー方式で実装する予定だったが、n8n CE では API 経由で Webhook ワークフローをアクティベートできないことが判明。
代替案として、MCP Integration の toolCode 方式を採用した。OpenClaw MCP Integration ワークフロー内に execute_remediation という toolCode ノードを追加し、全ロジック(承認検証、ホワイトリスト、K8s API 呼び出し、ヘルスチェック、ロールバック)を単一のコードブロックに実装した。
結果的に、この方式の方がシンプルで保守しやすかった。
cron 消失問題
OpenClaw の cron ジョブはメモリ上で管理されるため、ゲートウェイの再起動で消失する。これは SOC 運用構築時にも遭遇した問題で、以下の対策を実施:
- バックアップスクリプトで cron 設定をエクスポート
- ゲートウェイ再起動後に確認、必要に応じて再登録
- 重要な定期処理(週次レポート、SLA 監視)は n8n の Schedule Trigger で管理(cron 消失の影響を受けない)
成果と数字
定量的成果
| 指標 | 値 |
|---|---|
| 総タスク数 | 59 |
| 完了タスク | 59/59 (全フェーズ完了) |
| n8n ワークフロー | 4つ (週次レポート, スキャン結果処理, SLA 監視, 半自動修正) |
| 要件定義書バージョン | v1.0 → v2.6 (16回改訂) |
| レビュー指摘件数 | 112件 (5ラウンド) |
AGENTS.md サイズ |
19,607 / 20,000 bytes |
| ヘルスチェック応答 | 60秒以内 |
| ロールバック応答 | 約2秒 (ErrImagePull 検知 → ロールバック完了) |
冒頭のアーキテクチャ全体像の通り、3つのデータフロー(リアルタイム検知・定期レポート/監視・半自動修正)が独立して稼働し、検知から修正までを一人で回せる体制が整った。
今後の展望
- ヘルスチェックの高度化: 現在は Pod ステータス(Ready/CrashLoopBackOff 等)の確認のみ。HTTP エンドポイントの応答確認やリソース使用量の監視など、アプリケーションレベルのヘルスチェックを追加したい
- 月次トレンドレポート: 脆弱性数の推移を可視化し、改善傾向を追跡
おわりに
「AI に任せれば大丈夫」— この思い込みが、"OKやって" 事件を引き起こした。
AI は素晴らしいトリアージ能力を持っている。修正計画の生成、リスク評価、自然言語による脆弱性照会。これらは AI の得意分野であり、積極的に活用すべきだ。
しかし、セキュリティゲート(承認検証)を AI に任せてはいけない。AI は会話的な存在であり、「いい感じに」解釈するのが仕事だ。「OK やって」を承認と解釈するのは、AI としては正しい判断だ。でも、セキュリティとしては致命的な判断だ。
AI は「考える」ために使い、「検証する」にはコードを使う。この原則は、ホームラボだけでなく、エンタープライズの AI 活用においても普遍的な教訓だと思う。







