はじめに
脆弱性情報の収集は、CVE一覧を眺めるだけだと運用に乗りにくいところがあります。
特に、以下のような環境では「このCVEは自社のどのサーバーに関係しそうか」を毎回人間が判断する必要があります。
- 本番、検証、踏み台、DB、メールなど役割の違うサーバーが混在している
- OSやミドルウェアの種類が複数ある
- 公式パッチだけではなく、ワークアラウンドも判断対象になる
- CVEの件数が多く、全件を人手で読むと続かない
その上、近年はAIによるcriticalなゼロディ脆弱性の発表も相次ぎ、できるだけ効率的に脆弱性情報を入手し、自社サーバー向けに判定する手段が欲しいと思いました。
そこで、公開されている脆弱性情報をOpenClawのcronで定期取得し、重要度の高いものだけをSlackへ通知する仕組みを作りました。
さらに、サーバーインベントリをMarkdownで持たせることで、通知の末尾に「自社候補」と「重要チェックポイント」も出すようにしました。
この記事では、その設計と実装方針をまとめます。
OpenClawをどのように使えばいいか模索している人にも参考になれば幸いです。
(なお、実装にあたっては、OpenClaw経由でChatGPT5.5を使い、この記事もOpenClawで大半をまとめました)
やりたかったこと
目的は、脆弱性情報の「収集」ではなく「初動判断の短縮」です。
具体的には、以下を自動化したいと考えました。
- CISA KEV、NVD、Ubuntu Security Notices、Amazon Linux、AWS Security Bulletinなどを定期確認する
- Linux / AWS / PHP系に関係しそうなものだけを拾う
- High / Critical 相当だけSlack通知する
- 既に通知したCVEは重複通知しない
- 自社サーバーの役割やミドルウェア情報と照合する
- 通知を見た時点で「まずどのサーバーを見るか」が分かるようにする
通知しすぎると見られなくなるので、通知対象はかなり絞りました。
全体構成
構成はシンプルです。
OpenClaw cron
↓
isolated agent session
↓
Python script
↓
public vulnerability sources
- CISA KEV
- NVD
- Ubuntu Security Notices RSS
- Amazon Linux ALAS RSS
- AWS Security Bulletins RSS
- vendor advisory feeds
↓
filter / classify / deduplicate
↓
server_inventory.md と照合
↓
Slack通知
AIモデルに丸投げして判定させるのではなく、一次判定はPythonで決定的に処理しています。
LLMを使う場合でも、通知文の要約や補助的な判断に寄せるのが安全だと思います。脆弱性通知は誤検知も困りますが、見逃しのほうがもっと困るためです。
OpenClaw側で動いている仕組み
今回のポイントは、単にPythonスクリプトをcronで叩いているだけではなく、OpenClawのcronジョブとして動かしていることです。
OpenClawでは、定期実行ジョブを以下のような形で持たせています。
- 実行タイミング: 4時間ごと
- セッション: isolated
- コンテキスト: lightweight
- モデル設定: 軽めの推論設定
- 利用ツール: 基本的にexecのみ
- 配信先: Slackチャンネル
- 通知条件: 新しいHigh/Critical候補があるときだけ
isolated sessionにしているのは、通常の会話コンテキストに依存させないためです。定期ジョブは毎回同じ前提で動いてほしいので、軽量なbootstrap contextだけを読み、必要なファイルを明示的に参照します。
OpenClawの役割は、次の3つです。
- 決まった時刻にジョブを起こす
- スクリプトを安全な範囲のツールで実行する
- 結果が通知に値するときだけSlackへ届ける
スクリプト側は、Slackへ出すべき内容がない場合は何も投稿しません。たとえば新規のHigh/Critical候補が0件なら、cronは成功扱いですがチャンネルには出しません。
これにより、Slackには「見る価値があるときだけ」流れます。一方で、実行履歴やstateファイルには処理結果を残しているので、あとから「なぜ通知がなかったのか」も確認できます。
サーバーインベントリをMarkdownで持つ
自社のシステム状況をサーバーインベントリにまとめておきOpenClawに教えます。
最初はJSONやYAMLで厳密に書く案もありましたが、運用しやすさや人間が更新しやすいことを優先してMarkdownにしました。
例です。
## web-01
- 用途: 本番Web
- OS: Ubuntu 24.04 LTS
- ミドルウェア: nginx, OpenSSL
- 仮想基盤: KVM
- 公開範囲: Internet-facing
- 重要度: 高
- チェック観点:
- nginx / OpenSSL / glibc / kernel は優先確認
- 外部公開のため、リモートRCEや認証回避は優先度を上げる
## db-01
- 用途: 本番DB
- OS: Ubuntu 24.04 LTS
- ミドルウェア: PostgreSQL
- 公開範囲: 内部のみ
- 重要度: 高
- チェック観点:
- PostgreSQL / OpenSSL / kernel は優先確認
- HA構成の場合は片系ずつ対応する
## worker-01
- 用途: バッチ / 運用ワーカー
- OS: Ubuntu 24.04 LTS
- ミドルウェア: Python, OpenSSH
- 公開範囲: 内部のみ
- 重要度: 中
- チェック観点:
- OpenSSH / Python / glibc / kernel は優先確認
- 権限昇格系CVEは運用アカウントの権限も確認する
ポイントは、項目名だけはすべてきちっと揃えることです。
- 用途
- OS
- ミドルウェア
- 公開範囲
- 重要度
- チェック観点
ここが揃っていれば、Markdownでも十分に機械処理できます。
取得している情報源
現時点では、次の情報源を見ています。
- CISA KEV
- 既に悪用が確認されている脆弱性
- NVD
- CVE全般とCVSS
- Ubuntu Security Notices RSS
- Ubuntu系kernelや主要パッケージの確認
- Amazon Linux ALAS RSS
- Amazon Linux系
- AWS Security Bulletins RSS
- AWS側のサービスやSDK、基盤影響の確認
- vendor advisory feeds
- 各ベンダーの影響有無や修正状況の補足
運用上は、NVDだけを見るよりもベンダー情報を合わせたほうが実用的でした。
NVDは網羅性がありますが、実際に自社で「今日見るべきか」を判断するには、UbuntuやAWSなど、利用環境に近い情報源が効きます。
フィルタリング方針
全部通知すると破綻するので、キーワードと重要度で絞ります。
対象にしたキーワード例です。
LINUX_KEYWORDS = [
"linux",
"kernel",
"ubuntu",
"debian",
"glibc",
"openssl",
"openssh",
"sudo",
"netfilter",
"ebpf",
]
AWS_KEYWORDS = [
"aws",
"amazon linux",
"ec2",
"ecs",
"fargate",
"iam",
"ssm",
"rds",
]
PHP_KEYWORDS = [
"php",
"php-fpm",
"composer",
"laravel",
"symfony",
]
通知対象は原則としてHighとCriticalのみにしています。
また、自社では関係が薄い製品カテゴリは除外しています。たとえばブラウザ単体、モバイルOS、特定のプリンタ製品などです。
このあたりは、最初から完璧にするよりも、通知を見ながら除外ルールを育てるほうが現実的でした。
重複通知を避ける
一度通知したCVEはstateファイルに記録します。
キーは基本的にCVE IDです。CVE IDが取れない場合はURLやタイトルからハッシュを作ります。
{
"seen": {
"CVE-2026-0001": {
"first_seen": "2026-05-23T00:00:00+00:00",
"source": "NVD",
"title": "CVE-2026-0001: ...",
"url": "https://example.com/...",
"severity": "High",
"notified": true
}
}
}
これにより、4時間ごとに回しても同じCVEで何度も通知されません。
インベントリとの照合
今回入れてよかったのが、通知時にサーバーインベントリを見る処理です。
たとえばOpenSSLに関するCVEなら、通知に以下のような行を追加します。
自社候補: web-01, db-01, worker-01
重要チェックポイント: web-01: nginx / OpenSSL / glibc / kernel は優先確認 / db-01: PostgreSQL / OpenSSL / kernel は優先確認
PostgreSQLならDB系、OpenSSHなら踏み台や運用サーバー、kernelなら広めに全Linuxサーバー、というように候補を出します。
精度は100%を目指していません。
ここで大事なのは、通知を見た人が「まずどこを見ればよいか」をすぐ判断できることです。
Slack通知の例
通知はこのような形にしています。
[VULN] Linux/AWS/PHP 高リスク新規情報
概要: CVE-2026-0001 Linux kernel privilege escalation affecting Ubuntu systems
影響対象: Linux / Ubuntu ecosystem
重要度理由: CVSS 8.8 / High
対応要否: 影響有無を確認し、該当する場合はベンダー修正または回避策を適用
参照URL: https://example.com/security/cve/CVE-2026-0001
自社候補: web-01, db-01, worker-01 ほか
重要チェックポイント: web-01: 外部公開のためRCEや認証回避は優先 / db-01: PostgreSQL / OpenSSL / kernel は優先確認
通知本文には、最低限以下を入れています。
- 概要
- 影響対象
- 重要度理由
- 対応要否
- 参照URL
- 自社候補
- 重要チェックポイント
OpenClaw cronで回す
OpenClawのcronジョブから、4時間ごとにスクリプトを実行します。
スクリプト単体では、次のように動きます。
python3 scripts/linux_aws_vuln_watch.py \
--state state/linux_aws_vuln_watch.json \
--inventory server_inventory.md \
--hours 12
OpenClaw側では、この実行をisolated sessionに載せています。
実運用では、ジョブ定義側に以下を持たせます。
- 実行間隔
- 利用するモデルと推論量
- 利用可能なツール
- Slackへの配送先
- 成功時に通知するか、必要時だけ通知するか
取得元の一部がタイムアウトすることはあります。
そのため、1つの情報源が失敗しても全体を止めないようにしています。失敗した情報源はlast_errorsに残し、次回実行で再取得します。
実装で気をつけたこと
1. ひとつの情報源に依存しない
NVDだけ、Ubuntuだけ、AWSだけ、という形にしないようにしました。
それぞれ得意不得意があります。
- NVD: 網羅性は高いが、運用判断には遠いことがある
- Ubuntu Security Notices: Ubuntuパッケージの実運用影響を確認しやすい
- AWS: AWS管理サービスやAmazon Linux系に強い
- CISA KEV: 悪用済みなので優先度判断に強い
2. 通知を増やしすぎない
セキュリティ通知は、多すぎると見られなくなります。
今回の仕組みでは、High/Criticalだけを通知し、それ以外はstateに記録するだけにしています。
OpenClaw cronは正常終了していても、通知対象がなければSlackには出しません。これにより、チャンネルのノイズを抑えられます。
3. 外部公開サーバーと権限の強いサーバーは別扱いにする
同じHighでも、外部公開Webサーバーと内部検証サーバーでは初動の優先度が違います。
そのため、インベントリ側に以下のようなメモを持たせています。
- チェック観点:
- 外部公開のためリモートRCEや認証回避は最優先
- 踏み台や運用サーバーでは権限昇格系CVEを優先確認
この情報が通知に出るだけで、初動の判断がかなり速くなります。
4. Markdownインベントリは「人間が直せる」ことを重視する
最初から厳密なスキーマにすると、更新が面倒になります。
インベントリは古くなると価値が落ちるので、多少ゆるくても人間がすぐ直せる形式のほうが運用に向いていました。
必要になったら、後からYAML front matterや別YAMLに切り出せばよいと思います。
今後やりたいこと
今後は、次のような改善を入れる予定です。
- パッケージの実バージョンもインベントリに持たせる
- 外部公開ポートを明示する
- 検証環境と本番環境の優先度をより明確にする
- CVEごとに「要対応」「要調査」「対象外」を人間が後から記録できるようにする
- 通知後の対応履歴を残す
- 誤検知・過通知の除外ルールを継続的に育てる
まとめ
CVE情報の自動収集は、それだけだとあまり運用に乗りません。
重要なのは、通知を受け取った人がすぐに次の判断をできることです。
今回の仕組みでは、OpenClaw cronで公開CVE情報を収集し、重要度で絞り、重複を避け、自社インベントリと照合してSlackへ通知するようにしました。
特に、Markdownのサーバーインベントリを足したことで「このCVEはまずどのサーバーを見るべきか」が通知内で分かるようになりました。
完璧な自動判定を目指すより、まずは初動を速くする。
脆弱性対応の現場では、このくらいの粒度の自動化がかなり効くと思います。