InfraGuard

セキュリティ診断

多数のサーバーに対してルートキットスキャンとハードニングスキャンを一括で実行し、ホストごとに A〜F で採点して、致命的な失敗を自動的に問題解決のケースへ昇格させます。

概要 — 何をする画面か

セキュリティ診断 は、登録済みのサーバーに対して ルートキット痕跡スキャンとシステムハードニングスキャン を実行し、それぞれの結果を 0〜100点のスコアと A〜F の評価 で採点して、フリート全体のセキュリティ体制を1画面に表示します。すべてのチェックには「何を/なぜ/どう直すか」の解説が付いており、失敗であり、かつ深刻度が致命的な検出結果(fail + crit) は自動的に 問題解決 のケースへ 昇格します(重複は排除され、後のスキャンで合格すれば自動的に解消されます)。

主要な構成要素:

プローブを root で 実行すると、/etc/shadow・sudoers・sshd -T などの特権チェックが有効になります。root でない場合、特権チェックは skip に降格し、スコアの分母から除外されます(減点なし)。

こんなときに使う

画面構成

エリア 内容
KPI ストリップ フリート平均スコア · スキャン済みホスト数 · 未解決の致命的ケース · 失敗チェックの総数 · 最終スキャン時刻
評価の分布 A/B/C/D/F のホスト数のヒストグラム
カテゴリ別スコア 9つのカテゴリのロールアップ(フリート平均、カタログ順、トーン色)
スコア推移 直近60件のスキャンを時系列で表示
評価別ホスト ホストカードのグリッド — スコアの低い(リスクの高い)順に並びます。各カードには評価、pass/warn/fail/crit の件数、取り込み経路、スキャン時刻が表示されます。クリック → レポートモーダル
レポートモーダル 総合スコアと評価 + サマリー(OS/カーネル)+ チェックごとの詳細(何を/なぜ/対処/根拠)+ 昇格したケースへのリンク

上部ツールバー: [スキャン実行](複数サーバーのリモート実行)· [結果を貼り付け] · 更新 · 自動更新トグル · 全画面。

使い方

4つの取り込み経路

infr_sec_scan.source は、スキャンをどのように受け取ったかを分類します:

source 入口 方法
emulate [スキャン実行] モーダル インスタンスを選択 → プローブをリモートに配置して実行します。instance_id を伴う貼り付けやリモートエミュレートの適用も emulate として記録されます
paste [結果を貼り付け] モーダル 対象ホスト上で直接生成したプローブの JSON を貼り付けます(インスタンスとの紐付けなし)
api POST /api/v1/security/scan 各ホストの cron がプローブを実行 → API キーで送信します(レポーター 経路)
manual フォールバック source の値が有効でない場合の既定値

1) 複数サーバーのリモート実行 — 非同期ポーリング

同期的にすべてを実行すると、多数のホストにまたがるため PHP の実行時間を超過します。そのため 登録 → 撃ちっぱなしのワーカー → ポーリング という流れになっています。

  1. [スキャン実行] → 対象インスタンスにチェックを入れます(それぞれ到達可能/到達不可が表示されます)。
  2. POST /security/diagnostics/run → 進捗レコードを作成し、run_id を即座に返します(Cache、TTL 30分)。
  3. フロントエンドは POST /security/diagnostics/run-worker/{runId} を 待たずに 呼び出します → ワーカーは set_time_limit(0) と ignore_user_abort のもとで全ホストを順番に実行します(1台が失敗しても残りは止まりません)。
  4. フロントエンドは GET /security/diagnostics/run-status/{runId} をポーリングして進捗(done/total/ok)を更新します。

到達不可 と表示されるインスタンスは run_report コレクターのノードと一致しないため、リモートでは実行できません。ホスト上で直接プローブを実行し、[結果を貼り付け] から投入してください。リモート実行の配線については リモートエミュレート を参照してください。

2) 貼り付け/JSON の取り込み

対象ホストでプローブを実行し、その JSON を貼り付けます。

# 対象ホスト上で(推奨: root)
node infr_seccheck_probe.cjs --min

--min は1行の JSON を出力します。出力の形:

{
  "host": "web-01",
  "generated_at": "2026-08-13T03:30:00Z",
  "os": "Rocky Linux 9.3",
  "kernel": "5.14.0-362.el9",
  "root": true,
  "checks": [
    { "key": "ssh_root_login", "category": "ssh", "status": "fail", "severity": "crit", "detail": "PermitRootLogin yes", "evidence": "/etc/ssh/sshd_config:32" },
    { "key": "svc_firewall",   "category": "services", "status": "pass", "severity": "crit", "detail": "firewalld active" }
  ],
  "summary": {}
}

受け取るのは POST /security/diagnostics/ingest です。インスタンスを紐付けると source=emulate として記録され、紐付けないままだと source=paste として記録されます。

3) API レポーター(自動化)

各ホストの cron がプローブを実行し、結果を InfraGuard へ送信します。API キーは 管理 ▸ APIキー で発行してください。キーには scan.write スコープが必要です。

30 3 * * *  node /opt/infr/infr_seccheck_probe.cjs --min | curl -sS -X POST https://infraguard.example.com/api/v1/security/scan -H "Authorization: Bearer YOUR_API_KEY" -H "Content-Type: application/json" --data @-

レスポンス(201):

{ "ok": true, "scan_id": 812, "host": "web-01", "score": 74, "grade": "C" }

採点 — SecurityCheckbook

スコアは、各チェックの 重みとステータス による加重比率です。

ステータス係数(STATUS_FACTOR) — 対象になるのはこれだけで、skip/info は分母から除外されます:

ステータス 係数
pass 1.0
warn 0.6
fail 0.0
skip · info 除外

深刻度の乗数(SEV_MUL) — 失敗したときの含意が重いほど、スコアを大きく動かします:

fail_severity 乗数
crit 1.6
warn 1.0
info 0.5

チェックごとの重みは w = check_weight(1–3) × severity_multiplier、総合スコアは = round(100 × Σ(w × status_factor) / Σw) です。カテゴリ別スコアも、カテゴリ単位で同じ式を使います。

評価とトーン:

スコア 評価 トーン
≥ 90 A good
≥ 80 B good
≥ 70 C warn
≥ 60 D warn
< 60 F crit

(評価は A/B/C/D/F、トーンは good ≥ 80 · warn ≥ 60 · crit < 60 です。)

9つのカテゴリ · 36項目のチェック

各カテゴリには表示用の重みとアイコンがあります。実際の採点には、上記の チェックごとの重み × 深刻度の乗数 を使います。

カテゴリ ラベル チェック数 主なチェック(失敗時の含意)
rootkit ルートキット/マルウェア 6 rk_known_files·rk_ld_preload·rk_hidden_proc(crit)
patch パッチ/更新 3 pt_security_updates·pt_eol_os(crit)
ssh SSH 設定 5 ssh_root_login·ssh_empty_pass(crit)
accounts アカウント/権限 5 acc_uid0·acc_empty_pass(crit)
network ネットワークの露出 3 net_legacy_services·net_db_exposed(crit)
filesystem ファイルシステムの権限 5 fs_shadow_perm(crit)·fs_world_writable·fs_suid(warn)
hardening システムハードニング 5 hd_selinux·hd_aslr·hd_syncookies(warn)
services サービス/ファイアウォール 2 svc_firewall(crit)·svc_unnecessary(info)
logging 監査/ロギング 2 log_auditd·log_syslog(warn)

未知のキーは other カテゴリに吸収されます。各チェックの「何を/なぜ/対処」のテキストは、seccheck.check.* からレポートモーダルに描画されます。

致命的な検出結果 → 問題解決への昇格

取り込みのたびに、reconcileCases() がそのホストのケースを突き合わせます。

昇格したチェックはレポートモーダルに「問題解決へ昇格」バッジと [ケースを表示] リンク付きで表示されます。ケースは セキュリティ ▸ 問題解決 のボードで対応します。

スキャンの削除(POST /security/diagnostics/delete)は、昇格済みのケースを 削除しません — 両者は独立しています。

データソース

ソース 役割
infr_sec_scan(000766) スキャン結果(ホストごとに1件)。score·grade·pass/warn/fail/crit/total_count·category_scores·summary·findings の JSON。インデックスは (host,scanned_at)·(instance_id,scanned_at)·grade·score
infr_instance 対象インスタンス(alias/hostname から host → instance_id を解決します)
infr_incident / infr_incident_event 昇格した致命的ケースとそのタイムライン
infr_api_key API レポーターの認証(スコープ scan.write)
Cache ダッシュボードの集計を20秒キャッシュ(infr:sec:diag:build)· 非同期実行の進捗レコード(TTL 1800秒)
infr_seccheck_probe.cjs 元となるプローブ(ローカル、読み取り専用)

アクセス権限

レベルモデル全般(infr.level:N ミドルウェア)については、RBAC レベル を参照してください。

トラブルシューティングと FAQ

Q. [スキャン実行] でインスタンスが「到達不可」と表示されます。 run_report コレクターのノードと一致しないため、リモート実行の配線がありません。ホスト上で直接プローブを実行し、[結果を貼り付け] から投入してください。

Q. 「No checks found」エラーが出ます。 貼り付けた JSON に checks(または items)配列がないか、要素に key がありません。infr_seccheck_probe.cjs --min の出力をそのまま貼り付けてください。

Q. 失敗件数は少ないのに、スコアが低いです。 深刻度の乗数のせいです。crit の失敗1件は warn の1.6倍の重みを持ちます。カテゴリ別スコアとレポートモーダルで、どの検出結果がスコアを押し下げているのかを確認してください。

Q. 特権チェックがすべて skip になります。 プローブが root 以外のアカウントで実行されました。/etc/shadow・sudoers・sshd -T などのチェックには root が必要です。skip は分母から除外されます(減点なし)が、完全な診断のためには root で(またはゲートウェイ経由で)実行してください。

Q. 実行に時間がかかります。ブラウザを閉じてもよいですか。 はい。ワーカーは撃ちっぱなしで動作し、リクエストが中断されても ignore_user_abort のもとで継続します。開き直して run-status をポーリングするか、ダッシュボードに評価が現れてから読むだけでも構いません。進捗レコードは30分間保持されます。

Q. ケースが勝手に消えました。 その後のスキャンで、その検出結果が fail+crit として報告されなくなった場合(修正済み、または検出されない場合)、エンジンがケースを自動的に解消します。これは想定どおりの動作です。

関連項目

보안 진단 — 점검 결과와 등급
보안 진단 — 점검 결과와 등급