侵害される可能性のある DMZ 環境で「Agent を信頼しない」前提の設計
1. はじめに — DMZ にはセキュリティ監視の「死角」がある #
クラウド事業者やホスティング事業者にとって、公開Webサーバーやリバースプロキシといった DMZ(非武装地帯) のサーバーは、最も狙われやすい場所です。
このような場所でも syslog 転送でログを集約してくる構成は珍しくありません。しかし、ここに重要な前提があります:
BASTION の DMZ Agent は、この「Agent そのものが侵害されるかもしれない」という前提に立って設計されています。本記事では、その「Agent 非信頼モデル」と、それを支える検証エンジンの設計を解説します。
2. なぜ既存の EDR や SIEM Agent では不十分なのか #
EDR(Endpoint Detection & Response) や、SIEM 製品の Agent 機能でも、エンドポイントの異常検知は可能です。ただ、これらの製品の多くは次の前提に立っています:
- Agent は信頼できる(=サーバー側で発行した署名済みバイナリだから)
- Agent から送られてくるイベントは正しい(=Agent内部で検証済みだから)
- Agent が静かなら、そのサーバーは安全(=異常なら Agent が通知するはずだから)
これらは平時には有効な前提ですが、侵害された DMZ サーバーでは全て破綻します:
- 署名済みバイナリでも、root 権限を取られたら Agent プロセス自体を改ざんできる
- Agent 内部のイベント検証ロジックを攻撃者が把握すれば、検証を通過する「偽イベント」を生成できる
- Agent を kill して別の偽 Agent を起動すれば「正常な静寂」を演出できる
BASTION は、これらの抜け穴を全て塞ぐ設計です。
3. Agent 非信頼モデル — 3つの責務分離 #
BASTION の Agent と中央サーバー(AI-SLOG)の関係は、次の3つの責務に明確に分離されています。
| 責務 | 担当 | 理由 |
|---|---|---|
| ログ収集と1次イベント生成 | Agent (DMZ側) | 低遅延でリアルタイム性が重要 |
| イベント検証と判定 | AI-SLOG (内部側) | 侵害不可能な場所で行う必要がある |
| 防御アクション(ブロック) | Agent (DMZ側) | 該当サーバー上でしかブロックできない |
ポイントは、「検知」と「検証」が物理的に分離されていることです。Agent が「攻撃を検知した」と通知してきても、AI-SLOG はそれをそのままでは信じません。必ず別経路で同じサーバーから直接吸い上げた生ログを使って独立に検証します。
DMZ サーバー AI-SLOG (内部)
┌──────────────┐ ┌──────────────┐
│ Agent │── WebSocket ──→│ 受信 │
│ ・ログ監視 │ (イベント) │ │
│ ・1次検知 │ │ ↓ │
│ │ │ 検証エンジン │←─ 生ログ
│ ・ブロック │←── WebSocket ──│ ↓ │ (rsyslog経由)
│ 実行のみ │ (block 命令) │ 命令送信 │
└──────────────┘ └──────────────┘
↑ ↑
Agent が嘘をついても 生ログと照合して
ブロック以上のことはできない 不一致ならイベント破棄4. 検証エンジン — Agent イベントを生ログで照合する #
検証エンジンは、Agent から届いたイベントが「事実」かどうかを独立に確認する仕組みです。
Agent からのイベント例(攻撃検知の通知):
{
"agent_id": "dmz-web-01",
"event_type": "vuln_scan_detected",
"src_ip": "203.0.113.42",
"target": "/.env",
"timestamp": "2026-05-13T10:23:45+09:00",
"evidence_lines": [
"203.0.113.42 GET /.env HTTP/1.1 404",
"203.0.113.42 GET /.git/config HTTP/1.1 404",
"203.0.113.42 GET /wp-admin HTTP/1.1 404"
]
}このイベントを受け取った AI-SLOG は、以下のステップで検証します:
- 生ログの取得: 該当 DMZ サーバーの Apache/Nginx ログを、rsyslog 経由で別途取得済み
- 該当タイムスタンプ周辺の検索: イベントの timestamp ±30秒の範囲で同一 IP のリクエストを検索
- evidence_lines との照合: Agent が言っている3行が、実際のログに存在するか確認
- 不一致なら破棄: 1行でも一致しなかったら、このイベントは「捏造の可能性」として破棄、警告通知
Agent が誠実に動作していれば、evidence_lines は実際のログと一致するので問題なく検証通過します。Agent が攻撃者に乗っ取られて偽イベントを送ってきても、生ログとの照合で容易に弾けます。
重要なのは、生ログ取得経路と Agent イベント経路が完全に分離されていることです。rsyslog 経由のログ転送は Agent プロセスとは別の経路で動いており、Agent を制御下に置いた攻撃者でも、内部側の rsyslog 受信を改ざんできません。
5. Agent の権限設計 — できることを最小限に #
Agent そのものに与える権限も、極限まで絞っています。
| 操作 | 権限 | 備考 |
|---|---|---|
| IPブロック(ufw deny等) | 許可 | 該当サーバーの防御のため必要 |
| IPブロック解除(ufw delete等) | 禁止 | 攻撃者による自己解除の防止 |
| 設定ファイル編集 | 禁止 | config.yaml の書き換え攻撃を防止 |
| 外部 shell コマンド実行 | 禁止 | config.yaml の allowed_commands でホワイトリスト化 |
| 他サーバーへのアクセス | 禁止 | 横展開の防止 |
特に重要なのは、「ブロック解除を Agent ができない」という設計です。攻撃者が Agent を制御下に置いても、自分のIPブロックを解除して再侵入することはできません。
では、誤検知でブロックされた正規ユーザーはどうやって解除されるのか? それは、後述する「24時間自動失効」と、AI-SLOG 側からの手動解除コマンドの2系統で対応します。
6. ハートビート凍結 — 失活時の安全装置 #
「Agent が静かなら安全」という前提は信用しない、と書きました。では実際に Agent が止まっている状態を、BASTION はどう扱うのか?
各 Agent は定期的にハートビートを AI-SLOG に送信します。これが一定時間途絶えた時、BASTION は次の動作を取ります:
- 該当 Agent からの新規イベントを全て凍結(処理せず破棄)
- Slack で運用者に通知
- Agent 再起動 + ハートビート復活までイベント受け入れを停止
これは「Agent が静かに偽情報を流し続ける」攻撃シナリオへの対策です。攻撃者がハートビートだけは送り続けて、本物のログ転送を止めるという手口も考えられますが、その場合は別経路の rsyslog 側で「ログが急に止まった」ことが検知できます。
つまり、Agent の状態を判断する経路を意図的に複数化してあります。
7. 24時間自動失効 — ブロックの永続化を防ぐ #
Agent が実行するブロック(ufw deny等)は、必ず24時間後に自動失効するよう設計されています。
# Agent 側の cron.hourly
/opt/bastion-agent/bastion-ufw-prune.sh
# → ufw のコメントに埋め込んだタイムスタンプから経過時間を判定
# → 24時間を超えたエントリは ufw delete で削除
# → AI-SLOG 側からの解除指示を待たない (Agent ローカルで自律動作)この設計の意図は3つあります:
- 誤検知の永続化を防ぐ: 一時的な誤検知でブロックされても、放置すれば24時間で復旧する
- Agent が解除権限を持たなくて済む: 自動失効があるので、Agent に「解除する」権限を与える必要がない
- AI-SLOG への依存を減らす: 失効処理は Agent ローカルで完結。AI-SLOG が落ちていても問題なし
同じ攻撃者が24時間後に再アクセスしてきた場合、当然また検知してブロックされます。攻撃者が活動を継続している限り、ブロックも継続的に発動します。
8. 実装上の判断 — なぜ WebSocket を選んだか #
Agent と AI-SLOG 間の通信は WebSocket で実装しています。理由を共有します。
| 選択肢 | BASTIONでの判断 |
|---|---|
| HTTP POST (Agent → AI-SLOG) | 不採用。命令を送り返す経路が別に必要になる |
| MQTT | 不採用。ブローカー追加が運用負担。閉域環境では過剰 |
| gRPC | 不採用。プロトコル定義の運用負担が大きい。デバッグ困難 |
| WebSocket (双方向) | 採用。1つのコネクションで双方向通信、TLS、HTTP互換 |
特に重要なのは、「Agent → AI-SLOG のイベント送信」と「AI-SLOG → Agent のブロック命令」を1コネクションで扱えることです。これにより、ファイアウォール越しの通信が単純化し、運用負担が大幅に減ります。
また、HAProxy 等の標準的なリバースプロキシで終端できる(HTTPと同じセマンティクス)ため、TLS 終端や認証連携も既存資産で完結します。
9. 設計上のトレードオフと制約 #
正直に書くと、この仕組みにも制約があります。
1. 検証エンジンの計算コスト: Agent からのイベントを1件1件、生ログと照合するため、イベント量が多いと処理時間が増える。BASTION では evidence_lines を3〜5行に制限し、検索範囲を時間ウィンドウで絞ることでコストを抑制している。
2. 生ログ取得経路の冗長性: 検証エンジンは rsyslog 経由のログを使うが、rsyslog 自体が止まると検証ができなくなる。これに対しては、rsyslog 自体の死活監視を別途実装し、止まったら即時アラートを上げる構成にしている。
3. 検証ロジックの公開性: 検証ロジックの詳細を公開すれば、攻撃者がそれを回避できる偽イベントを設計できてしまう。そのため、具体的な検証アルゴリズムは非公開(本記事では概念のみ説明)。
4. ブロック解除の運用負担: Agent に解除権限を与えていないため、誤ブロックを即時解除したい場合は AI-SLOG 側からの操作が必要。これは「24時間我慢する or 運用者が手動操作する」の二択になる。
これらは「侵害された Agent を信頼しない」という大前提から導かれる必然的なトレードオフです。利便性とセキュリティのバランスを、明らかにセキュリティ側に倒した設計になっています。
10. 実運用での効果 — 自社環境での実証 #
BASTION の DMZ Agent は、現在3台の公開 Web サーバーで本番稼働しています。本記事執筆時点で:
- Agent rejected events: 0件(検証で破棄されたイベントなし = 全て正規イベント)
- Agent heartbeat anomalies: 0件(ハートビート断絶ゼロ)
- 誤ブロック件数: 0件(自社IP/取引先IPのブロック実績ゼロ)
- 24時間自動失効による意図しない解除: 0件(全て期待通り動作)
これは「Agent が誠実に動作しているから検証が通過している」状態であり、侵害が発生した時に初めて非信頼モデルが本領を発揮する仕掛けです。普段は静かに動いていて、何かあった時に運用者を守る設計です。
11. 今後の発展 #
Agent と検証エンジンは現状で十分実用域ですが、さらに改良の余地があります。
- Go 言語移植: 現在 Python 実装の Agent を Go に移植し、シングルバイナリで配布可能にする(顧客環境への配布が容易に)
- 署名検証: Agent バイナリの署名と、起動時の自己検証ロジック追加
- 監査ログのブロックチェーン化: 検証エンジンの判定履歴を改ざん不可能な形で記録する仕組み(顧客監査対応)
- 多重検証経路: rsyslog だけでなく、SNMP やネットワークフロー情報も使った多重検証
これらは順次実装予定です。
12. 関連記事 #
- 多層相関キャンペーン検知の仕組み — Agent が検知した1次イベントを、全体としての攻撃キャンペーンとして判定する仕組み
- BASTIONを「セキュリティ商品」から「AI Ops Platform」へ — BASTION 全体の進化のストーリー
- Coming soon ローカルLLMでインフラログを自動分析する仕組み
- Coming soon LLMハルシネーション監査の実装
13. お問い合わせ #
BASTION の導入をご検討の企業様、共同実証プログラムにご興味のある方は、お問い合わせフォーム からご連絡ください。
DMZ や隔離環境を持つお客様にとって、本記事で紹介した Agent 非信頼モデルは大きな差別化要素になります。スコープに応じた個別見積でご提案いたします。