メインコンテンツへスキップ
← ブログに戻る
開発ログ2026年3月12日by K.hirano

WazuhとClaude Codeを繋いだら、CVE調査と修正提案が自動化できた【自宅ラボ セキュリティ実録 #3】

Wazuh REST APIでCVEリストを取得し、Claude Codeに渡したら調査と修正コマンド生成が自動化できた。93件のCriticalを手動で追っていた状態からの脱出実録。

#Wazuh#Claude Code#CVE#セキュリティ#自動化#homelab#Python#RaspberryPi

この記事で分かること

  • WazuhのREST APIでCritical CVEリストをJSONで取得する手順
  • JSONをClaude Codeに渡して「即対処 / 無視可」を自動分類させるプロンプト設計
  • cronで毎週自動実行する仕組みの作り方
  • Wazuh 4.14.x以降のAPIエンドポイント廃止への対処法(OpenSearch直接クエリ)

目次

1. WazuhのREST APIを使ってみる

1-1. JWT認証トークンの取得

WazuhのREST APIにはJWT認証(JSON Web Token: リクエストごとに発行される短命の認証トークン)が必要だ。まずトークンを取る。

bash
# JWTトークン取得(RasPi5でWazuh Manager稼働中)
TOKEN=$(curl -s -X POST \
  "https://localhost:55000/security/user/authenticate?raw=true" \
  -u admin:PASSWORD -k)
echo $TOKEN

-k は自己署名証明書を許可するオプション。自宅ラボなので割り切っている。このトークンの有効期限は900秒(15分)。「15分で切れるの不便じゃないか」と思う人もいると思うが、スクリプト内で都度取得すれば問題ない。後述する。

1-2. エージェント別にCVEリストを取得

うちのWazuh構成はRasPi 5にWazuhを入れたら、Dockerが死んだ話——自宅セキュリティ監視の実録BlogRasPi 5にWazuhを入れたら、Dockerが死んだ話——自宅セキュリティ監視の実録RasPi 5(ARM64)にWazuhをDocker Composeで構築しようとしたらIndexerが落ちて失敗。直接インストールに切り替えた実録。3台を監視したらCritical脆弱性が203件出てきた話。に書いた通り、RasPi5をManagerとして、WorkStation(008)・AIServer(009)・Win11(010)の3台をAgentにしている。

bash
# エージェント一覧確認
curl -s -X GET "https://localhost:55000/agents" \
  -H "Authorization: Bearer $TOKEN" -k \
  | jq '.data.affected_items[] | {id, name, ip}'

# エージェント008(WorkStation)のCritical脆弱性を取得
curl -s -X GET \
  "https://localhost:55000/vulnerability/008?severity=Critical&limit=100&offset=0" \
  -H "Authorization: Bearer $TOKEN" -k | jq .

severity=Critical で絞ることで、High以下のノイズを排除できる。limit は最大500まで指定可能。

⚠️ Wazuh 4.14.x 以降の注意 /vulnerability/{agent_id} エンドポイントは 4.14.x で廃止された。 代わりに OpenSearch インデックスへ直接クエリする方法を使う(後述)。

1-3. 実際のJSONレスポンス(構造の説明)

APIが返すJSONはこういう構造になっている。

json
// wazuh-states-vulnerabilities-raspi5 から取得(上位5件、CVSSスコア降順)
{
  "data": {
    "affected_items": [
      {
        "cve": "CVE-2025-25467",
        "name": "libavformat60",
        "version": "7:6.1.1-3ubuntu5+esm7",
        "severity": "Critical",
        "cvss3_score": 9.8,
        "title": "libx264 memory tracking vulnerability — arbitrary code execution via crafted AAC file"
      },
      {
        "cve": "CVE-2023-3326",
        "name": "libnss-sss",
        "version": "2.9.4-1.1ubuntu6.3",
        "severity": "Critical",
        "cvss3_score": 9.8,
        "title": "pam_krb5 KDC response validation bypass — authentication bypass for any user"
      },
      {
        "cve": "CVE-2023-3326",
        "name": "sssd-krb5",
        "version": "2.9.4-1.1ubuntu6.3",
        "severity": "Critical",
        "cvss3_score": 9.8,
        "title": "同上(sssd-krb5パッケージ)"
      },
      {
        "cve": "CVE-2025-25467",
        "name": "libx264-164",
        "version": "2:0.164.3108+git31e19f9-1",
        "severity": "Critical",
        "cvss3_score": 9.8,
        "title": "libx264 memory tracking vulnerability(本体)"
      },
      {
        "cve": "CVE-2025-25467",
        "name": "libavutil58",
        "version": "7:6.1.1-3ubuntu5+esm7",
        "severity": "Critical",
        "cvss3_score": 9.8,
        "title": "libx264 memory tracking vulnerability(libavutil58)"
      }
    ],
    "total_affected_items": 152
  }
}

total_affected_items で件数を確認できる。今回は152件。condition フィールドに「どのバージョン未満が対象か」が書いてあるのが便利で、「対象バージョンを超えるパッケージをインストールすれば直る」という判断がそこで即座にできる。

参考: CVE-2025-25467(NVD) / CVE-2023-3326(NVD)

1-4. Wazuh 4.14.x以降: OpenSearch直接クエリ

Wazuh 4.14.x では /vulnerability/{agent_id} が廃止されたため、OpenSearch(Wazuhが内部で使用する分散検索エンジン)のインデックスを直接クエリする必要がある。インデックス名はホスト名を含む形式(例: wazuh-states-vulnerabilities-raspi5)。

bash
curl -s -X GET 'https://localhost:9200/wazuh-states-vulnerabilities-raspi5/_search' \
  -u admin:PASSWORD -k \
  -H 'Content-Type: application/json' \
  -d '{
    "size": 5,
    "query": {"term": {"vulnerability.severity": "Critical"}},
    "sort": [{"vulnerability.score.base": {"order": "desc"}}],
    "_source": ["vulnerability.id", "package.name", "package.version",
                "vulnerability.score.base", "vulnerability.description"]
  }'

9200 は OpenSearch のデフォルトポート。接続には Wazuh と同じ admin 認証情報を使う。

参考: OpenSearch Query DSL(公式ドキュメント)

2. JSONをClaude Codeに渡す方法

2-1. fetch-wazuh-cves.sh スクリプト

毎回手打ちするのは面倒なので、CVEリスト取得を1コマンドにまとめた。

bash
#!/bin/bash
# fetch-wazuh-cves.sh
# WazuhからCritical CVEリストを取得してJSONファイルに保存
set -e

WAZUH_HOST="localhost:55000"
WAZUH_USER="admin"
AGENT_ID="${1:-008}"
OUTPUT_FILE="cve-report-$(date +%Y%m%d).json"

# JWT取得(都度取得でトークン切れを防ぐ)
TOKEN=$(curl -s -X POST \
  "https://${WAZUH_HOST}/security/user/authenticate?raw=true" \
  -u "${WAZUH_USER}:${WAZUH_PASS}" -k)

# Critical CVE取得
curl -s -X GET \
  "https://${WAZUH_HOST}/vulnerability/${AGENT_ID}?severity=Critical&limit=500" \
  -H "Authorization: Bearer $TOKEN" -k \
  | jq '.data.affected_items' > "$OUTPUT_FILE"

echo "保存完了: $OUTPUT_FILE ($(jq length "$OUTPUT_FILE")件)"

WAZUH_PASS はスクリプトにハードコードしていない。WAZUH_PASS=xxx bash fetch-wazuh-cves.sh 008 のようにシェル変数として渡す。パスワードをコード中に書くのは20年エンジニアとして体が拒否する。

2-2. Claude Codeへのプロンプト設計

JSONを渡す前に、プロンプトの設計が肝心だ。「分析して」だけ渡すと、Claude Codeは律儀にすべてのCVEを丁寧に解説し始める。それをやられると逆に読む気がなくなる。

自分が欲しかったのは「今週これだけ対処すれば十分」というリスト。そのためのプロンプトがこれだ。

terminal
これはWazuh(SIEM: Security Information and Event Management)が検出したCritical CVEリストです。
以下の観点で優先度分類と対処コマンドを生成してください。

優先基準:
1. 即対処: CVSS(脆弱性深刻度スコア)>= 9.0 かつ攻撃経路がネットワーク(AV:N)
2. 待機: パッチ未リリース または影響限定的
3. 無視可: 当該サービスを使っていない(例: Kerberos, SNMP等)

出力形式:
- 優先度別の表(CVE ID / パッケージ / 理由 / 対処コマンド)
- 即対処分の apt コマンド(コピペ可能な形式)
- 「無視可」判断の根拠を明示すること

[JSONデータをここに貼り付け]

「無視可」の根拠明示がポイントだ。以前は「Kerberosのパッケージに脆弱性がある」と書いてあるのを見て「Kerberos……うちのWSSで動いてたっけ?」と10分悩んでいた。Kerberos認証なんて使っていないのに。Claude Codeに「使っていないサービス名を列挙して無視可と判断せよ」と指示すると、その根拠が明示されて判断の迷いが消えた。

参考: Wazuh REST API リファレンス

2-3. 実際のClaudeの出力

実際に上記JSONを渡したところ、以下の出力が返ってきた。

優先度CVEパッケージ判断理由対処
即対処CVE-2025-25467libx264-164, libavformat60, libavutil58CVSS 9.8・細工されたメディアファイルで任意コード実行・ffmpeg経由で悪用可能性ありsudo apt upgrade libx264-164 libavformat60 libavutil58
無視可CVE-2023-3326libnss-sss, sssd-krb5Kerberos/SSSD認証未使用・RasPi5はローカル認証のみ・ネットワーク経由の到達経路なし対処不要

即対処コマンド(コピペ可能):

bash
sudo apt update && sudo apt upgrade libx264-164 libavformat60 libavutil58

表の下に「即対処分のaptコマンド一覧」がまとめて出力される。それをコピーして実行するだけ。「判断を人間がして、コマンド生成をAIに任せる」——この分担が自分には合っている。

CVE-2023-3326 の「無視可」判断は的確だった。Kerberos認証は使っておらず、SSSD(System Security Services Daemon)もローカルユーザー管理だけで使っているケースでは、ネットワーク越しの悪用経路がない。Claude Codeはこの根拠を自動で明示してくれた。

3. スクリプト化(定期実行)

週1でこれを手動で叩くのもいいが、どうせならcronで自動実行してしまう。

bash
#!/bin/bash
# check-cves-with-claude.sh
# 使い方: WAZUH_PASS=xxx bash check-cves-with-claude.sh [agent_id]

set -e
AGENT_ID="${1:-008}"
DATE=$(date +%Y%m%d)
JSON_FILE="/tmp/cve-${DATE}.json"
PROMPT_FILE="/tmp/cve-prompt-${DATE}.txt"

# 1. CVEリスト取得
bash ~/scripts/fetch-wazuh-cves.sh "$AGENT_ID"
mv "cve-report-${DATE}.json" "$JSON_FILE"

# 2. プロンプト構築
cat > "$PROMPT_FILE" <<EOF
$(cat ~/.wazuh/cve-analysis-prompt.txt)

$(cat "$JSON_FILE")
EOF

# 3. Claude Codeで分析
claude --print < "$PROMPT_FILE"

crontab登録例:

terminal
# 毎週月曜 9:00 にCVEレポートを生成
0 9 * * 1 WAZUH_PASS=xxx bash ~/scripts/check-cves-with-claude.sh 008 > ~/logs/cve-report.log 2>&1

一点、注意。WAZUH_PASS=xxx をcrontabに直書きするのは、crontab -l の出力がログに残るケースで問題になる。より安全な方法は .env ファイルを作って source する方式だ。

bash
# ~/.wazuh/.env
WAZUH_PASS=xxx

# crontab
0 9 * * 1 source ~/.wazuh/.env && bash ~/scripts/check-cves-with-claude.sh 008 > ~/logs/cve-report.log 2>&1

.env ファイルは chmod 600 で自分だけ読めるようにしておく。これくらいの対策は最低限やっておく。

ちなみに SSH 鍵のほうはパスワードも SSH 鍵も、自宅に置いておく——Vaultwarden + Bitwarden SSH Agent で作る個人セキュリティ基盤Blogパスワードも SSH 鍵も、自宅に置いておく——Vaultwarden + Bitwarden SSH Agent で作る個人セキュリティ基盤クラウドパスワードマネージャーへの不安と ~/.ssh/ ファイル管理の煩雑さを、Vaultwarden(RasPi 5 Docker)と Bitwarden SSH Agent の組み合わせで解決した実録。GNOME Keyring の競合解消・毎回確認ダイアログによるプロンプトインジェクション対策・snap バージョン非依存のソケット設定まで解説。で保管庫管理に移していて、~/.ssh/ に秘密鍵ファイルを置くのをやめた。秘密の置き場所を減らすという意味では同じ話だ。

4. Before / After

この方法に切り替えて3週間ほど経つ。実際にどう変わったかを正直に書く。

指標Before(手動)After(Claude Code連携)
週あたり調査時間20〜30分3〜5分
CVE 1件あたりの作業NVDを開いて手動検索自動分類+コマンド生成
見落としリスク高(疲れると雑になる)低(全件スキャン)
「無視可」の根拠曖昧(なんとなく)明示(使っていないサービス名)
新しいCVEの検出次にWazuhを開いたときcronで自動検出

数字は正確ではないが、体感はこれに近い。特に「無視可の根拠が明示される」のは想定以上に価値があった。「なんとなく対処しなかった」から「理由があって対処しなかった」に変わる。後から見返したときの安心感が違う。

3週間で手動確認は2回しかしていない。残り10回はレポートを一読してaptコマンドを実行するだけで済んだ。自分にとって「続けられる運用」とはこういうものだと思っている。

デメリットも書く。CVEのJSONにはホストのパッケージ名・バージョンが含まれる。それをClaudeに送ることへの懸念は理解できる。自分は「CVEはすべて公開情報で、パッケージ名は攻撃者にとって大して有益ではない」と判断したが、これはユーザー自身が決めることだ。claude.ai/cloudへの送信になる点は把握した上で使っている。

5. まとめ

この自動化の土台になっている監視環境そのもの(RasPi 5×Wazuhの4.5ヶ月運用実測)は Linux向け無料EDRは実質Wazuh一択だった — 自宅RasPi 5で4.5ヶ月運用した本音と実測BlogLinux向け無料EDRは実質Wazuh一択だった — 自宅RasPi 5で4.5ヶ月運用した本音と実測個人のLinuxで無料EDR的監視はWazuhが現実的。RasPi 5で4台を4.5ヶ月運用した実測と、商用EDRとの違いを整理します。 で全体像を書いています。

Wazuhを入れたら脆弱性が3,247件検出された話【RasPi 5 自宅ラボ セキュリティ実録 #2】BlogWazuhを入れたら脆弱性が3,247件検出された話【RasPi 5 自宅ラボ セキュリティ実録 #2】Wazuhの脆弱性ダッシュボードに「3,247件」と表示された。焦ったが、分解してみると数字の正体はシンプルだった。旧カーネル・謎のThunderbird・CVSS 9.8の正体——自宅ラボ実録。で「管理できている状態を維持することが目的」と書いた。その言葉は今も変わっていない。

ただ、「管理できている状態」のコストを下げることはできた。週30分が週5分になれば、続けられる確率が上がる。セキュリティ対策が続かない最大の理由は「面倒になること」だと思っている。

Claude Codeは調査を加速してくれた。判断は自分でした。「このCVE、対処すべきか」は最終的に自分が決めた。それでいいと思っている。AIに判断まで投げると、なぜそうなったのかがわからなくなる。

次は、SlackへのアラートBot連携と、AIServer(009)・Win11(010)を同様のフローに乗せることを考えている。Win11は別途Agentの挙動確認が必要になるが、それはまた別の話だ。

現時点では raspi5(RasPi5/Manager 兼 Agent)のみ脆弱性インデックスが生成されている。WorkStation(008)・AIServer(009)・Win11(010)は Agent 設定完了後、脆弱性スキャンが走るまでインデックスが生成されない。これも順次対応予定だ。

よくある質問

Q. Wazuh APIのJWT認証、15分で切れるのは不便では?

スクリプト内で TOKEN=$(...) を毎回実行すれば問題ない。自分のスクリプトはすべてこの形にしている。関数化すると見通しがよくなる。

Q. CVEの詳細をClaude Codeに渡して情報漏洩のリスクは?

CVEはすべて公開情報だ。含まれるのは自ホストのパッケージ名とバージョンだが、それ自体が直接のリスクかどうかはユーザーが判断する話。「claude.ai/cloudに送信される」という点は認識した上で使うべきで、気になる人はオンプレミス環境のClaude APIを使う選択肢もある。

Q. VPSやクラウド環境でも同じ方法は使える?

Wazuh AgentはほぼすべてのLinuxディストリビューションに対応している。Managerが別ホストにある場合は、APIのアクセス先を変えるだけだ。クラウド環境ではManagerへのAPIアクセスにセキュリティグループやファイアウォール設定が別途必要になる。自宅ラボより設定の手間は増えるが、仕組みは同じ。

関連記事

この記事を書いた人

HW系エンジニアとして20年以上、10,000件を超える顧客訪問と2,000件を超える単独ソリューション実績。AIツールを使った個人開発やIoT農園など、Raspberry Piを使ったオートメーション化なども実践中です!エンジニア専門結婚相談所も運営中、ClaudeCodeで解決できない心の課題も解決いたします!

Claude Codeを10時間放置で実測:177k再読は空ターン何回分か
2026年9月3日
開発ログ

Claude Codeを10時間放置で実測:177k再読は空ターン何回分か

約10時間放置したセッションの再開で 177k トークンが再書き込みされた実測を起点に、Claude Code で1時間キャッシュを空ターンで温め続ける価値を API 単価とサブスク枠の両面から損益分岐で計算しました。

#Claude Code#Claude#Anthropic#LLM#コスト削減
effort を途中で変えるとキャッシュはどうなる? Fable 5.1 と Opus 5 で実測した
2026年9月3日
開発ログ

effort を途中で変えるとキャッシュはどうなる? Fable 5.1 と Opus 5 で実測した

Fable 5.1 の値下げはキャッシュ読みだけ。Claude Code が TTL を決める仕組み、キャッシュを壊す操作と壊さない操作、effort 切替時の再書き込みを Fable 5.1 と Opus 5 の usage で実測しました。

#Claude Code#Claude#Anthropic#LLM#コスト削減
Fable 5.1 は『low で旧 max 超え』なのか|公式グラフ5枚をベンチ別に正直に読む
2026年9月3日
開発ログ

Fable 5.1 は『low で旧 max 超え』なのか|公式グラフ5枚をベンチ別に正直に読む

Claude Fable 5.1 の公式グラフ5枚をベンチ別に読み、low が旧 Fable 5 の max を超えたベンチと超えなかったベンチを表にしました。常用 effort の決め方と Pro/Max の課金条件も整理します。

#Claude#Claude Code#Anthropic#LLM#ベンチマーク
月$200のClaude Codeを$138にした2日間 — z.ai GLM移行で踏んだ罠と、規約の本当の分かれ目
2026年8月27日
開発ログ

月$200のClaude Codeを$138にした2日間 — z.ai GLM移行で踏んだ罠と、規約の本当の分かれ目

Claude Code の接続先を z.ai の GLM へ差し替え、月$200を$138にした2日間の実測記録。踏んだ罠7つ、reasoning effort が外部バックエンドでも効くかの実測、そして「サブスク枠なら白」が成り立たない理由を公式ページの実読から整理します。

#Claude Code#GLM#LLM#コスト削減#実機検証

META-MARK × AI

ローカルAIを動かすGPU、ちゃんと選べていますか?

VRAM・性能・コスパをMetaScoreで数値化。AIアプリ別の推奨ハードウェア要件も確認できます。