FAQ

これは Arcivéo Security Monitor のインストール・設定・運用に関するガイドです。 各セクションは、全体概要、ダッシュボードの導入、セキュリティツールの接続、 組み込みモジュール、診断の順にまとめられています。コマンドは右側のボタンでコピーできます。

はじめに

01. パネルのインストール — 方法を選択

パネルのインストール手順は、ステップごとの個別ページにまとめています。方法を選択してください:

迷ったら自動を選んでください。このガイドはSSL、ツール、cron、診断に関する唯一の情報源であり続けます。インストールページは重複なくこのガイドの各セクションを参照します。

概要

02. Arcivéo Security Monitor とは

Arcivéo Security Monitor はサーバーセキュリティのダッシュボードです。導入済みのツール(Fail2ban、UFW、Lynis、ModSecurity、AIDE、ClamAV、Auditd、CrowdSec、Suricata、Falco など)からデータを収集し、ダッシュボード・攻撃マップ・各ツールの詳細ページを備えた統一インターフェースで表示します。

Monitor は能動的な防御手段ではなく、攻撃自体をブロックすることはありません。稼働中のツールから情報を集約し、見やすい形で提示することが役割です。

03. サーバー上でのモニターの動作

モニターはローカルでのみ動作します — 監視対象と同じサーバーにインストールする必要があります。SSH やリモート API は使用しません。

すべてのコマンド(fail2ban-client、ufw status、ipset list など)は、ダッシュボードが Web サーバーのユーザー(通常は www-data、ホスティングパネルではサイトのアカウント)として、限定された権限の sudo で実行します — 特定のユーティリティのみに限られ、root 全体へのアクセスはありません。結果は解析されてブラウザーに表示されます。

二つのリズム。 パネルの各ページは開いたその時点でツールに問い合わせます。したがって常に現在の状態が表示されます。それとは別に、スケジュール(cron)で動く裏方の処理があります。

  • 5分ごと — 「パフォーマンス」ページ用の負荷スナップショット。
  • 5分ごと — セキュリティイベントのセンサー: 新しい遮断、IDS の検知、停止した保護サービス、開いたポート(「セキュリティイベント」の節)。
  • 1日1回 — Telegram とメールへのセキュリティレポート、および重い点検(Lynis、AIDE、ClamAV、debsums、Logwatch)。
  • 15分ごと — 有効にしている場合、アカウントへのサーバー要約の送信。

ジョブの一覧は「すべての cron ジョブを一箇所で」の節にあります。

サーバーが複数ある場合は、それぞれに固有のドメインで個別にモニターを設置してください。一つの一覧でまとめて見るには、サーバー一覧を有効にします(「アカウントでのサーバー一覧」の節)。

04. セキュリティスコアの算出方法

スコアは満点から始まり、検出された問題ごとに減点されます:

  • UFWが無効 — −30
  • Fail2banが未起動(有効なjailなし) — −25
  • WebAuthnキーがない — −15
  • Lynis hardening index < 60 — −20;60〜79 — −10
  • IPset ipsumが未読み込み — −10
  • ClamAVが脅威を検出 — −20
  • AIDEがファイル変更を検出 — −15
  • SSL失効 — −30、期限まで14日未満 — −15、30日未満 — −5
  • CrowdSecがインストール済みだが未起動 — −5
  • Suricataがインストール済みだが未起動 — −5
  • DB/キャッシュ(MySQL、PostgreSQL、Redis…)が外部からアクセス可能 — −10
  • SSHでのrootログインが許可(PermitRootLogin yes) — −20
  • SSHでのパスワードログインが有効(PasswordAuthentication yes) — −10
  • SSH の細かな設定: X11Forwarding が有効、MaxAuthTries が 3 を超える、または sshd_config を全アカウントが読める — −5
  • Web サーバーの堅牢化: 応答に Web サーバーや PHP のバージョンが出ている、TRACE メソッドが有効、または Strict-Transport-Security や X-Frame-Options ヘッダーがない — −5
  • セキュリティ更新が未適用 — −5

結果: 80以上 = 保護済み、60〜79 = 要注意、<60 = 危険。

ClamAV、AIDE、CrowdSec、Suricataの減点はツールがインストールされている場合のみ適用されます。データベースが初期化されていないLynisとAIDEは「データなし」と表示され、スコアには影響しません。本日の攻撃件数はダッシュボードに表示されますが、セキュリティスコアには影響しません。 どのサイトもチェックに応答しなかった場合、Web サーバーの堅牢化は計算に含まれません。

設定とライセンス

05. WebAuthn — 2要素認証

WebAuthn はハードウェアキーによるパスワード不要の認証規格です。YubiKey、Touch ID、Face ID、Windows Hello、Passkey に対応します。

パスワードでのログイン後、登録済みのキーによる確認が求められます。パスワードが漏洩しても、物理キーや生体認証がなければログインできません。

設定するには、サイドメニューの WebAuthn キー を開き、「キーを登録」をクリックします。キーは一度に2つ登録してください。唯一のキーを紛失または破損すると、そのキーでのパネルへのログインができなくなります。

WebAuthn は HTTPS 経由でのみ動作します。HTTP 接続ではキーの登録とログインは利用できません。

06. 通知: Telegram、メール、Webhook、プッシュ

チャネルは四つあります。Telegram、メール、Webhook、そしてブラウザーへのプッシュです。互いに独立しているので、好きなものだけでも、四つすべてでも有効にできます。日次のセキュリティレポート、負荷アラート、イベントセンサーの検出結果も同じチャネルを通ります。設定は一度だけ、ここ「設定」で行います。

Telegram。ボットトークンと chat id が必要です:

  1. Telegram で @BotFather → /newbot に送信し、123456:ABC... の形式のトークンを取得します。
  2. 新しいボットに任意のメッセージを送ります(ボットが返信できるようにするため)。
  3. 自分の chat id を確認します: ボット @userinfobot にメッセージを送るか、https://api.telegram.org/bot<TOKEN>/getUpdates を開いて "chat":{"id":...} を探します。
  4. トークンと chat id を「設定」→ Telegram に入力し、「保存してテスト送信」をクリックします。

メール。「設定」→ メールで選べる方法は 2 つ:

  • SMTP — メールボックスのホスト、ポート(465/SSL または 587/TLS)、ログインとパスワード;
  • Resend — 最新の API: API キー(re_...)と確認済みの送信元ドメインを指定します。

Webhook。 POST を受け取るアドレスが一つあれば、Slack、Discord、Mattermost、n8n、そして自作のハンドラーまで賄えます。「設定」→ Webhook: アドレスを貼り付けてスイッチを入れてください。

  • Slack と Mattermost には {"text": …} を、Discord には {"content": …} を送ります(Discord は 2000 文字を超えるメッセージを拒否するため、本文は切り詰められます)。
  • それ以外のアドレスには構造化された JSON が届きます: text、event、severity、source、count、host、ts。メッセージ本文を改めて解析する必要はありません。

プッシュ通知。 メッセージはブラウザーに直接届きます。パネルをスマートフォンのホーム画面に追加してあれば、通常のアプリ通知として表示されます。ボットもメールサーバーも間に挟みません。「設定」→ プッシュ通知: スイッチを入れ、通知を受け取りたい端末ごとに「この端末を接続」を押します。「接続済みの端末」のカウンターで台数を確認できます。

プッシュは HTTPS でのみ動作します(WebAuthn と同じです)。しかも許可は二度必要です。まずブラウザーで — 「この端末を接続」ボタンを押すと確認が表示されます — 次に OS 自体でも許可します。Windows で 設定 → システム → 通知 の大元のスイッチが切れていると、ブラウザーは許可を出し、パネルも送信済みと報告するのに、画面には何も現れません。「集中モード(応答不可)」や macOS でブラウザーの通知を切っている場合(システム設定 → 通知)も同じ結果になります。
各チャネルにはそれぞれ「テスト送信」ボタンがあり、その場で確認できます。自動レポートのスケジュールは cron が担います(「すべての cron ジョブ」の節)。cron が送信を起動し、チャネルは設定から読み取られます。

レポートのステータス:「注意」または「OK」。ヘッダーが「注意」になるのは、実際の問題または対応待ちがある場合のみです: ClamAV による脅威の検出、AIDE でのファイル変更、Falco の重大イベント(直近 24 時間の Emergency/Alert/Critical)、Monit で停止したサービス、再起動が必要、SSL の期限切れ間近(14 日以内)、またはセキュリティ更新の待機。バックグラウンドノイズ — ボットによる SSH の総当たり、fail2ban で BAN された IP、Suricata のアラート、Lynis の警告、すでに撃退済みの ModSecurity リクエスト — はステータスを上げません。したがってレポート内のこれらの数値だけで「注意」を意味することはありません。

07. ライセンス — 入力と有効化

詳細な監視モジュール(Lynis、UFW、ModSecurity、攻撃マップ、AIDE、ClamAV など)は、有効なライセンスがある場合に利用できます。ライセンスがなくてもダッシュボード、設定、アカウントは動作しますが、モジュールには「ライセンスが必要です」というカードが表示されます。

アカウントで購入すると、ARCIVEO-XXXX-XXXX-XXXX-XXXX の形式の有効化コードが発行されます。これをお使いのパネルのドメインに対して「有効化」する必要があります。これによりコードが署名済みのライセンスファイル([license] ブロック)に変換され、それをパネルに貼り付けます。

有効化の手順(3ステップ):

  1. 有効化コードを取得します。アカウント my.arciveo.com →「ライセンス」/「ライセンス有効化」セクションで ARCIVEO-… コードをコピーします。
  2. コードを自分のドメインで有効化します。同じくアカウント内で「ライセンス有効化」を開き、有効化コード、メールアドレス、パネルのドメイン(モニターが開くアドレス、例:monitor.example.com)を入力します。有効化を押すと、システムがそのドメインに紐付いたライセンスファイルを生成し、「コピー」ボタン付きのフィールドに表示します。
  3. キーをパネルに貼り付けます。ライセンスのテキスト全体をコピー →パネルで「設定」→「ライセンス」ブロックを開き、貼り付けて「保存」を押します。モジュールはすぐにロック解除されます。

パネルはキーを暗号的に検証します:署名、ドメインへの紐付け、有効期限を確認します。

有効化時のドメインは、パネルのアドレスと完全に一致している必要があります。config.php の APP_URL 定数から取得し、ホスト名のみを入力してください。https:// や www プレフィックスは付けません。有効化は一度限りです:コードは入力したドメイン用のライセンスに変換され、再度有効化することはできません。ドメインを間違えるとキーがお使いのパネルに合わず、コードは消費されてしまいます。そのため、ドメインは慎重に入力してください。
有効期限が切れたりドメインが変わったりすると、パネルのヘッダーに警告が表示されます。ライセンスはドメインに永久に紐付けられ、別のドメインには移せません。新しい期間や新しいドメインには新しいキーが必要です(アカウントで購入し、一度限り有効化します)。

08. config.php ファイル — パネルの全設定

パネルの主要な設定はすべて、ルート(public/ フォルダと同じ階層)にある単一の config.php ファイルに、通常の define() 定数として記述されています。ファイルはインストール時に作成され、手動で編集する機会はまれです。主にドメイン変更、移行、別のデータベースへの接続時に限られます。編集後は必ず PHP-FPM を再起動してください(そうしないと OPcache により変更が反映されません)。

ハイライトされた箇所にご自身の値を入力し、それ以外はそのままにしてください。

// --- データベース --- define('DB_HOST', 'localhost'); // そのまま define('DB_NAME', 'db_name'); // DB作成時に指定した名前 define('DB_USER', 'user'); // DB作成時に指定したユーザー define('DB_PASS', 'db_password'); // DB作成時に指定したパスワード define('DB_CHARSET', 'utf8mb4'); // そのまま // --- アプリケーション --- define('APP_URL', 'https://monitor.example.com'); // パネルのアドレス、末尾のスラッシュなし define('TIMEZONE', 'Asia/Tokyo'); // タイムゾーン // --- セッション時間 --- define('SESSION_LIFETIME', 28800); // 再ログインまでのアイドル時間、秒(28800 = 8時間)

データベース。 MySQL/MariaDB への接続情報です。

  • DB_HOST — DBMS のホスト。ほぼ常に localhost;
  • DB_NAME — パネルのデータベース名;
  • DB_USER — DB ユーザー(自分のデータベースにのみアクセス可);
  • DB_PASS — このユーザーのパスワード;
  • DB_CHARSET — 接続の文字コード。utf8mb4 のままにしてください。

アプリケーション。

  • APP_URL — パネルの完全なアドレス(例:https://monitor.example.com)。ライセンスを有効化したドメインと一致している必要があります。一致しない場合はキーが拒否されます(「ライセンス」の項を参照);
  • TIMEZONE — PHP のタイムゾーン:パネルが日時を表示する方法にのみ影響します。cron ジョブの実行時刻には影響しません。そちらはシステムのタイムゾーンが適用されます(「すべての cron ジョブ」を参照)。

セッション時間。 SESSION_LIFETIME — セッションのアイドルタイムアウト(秒単位。スライド式:操作するたびに更新)。デフォルトは 28800 = 8時間で、この時間操作がないとパネルは再ログインを求めます。例:3600 = 1時間、86400 = 1日。

エラーのログ記録。 エラーが訪問者に表示されることはなく、logs/php_errors.log に記録されます。「アプリケーションログ」ページで確認できます。これらの行(display_errors=0、log_errors=1、error_log のパス)は通常変更する必要はありません。設定はファイル内に直接記述されており、php.ini に依存しません。

config.php は機密ファイルです。 DB のパスワードが含まれています。ファイルはパネルのルート(public/ と同じ階層)にあり、このパネルの Web ルート(DocumentRoot)は public/ ではなくパネルのルートそのものです。ファイル自体が「漏洩」することはありません。ルートの .htaccess にこのファイルへの明示的な拒否設定(Require all denied)があり、サーバーは 403 を返します。このルールがなくてもソースは漏洩しません。これは PHP であり、サーバーはテキストとして返すのではなく実行するからです。念のため:公開リポジトリに置いたり、実際のパスワードを含めたままサポートに送ったりしないでください。ファイルの権限は 640 です。
移行やアクセス復旧の際、このファイルが接続情報の主要な取得元となります。DB 名、ユーザー、パスワードはここから取得されます(「パネルの更新と移行」および「アクセスの復旧」の項を参照)。

09. 読み取り専用 API(JSON と Prometheus)

パネルはパスワードによるログインで、さらに有効にしていれば IP フィルターでも守られています。プログラムにはどちらも使えないため、外部システムには専用の入り口があります。トークンで保護された読み取り専用のアドレスが二つです。トークンはサーバー上の何も変更しません。取得できるのは数値だけです。

有効化 — 「設定」→ 「読み取り専用 API」: スイッチ「API を有効にする」。トークンは最初の保存時に自動生成されます。「新しいトークン」ボタンは別のトークンを発行し、その瞬間に以前のトークンを使っていた相手をすべて遮断します。

  • /api/status.php — JSON: セキュリティスコア、各モジュールの状態、上位プロセスを含む直近の負荷測定、直近24時間のセキュリティイベント。
  • /api/metrics.php — 同じ数値を Prometheus のテキスト形式で(arciveo_* のメトリクス: arciveo_security_score、arciveo_fail2ban_banned、arciveo_cpu_busy_pct など)。
curl -H "Authorization: Bearer <TOKEN>" https://monitor.example.com/api/status.php curl -H "Authorization: Bearer <TOKEN>" https://monitor.example.com/api/metrics.php

クライアントがヘッダーを付けられない場合は、トークンをパラメーターとして渡すこともできます — ?token=…。ただしこの形式ではウェブサーバーのアクセスログに残るため、ヘッダーのほうが望ましいです。

Prometheus。 scrape_configs 用のそのまま使えるブロックです。

scrape_configs: - job_name: arciveo metrics_path: /api/metrics.php scheme: https authorization: credentials: <TOKEN> static_configs: - targets: ['monitor.example.com']
応答は5分間キャッシュされます。これらの数値の背後にあるのは、ダッシュボードと同じ重い収集処理だからです。したがって頻繁に取得してもサーバーの負担にはなりません(次の更新まで同じ値が返るだけです)。とはいえ、1分未満の間隔にする意味もありません。
401 — トークンが違うか、渡されていません。api disabled という本文を伴う 404 — 設定で API が無効になっています。拒否したリクエストは送信元 IP とともにアプリケーションログに記録されるため、トークンの総当たりは「アプリケーションログ」ページで確認できます。

10. アカウントでのサーバー一覧

パネルはサーバーごとに設置され、「このマシンの調子はどうか」にはよく答えます。しかし「自分のマシンすべての調子はどうか」には答えません。そのために、各パネルは短い要約をアカウントへ送ることができます。そこではサーバーが一つの一覧に並びます。

アカウントで用意するもの my.arciveo.com → 「マイサーバー」の節: ここでトークン(fk_… の形式)が発行され、送信先アドレス https://my.arciveo.com/api/fleet.php が示されます。トークンはアカウントにつき一つで、すべてのサーバーで共通です。

パネルで設定するもの — 「設定」→ 「サーバー一覧」: 送信先アドレス、トークン、サーバー名(一覧での表示名。空欄ならパネルのドメインが使われます)、スイッチ「要約を送信する」。「今すぐ送信」ボタンを押せば、スケジュールを待たずにその場で接続を確認できます。

以後、要約は cron により15分ごとに送信されます。

# 15分ごと — サーバー要約をアカウントへ送信 3,18,33,48 * * * * /usr/local/bin/fleet-push-all.sh >> /path/to/monitor/logs/cron.log 2>&1

送信される内容: サーバー名とドメイン、OS、稼働時間、PHP のバージョン。セキュリティスコア。各モジュールの状態(UFW、Fail2ban の jail 数、本日の遮断件数、CrowdSec・Suricata・Falco が動作中かどうか、ClamAV の脅威、AIDE の変更、Lynis の指数、保留中の更新、再起動の要否、証明書の残り日数、危険な開放ポートの数)。直近の負荷測定(load、CPU、RAM、ディスク、inode)。直近24時間のイベント件数と、最後の重大イベント。ログもパスワードもファイルの中身も要約には含まれません。

送るのはサーバー側で、アカウント側から問い合わせるわけではありません。 そのため外部からご自身のネットワークへ入り口を開ける必要はなく、IP フィルターで閉じたパネルでも報告できます。ノードはパネルのドメインで識別されるため、再起動や IP 変更のあとに一覧が二重になることもありません。
アカウントでサーバーが「無応答」と表示されている場合、要約が長らく届いていないということです。順に確認してください: スイッチ「要約を送信する」、cron の行(sudo crontab -l)、そして「今すぐ送信」ボタンの応答。送信エラーは FLEET の印とともにアプリケーションログに記録されます。そこでトークンが拒否された(HTTP 401)のか、接続自体が確立しなかったのかを確認できます。

セキュリティツール

11. UFW ファイアウォール

UFW(Uncomplicated Firewall)は nftables/iptables への簡単なインターフェイスです。明示的に許可したポート以外の受信ポートをすべて閉じます。「UFW ファイアウォール」ページでは状態とルールを確認できます。

sudo apt install ufw # SSH(有効化する前に必ず!)とウェブを許可 sudo ufw allow OpenSSH sudo ufw allow 80,443/tcp # 外部からのDBアクセスを遮断(ローカルのみ許可) sudo ufw deny 3306 # 有効化して確認 sudo ufw enable sudo ufw status verbose
ufw enable の前に必ず SSH を許可してください(ufw allow OpenSSH)。さもないとサーバーへのアクセスを失います。
ダッシュボードの「外部への露出」は UFW を考慮します。deny ルールで閉じたポートは外部からアクセス可能とは見なされません。
Skipping adding existing rule はエラーではありません。 同一のルールがすでに存在するため重複して追加しないことを UFW が知らせているだけです。自動設定を再実行した場合(冪等です)、これは通常のメッセージなので対応は不要です。

12. Fail2ban のインストール

ログイン失敗の回数が上限を超えたIPを自動的にブロックします。SSH、nginx、Apache などのサービスのログを解析します。

sudo apt install fail2ban sudo systemctl enable --now fail2ban # ステータスを確認: sudo fail2ban-client status
実運用向けの設定(多数の jail と ipsum による自動バンを含む jail.local)は次のセクションで説明します。

13. Fail2ban + ipsum の実用構成

基本インストールは上記を参照。ここでは、数十のアクティブな jail と数千のブロックを実現する実用構成を紹介します。全体設定、主要な jail、そして ipsum リストによる悪質な IP の自動 ban です。

/etc/fail2ban/jail.local ファイル — 全体設定と重要な jail:

[DEFAULT] bantime = 1w findtime = 900 maxretry = 3 backend = systemd banaction = nftables-multiport ignoreip = 127.0.0.1/8 ::1 <YOUR_IP> <TRUSTED_NETS> # 段階的 ban: 繰り返すほど期間が延びる bantime.increment = true bantime.factor = 2 bantime.maxtime = 5w bantime.rndtime = 300 [sshd] enabled = true maxretry = 5 bantime = -1 # SSH ブルートフォースは永久 ban findtime = 3600 # 常習犯: 複数回 ban された相手は永久に ban [recidive] enabled = true logpath = /var/log/fail2ban.log banaction = %(banaction_allports)s bantime = -1 findtime = 86400 maxretry = 2 [http-get-dos] enabled = true maxretry = 100 findtime = 300 bantime = 1w # Web サービス (apache-*, nginx-*, php-url-fopen, phpmyadmin-syslog): [nginx-http-auth] enabled = true port = http,https [apache-badbots] enabled = true port = http,https # … 他のサービス別 jail (dovecot, exim, postfix-sasl, # mysqld-auth, vsftpd, portscan, pam-generic) も enabled = true
ignoreip には必ず自分の IP と信頼するネットワークを記載してください。そうしないと自分自身を ban してしまう恐れがあります。編集後: sudo fail2ban-client reload。

ipsum ブロックリストの自動読み込みは root の cron に設定します (sudo crontab -e): level 1 (10万以上の IP) が ipsum セットに読み込まれ、ファイアウォールで遮断されます (詳細は「IPset ブロックリスト」セクションを参照):

# 04:00 — ipset ipsum の更新 (level 1、最大カバレッジ): 0 4 * * * /usr/local/bin/load-ipsum.sh >> /path/to/monitor/logs/cron.log 2>&1
セット名は ipsum でなければなりません — ダッシュボード (「IPset ipsum」カード) が読み取るのはこの名前です。レベル: levels/1.txt は最大カバレッジ、levels/3.txt はより高精度 (3つ以上の情報源)。

「セキュリティモニター」が2つのゾーンに分かれている理由。 保護は2つのレベルで機能しており、ダッシュボードは両者を混同しません:

  • 実際の攻撃 (リアクティブ) — fail2ban が捕捉したすべて: 実際の侵入試行 (sshd、apache-*、nginx-* などの jail) と悪質な常習犯 (recidive jail — すでに複数回 ban された相手)。これらは実際にあなたのサーバーへ侵入を試みた IP で、攻撃マップや「タイムライン」に表示されます。
  • 予防的ブロック (プロアクティブ) — 既知の悪質 IP の公開ブロックリスト ipset ipsum で、ファイアウォールの DROP ルールで遮断されます。これらのアドレスは大半があなたのサーバーに触れてすらいません — 事前に遮断されます。「IPset ipsum」カウンターは、予防的に遮断された数を表示します。

違いはシンプルです: リアクティブは「攻撃してきて ban された相手」、プロアクティブは「試行前にブロックされた相手」です。以前は recidive に ipsum の list-3 を人為的に流し込んでいました (これが旧来の「リスト型 recidive」区分の由来です)。現在の recidive は本物の常習犯のみで、予防的ブロックはすべてファイアウォール側にあります。

14. IPset ブロックリスト(ipsum)

ipsum は、毎日更新される公開の悪意あるIPリストです。モニターはダッシュボードと攻撃マップに読み込んだアドレス数を表示し、セキュリティ評価に反映します(セットが未読み込みの場合は −10)。

fail2ban を使わない最小構成では、専用セット ipsum を作り iptables でブロックします:

# セットを作成(一度だけ): sudo ipset create ipsum hash:ip # 更新スクリプト /usr/local/bin/update-ipsum.sh: #!/bin/bash ipset flush ipsum for ip in $(curl -s https://raw.githubusercontent.com/stamparm/ipsum/master/levels/3.txt); do ipset add ipsum "$ip" 2>/dev/null done iptables -C INPUT -m set --match-set ipsum src -j DROP 2>/dev/null \ || iptables -I INPUT -m set --match-set ipsum src -j DROP # Cron(sudo crontab -e、毎日 4:00): 0 4 * * * /usr/local/bin/update-ipsum.sh
fail2ban-recidive を使う拡張構成は「Fail2ban + ipsum の実用構成」の項を参照してください。
ipset はメモリ上に存在し、再起動で失われます。 毎日のcronだけでは、再起動から次の実行までセットが空のままになります(ダッシュボードは0を表示)。起動時にもセットを読み込むよう、読み込み処理をスクリプトにまとめて @reboot に登録してください。同時に create … -exist コマンドで上限 maxelem 300000 を指定します(デフォルトの65536では level 1 が収まらず「Hash is full」になります):
# /usr/local/bin/load-ipsum.sh #!/bin/bash curl -s https://raw.githubusercontent.com/stamparm/ipsum/master/levels/1.txt \ | grep -v '^#' | sed 's/^/add ipsum /' \ | (echo "create ipsum hash:ip hashsize 131072 maxelem 300000 -exist"; echo "flush ipsum"; cat) \ | ipset restore -exist # sudo crontab -e — 毎日 04:00 と毎回の起動時: 0 4 * * * /usr/local/bin/load-ipsum.sh @reboot sleep 60 && /usr/local/bin/load-ipsum.sh
自動インストール時の仕組み。 スクリプトは level 1 の完全リスト(10万以上のIP)をセット ipsum に読み込み、ファイアウォールをインストーラーが管理している場合(新規VPS — プロファイル「フル」/「軽量」)は DROP ルールでセットを UFW に接続します — これらのIPからのトラフィックは実際にブロックされます。ルールは ESTABLISHED,RELATED の後に置かれるため、現在の接続(SSHを含む)は切断されず、リスト上のIPからの新規接続のみが遮断されます。セットはサービス ipsum-load.service の起動時にファイアウォールより前に復元され(そうでないとUFWが起動できません)、cronで04:00に更新されます。すでに設定済みのサーバー(パネルや独自ファイアウォール)では、インストーラーはファイアウォールに手を加えません — そこでは ipsum はダッシュボードと攻撃マップ用のリストとして残り、必要なら DROP ルールを手動で追加します(iptables … --match-set ipsum … -j DROP による最小構成は上記)。自動インストールでは手動で行うことは何もありません。

15. CrowdSec のインストール

集合知による threat intelligence を備えた Fail2ban の最新の代替。コミュニティによるブロックに加え、独自ルールも利用できます。ブロックをファイアウォールに適用するには別途 bouncer が必要です。

curl -s https://packagecloud.io/install/repositories/crowdsec/crowdsec/script.deb.sh | sudo bash sudo apt install crowdsec sudo systemctl enable --now crowdsec # iptables/nftables 用の bouncer: sudo apt install crowdsec-firewall-bouncer-iptables # ステータスを確認: sudo systemctl status crowdsec sudo cscli decisions list sudo cscli bouncers list
パネル上の「未起動」ステータス = サービスはインストール済みだがデーモンが非アクティブ(モニターは systemctl is-active crowdsec で確認します)。起動するには sudo systemctl enable --now crowdsec。落ちる場合は sudo journalctl -u crowdsec -n 30 を確認。「未起動」ステータスのあらゆるサービス(Suricata、Falco、Monit、MySQL)にも同じ手順が当てはまります。
ダッシュボードの「シナリオ 0 件」または「bouncer 0 件」。 CrowdSec は初期状態ではほぼ空で、コレクションがなければ何も検知せず、bouncer が登録されていなければブロックがファイアウォールに適用されません。基本コレクションを導入し、bouncer が一覧にあることを確認してください:
# 基本コレクション(Linux + SSH + Web サーバー): sudo cscli collections install crowdsecurity/linux crowdsecurity/sshd crowdsecurity/base-http-scenarios sudo systemctl reload crowdsec # bouncer が一覧にあり、接続がアクティブなステータスであること: sudo cscli bouncers list
bouncer のログに stream halted / ブロックが適用されない。 これは孤立した api キーです。bouncer は cscli bouncers list から削除されたのに、古いキーが /etc/crowdsec/bouncers/*.yaml に残っています。bouncer を再登録し、新しいキーを記入してください:
sudo cscli bouncers add fw-bouncer # 新しい api_key が出力される # このキーを /etc/crowdsec/bouncers/crowdsec-firewall-bouncer.yaml の api_key: に記入 sudo systemctl restart crowdsec-firewall-bouncer
自動インストール(「フル保護」プロファイル)はコレクションの導入と firewall-bouncer の登録を自動で行います。手動での作業が必要になるのは、手動インストール時や CrowdSec に手動で手を加えた後だけです。

16. AIDE のインストール

AIDE(Advanced Intrusion Detection Environment)はファイルシステムのスナップショットを取得し、チェックのたびに /etc、/bin、/usr の変更を報告します。インストール後はデータベースの初期化(aideinit)が必須です。

sudo apt install aide # データベースの初期化(5〜15分): sudo aideinit sudo cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db # Ubuntu 24.04: /var/lib/aide ディレクトリは 700(所有者 _aide)で作成され、 # パネル(www-data)がデータベースを認識できず →「未初期化」と表示される。 # ディレクトリに通過権限を付与(データベースファイルは 600 のまま): sudo chmod 755 /var/lib/aide # モニターが読み取るログへ書き込む最初のチェック。 # Ubuntu/Debian では aide に明示的な --config が必要(ないと「missing configuration」; # 新バージョンでは aide.wrapper バイナリが同梱されない): sudo bash -c 'aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1'
aideinit の実行中、ターミナルは 5〜15分間 Running aide --init... の行で止まりますが、これは正常です(ファイルシステム全体のハッシュ化でディスク負荷がかかります)。Ctrl+C で中断しないでください。処理が「固まった」ように見えて何も出力されない場合、隠れた確認 Overwrite existing aide.db.new [Yn]? への応答を待っている可能性があります(Y を押してください)。別セッションから動作を確認: pgrep -af aide。
aideinit のエラー:「21_aide_spamassassin … printf: invalid number」(return code 20) — Ubuntu 22.04 の AIDE 設定スニペットの既知のバグです。データベースが作成されません。壊れたスニペットを退避して再実行してください:
sudo mv /etc/aide/aide.conf.d/21_aide_spamassassin /etc/aide/21_aide_spamassassin.disabled sudo aideinit sudo cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db
ダッシュボードのステータス。「未初期化」= パネルがデータベースファイルを認識できない状態: aideinit が実行されていないか、あるいは(Ubuntu 24.04)/var/lib/aide ディレクトリが 700 で作成され www-data からアクセスできない状態です — sudo chmod 755 /var/lib/aide で解決します(上のブロック参照)。「チェック未実施」= データベースはあるがチェックがまだ実行されていない状態で、エラーではありません。結果はモニターが /var/log/aide/aide.log から読み取ります。
定期チェック → パネル用ログ。 標準の /etc/cron.daily/aide は新しい Ubuntu/Debian では /var/log/aide/aide.log を必要な形式で書き込まないことがあります(それらには aide.wrapper も既にありません)。明示的な --config を指定した独自の cron を追加するほうが確実です — root がログを 644 で書き込むため、モニターは追加のグループなしで読み取れます:
# sudo crontab -e — 毎日 02:00 のチェック: 0 2 * * * /usr/bin/aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1 # スケジュールを待たずに今すぐ実行: sudo bash -c 'aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1'
自動インストールはこれらをすべて実行済みです: chmod 755 /var/lib/aide と 02:00 のチェック cron — 手動での作業は不要です。
最初の初期化はクリーンなサーバーで、Web アプリのインストール前に行ってください。正当な変更のあとはデータベースを再作成してください: sudo aideinit && sudo cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db。

17. ClamAV のインストール

Linux 向けのアンチウイルススキャナーです。/var/www を PHP シェルや悪意あるコードについて検査するのに特に役立ちます。

sudo apt install clamav clamav-daemon sudo systemctl enable --now clamav-daemon # シグネチャデータベースを更新: sudo freshclam # フォルダーを手動でスキャン: sudo clamscan -r /var/www --infected
enable --now の後も clamd デーモンが「非アクティブ」と表示される? よくある原因は3つです:

1. 設定に Example 行が残っている — この行があるうちは clamd は起動を拒否します:

sudo sed -i '/^Example/d' /etc/clamav/clamd.conf sudo systemctl restart clamav-daemon

2. シグネチャデータベースがダウンロードされていない — これがないと clamd は起動しません:

sudo systemctl stop clamav-freshclam sudo freshclam sudo systemctl start clamav-freshclam sudo systemctl restart clamav-daemon

3. 単に読み込み中 — clamd は約800万件のシグネチャをメモリに30〜60秒かけて読み込みます。待ってから確認してください: systemctl is-active clamav-daemon(ステータスが activating → まだ読み込み中)。

診断: sudo journalctl -u clamav-daemon -n 30 --no-pager。
ダッシュボードで「検査済みファイル: 0」/「最終スキャン: —」と表示される? clamd デーモンはシグネチャをメモリに保持するだけで、スケジュールに従って自らスキャンすることはありません。パネルは定期スキャンの結果を表示するため、スキャンしてログを書き込む cron が必要です。自動インストールではラッパー /usr/local/bin/clamav-scan.sh と01:30の cron を設定します — 初回実行後に「検査済みファイル」と「最終スキャン」が埋まります。スケジュールを待たずにすぐ実行するには: sudo /usr/local/bin/clamav-scan.sh。

18. Linux Malware Detect (maldet) のインストール

Linux Malware Detect (LMD) — Web の脅威に対応したマルウェアスキャナーで、PHP シェル、Web バックドア、ダウンローダーを検出します。ClamAV エンジンを利用し、独自のシグネチャで補完します。

wget https://www.rfxn.com/downloads/maldetect-current.tar.gz tar -xzf maldetect-current.tar.gz dir=$(ls -d maldetect-*/ | head -1) && cd "$dir" && sudo bash install.sh && cd ~ rm -rf maldetect-* maldetect-current.tar.gz # シグネチャを更新: sudo maldet -u # /var/www をスキャン: sudo maldet -a /var/www
LMD と ClamAV は組み合わせて使うと効果的です。最新レポート: maldet --report。
インストール時に update-rc.d: error: unable to read /etc/init.d/maldet という行が表示されることがありますが、問題ありません。maldet は init.d を使わず、シグネチャ更新とスキャンは /etc/cron.daily/maldet から実行されます。下に installation completed と表示されていれば、正常にインストールされています。
インストール済みなのにページには LMD が「未インストール」と表示される? maldet は apt ではなく /usr/local/maldetect にインストールされ、open_basedir が有効な場合は shell 経由で存在を確認します。「サーバーにデータがあるのにページが空」のセクションを参照してください。

19. Suricata のインストール

ネットワーク型侵入検知システム。トラフィックをパケットレベルで解析し、数千種類の攻撃シグネチャを認識します。ModSecurity を補完します(ModSecurity は HTTP レベル、Suricata は TCP/IP レベルで動作)。

sudo add-apt-repository ppa:oisf/suricata-stable sudo apt update && sudo apt install suricata # 最新のルールをダウンロード: sudo suricata-update sudo systemctl enable --now suricata
Suricata は「稼働中」なのにダッシュボードにアラートが表示されない/イベント数が 0 ですか? Suricata は /var/log/suricata/eve.json を root 権限でディレクトリのパーミッション 750 で書き込むため、Web サーバー(www-data)が読み取れません。ディレクトリに通過権限を付与してください。内部のファイルは保護されたままです:
sudo chmod o+rx /var/log/suricata
自動インストールではこの処理も行われるため、手動での作業は不要です。

20. Falco のインストール

eBPF/カーネルモジュールを通じてシステムコールを捕捉し、異常をリアルタイムで検出します。nginx からの shell 起動、Web プロセスによる /etc/passwd の読み取り、/bin への書き込みなど。

curl -fsSL https://falco.org/repo/falcosecurity-packages.asc > /tmp/falco.asc gpg --dearmor < /tmp/falco.asc | sudo tee /usr/share/keyrings/falco-archive-keyring.gpg > /dev/null sudo chmod 644 /usr/share/keyrings/falco-archive-keyring.gpg && rm /tmp/falco.asc echo "deb [signed-by=/usr/share/keyrings/falco-archive-keyring.gpg] https://download.falco.org/packages/deb stable main" \ | sudo tee /etc/apt/sources.list.d/falcosecurity.list sudo apt update && sudo apt install falco sudo systemctl enable --now falco
モニターは journalctl -u falco を通じて Falco のイベントを読み取ります(sudo なし — systemd-journal グループ経由)。www-data がこのグループに属していることを確認してください。手動インストールページの「sudo の設定」(項目2)を参照してください。
「24時間で0件」は正常であり、エラーではありません。 Falco はイベント駆動型です。すべて正常な間は沈黙し、異常時のみイベントを記録します(Web プロセスからの shell 起動、/etc/passwd の読み取り、システムディレクトリへの書き込み)。平穏なサーバーで1日あたり重大イベントがゼロなのは健全な状態です。
パネルにはファイル出力の方が確実です。 journalctl 経由の読み取りにはジャーナルへの権限が必要です。パネルが安定してイベントを見られるよう、自動インストールでは Falco の file_output → /var/log/falco/falco.log を有効化し、サービスに UMask=0022 を設定します(ログは Web サーバーから読み取れます)。新規インストールでは手動で設定する必要はありません。

21. ModSecurity(WAF)のインストール

ModSecurity は Apache または Nginx 向けの Web ファイアウォール(WAF)です。SQL インジェクション、XSS、パストラバーサル、スキャナーなど、アプリケーション層への攻撃をブロックします。

# Apache: sudo apt install libapache2-mod-security2 sudo a2enmod security2 # OWASP Core Rule Set ルールセット: sudo apt install modsecurity-crs # 必須: このファイルがないとルールエンジンは無効のまま sudo cp /etc/modsecurity/modsecurity.conf-recommended /etc/modsecurity/modsecurity.conf sudo sed -i 's/^SecRuleEngine DetectionOnly/SecRuleEngine On/' /etc/modsecurity/modsecurity.conf sudo apache2ctl configtest && sudo systemctl reload apache2 # 確認: 403 が返るはず curl -s -o /dev/null -w '%{http_code}\n' "https://monitor.example.com/?id=1%20UNION%20SELECT%201,2--"
パッケージをインストールしただけでは何も保護されません。 Apache は IncludeOptional /etc/modsecurity/*.conf という行で設定を読み込みますが、パッケージが配置するのは modsecurity.conf-recommended だけで、*.conf のマスクには一致しません。これを modsecurity.conf にコピーしないと SecRuleEngine は Off のまま。モジュールは読み込まれ、CRS ルールも読み込まれますが、トラフィックは検査されず監査ログも作成されません。中間モードの DetectionOnly はイベントをログに記録するだけでリクエストをブロックしないため、パネルでは黄色で表示されます。

パネルからの監査ログへのアクセス。 ログ /var/log/apache2/modsec_audit.log は root 所有(権限 640)で、Web ユーザーは読み取れません。パネルはラッパー経由でデータを取得します。次のように作成してください:

sudo tee /usr/local/bin/monitor-modsec >/dev/null <<'EOF' #!/bin/sh echo "ENGINE=$(grep -hE '^SecRuleEngine[[:space:]]+' /etc/modsecurity/*.conf 2>/dev/null | tail -1 | awk '{print $2}')" echo "---LOG---" tail -n 3000 /var/log/apache2/modsec_audit.log 2>/dev/null echo "---RULES---" for f in /etc/modsecurity/crs/rules/*.conf /usr/share/modsecurity-crs/rules/*.conf /etc/modsecurity/custom-rules.conf; do [ -f "$f" ] && { echo "===FILE:$(basename "$f")==="; cat "$f"; } done EOF sudo chown root:root /usr/local/bin/monitor-modsec sudo chmod 755 /usr/local/bin/monitor-modsec # /etc/sudoers.d/monitor に(ユーザー = PHP-FPM が動作しているユーザー): # www-data ALL=(ALL) NOPASSWD: /usr/local/bin/monitor-modsec
ラッパーはインデントのない最後の SecRuleEngine ディレクティブを取得します。インデントされた行は <LocationMatch>/<Directory> ブロック内(例えば phpMyAdmin での WAF 無効化)にあり、グローバルモードは決定しません。
sudoers のユーザーは FPM プールのユーザーと一致している必要があります。通常の Apache/Debian では www-data ですが、HestiaCP ではサイトのプールがサイト所有者(例えば admin)で動作します。grep -E '^user' /etc/php/*/fpm/pool.d/monitor.example.com.conf で確認してください。
サイトが Nginx プロキシ(HestiaCP)の背後にある場合、Apache にはクライアントとしてプロキシ自身が見えるため、パネルは攻撃者の実際の IP を X-Forwarded-For ヘッダーから取得します。統計にはルールが発動したトランザクションのみが含まれます。SecAuditLogRelevantStatus ディレクティブは 4xx/5xx のあらゆる応答を監査ログに記録するため、通常の 403/500 もそこに入りますが、パネルはそれらを WAF イベントとは見なしません。
---RULES--- ブロックは「すべての有効なルール」セクションに必要です。パネルは発動したものだけでなく、読み込まれたすべての CRS ルールとカスタムルールを表示します。ループ for f in … 内の 3 つのパスは、CRS ルールとローカルの追加分の典型的な配置先です。別の構成の場合(パッケージが独自のディレクトリにファイルを配置する、またはカスタムルールが /etc/modsecurity/custom-rules.conf 以外にある)は、sudo grep -rl 'IncludeOptional\|^Include ' /etc/apache2/mods-enabled/security2.conf /etc/apache2/conf-enabled/*.conf 2>/dev/null コマンドで実際のパスを見つけてリストに追加してください。ラッパーが古い場合(このセクションがない場合)は、セクションに「利用不可」という警告が表示されるだけで、ページの他の部分は従来どおり動作します。

22. Auditd のインストール

Auditd(Linux Audit Daemon)はカーネルレベルでシステムコールを記録します。ログインとログアウト、sudo コマンド、認証の失敗、ファイルの変更などです。モニターは当日のログイン、失敗した試行、sudo コマンドを表示します。

sudo apt install auditd audispd-plugins sudo systemctl enable --now auditd # ステータスとイベントを確認: sudo systemctl status auditd sudo ausearch -m USER_LOGIN -ts today
モニターは ausearch(/usr/sbin/ausearch)でイベントを読み取り、必要に応じて tail コマンドで /var/log/audit/audit.log からも読み取ります。両方とも sudoers に登録されている必要があります。

23. Monit のインストール

サービス(nginx、php-fpm、mysql など)を監視し、ダウン時に再起動します。email でアラートを送信できます。

sudo apt install monit sudo systemctl enable --now monit # 設定ファイル: sudo nano /etc/monit/monitrc ls /etc/monit/conf.d/
モニターは monit status でサービス一覧を取得します。/etc/monit/monitrc で HTTP インターフェース(allow localhost を含む set httpd ブロック)を有効にする必要があります。そうでないと monit status がエラーを返します。
ダッシュボードに「監視中のサービスが 0 件」と表示される? 原因は 2 つあります。(1) HTTP インターフェースが無効 — monitrc の set httpd 行がコメントアウトされている(デフォルトでは # set httpd port 2812 … となっています)。ブロックのコメントを解除し、localhost を許可してください。(2) httpd を有効にしただけでは何も監視されません — Monit は check スタンザで記述したものだけをカウントするため、それがなければインターフェースが動作していても一覧は空です。最小限の動作設定:
# /etc/monit/conf.d/00-httpd — localhost 用の HTTP インターフェース: set httpd port 2812 use address localhost allow localhost # check スタンザの例(監視対象): check process sshd with pidfile /run/sshd.pid start program = "/usr/bin/systemctl start ssh" stop program = "/usr/bin/systemctl stop ssh" check filesystem rootfs with path / if space usage > 90% then alert
sudo monit -t # 構文チェック (Control file syntax OK) sudo systemctl reload monit sudo monit status
自動インストールでは、2812 ポートの httpd と一連のチェックを備えた conf.d が配置されます。新規インストールでは手動設定は不要です。
サービスのステータスが「エラーあり」? モニターは状態を表示するだけで、意図的にウェブパネルからサービスを再起動しません(セキュリティパネルでの root コマンドのリモート実行になってしまうため)。診断と再起動は SSH 経由で Monit を使って行います:
sudo monit status <service> # エラーの原因 sudo monit restart <service> # Monit 経由で再起動 # Monit がサービスを起動できない場合は、そのユニット自体を確認: sudo systemctl status <unit> --no-pager sudo journalctl -u <unit> -n 50 --no-pager

24. PSAD のインストール(ポートスキャン検知)

PSAD は iptables のログを解析してポートスキャンやネットワーク攻撃を検出し、送信元ごとに脅威レベル(1〜5)を割り当てます。fail2ban と Suricata を補完します。

sudo apt install psad # PSAD は iptables のログを読み込むため、ロギングを有効にする必要があります(UFW は自動で行います)。 # 素の iptables では INPUT/FORWARD チェインに LOG ルールを追加してください。 sudo psad --sig-update sudo systemctl enable --now psad
モニターは psad --Status 経由でデータを読み込みます(sudoers への登録が必要)。iptables のロギングがない場合、ページは空になりますが、スキャンがまだ発生していないだけなので正常です。

25. AppArmor / SELinux(アクセス制御)

Mandatory Access Control は、プログラムが侵害された場合でも、そのプログラムがアクセスできるファイルやリソースを制限します。Ubuntu/Debian では標準で AppArmor が使われます(通常はすでにインストール・有効化済み)。

# AppArmor (Ubuntu/Debian): sudo apt install apparmor apparmor-utils sudo systemctl enable --now apparmor sudo aa-status # プロファイルを確認
モニターは aa-status でステータスを読み取ります(sudoers への登録が必要)。enforce/complain モードのプロファイル数と、プロファイルのないプロセスを表示します。

「読み込み済みプロファイル」が enforce + complain より多くても正常です。 AppArmor 4.x(Ubuntu 24.04 以降)では unconfined モードが導入されました。プロファイルはカーネルに読み込まれていますが、何も制限しません。Ubuntu は、user namespaces を使うプログラム(ブラウザ、torrent クライアントなど)のプロファイル数十個をこの状態にしています。こうしたプロファイルがあると、「読み込み済みプロファイル」カードは琥珀色になり、その数を表示します。例えば読み込み済み 120、enforce 26 に対して unconfined: 90 のように表示されます。実際に保護しているのは enforce のプロファイルだけです。Ubuntu 22.04(AppArmor 3.x)にはこのモードがなく、数値は常に一致します。

sudo aa-status | grep -E "profiles are" # モード別の内訳 sudo aa-enforce /etc/apparmor.d/profile-name # プロファイルを enforce に切り替え
Ubuntu が意図的に unconfined のままにしているプロファイルを enforce に切り替えるのは、十分に理解した上で行ってください。これらは誤って無効化されているのではなく、enforce にするとプログラム自体が動かなくなるためです。complain のプロファイルは別で、ルールはすでに書かれており、適用されていないだけです。

26. debsums のインストール(パッケージ整合性)

debsums は、インストール済みパッケージのファイルがリポジトリのチェックサムと一致するかを検証し、改ざんされたシステムバイナリを検出します(AIDE を補完)。完全スキャンには 1〜2 分かかるため cron で実行し、パネルは data/debsums/debsums.log から結果を読み取ってカテゴリ別に自動整理します(重要なのはバイナリとライブラリのみ)。

ジョブは root の cron(sudo crontab -e)に登録します。既製のラッパー debsums-scan.sh を /usr/local/bin/ に配置し(chmod +x。cron ジョブ一覧を参照)、レポートはパネルの data/debsums/ へ自動的に書き込まれます。

sudo apt install debsums # Cron 行(毎日 4:30): 30 4 * * * /usr/local/bin/debsums-scan.sh >> /path/to/monitor/logs/cron.log 2>&1

ラッパー debsums-scan.sh はパネルの data/ を自動検出するため、パスを指定する必要はありません。

サーバー上の /etc/(設定ファイル)や /usr/share/(リソース)の変更は通常は問題なく、パネルは別の色で表示します。警戒すべきはバイナリやライブラリ(/bin、/sbin、/usr/lib など)の変更で、「バイナリ / ライブラリ」カードにはまさにそれらが表示されます。

27. Lynis レポートの設定

Lynis は手動または cron で実行します。レポートはプロジェクトの data/lynis/ フォルダに保存する必要があります。モニターは lynis-report.dat ファイルを読み取ります。

# 単発実行(パネルのルートへのパスを指定してください): sudo lynis audit system --report-file /path/to/monitor/data/lynis/lynis-report.dat # 毎日の監査 — cron 行(すぐ使えるラッパー lynis-scan.sh は /usr/local/bin/ にあります。概要を参照): 0 3 * * * /usr/local/bin/lynis-scan.sh >> /path/to/monitor/logs/cron.log 2>&1
初回実行後、「Lynis 監査」ページに hardening index、警告、推奨事項がすぐに表示されます。
Lynis ページの「監査を実行」ボタン。 このボタンは(cron を待たずに)パネルから直接 lynis-scan.sh をバックグラウンドで実行します。「スキャン中…」と表示し、完了すると自動でレポートを更新します。そのためには、Web ユーザーがスクリプトを実行できる sudoers 行が必要です。インストーラーが /etc/sudoers.d/monitor に自動で追加します。パネルを手動で/以前にインストールした場合は、ファイルにすでに指定されているユーザーで同じ行を追記してください:
u=$(sudo awk '/NOPASSWD/ && !/lynis-scan/ {print $1; exit}' /etc/sudoers.d/monitor) [ -n "$u" ] && echo "$u ALL=(ALL) NOPASSWD: /usr/local/bin/lynis-scan.sh" | sudo tee -a /etc/sudoers.d/monitor sudo visudo -c && sudo chmod 440 /etc/sudoers.d/monitor

28. Logwatch レポートの設定

Logwatch は日次レポートをプロジェクトの data/logwatch/ フォルダに .txt 形式で保存する必要があります。モニターは最新レポートとアーカイブを表示します。

# 毎日(6:00)— cron 行(/usr/local/bin/ の logwatch_daily.sh ラッパーは準備済み。概要を参照): 0 6 * * * /usr/local/bin/logwatch_daily.sh >> /path/to/monitor/logs/cron.log 2>&1

パネルモジュール

29. ネットワークモニター(組み込み)

ネットワークモニターはインストール不要で、ダッシュボードに組み込まれたページです。ローカルソースからサーバーのネットワーク状態を表示します:

  • インターフェースとトラフィック — /proc/net/dev から;
  • リンク状態(UP/DOWN)と IP — ip 経由;
  • 接続とリッスン中のポート — ss 経由;
  • 24時間分のカーネルネットワークイベント — journalctl -k 経由。

最初の3つのソースは sudo なしで動作するため、インターフェース、トラフィック、接続、ポートはすぐに表示されます。「カーネルイベント」ブロックは journalctl -k を使用し、systemd-journal グループ経由で読み取られるため(「sudo の設定」、項目2)、sudo は不要です。すべてが Web ユーザーからアクセス可能か確認するには:

# www-data として確認(PHP はこのユーザーで動作します): sudo -u www-data bash -c 'cat /proc/net/dev' sudo -u www-data bash -c 'ip -o link show' sudo -u www-data bash -c 'ss -s' sudo -u www-data journalctl -k --no-pager -n 5
「カーネルネットワークイベント」ブロックは、カーネルネットワークスタックのイベント(リンクの up/down 切り替え、キャリアエラー、「network unreachable」)を表示します。ファイアウォールの UFW BLOCK エントリはここには含まれません — それらは「ファイアウォール UFW」と「攻撃マップ」ページにあります。緑のチェックマーク付きの空ブロック = 24時間以内にネットワーク障害はありませんでした。

30. ディスクと SMART

組み込みのページには 3 つの情報が表示されます。

  • ファイルシステム — パーティションの使用状況(df)。90% 以上でゲージが赤くなります。
  • ストレージデバイス — ディスク一覧(lsblk)。実デバイスのみ(loop/snap は非表示)。
  • ヘルス(SMART) — ディスクの状態と属性(smartctl)。

空き容量とデバイス一覧は設定不要ですぐに動作します。SMART には smartmontools パッケージが必要です。Web プロセスはディスクデバイスへ直接アクセスできないため、SMART は cron 経由で data/disk/smart.txt ファイルに取得され、ダッシュボードがそれを読み取ります。

ジョブは root の cron に登録します(sudo crontab -e)。既製のラッパー smart-scan.sh を /usr/local/bin/ に配置し(chmod +x、cron ジョブ一覧を参照)、ラッパー自身がダッシュボードの data/disk/ に書き込みます。

sudo apt install smartmontools # Cron 行(30 分ごと): */30 * * * * /usr/local/bin/smart-scan.sh >> /path/to/monitor/logs/cron.log 2>&1

ラッパー smart-scan.sh はダッシュボードの data/ を自動で見つけるため、パスの指定は不要です。内部で lsblk -e7,11 により loop/cdrom を除外しています。

仮想ディスク(QEMU/KVM など)では通常、全体的な状態 「ヘルス: OK」のみが取得でき、温度・稼働時間・代替処理済みセクターは空になることがあります。これは正常です。物理サーバーではすべての属性が表示されます。

31. パフォーマンス(CPU/RAM/ネットワーク/ディスク)

このページは直近24時間のサーバー負荷履歴を表示します。Load Average、CPU使用率とI/O待ち、RAM/Swap、ネットワークトラフィック(受信/送信)、ディスクI/O(読み取り/書き込み)、ディスクとinodeの使用状況、開いているファイルディスクリプタとMySQL接続数、加えて現在のTCP接続数とプロセス数を確認できます。 既定は24時間の範囲です。上部のボタンで7日または30日に切り替えられます。

データは cron/collect_metrics.php が収集します。5分ごとにカウンタの「生の」スナップショットを1件(/proc/loadavg、/proc/meminfo、/proc/stat、/proc/net/dev、/proc/diskstats、df/df -i、/proc/sys/fs/file-nr、SHOW GLOBAL STATUS LIKE 'Threads_connected')データベースの system_metrics テーブルに書き込みます。パーセンテージと速度は、隣接するスナップショットの差分からページ自身が計算します(ディスク/inode/ディスクリプタ/MySQL接続の使用状況は瞬時値で、再計算は行いません)。root権限は不要で、ソースはroot権限なしで読み取れます。

保存の深さは二段階。 5分ごとの「生の」スナップショットは1日分保持されます(さらに1時間の余裕を持たせ、グラフ最古の点にも計算用の直前の点が残るようにしています)。終了した各時間は1行にまとめられ、この時間単位の系列は30日保存されます。「7日」「30日」の表示が読むのはまさにこの系列です。おかげで1か月分の履歴は約8600行ではなく約720行で済みます。

7日と30日の期間で表示されるのは個々の測定値ではなく時間単位の平均です。5分程度の急上昇はこの縮尺では均されてしまい、ページ上にもその旨が示されます。Load Average については時間行に平均とその時間のピークの両方を保存しているため、グラフには2本の線が出ます。更新直後は長い期間が空です(「この期間の履歴はまだありません」)。時間単位の系列はその時点から蓄積が始まります。
# Cron行(5分ごと): */5 * * * * /usr/local/bin/collect-metrics-all.sh >> /path/to/monitor/logs/cron.log 2>&1

ラッパー collect-metrics-all.sh(cronタスク一覧を参照)は、サーバー上にインストールされたすべてのパネルインスタンスを自動検出し、各インスタンスの cron/collect_metrics.php をサイト所有者の権限で実行します。

コレクタが少なくとも2回実行されるまで(インストール後の最初の約10分間)、ページには「データを収集中」と表示されます。グラフは速度とパーセンテージを計算するために、隣接するポイントのペアが最低1組必要です。

上位プロセス。 グラフの下には、CPU を最も消費している5つのプロセスを示す表が二つあります。

  • 「直近の測定時のプロセス」 — 最後のスナップショットの時点でサーバーに負荷をかけていたもの(時刻はカードの見出しに表示されます)。
  • 「当日のピーク時のプロセス」 — 同じ5つですが、直近24時間で Load Average が最も高かった測定から取ったものです。グラフの山がとうに過ぎたあとで「夜中のあれは何だったのか」に答えてくれます。

グラフが1か月を表示していても、この二つの表は常に直近24時間を示します。プロセスのスナップショットを持つのは5分ごとの行だけで、その行は1日を超えて保存されないためです。

測定自身のプロセス(ps、収集用の PHP スクリプト、その上の cron/sudo の連鎖)は一覧から除外されます。そうしないと毎回先頭を占めてしまうからです。ps は起動した瞬間には CPU をほぼ 100 % 使っているように見えます。同じ分に動いた他の cron ジョブはそのまま表示されます。表があるのはまさにそれを見るためです。

負荷アラート(「設定」→「負荷アラート」)— CPU/RAM/ディスク/inode のしきい値を超えると、パネルは有効なすべてのチャネル(Telegram、メール、Webhook、プッシュ。日次レポートと同じチャネルで、アラート用に別途有効化する必要はありません)で通知を送り、値が正常に戻ったときにもう一度通知します。しきい値を超えたままの間は繰り返し送りません。次の通知は「正常に戻った → 再び超えた」というサイクルのあとに初めて届きます。

しきい値は同じ collect_metrics.php が実行ごと(5分ごと)にチェックするため、別途cronは不要です。「通知済み/未通知」の状態は data/alerts_state.json に、しきい値はパネルの設定に保存されます。

32. セキュリティイベント(5分ごとのセンサー)

日次レポートは「サーバーの調子はどうか」という問いに答えます。センサーが答えるのは別の問い — 「たった今、何が起きたのか」です。5分ごとに応答の速い情報源へ問い合わせ、前回の点検から変わった点だけを記録します。メニューの「セキュリティイベント」ページに検出結果の一覧が表示されます。

時間のかかる点検 — Lynis、debsums、パッケージ更新、SSL の有効期限 — にセンサーは触れません。これらの数値はどのみち1日1回しか変わらず、日次レポートの担当のままです。

検知する対象は九つの情報源です。

  • Fail2ban — 新しい遮断(遮断済みアドレスの一覧を比較するため、解除はイベントになりません。有効な遮断はちょうど1回だけ報告されます)。
  • CrowdSec — 新しい判定。
  • Falco — Emergency、Alert、Critical レベルのイベント。
  • Suricata — 新しい検知。[Priority: 1] の印が付くと重大に格上げされます。
  • ClamAV — スキャンログの新しい FOUND 行。
  • AIDE — 直近の点検結果によるファイルの変更(つまり、その点検が独自のスケジュールで走る頻度以上には報告されません)。
  • SSH — root でのログイン成功(重大)と、これまで見たことのないアドレスからのログイン(警告)。
  • 保護サービス — UFW、Fail2ban、CrowdSec、Suricata、Falco: サービスが停止したときに1件、再び動き出したときに1件。
  • ポート — 外部から到達できるようになったポート。背後にデータベースやキャッシュがあれば重大です。

インストールされていないツール、あるいはパネルがログを読めないツールは、単に黙っているだけです。これはエラーでもイベントでもありません。

最初の点検では何も送信されません。 各情報源は「どこまで読んだか」の印(ファイル data/secwatch_state.json)を保持しており、初回の実行はその印を書き込むだけです。そうでなければ、監視を有効にした途端、ログに何か月も溜まっていたものすべてについて通知が来てしまいます。

設定 — 「設定」→ 「イベントの監視」:

  • 監視を有効にする — 無効の間は cron ジョブが空回りし、一覧も埋まりません。
  • 通知する下限 — 「警告」か「重大」。下限に満たない検出結果も一覧には入りますが、通知には入りません。
  • 間隔(分) — 通知と通知の最小間隔。既定は 15 です。
  • 静音時間帯 — 23-7 の形式で指定する時間帯で、その間は警告を送りません。
重大な検出結果はただちに送信されます。 間隔も静音時間帯もこれを止めません。5分ごとのセンサーがあるのは、まさにそのためです。それでも安全弁はあります。1時間に6件まで。ある情報源が「点滅」し始めた場合に備えたものです。

通知は日次レポートと同じチャネル(Telegram、メール、Webhook、プッシュ)を通ります。別途有効にする必要はありません。検出結果は通知が送られたかどうかに関わらずすべて一覧に記録され、90日保存されます。

イベント一覧。 フィルター: 期間(24時間、7日、30日、90日)、重要度、情報源。直近 500 件が表示されます。「今すぐ点検」ボタンは cron とまったく同じ点検を実行します。設定した直後に5分待たずに済むので便利です。通知が送られなかった場合はその理由も示します(「重要度の下限未満」「静音時間帯」「通知チャネルが未設定」)。メニュー項目の赤いカウンターは、直近24時間の重大イベントの件数です。

アドレスカード。 イベント一覧の行、または関連するアドレスのブロックの行をクリックすると、ひとつのアドレスについて分かっていることがすべて開きます。イベントの時系列、国、そして最初の行に結論です。イベントがちょうどひとつのアドレスに関するものであれば一覧の行がカードを開き、複数ある場合は行内のアドレス自体がリンクになります。結論は4種類です。

  • アクセスの可能性:そのアドレスからのログインが成功し、同じアドレスが他の防御にも引っかかっている、あるいは直後にAIDEやClamAVが反応した、新しいポートが開いた。すぐ確認する価値があるのはこの場合だけです。
  • このアドレスからのログイン:ログインがあるだけで、ほかには何もありません。たいていは所有者が新しい場所から入った場合なので、カードは赤くなりません。
  • ブロック済み:アドレスを捕捉してブロック済みです。防御が仕事をしたので、することはありません。
  • 確認が必要:検出はありますが、ブロックはされていません。繰り返し現れるなら手動で閉じてください。

時系列の下には同じ時間帯にサーバーで起きたことのブロックがあります。そもそもアドレスを持たないイベントです。AIDEは誰がファイルを変えたかを知らず、ClamAVは誰が感染ファイルを持ち込んだかを知りません。アドレスが動いていた時間帯と、その後6時間に当たるものを表示します。ログインとそれに続いて起きたことの組み合わせこそが、試みと実際のアクセスを分けます。

結び付けはアドレスを手がかりにしています。 アドレスを変える攻撃者はこの手がかりを断ちます。カードは防御が見たものを示すだけで、侵入とご自身の新しい場所からのログインを見分ける役目までは負いません。

スケジュール。 ジョブは root の crontab に登録します(sudo crontab -e)。メトリクス収集と同時に始まらないよう、2分ずらしてあります。

# 5分ごと — セキュリティイベントの点検 2-57/5 * * * * /usr/local/bin/security-watch-all.sh >> /path/to/monitor/logs/cron.log 2>&1
必ずラッパー経由で実行してください。php cron/security_watch.php を直接呼び出してはいけません。 ラッパーはサーバー上のパネルの全インストールを見つけ、サイト所有者の権限で点検を実行します。パネルがウェブから使っているのとまったく同じ sudo 権限です。root の crontab から直接呼び出すと、状態ファイル data/secwatch_state.json の所有者が root になり、パネルの「今すぐ点検」ボタンが動かなくなります。
サーバーを自動設定で用意した場合、このジョブはすでに追加済みです。あとは設定で監視を有効にするだけです。

33. ウェブサーバー: リクエストと応答コード

「ウェブサーバー」ページはアクセスログを解析し、リクエストが何件あり、そのうちどれだけがエラーで終わり、誰が送ってきたのかを示します。ModSecurity が答えるのは「何がブロックされたか」、このページが答えるのは「そもそもサイトに何が届いていたか」です。

表示される内容: リクエスト数、RPS、エラー率、5xx の個別カウンター、送出したデータ量。時間ごとのリクエストのグラフ、2xx/3xx/4xx/5xx の内訳、よく現れるアドレス、よく現れる IP、よく起きるエラー(コードとパス)、クライアント(User-Agent)。期間は24時間または7日です。

情報源は combined 形式のアクセスログなので、Nginx でも Apache でも使え、追加モジュールも不要です。パネルは次の標準ファイルの中からログを探します。

  • /var/log/nginx/access.log
  • /var/log/apache2/access.log
  • /var/log/apache2/other_vhosts_access.log
  • /var/log/httpd/access_log

これらに加えて、パネルは /var/log/nginx/、/var/log/apache2/、/var/log/httpd/ にある *access*.log と *requests*.log のファイルも調べます。これにより、サイトが CustomLog ディレクティブで独自のファイルに書き込むログも見つかります。たとえば Arcivéo のインストーラーが作成する arciveo-monitor_requests.log です。

読み取るログは1つだけです。Nginx が Apache の前段にある構成では、2つのログを足すと各リクエストを二重に数えてしまうからです。上の一覧のうち、直近1日以内に書き込みのあった最初のログを使います(名前のパターンで見つかったファイルはその後に、新しい順に試します)。1日どのログにも書き込みがなければ、空でないログのうち最も新しいものを使います。そのため、logrotate で空になったログや、停止したウェブサーバーが残したログが、稼働中のログを覆い隠すことはありません。読み取るのはログの末尾(3 MB)で、結果は5分間キャッシュされます。そのためアクセスの多いサイトでは、選んだ期間が完全には覆われないことがあります。実際にカバーできた範囲はグラフの上に表示されます。

「パネルがウェブサーバーのログを読めません」。 原因はほぼ常に権限です。ログとそのディレクトリは adm グループに属していますが、ウェブユーザーはこのグループに入っていません。Arcivéo のインストーラーはウェブユーザーをこのグループに追加します。手動でインストールした場合は、ログを表示するという一つの動作だけを許可した root 所有の小さなラッパーで解決します(ModSecurity のページも同じ仕組みです)。ラッパーはパネルと同じ規則でログを選びます。コマンドはページ自体にも表示されますが、ここにも載せておきます。1つのコマンドなので、まとめて貼り付けてください。
sudo tee /usr/local/bin/monitor-weblog >/dev/null <<'EOF' && sudo chmod 755 /usr/local/bin/monitor-weblog && echo 'www-data ALL=(root) NOPASSWD: /usr/local/bin/monitor-weblog' | sudo tee /etc/sudoers.d/monitor-weblog >/dev/null && sudo visudo -c #!/bin/sh L="/var/log/nginx/access.log /var/log/apache2/access.log /var/log/apache2/other_vhosts_access.log /var/log/httpd/access_log $(ls -t /var/log/nginx/*access*.log /var/log/apache2/*access*.log /var/log/apache2/*requests*.log /var/log/httpd/*access_log 2>/dev/null)" for f in $L; do [ -r "$f" ] && [ -n "$(find "$f" -size +0 -mmin -1440 2>/dev/null)" ] && exec tail -c 3000000 "$f"; done for f in $(ls -t $L 2>/dev/null); do [ -r "$f" ] && [ -s "$f" ] && exec tail -c 3000000 "$f"; done EOF

ウェブユーザーは www-data とは限りません。サイトの PHP-FPM プールがどのユーザーで動いているかを確認し(ps -o user= -C php-fpm)、そのユーザー名を sudoers の行に入れてください。ログの名前に access も requests も含まれない場合は、ラッパー内の L=… の行の先頭にそのパスを追加してください。

「選択した期間のレコードがありません」— ログは読めたものの、条件に合う行が見つからなかったということです。サイトが実際に開かれなかったか、ログがちょうどローテートされ(logrotate)、最初から始まったかのどちらかです。

既知の脆弱性を狙った試行。パネルはログのリクエストを、よく使われる約 20 種のエクスプロイトのシグネチャと照合します。PHP-CGI、Apache のディレクトリトラバーサル、PHPUnit、Log4Shell、ルーター・カメラ・VPN ゲートウェイの脆弱性などです。試行ごとに、このサーバーで成功しうるかどうかを事実に基づいて示します。根拠は、インストール済みの Apache と PHP のバージョン、Java の有無、ファイル要求に対するサーバーの応答コードです。赤で示すのは、実際に成功しうるものだけです。

Web サーバーの堅牢化。パネルはサーバー上の各サイトに 127.0.0.1 経由でリクエストを送り、訪問者が受け取る内容を確認します。Web サーバーと PHP のバージョンが見えるか、TRACE メソッドが有効か、保護用ヘッダー Strict-Transport-Security, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy、Content-Security-Policy があるかです。サイトはパネルを開いたアドレスと、パネルが読める場合はバーチャルホストのファイルから取得します。結果は 10 分ごとに更新されます。

不足があると、表の下にコマンドが 1 つ表示されます。別ファイル zz-arciveo-hardening.conf を作成し、設定テストに合格した場合にのみ Web サーバーを再読み込みします。サイトがすでに送っているヘッダーは重複させません。CSP は 1 行では提案しません。ポリシーはサイトごとに作る必要があり、そうしないとサイト自身のスクリプト、スタイル、フォントが読み込めなくなるためです。Apache で変更を元に戻すには:

sudo a2disconf zz-arciveo-hardening && sudo systemctl reload apache2
Web サーバーのバージョンがすでに隠されている場合、エラーページの署名 (ServerSignature) は指摘しません。バージョンがなければ、署名に表示されるのはサーバー名とホスト名だけです。

34. 攻撃マップ(GeoIP)

「攻撃マップ」ページは geoiplookup コマンドで IP から国を判定します。GeoIP パッケージがないと国は判定できず、マップ上に点は表示されません:

sudo apt install geoip-bin geoip-database # 確認: geoiplookup 8.8.8.8
sudo は不要です。データベース /usr/share/GeoIP/GeoIP.dat は全ユーザーが読み取り可能で、結果は tmp/geoip_cache.json にキャッシュされます。マップ本体(Leaflet + OpenStreetMap タイル)はブラウザで読み込まれるため、ダッシュボードを開くコンピューターにはインターネット接続が必要です。

35. 外部への露出、アップデートと自動アップデート

ダッシュボードに組み込まれた2枚のカードで、ツールの「オン/オフ」ではなく、サーバーの実際の防御状態を表示します。インストール不要、sudoなしでローカルに読み取ります。

外部への露出 — 全インターフェース(0.0.0.0/[::])で待ち受けし、外部からアクセス可能なサービスの数です。DBMSやキャッシュ(MySQL、PostgreSQL、Redis、MongoDB、Memcached、Elasticsearch)が外部に露出している場合は赤で強調表示されます — これは直接的な穴です(セキュリティ評価に−10)。ソース: ss -tuln。

カードが赤い場合は、DBMSを外部に対して閉じてください: 127.0.0.1 にバインドするか(MySQL/PostgreSQL設定の bind-address、Redisの bind 127.0.0.1)、UFWでポートを閉じてください。
「開いているポート」≠「外部からアクセス可能」。 127.0.0.1(loopback)で待ち受けするサービスはサーバー自身にしか見えず、ポートが「開いて」いても外部からはアクセスできません。そのため、loopbackにバインドされたポート25のPostfixは安全です: 自動設定が inet_interfaces = loopback-only を設定します(加えて中立的な smtpd_banner により、バージョン漏洩に関するLynisの指摘 MAIL-8818 を解消します)。「外部への露出」カードは 0.0.0.0/[::] で待ち受けするものだけを外部としてカウントし、loopbackサービスは含まれません。
Lynis MAIL-8818を手動で(メールを自分で構築した場合): /etc/postfix/main.cf に smtpd_banner = $myhostname ESMTP(バージョンとOSなし)と inet_interfaces = loopback-only を設定し、その後 sudo systemctl restart postfix を実行します。

セキュリティアップデート — インストール待ちのセキュリティパッチの数と、カーネル更新後に再起動が必要かどうかを示します(パッチがある場合、セキュリティ評価に−5)。ソース: /usr/lib/update-notifier/apt-check、ファイル /var/run/reboot-required。詳細な一覧は「セキュリティアップデート」ページにあります。

# アップデートをインストール: sudo apt update && sudo apt upgrade # 外部に何が待ち受けているか確認: ss -tuln | grep -E '0\.0\.0\.0|\[::\]'
アップデートカードはUbuntu/Debianで動作します(update-notifier-common)。apt-check がない場合、モニターは apt-get -s upgrade でパッチを数えます。

セキュリティ自動アップデート(unattended-upgrades) — 「セキュリティアップデート」ページの専用カードで、セキュリティパッチの自動インストールが有効かどうか、最後に実行されたのがいつかを表示します。sudoは不要で、ステータスは apt-config dump で読み取ります。

sudo apt install unattended-upgrades sudo dpkg-reconfigure -plow unattended-upgrades # 有効化 # 有効になっているか確認: apt-config dump | grep Unattended-Upgrade

メンテナンス

36. バックアップ

バックアップは最大の保険です。データの喪失はどんな侵入よりも深刻です。必要なものは2つ——サーバー/サイトのバックアップと、それとは別にパネルのDBのバックアップ(ユーザー、WebAuthnキー、設定、ライセンスが含まれます)です。

方法A — HestiaCP:ユーザーのBackupタブ → バックアップ作成ボタン(またはサーバー設定でスケジュール実行)。バックアップにはサイトとそのDBが含まれます。

方法B — 手動(cron):DBのダンプ+パネルのdata/ディレクトリのアーカイブ:

# root の cron(sudo crontab -e)— 毎日 2:30 にバックアップ(自分の名前/パスに置き換えてください): 30 2 * * * mysqldump -u root MY_DB | gzip > /var/backups/monitor-db-$(date +\%F).sql.gz 40 2 * * * tar czf /var/backups/monitor-data-$(date +\%F).tar.gz -C /path/to/monitor data # 14日より古いアーカイブを削除: 0 3 * * * find /var/backups -name 'monitor-*' -mtime +14 -delete
同じサーバー上のバックアップはミスからは守れますが、サーバー自体の喪失には対応できません。アーカイブは外部ストレージ(別のサーバー、S3、rcloneでクラウドへ)にコピーしてください。復元が実際に機能することを確認してください。

37. パネルのアップデートと移行

新しいバージョンへのアップデート。 まずバックアップを取ってください。その後、データを保持したままコードファイルを上書きします。

  • 上書きする(コード): public/、includes/、assets/、cron/、database/、およびルートの .htaccess(フロントコントローラー — ルーティングを旧バージョンのまま残してはいけません)、manifest.json、sw.js;
  • 触れないもの: config.php(DB情報)、data/(レポート)、logs/、tmp/(セッションとキャッシュ)。
# アップロード後 — PHPキャッシュをクリア(opcache有効時): sudo systemctl reload php*-fpm
FileZilla が SSH_FX_PERMISSION_DENIED(Permission denied)と表示する場合。 パネルのファイルは www-data の所有です(インストール時にそう設定されています)が、SFTP クライアントは書き込み権限のないご自身のユーザーで接続しています。「動かすため」にパネル全体を www-data に渡すことこそ、まさにこのエラーを招く原因です。以下の 3 つの方法のいずれでも問題は解決します。
# 方法 A(推奨)— 所有者を分ける: コードは自分、作業用フォルダは Web サーバー。 # Web サーバーはパネルのコードへの書き込み権限をまったく得ません: sudo chown -R deploy:www-data /path/to/monitor sudo chown -R www-data:www-data /path/to/monitor/data /path/to/monitor/tmp /path/to/monitor/logs sudo find /path/to/monitor -type d -exec chmod 755 {} \; sudo find /path/to/monitor -type f -exec chmod 644 {} \; sudo chmod 750 /path/to/monitor/data /path/to/monitor/tmp /path/to/monitor/logs sudo chmod 640 /path/to/monitor/config.php # 方法 B — 現在の所有者はそのままに ACL を重ねる(何も移動しません): sudo apt install -y acl sudo setfacl -R -m u:deploy:rwX /path/to/monitor sudo setfacl -R -d -m u:deploy:rwX /path/to/monitor # 方法 C — www-data グループ経由。より簡単ですが、パネルのファイルへの # 書き込み権限は Web サーバーにも渡ります(PHP の脆弱性でコードを差し替えられます): sudo usermod -aG www-data deploy sudo find /path/to/monitor -type d -exec chmod 2775 {} \; sudo find /path/to/monitor -type f -exec chmod 664 {} \; sudo chmod 640 /path/to/monitor/config.php sudo chmod 2750 /path/to/monitor/data /path/to/monitor/tmp /path/to/monitor/logs
方法 A が安全な理由。 パネルが書き込むのは 3 つのディレクトリだけです — data/(レポート)、tmp/(セッションとキャッシュ)、logs/。これらは www-data の所有のまま残ります。残りはコードで、Web サーバーには読み取りだけが必要で、それは 644 の権限を持つ www-data グループが与えます。副次的な利点として、PHP に脆弱性があってもパネルのファイルはもう書き換えられません。ホスティングパネル(HestiaCP など)では方法 A は不要です。そこではサイトのファイルはもともと SFTP で接続するアカウントの所有であり、Web サーバーはグループ経由で読み取ります。
方法 B の落とし穴: その後にファイルへ chmod を実行すると ACL マスクがリセットされ、アクセスが黙って失われます。「権限を整理」した後にアップロードが再び Permission denied で止まる場合は、両方の setfacl コマンドを再実行してください。
方法 C の 2 が setgid です。SFTP でアップロードしたファイルは www-data グループに残り、そうでなければパネルは上書きできません。方法 C の後は FileZilla で接続し直してください — 新しいグループは新規ログイン時にしか反映されません。確認: id deploy(www-data グループが表示されること)と ls -ld /path/to/monitor(drwxrwsr-x — 文字 s は setgid が設定されていることを示します)。

別サーバーへの移行:

  1. 新しいサーバーでサイト + HTTPS を構築します(手動インストールのページを参照)。
  2. パネルの全ファイルを config.php、data/ とともにコピーします。
  3. DBを移行: 旧サーバーで mysqldump → 新サーバーでインポート。config.php のDB情報を修正してください。
  4. 新しいサーバーで再設定: sudoers、adm グループへの所属、cronジョブ。
  5. ライセンスはドメインに紐づいています — ドメインが同じであれば、キーはそのまま機能し続けます。

38. このサーバーに2つ目のサイト(もう1つのドメイン)

パネルはすでに動作しており、同じサーバーにもう1つサイトが必要になった場合です — モニターの隣に独自ドメインで置きます。自動セットアップ後のサーバーは通常の Apache + PHP-FPM なので、サイトに必要なのは3つだけです。専用のディレクトリ、専用の vhost、専用の証明書です。パネルには手を触れません。パネルはサーバー全体を監視するため、2つ目のドメインのために2つ目のパネルを入れる必要はありません。

この節はホスティングパネルのないサーバー向けです。HestiaCP などのパネルが入っている場合は、手作業ではなくそのパネルからドメインを追加してください。また、サイトを多数(さらにメールも)運用する予定なら、パネルは自動セットアップの前にインストールします。スクリプトはパネルを検出して追加専用モードに切り替わりますが、後からインストールしたパネルは Apache・ファイアウォール・メールの設定を作り直してしまいます。
コマンドで置き換える値。 site2.example.com は新しいサイトのアドレスです。独立したドメイン(mycompany.com)でもサブドメイン(shop.mycompany.com)でもかまいません。Apache にとって違いはなく、異なるのは DNS レコード(手順1)と証明書の発行(手順5)だけです。site2 は単なるサイトの短い名前で、ディレクトリ /var/www/site2 と設定ファイル site2.conf の名前に使われています。ドメインとは無関係で、Apache はアドレスを ServerName からのみ取得します。名前は自由で、たとえば shop.mycompany.com なら shop が便利です(ディレクトリ /var/www/shop、設定 shop.conf)。

1. DNS。 ドメイン登録業者の DNS 画面で、このサーバーの IP を指す A レコードを作成します。

# サブドメイン (shop.mycompany.com): タイプ: A 名前: shop 値: 203.0.113.10 TTL: 3600 # 独立したドメイン (mycompany.com) — www 付きのアドレスも使えるように2件: タイプ: A 名前: @ 値: 203.0.113.10 TTL: 3600 タイプ: A 名前: www 値: 203.0.113.10 TTL: 3600

数分後に確認します。dig +short site2.example.com がサーバーの IP を返すはずです。

2. サイトのディレクトリ。

sudo mkdir -p /var/www/site2 sudo chown -R www-data:www-data /var/www/site2

3. vhost。 ブロックはまとめてターミナルに貼り付けます。PHP-FPM ソケットのパスは自動で判別されます。

PHPSOCK=$(ls -1 /run/php/php*-fpm.sock 2>/dev/null | head -1) # PHP-FPM ソケットの自動検出 sudo tee /etc/apache2/sites-available/site2.conf > /dev/null <<'EOF' <VirtualHost *:80> ServerName site2.example.com # 独立したドメインの場合: www でも開けるように下の行の # を外します。 # サブドメインならこの行は不要です。 #ServerAlias www.site2.example.com DocumentRoot /var/www/site2 <Directory /var/www/site2> Options -Indexes +FollowSymLinks AllowOverride All Require all granted </Directory> <FilesMatch \.php$> SetHandler "proxy:unix:__PHPSOCK__|fcgi://localhost" </FilesMatch> ErrorLog ${APACHE_LOG_DIR}/site2_error.log CustomLog ${APACHE_LOG_DIR}/site2_access.log combined </VirtualHost> EOF sudo sed -i "s#__PHPSOCK__#${PHPSOCK}#" /etc/apache2/sites-available/site2.conf

4. サイトを有効化し、再読み込みの前に設定を検証します。

sudo a2ensite site2.conf sudo apache2ctl configtest # 期待される結果: Syntax OK sudo systemctl reload apache2

5. SSL。 certbot は自動セットアップですでにインストール済みです。この時点で DNS はサーバーを指している必要があります。

# サブドメイン — アドレスは1つ: sudo certbot --apache -d site2.example.com # 独立したドメイン — 最初から www も一緒に(手順1の2つの A レコードがサーバーを指し、 # www が ServerAlias に入っている必要があります。そうでないと certbot は2つ目のアドレスを検証できません): sudo certbot --apache -d site2.example.com -d www.site2.example.com

3つ目以降のドメインも同じ手順です。ドメインとディレクトリを変えて手順1〜5を繰り返してください。

「よそからの」リクエストに応答するのは誰か。 Host がどの ServerName とも一致しない場合(IP でのアクセスや、あなたのサーバーに向けられた他人のドメイン)、Apache は /etc/apache2/sites-enabled/ の中でアルファベット順で最初の有効なサイトを返します。標準の 000-default は自動セットアップが無効化するため、この「当番」の役目はパネルの vhost(arciveo-monitor.conf)が担い続けます。各サイトを自身の名前で開く分には影響ありません。IP で別のサイトを開きたい場合は、そのファイル名をアルファベット順で先に来るようにします(たとえば 000-site2.conf)。
保護はサーバー全体に適用されます。UFW、Fail2ban、CrowdSec、ModSecurity、Suricata はすでに動作しており、新しいサイトも自動的にカバーされるため、個別の設定は不要です。ポート 80/443 もすでに開いています。サイトには専用のログが作られます: /var/log/apache2/ の site2_error.log と site2_access.log。
新しいサイトのディレクトリをパネルのディレクトリの中に置かないでください。 パネルのウェブルートは独自の .htaccess(フロントコントローラー)で制御されているため、入れ子にしたサイトは想定と異なるものを返します。サイトは並べて配置してください: /var/www/monitor、/var/www/site2。

39. アクセスの復旧(セキュリティキー・パスワード・IPブロックの喪失)

ログインできない場合は、サーバー上のデータベースで直接修復できます。データベースを開いてください(データベース名は config.php から):

sudo mysql MY_DB

WebAuthn のセキュリティキーを紛失した(第二要素が通らない)場合は、2FA を無効化し、パスワードでログインして新しいキーを登録してください:

UPDATE users SET webauthn_enabled = 0;

パスワードを忘れた場合は、新しいハッシュを設定してください(サーバー上で生成して置き換えます):

# 新しいパスワードのハッシュを生成: php -r "echo password_hash('NEW_PASSWORD', PASSWORD_BCRYPT), \"\n\";" # データベースへ(生成したハッシュを貼り付け): # UPDATE users SET password = '$2y$10$...' WHERE username = 'admin';

IPフィルターで自分を締め出した場合は、制限を無効化してください:

UPDATE settings SET value = '0' WHERE name = 'ip_restriction_enabled';
データベースへはいつでもアクセスできます:サーバー上で sudo mysql、または phpMyAdmin / ホスティングパネルのデータベース欄から。復旧後は WebAuthn と IPフィルターを再度有効化してください。

40. すべての cron ジョブを一箇所で

ジョブの一覧はサーバーの root crontabに記述します(sudo crontab -e で追加)。使用するツールの行だけを残し、パスは自分のサーバーに合わせて調整してください。

# モニターのサーバー crontab(root)— 次で記述: sudo crontab -e # 01:30 — 危険なパス(web, home, temp)を対象とした ClamAV スキャン → カード「検査済みファイル数」「最終スキャン」 30 1 * * * /usr/local/bin/clamav-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 02:00 — AIDE によるファイル整合性チェック(明示的な --config が必要) 0 2 * * * /usr/bin/aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1 # 起動時 — /var/lib/aide の権限を復元(パッケージの tmpfiles ファイル # aide-common.conf が 0700 にリセットし、パネルがデータベースを見られなくなる) @reboot chmod 755 /var/lib/aide # 03:00 — Lynis によるセキュリティ監査 0 3 * * * /usr/local/bin/lynis-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 04:00 — ipsum ブロックリストの更新(level 1) 0 4 * * * /usr/local/bin/load-ipsum.sh >> /path/to/monitor/logs/cron.log 2>&1 # 起動時の ipsum セット読み込みは ipsum-load.service が担当(ファイアウォールより前、でないと # UFW が before.rules 内のセットを認識しない)— cron ではない。ここは上記の日次リフレッシュのみ。 # 06:00 — Logwatch レポート 0 6 * * * /usr/local/bin/logwatch_daily.sh >> /path/to/monitor/logs/cron.log 2>&1 # 30 分ごと — SMART ディスクチェック */30 * * * * /usr/local/bin/smart-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 04:30 — debsums によるパッケージ整合性 30 4 * * * /usr/local/bin/debsums-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 08:00 — Email と Telegram へのスケジュールレポート 0 8 * * * /usr/local/bin/daily-report-all.sh >> /path/to/monitor/logs/cron.log 2>&1 # 1 時間ごと — パッケージ一覧の更新(「セキュリティアップデート」カード用) 0 * * * * /usr/bin/apt-get update -qq >/dev/null 2>&1 # 5 分ごと — リソースのスナップショット(CPU/RAM/ネットワーク/ディスク)※「パフォーマンス」ページ用 */5 * * * * /usr/local/bin/collect-metrics-all.sh >> /path/to/monitor/logs/cron.log 2>&1 # 5分ごと、上のスナップショットからずらして実行 — セキュリティイベントのセンサー: # 新しい遮断、IDS の検知、停止した保護サービス、開いたポート。 # 設定で監視を有効にするまで、このジョブは何もしません。 2-57/5 * * * * /usr/local/bin/security-watch-all.sh >> /path/to/monitor/logs/cron.log 2>&1 # 15分ごと — 複数のサーバーを一つの一覧で見られるよう、サーバー要約をアカウントへ送信。 # 設定で要約を有効にするまで、このジョブは何もしません。 3,18,33,48 * * * * /usr/local/bin/fleet-push-all.sh >> /path/to/monitor/logs/cron.log 2>&1
各項目の詳細は該当セクションを参照してください。バックアップジョブ(前セクション)も同じ crontab に追加します。編集後は確認を: sudo crontab -l、そして cron サービスが有効であること。
cron の時刻はサーバーのタイムゾーンであり、config.php の TIMEZONE ではありません。 定数 TIMEZONE は PHP にのみ影響します(パネルが日付をどう表示するか)が、cron デーモンは OS のシステム時刻でジョブを実行します。サーバーのタイムゾーンが自分と異なると、「08:00」のレポートは違う時刻に届きます。例: サーバーが別のタイムゾーン(Asia/Shanghai, UTC+8)にあり、あなたは東京(UTC+9)にいる場合 →「08:00」のレポートはあなたの時刻で 09:00 に届きます。確認し、必要ならシステムのタイムゾーンを自分に合わせてください:
# サーバーの現在のタイムゾーンを確認: timedatectl # 自分のタイムゾーンを設定(例)して cron を再起動: sudo timedatectl set-timezone Asia/Tokyo sudo systemctl restart cron
これで 0 8 * * * の行は現地時間の 08:00 に実行されます。そうでなければ cron 自体をずらすことになりますが、冬時間/夏時間の切り替え時に再びずれてしまいます — そのためシステムのタイムゾーンを設定するのが正しいやり方です。
すぐ使えるラッパースクリプト。 その稼働用コピーと crontab のサンプル(crontab.txt)は、プロジェクトの隣、public_html の外にある system/ フォルダにあります。これはサイトの一部ではなく、web ルートにアップロードする必要はありません。サーバー上のシステムパスに配置してください(上記 crontab のとおり):
  • lynis-scan.sh → /usr/local/bin/(chmod +x)— lynis audit system を実行し、スキャン中はフラグ /tmp/lynis-running を立て、lynis-report.dat をパネルの data/lynis/ にコピーします。
  • logwatch_daily.sh → /usr/local/bin/(chmod +x)— 日次の Logwatch レポート(sshd, fail2ban, sudo, postfix)を data/logwatch/ に生成します。
  • smart-scan.sh → /usr/local/bin/(chmod +x)— ディスクの状態(smartctl)を data/disk/ に取得します。
  • debsums-scan.sh → /usr/local/bin/(chmod +x)— パッケージ整合性(debsums)を data/debsums/ でチェックします。
  • clamav-scan.sh → /usr/local/bin/(chmod +x)— 危険なパス(web, home, temp)を対象とした ClamAV アンチウイルススキャン。サマリを /var/log/clamav/scan.log に書き込み、そこから ClamAV ページが読み取ります(上記 crontab の 01:30 の行)。
  • load-ipsum.sh → /usr/local/bin/(chmod +x)— ipset の ipsum セット(level 1)をその場で更新し、有効なファイアウォール規則を壊さない(上記 crontab の 04:00 行)。
  • daily-report-all.sh → /usr/local/bin/(chmod +x)— パネルのレポート cron/daily_report.php を実行します(上記 crontab の 08:00 の行)。
  • daily_report.php — すでにパネルに含まれており(cron/daily_report.php)、daily-report-all.sh 経由で実行されます。個別に設置する必要はありません。
  • collect-metrics-all.sh → /usr/local/bin/(chmod +x)— パネルの cron/collect_metrics.php を実行します(「パフォーマンス」ページ、上記 crontab の */5 の行)。collect_metrics.php はすでにパネルに含まれており、個別に設置する必要はありません。
  • security-watch-all.sh → /usr/local/bin/(chmod +x)— パネルの cron/security_watch.php をサイト所有者の権限で実行します(「セキュリティイベント」ページ、上の crontab の 2-57/5 の行)。security_watch.php はパネルに同梱されており、別途の設置は不要です。
  • fleet-push-all.sh → /usr/local/bin/(chmod +x)— パネルの cron/fleet_push.php を通じてサーバー要約をアカウントへ送信します(上の 3,18,33,48 の行)。
  • crontab.txt (system/cron/) — ジョブのサンプル。必要な行を sudo crontab -e で記述してください。
crontab 内のスクリプトのパスは、実際に配置した場所と一致していなければなりません。
スクリプトを /usr/local/bin/ に置く方法。 FileZilla から直接そこへ書き込むことはできません — ディレクトリは root の所有で、SFTP クライアントは SSH_FX_PERMISSION_DENIED を受け取ります。手順はこうです: まずファイルを /tmp にアップロードし(そこは誰でも書き込めます)、次に一つのコマンドで所定の場所へ移します:
# FileZilla で:「リモートサイト」欄に /tmp を入力し、そこへスクリプトをアップロード、 # 次に SSH で(install が所有者と権限を同時に設定、chown/chmod は不要): sudo install -o root -g root -m 755 /tmp/lynis-scan.sh /usr/local/bin/lynis-scan.sh rm -f /tmp/lynis-scan.sh # 確認: ファイルが所定の場所にあり、権限が rwxr-xr-x、構文が正常 bash -n /usr/local/bin/lynis-scan.sh && ls -l /usr/local/bin/lynis-scan.sh
ディレクトリを取り違えないでください: 必要なのはサーバールートの /tmp であり、/var/tmp でもパネル内の tmp/ でもありません(後者は www-data の所有で、あなたのユーザーからはアクセスできません)。FileZilla のツリーでは /tmp は最上位のブランチで、var の隣にあり、その中ではありません。
サーバーを自動セットアップで構築しましたか? これらのラッパーとその cron ジョブは、スクリプトによってすでにインストール済みです(/usr/local/bin/、ログは /var/log/arciveo-cron.log)— 手動で行うことは何もありません。
スクリプトがパネルを探す場所。 ラッパーはドメイン非依存です。/home/*/web/*/public_html と /var/www/* を巡回してパネルのインストールを見つけ、レポートをその data/ に置きます。パネルが別のパスにある場合は、スクリプト内の for app in … の行に追加してください。そうしないと Lynis/SMART/debsums/Logwatch のレポートがパネルに反映されません。
cron.log とアクセス権。 ファイル logs/cron.log を最初に作成するのは root crontab です — root の所有となり、パネルの「cron ログ」タブはそれを読むことも消去することもできなくなります。事前に web ユーザー(サイトディレクトリの所有者。HestiaCP ではアカウント、例: admin)としてファイルを作成しておいてください — そうすれば root crontab は所有者を変えずに追記するだけになります:
# web ユーザーで事前に作成(cron 行を追加する前に): sudo -u OWNER touch /path/to/monitor/logs/cron.log # cron.log がすでに root crontab で作成されている場合 — web ユーザーに渡す: sudo chown OWNER:OWNER /path/to/monitor/logs/cron.log sudo chmod 644 /path/to/monitor/logs/cron.log
ディレクトリの所有者を調べる: stat -c %U /path/to/monitor。
パネルからの管理。「システム」セクションに 「Crontab」ページがあり、SSH なしでジョブの表示・追加ができます。パネルが編集するのは、パネル自身で追加したジョブのみ(root-crontab 内の専用ブロック、サービスコメントで区切られています)。すでに crontab にあるもの(上記のリスト)はすべて、そこに「その他のサーバージョブ」という読み取り専用リストとして表示され、「エディターにコピー」ボタンがあります — このボタンはスケジュール/コマンドを追加フォームに移すだけで、元の行には手を触れません。既存のジョブをパネル管理下に「移行」するには — エディターにコピーして保存し、その後で古い行を手動で削除してください(sudo crontab -e)。でないと二重に実行されます。
サーバー側での一度きりの設定。 このページには特権的なラッパースクリプトが必要です — 生の sudo crontab ではなく(それではパネルのセッションにアクセスできた誰でもが root へ直接昇格できてしまいます)、2 つのコマンド(list/set)だけを持ち、サービスコメントで区切られた自分のブロックのみを操作する限定的なスクリプトです。一度だけインストールしてください:
sudo install -m 0755 -o root -g root /dev/stdin /usr/local/sbin/arciveo-cron <<'ARCIVEO_CRON_EOF' #!/bin/bash set -euo pipefail BEGIN='# >>> ARCIVEO-CRON-MANAGED (edited from the panel; do not edit by hand) >>>' END='# <<< ARCIVEO-CRON-MANAGED <<<' cmd=${1:-} case "$cmd" in list) [ "$#" -eq 1 ] || { echo "usage: ${0##*/} list" >&2; exit 2; } crontab -l -u root 2>/dev/null || true ;; set) [ "$#" -eq 1 ] || { echo "usage: ${0##*/} set (body on stdin)" >&2; exit 2; } body=$(cat) if grep -qF "$BEGIN" <<<"$body" || grep -qF "$END" <<<"$body"; then echo "invalid body: markers not allowed" >&2; exit 2 fi current=$(crontab -l -u root 2>/dev/null || true) tmp=$(mktemp); trap 'rm -f "$tmp"' EXIT { if grep -qF "$BEGIN" <<<"$current"; then awk -v b="$BEGIN" '{print} $0==b{exit}' <<<"$current" else [ -n "$current" ] && printf '%s\n' "$current" echo "$BEGIN" fi printf '%s\n' "$body" echo "$END" if grep -qF "$END" <<<"$current"; then awk -v e="$END" 'f{print} $0==e{f=1}' <<<"$current" fi } > "$tmp" crontab -u root "$tmp" ;; *) echo "usage: ${0##*/} <list|set>" >&2; exit 2 ;; esac ARCIVEO_CRON_EOF echo "www-data ALL=(ALL) NOPASSWD: /usr/local/sbin/arciveo-cron" | sudo tee -a /etc/sudoers.d/monitor sudo visudo -c
web ユーザーは www-data と異なる場合があります — サイトの PHP-FPM プールがどのユーザーで動作しているかを確認し(ps -o user= -C php-fpm)、それを sudoers の行に指定してください。
新しいファイルが別の所有者でアップロードされ、ページが「Access denied.」を返す。 ファイル public/crontab_monitor.php が、サイトの他のファイルとは別のシステムユーザー(例: root)で FTP/SFTP 経由でアップロードされると、web サーバーはそれを読めません。隣接ファイルと所有者・権限を照合して合わせてください:
ls -la public/crontab_monitor.php public/ssl_monitor.php sudo chown OWNER:OWNER public/crontab_monitor.php sudo chmod 644 public/crontab_monitor.php

診断

41. ツールがインストール済みなのに「未インストール」と表示される

モニターは dpkg-query(APT のパッケージデータベース)でツールの有無を判定します。apt 以外の方法(手動、snap、ソースからのビルド)でインストールした場合、dpkg では認識されません。

# dpkg で確認: dpkg -l fail2ban | grep '^ii' dpkg -l auditd | grep '^ii' # バイナリのパスを探す: which ufw fail2ban-client auditctl # www-data からの sudo をテスト: sudo -u www-data sudo fail2ban-client status sudo -u www-data sudo ufw status verbose

42. トラブルシューティング(500、データ表示なし)

500 エラー — PHP、nginx、およびモニター自体のログを確認してください:

tail -50 /var/log/nginx/error.log tail -50 /var/log/php*-fpm.log # モニターのログ: tail -50 logs/monitor_$(date +%Y-%m-%d).log # フォルダの権限: ls -la data/ tmp/ logs/
ホスティングパネル(HestiaCP、ISPmanager、cPanel)上のダッシュボードですか? そこでは PHP は www-data ではなく ユーザーアカウント(例えば admin — サイトディレクトリの所有者)で動作します。すべての sudo ルールとグループ所属(adm、systemd-journal)は このユーザーに対して設定する必要があります。さもないとサービスが稼働していてもモジュールが「非アクティブ / 0」と表示します。実際の PHP ユーザーを調べるには: ps -o user= -C php-fpm | sort -u またはサイトディレクトリの所有者 stat -c '%U' /path/to/monitor。以下のすべてのコマンドで www-data の代わりにそれを使用してください。自動インストールはウェブユーザーを自動的に判別し、それに対して sudoers を設定します。

データが表示されない — ほとんどの場合 sudo 権限が未設定です。ウェブユーザーとして具体的なコマンドを確認してください(www-data を自分のユーザーに置き換え)。フラグ -n = パスワードなし(PHP と同じ)— パスワードを求められる場合、sudoers にルールがありません:

sudo -u www-data sudo -n fail2ban-client status sudo -u www-data sudo -n ufw status verbose sudo -u www-data sudo -n ipset list -t ipsum sudo -u www-data sudo -n /usr/sbin/ausearch -m USER_LOGIN -ts today sudo -u www-data sudo -n /usr/sbin/aa-status sudo -u www-data sudo -n /usr/sbin/psad --Status sudo -u www-data journalctl -u falco --no-pager -n 5
ツールは動作しているのにモジュールが「非アクティブ」/「0」と表示する(例えば端末で sudo aa-status はプロファイルを表示するのに「AppArmor」ページは「非アクティブ」)。原因: ウェブユーザーにそのモジュールのコマンドに対する sudo 権限がありません。上記の一覧から確認してください: パスワードを求められる場合、不足している行を /etc/sudoers.d/monitor に追加してください(「sudo の設定」)。よくある「新しい」コマンド: /usr/sbin/aa-status(MAC)、/usr/sbin/psad --Status(PSAD)。
特定のページ(Falco、ModSecurity、Auditd、UFW の開放ポート)が空の場合、sudo に関するセクションの一覧と照合してください: おそらく apache2ctl、ausearch、aa-status、ss が許可されていないか、ウェブユーザーが adm/systemd-journal グループに属していません(そこから fail2ban/auth/modsec のログと journalctl — Falco とカーネルイベント — が読み取られます)。

43. サーバーにデータがあるのにページが空になる

症状:サーバーにはデータがあり(shell では見える)、ページには「データなし」または誤ったステータスが表示される。たとえば AIDE のデータベースは作成済みなのに「未初期化」と表示される。

原因は open_basedir です。多くのパネルやホスティングは PHP-FPM プールをドメインのディレクトリに制限するため、システムパス(/var/lib/aide、/var/log、/proc など)に対する PHP 関数 file_exists()、file_get_contents()、filemtime() がブロックされます。モニターはこうしたパスを標準のシステムコマンド(cat、test、stat)で読み取ることで回避します。

# shell からファイルが見えるか(モニターの読み取り方法): sudo -u www-data bash -lc 'test -e /var/lib/aide/aide.db && echo VISIBLE || echo NO' # ドメインのプールの現在の open_basedir 値: grep -ri open_basedir /etc/php/*/fpm/pool.d/ 2>/dev/null
shell では「見える」(VISIBLE)のにページで見えない場合、それは open_basedir です。正しい対処はシステムコマンドでの読み取りです(AIDE とネットワークモニターでは対応済み)。open_basedir を /var や /proc に拡張する必要はなく、安全性も下がります。

44. SSLページが動作しない

モニターはポート443経由でドメインに直接接続して証明書を確認します。サーバー自体からドメインにアクセスできない場合や、ポートがファイアウォールで閉じられている場合は、確認が失敗します。

# 証明書を手動で確認: echo | openssl s_client -connect monitor.example.com:443 2>/dev/null \ | openssl x509 -noout -dates # 到達性を確認: curl -I https://monitor.example.com
ドメインはモニターがNginxの設定ファイル(/etc/nginx/sites-enabled/、/etc/nginx/conf.d/)とApache(/etc/apache2/sites-enabled/)から自動で取得し、さらにHTTP_HOSTの現在のホストも含めます。
サブドメインの自動検出。 サブドメインはCertificate Transparencyの公開ログから自動で検出され、ネットワーク経由で確認されます。別のサーバーに配置されている場合でも同様です。手動で追加する必要はありません。

45. 複数あるうち1つのDBしか表示されない

モニターはconfig.phpのユーザーでMySQLに接続しますが、そのユーザーは自分のデータベースにしかアクセスできません。MySQLはinformation_schemaに権限のあるデータベースのみを表示するため、それ以外は見えません。

モニターにすべてのDBを表示させるには、このユーザーに読み取り専用の権限を付与してください(rootで一度だけ実行し、config.phpのユーザー名に置き換えてください):

sudo mysql -u root GRANT SELECT, PROCESS, SHOW DATABASES ON *.* TO 'DB_USER'@'localhost'; FLUSH PRIVILEGES; EXIT;
SELECT ON *.* は読み取り権限のみを付与します。変更・削除・作成は一切できないため、監視用途では安全です。
このGRANTがない場合、ダッシュボードには自分のデータベースしか表示されません。これはエラーではなく権限の制限です。ダッシュボードはsudo mysqlを使用しません。データベースの一覧は独自のPDO接続で取得しています。

46. PostgreSQL が「データベース」ページに表示されない

PostgreSQL には postgres ユーザー権限が必要ですが、パネルの Web ユーザーにはこの権限がありません。PHP から広い権限の sudo psql を開くのは危険です。代わりにパネルは、パラメーターなしの限定的なラッパーを呼び出し、バージョン・接続数・サイズ付きのデータベース一覧のみを出力します。これを作成してください:

sudo tee /usr/local/bin/monitor-pgstat >/dev/null <<'EOF' #!/bin/sh # Arciveo Security Monitor - read-only PostgreSQL version, connections and per-database size sudo -u postgres psql -tAc "SELECT version();" | grep -oE 'PostgreSQL [0-9.]+' echo "---" sudo -u postgres psql -tAc "SELECT count(*) FROM pg_stat_activity;" echo "---" sudo -u postgres psql -tAc "SELECT datname || '|' || pg_size_pretty(pg_database_size(datname)) FROM pg_database WHERE datistemplate = false;" EOF sudo chown root:root /usr/local/bin/monitor-pgstat sudo chmod 755 /usr/local/bin/monitor-pgstat # /etc/sudoers.d/monitor 内 (ユーザー = PHP-FPM を実行しているユーザー): # www-data ALL=(ALL) NOPASSWD: /usr/local/bin/monitor-pgstat
PostgreSQL を使わない場合は、sudoers から monitor-pgstat の行を削除し (手動インストールの手順 13)、スクリプト自体も作成しないでください: PostgreSQL のカードは非アクティブのまま表示されます。

47. アラートが発生した — 対処方法

ダッシュボードは何が起きているかを示します。以下は典型的なケースでどう対処するかです。基本方針は、慌てず、正規のアクティビティ(自分の操作、更新、バックアップ)と照合し、深刻度に応じて対応することです。

  • 攻撃マップ/fail2ban のban多数 — インターネット上のどのサーバーでも通常のことです(ボットが常に SSH/Web を試行します)。重要なのはbanが機能していることです。SSH ログインが鍵のみ(パスワード無効)で、自分の IP が ignoreip にあることを確認してください。
  • ModSecurity がリクエストをブロックした — WAF がサイトへの攻撃を防いでいるだけで、これがその役目です。正規のトラフィックがブロックされる(誤検知)場合は、詳細から rule id を探し、CRS の設定に除外を追加してください。
  • AIDE: ファイルが変更された — 自分がやったこと(パッケージ更新、設定編集は通常)とリストを照合してください。触れていないシステムバイナリの変更は要警戒です。正規の変更後は AIDE のデータベースを更新してください。
  • debsums: バイナリ/ライブラリが変更された(/etc 外、/usr/share 外)— 改竄の可能性があります。パッケージを照合し(debsums PACKAGE_NAME)、疑わしければ再インストールしてください(apt install --reinstall)。
  • ClamAV/maldet: 脅威を検出 — 隔離されたファイルを確認し、開かないでください。サイトのディレクトリ内の Web シェルの場合は、サーバーを隔離し侵入経路(脆弱なプラグイン、認証情報の漏洩)を探してください。
  • Falco: 重大なイベント(コンテナ内での shell 起動、機密ファイルへのアクセス)— イベントを解析してください: 誰のプロセスか、何が起動したか。多くは正規の管理者アクティビティです。
  • 外部への露出: DB/キャッシュが赤色 — 直ちに閉じてください: サービスを 127.0.0.1 にバインドするか、UFW でポートを閉じます。これは実際の穴です。
  • SSL の期限切れ間近/期限切れ — 証明書を更新してください(Let's Encrypt は自動更新されます。されない場合は certbot renew やダッシュボードの設定を確認してください)。
  • セキュリティ更新が保留中 — インストールしてください: sudo apt update && sudo apt upgrade。カーネル更新後はサーバーを再起動してください。
実際の侵害の兆候(不明なプロセス/ユーザー、変更されたバイナリ、外向きのスパム、不明な cron ジョブ): サーバーを外部アクセスから遮断し、解析用のバックアップを取り、データが重要であれば信頼できるバックアップからクリーンなサーバーを立ち上げてください — rootkit を確実に除去するのは困難です。