
複数のNextcloudインスタンスを一元監視
NcStatusCheckは、単一のWebインターフェースから複数のNextcloudサーバーの健全性を追跡できる監視ツールです。NextcloudとPHPのバージョンを分析し、アップデートの推奨事項を提供します。

< 15d / < 7d)にバッジを表示occ app:listをNextcloudアプリストアと照合し、レビューが必要なアプリをフラグ付け — ブロッキング(アップグレードブロック、非互換、本番環境でのテストアプリ)および情報提供(成熟度/変動シグナル:最近公開、プレ1.0、アルファ/ベータ/RCビルド、リリースの急増、新しくリリースされたバージョン)📦)と「Containers to watch」(🐳)の2つのブロックが、すべてのインスタンスにわたってフラグ付けされたアプリ/ Dockerイメージを1エントリずつ集約。グループごとのタイプフィルターと、影響を受けるインスタンスとそのバージョンを一覧表示するポップアップ付きALERT_WEBHOOK_URL)、および/またはバッチごとのダイジェストメール(ALERT_EMAIL_TO)をメールサーバーに直接SMTP送信(ベンダー版PHPMailer、SMTPリレー未設定時はローカルMTAにフォールバック)。また、ゆっくりとしたシグナル(ALERT_CHECKS)もカバー:SSL証明書期限切れ(段階的)、脆弱/非推奨のNextcloudバージョン、古いPushプローブ、重大な監査レポート、ブロッキングアプリの発見 — 新しい条件ごとに1回アラート送信、リマインダーはなしnc_update_grace_days)。同日にバグがあるリリースを追いかけないため⚠️警告、📦アプリ、🔄Docker、🔒SSL、🔴オフライン、🚧メンテナンス)を表示nc-audit.sh レポートをプッシュした場合のサーバー監査レポートセクションlocalStorageに保存chmod 700)を生成、オプション変更に応じて自動更新ncstatuscheck/ ├── Frontend │ ├── index.php # Main entry point │ ├── template.html # HTML template (dashboard) │ ├── admin.html # Administration interface │ ├── detail.php # Server detail page │ ├── audit.php # Server-audit script distribution page (nc-audit.sh) │ ├── troubleshooting.php # Probe troubleshooting guide │ ├── app.js / admin.js / detail.js / audit.js / troubleshooting.js │ └── style*.css # One stylesheet per page family ├── APIs (HTTP) │ ├── api.php # Main monitoring API │ ├── detail-api.php # Detail page API (serverinfo + warnings + acks + availability) │ ├── push-api.php # Push reception + trigger + audit-report reception │ ├── ack-api.php # Warning acknowledge / unmute │ ├── admin-api.php # Version configuration routing │ ├── servers-admin-api.php # Server list management │ ├── nextcloud-versions-api.php # Official version scraping │ ├── nextcloud-apps-api.php # App store catalog (slim cache) for the apps audit │ ├── php-versions-api.php # PHP branch support data │ └── apps-warnings-api.php # Manual app warnings (known-bug list) CRUD ├── Shared modules (lib/) │ ├── auth.php # Auth + CSRF + URL redaction (defense in depth) │ ├── csrf-client.js # Auto-inject X-CSRF-Token in fetch() │ ├── nextcloud-client.php # Centralized HTTP client → remote Nextclouds │ ├── servers-store.php # Single source of truth for servers.json │ ├── uptime-state.php # Up/down state machine + transition journal + availability │ ├── alerts.php # Proactive alert dispatch: webhook + email digest │ ├── alerts-checks.php # Slow-signal alerts (SSL/version/push/audit/apps) + dedup state │ ├── smtp-mailer.php # SMTP transport adapter over vendored PHPMailer │ ├── phpmailer/ # Vendored PHPMailer (3 files + LICENSE, pinned in VERSION) │ ├── apps-warnings-manager.php # Manual app warnings storage │ ├── ui-common.js # NcUI: notify / confirm / prompt + shared app-audit messages │ ├── url-guard.php # Anti-SSRF (loopback, RFC1918, link-local…) │ ├── json-cache.php # Locked JSON read/write helpers │ └── version-config-manager.php # Version rules CRUD ├── Business logic │ ├── version-rules.php # NC / PHP status analysis engine │ ├── warnings-rules.php # Configuration warning engine │ ├── apps-rules.php # Installed-apps audit engine (store catalog cross-check) │ ├── cron-update.php # Full collection script, CLI only (twice a day) │ └── cron-ping.php # Lightweight up/down probe, CLI only (every 5 min) ├── Tools (never web-served — blocked by nginx/.htaccess) │ ├── tools/nc-audit.sh # Standalone server audit script (root, read-only) │ └── tools/ncstatuscheck-push-core.sh # Generic Push probe core (fleet-shared) ├── Tests │ └── tests/run.php # Plain-PHP test suite (no framework): php tests/run.php ├── Configuration │ ├── config.php # Central configuration (git-ignored) │ └── servers.json # Server list with tokens (git-ignored) └── Cache ├── servers_data.json # All server data ├── serverinfo_.json # Raw per-server cache (Extended) ├── push_.json # Last push payload per server ├── ack_.json # Acknowledged warnings per server ├── audit_.json # Last nc-audit.sh report per server ├── version-config.json # Version configuration ├── uptime_state.json # Up/down state per server (mini uptime) ├── uptime_history.json # Bounded up/down transition journal (availability % + incidents) ├── alerts_state.json # "Already alerted" memory of the check alerts ├── nextcloud_versions.json # Official NC versions ├── nextcloud_apps.json # App store slim catalog (apps audit) ├── apps-warnings.json # Manual app warnings (admin-curated) ├── .csrf_secret # CSRF HMAC secret (binary, 0600) └── *.log # Activity logs
deploy/ansible/ # Fleet deployment of the Push core (Ansible / scp) deploy/docker/ # Container packaging of the monitor itself
## 🔌 収集モード
各モードは排他的ではありません — サーバーはExtendedモードとPushモードを同時に有効にできます。
| モード | バッジ | ソース | 収集データ |
|------|-------|--------|----------------|
| **Basic** | *(なし)* | `/status.php` + HTTPヘッダー | Nextcloudバージョン(露出している場合、PHP/ウェブサーバー) |
| **Extended** | `⚡ Extended`(紫→エラーまたは古いデータでオレンジ) | `/ocs/v2.php/apps/serverinfo/api/v1/info`(`NC-Token`使用) | NCバージョン、PHP、ウェブサーバー、OPcache、Redis、DB、アクティブユーザー… |
| **Push** | `📡 Push`(青→エラーまたは古いデータでオレンジ) | POST to `push-api.php` | cronスクリプト経由でリモートNCインスタンスからプッシュされたデータ |
**serverinfo NC-Token**は、**Nextcloud 設定 → 管理 → システム**で確認できます。
**プッシュトークン**は管理インターフェースから生成されます。管理者は、監視対象インスタンスにデプロイするための即時使用可能なbash cronスクリプト(`chmod 700`)を提供します。
Extendedモードのデータは、[nextcloud/serverinfo](https://github.com/nextcloud/serverinfo) アプリによって提供されます。このアプリは監視対象インスタンスにインストールされ、有効化されている必要があります。
**フォールバック動作**: Extended APIに到達できない場合(接続エラー、無効なトークン、アプリ未インストール)、NcStatusCheckは自動的に `/status.php` にフォールバックし、少なくともNextcloudバージョンを取得します。
**Pushの古さしきい値**: `auto_push_interval + 30分`以内にデータを受信しなかった場合、Pushサーバーは古いとみなされます。デフォルトのプッシュ間隔は12時間です。
### ダッシュボードテーブルのカラム
メインダッシュボードには5つのカラムが表示されます: **Server** | **NC Version** | **PHP** | **Probes** | **Health**
**Probes**カラムには、各サーバーのアクティブな収集モードが表示されます:
- `⚡ Extended` バッジ(紫、接続エラーまたは古いデータでオレンジに変化)
- `📡 Push` バッジ(青、しきい値内にデータを受信しない場合はオレンジに変化)
- 両方のモードがアクティブな場合、両方のバッジが同時に表示されることがあります
- バッジなし = Basicモードのみ
### Healthカラム
Healthカラムは、対処すべき状況がある場合のみ何かを表示します:
| インジケーター | バッジ | 意味 |
|-----------|-------|---------|
| Offline | `🔴 Offline` | インスタンスに到達不可(HTTPプローブ失敗)、"offline for X"表示 |
| Active warnings | `⚠️ N` | N件の設定問題 |
| Apps audit | `📦 N` | 確認すべきインストール済みアプリ(アップグレードブロック/互換性なし) |
| SSL expiry | `🔒 N d` | 証明書が間もなく期限切れ — オレンジ `< 15d`、赤 `< 7d` または期限切れ |
| Docker updates | `🔄 M` | M件のコンテナ更新が利用可能 |
| All OK | *(空)* | 報告事項なし |
| No data | `?` | Basicモードでプッシュデータなし |
#### アップ/ダウンとSSL有効期限
NcStatusCheckは、サーバーごとに**最小限の**アップ/ダウン状態を保持します(現在の状態と最終変更日のみ — 時系列や履歴ページはなし)。"Up"はアウトバウンドのHTTPSプローブがインスタンスに到達したことを意味し、ダウンの場合のみ赤い**Offline**バッジが表示されます。同じHTTPSプローブ中に、**SSL証明書の有効期限**が無料で読み取られ(`CURLOPT_CERTINFO`)、期限が近づくと表面化されます。どちらも詳細ページで完全に確認できます。*注: これらのアウトバウンドチェックは、モニターが接触しないPush専用インスタンスには適用されません。*
#### Apps audit(`📦`)
Pushサーバーがインストール済みアプリ(`occ app:list`、プッシュスクリプトv3+)を報告すると、NcStatusCheckはそれらをNextcloudアプリストアカタログと照合し、確認する価値のあるアプリをフラグ付けします。**シグナリングのみ** — ツールは何も無効化しません。候補を表面化します(アプリが実際に使用されているかどうかは判断できません)。**事実でバイナリな**シグナルのみが使用されます。`📦 N`バッジは、ブロックする発見(現在のNCバージョンに対応する互換リリースがない、NC N+1のリリースがない→アップグレードをブロックする、またはテスト/開発用アプリが本番環境で有効のまま)をカウントします。情報提供の発見(インスタンス上のアプリのバージョンが古い、上流で放棄された、PHP非互換)は詳細ページのみに表示されます。発見は、警告と同じアクノリッジメカニズムを使用してミュートできます。
### `servers.json` 形式```json
[
{"url": "https://cloud.example.com"},
{"url": "https://cloud2.example.com", "serverinfo_token": "abc123def456"},
{"url": "https://cloud3.example.com", "serverinfo_token": "...", "push_token": "xyz789"}
]
server { server_name monitoring.your-domain.com; root /var/www/ncstatuscheck; index index.php;
# HTTP Basic Authentication
auth_basic "Monitoring Access";
auth_basic_user_file /etc/nginx/.htpasswd;
# Protect sensitive files/dirs (tests/run.php has no CLI-only guard — it must
# never be reachable over HTTP; same blocklist as deploy/docker/nginx.conf)
location ~ ^/(cache/|\.git|deploy/|tools/|tests/) {
deny all;
return 404;
}
# .txt covers servers.txt (legacy server list — real monitored URLs)
location ~* \.(log|json|txt)$ {
deny all;
return 404;
}
# Security headers for static HTML pages (admin.html, template.html).
# PHP pages (index.php, detail.php) send the same headers themselves
# via send_security_headers() in lib/auth.php.
location ~* \.html$ {
add_header X-Content-Type-Options nosniff always;
add_header X-Frame-Options DENY always;
add_header Referrer-Policy no-referrer always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'" always;
try_files $uri =404;
}
# Standard PHP configuration (adjust the socket to your PHP version —
# use a security-supported one: 8.2 has been EOL since December 2025)
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.4-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
location / {
try_files $uri $uri/ =404;
}
}
> **Apache**: このリポジトリには、上記の `deny` ルールを反映した `.htaccess` ファイルが同梱されています(ルート: `*.log`/`*.json`/`*.txt` および `.git` をブロック; `cache/`、`tools/`、`deploy/`、`tests/`: `Require all denied`)。これらは vhost が `AllowOverride FileInfo AuthConfig`(または `All`)を設定している場合のみ機能します — Debian の `/var/www` のデフォルトは `AllowOverride None` であり、その場合は vhost 内でルールを直接複製してください。いずれの場合でも、HTTP Basic 認証は vhost で設定する必要があります。
### デプロイメント
1. **リポジトリをクローンする**```bash
git clone https://gitlab.com/jp.louvel/ncstatuscheck.git
cd ncstatuscheck
`config.php` を編集し、パスとURLをあなたの環境に合わせて調整してください:```php
define('MONITOR_PATH', '/var/www/ncstatuscheck');
define('MONITOR_URL', 'https://monitoring.your-domain.com'); // your public URL
define('CACHE_DIR', MONITOR_PATH . '/cache');
サーバーは管理インターフェース(⚙️ Adminボタン)から直接管理できます。
また、手動で servers.json を作成することもできます。```json
[
{"url": "https://cloud.example.com"},
{"url": "https://nextcloud.mycompany.org", "serverinfo_token": "your_token_here"}
]
> **`servers.txt` からの移行**: `servers.txt` ファイルが存在する場合、初回アクセス時に自動的に `servers.json` に変換されます。その後、`servers.txt` を削除できます。
4. **アクセス権限を設定する**
nginx/PHP-FPM はそれぞれのユーザーで実行されます(Debian/Ubuntu では `www-data`、RHEL 系では `nginx`/`apache` — 以下で調整)。自分のログインユーザーとしてクローンした場合、そのユーザーはほぼ間違いなく `www-data` でもそのグループでもないため、`chmod` だけでは Web サーバーに**まったくアクセス権がありません**。読み取りすらできず(すべてのリクエストが 403/404 になります):```bash
chown -R www-data:www-data /var/www/ncstatuscheck # adjust the user:group to your distro
chmod 750 /var/www/ncstatuscheck
chmod 750 cache img
chmod 640 *.php *.html *.js *.css *.md
chmod 600 config.php servers.json servers.txt # secrets / serverinfo & push tokens
chmod 660 cache/*.json cache/*.log
servers.jsonがまだ存在しない場合(手動の手順ではなく管理UIで作成させる場合)、上記のchmod 600は単に作用する対象がありません — 問題ありません:ServersStore::save()は書き込みのたびにファイルを自身で0600に chmod します。そのため、管理UIを通じて(再)作成されたservers.jsonが、内部のサーバー情報やプッシュトークンとともにグループまたはワールド読み取り可能なままになることは決してありません。
6. **スケジュールされたタスク(オプション)**
2つの補完的なcronジョブ — 両方を**同じ `crontab -` 呼び出しで**インストールしてください:
`crontab -` はstdinから新しいcrontab全体をインストールし、追加は行いません。そのため、
2回(行ごとに1回)実行すると、*2番目*のジョブだけが残り、1番目はエラーなしで静かに消えます。
これにより、既存のcrontabを消去する代わりに、 (`crontab -l` を先にパイプすることで) crontabにすでにあるものも保持されます:```bash
(crontab -l 2>/dev/null; cat <<'EOF'
# Full collection (NC/PHP versions, serverinfo, app-store catalog) — twice a day
0 6,18 * * * cd /var/www/ncstatuscheck && php cron-update.php
# Lightweight reachability probe (status.php only -> up/down state) — every 5 min
*/5 * * * * cd /var/www/ncstatuscheck && php cron-ping.php
EOF
) | crontab -
Re-running this appends duplicates if these lines are already present — check
with crontab -l first if unsure.
cron-ping.php は意図的に最小限にしています。各インスタンスの status.php をチェックし、アップ/ダウンの状態 (cache/uptime_state.json) を更新するだけなので、負荷なく頻繁に実行できます。インスタンスは、UPTIME_FAIL_THRESHOLD 回連続してプローブが失敗した場合にのみ ダウン と判定されます(デフォルト 2 → 5分間隔で約10分)。アップ への回復は即座に行われます。完全な cron-update.php は他のすべてに対して変更されません。
上記の手順1〜6の代わりに、NcStatusCheck は小さな docker compose スタック(PHP-FPM + nginx + cronコンテナ)としても実行できます。リポジトリはそのままバインドマウントされ、ビルド手順やComposerは不要で、ベアメタルのレイアウトをそのままミラーリングし、コンテナ化されているだけです。プレーンHTTPのみを提供します(デフォルトではポート 8080)。独自のTLS終端リバースプロキシを前面に配置してください。
完全なセットアップ(設定、パーミッションの注意点(uid 82、servers.json の事前作成)、HTTPベーシック認証、cron、アップデート、バックアップ)は、すべて deploy/docker/README.md にあります。まずそちらを参照してください。このセクションは意図的に単なるポインタであり、同じ手順の2つのコピーを同期させることを避けるためです。
https://monitoring.your-domain.com にアクセス任意の サーバー名またはそのHealthインジケーターをクリックしてアクセス可能。
Basic サーバー(ExtendedまたはPushプローブなし)の場合、簡略化されたページに利用可能なデータ(NCバージョン、Webサーバー、HTTPプロトコル)が表示され、プローブを有効にするための通知と提案が含まれます。
Extended / Push サーバーの場合、完全な詳細ページに個別のセクションが表示されます:
NcStatusCheck はいくつかのRESTエンドポイントを公開しています:
メインAPI (api.php)
GET ?action=get_data — データを取得(キャッシュまたはリフレッシュ)POST ?action=refresh_data — 全サーバーの強制更新Push API (push-api.php)
push_token ヘッダー付きの POST — リモートNCインスタンスからプッシュデータを受信POST ?action=request_push_all — 設定済みのすべてのプッシュサーバーに即時プッシュを要求(リモートcronスクリプトが消費するトリガーフラグを設定)管理UIによって生成されるcronスクリプトは2つに分割されています。汎用コア
/usr/local/bin/ncstatuscheck-push.sh— すべてのサーバーで同一(すべてのロジック)— 小さなインスタンスごとの設定/etc/ncstatuscheck/<slug>.conf(SERVER_URL、SLUG、OCC_CMD、DOCKER_ENABLED、SKOPEO_ENABLED)によって駆動されます。ncstatuscheck-push.sh /etc/ncstatuscheck/<slug>.conf [--test]として呼び出されます。コアはグループ/ワールド書き込み可能な設定ファイルの読み込みを拒否します(コードインジェクション防止)。これは**マルチターゲット(ファンアウト)**です。データは一度収集され、
/etc/ncstatuscheck/targets-<slug>.confにリストされたすべてのモニターにプッシュされます(1行に1つのモニターurl|push_token[|http_user|http_pass])。各モニターの管理者は、自身を登録する冪等なコマンドを発行します。1台のホスト上の複数のNextcloudインスタンス: インスタンスごとのパスには、監視対象URLから派生した
<slug>がサフィックスとして付加されます(例:latest.ezeo.coop→ ):、、、、状態 。コアのみが共有されるため、同じホスト上のインスタンスが衝突することはありません。
詳細API (detail-api.php)
GET ?server=<url> — Extended/Pushサーバーの完全なサーバー情報データ + 計算された警告管理API
admin-api.php — バージョン設定servers-admin-api.php — サーバー管理(get_servers、add_server、remove_server、update_server_token、generate_push_token、remove_push_token)nextcloud-versions-api.php — 公式バージョンnc-audit.sh)監視とは別のサブシステム:スタンドアロンの読み取り専用bashスクリプト(tools/nc-audit.sh)で、Nextcloudサーバー上でrootとして実行し、Web + PHP + データベースのチューニングを一度/毎月監査し、マシンの物理容量(RAM、CPU、ディスクタイプ)とクロスチェックします。管理された監視 サービスを目的としています。クライアントがインストールし、モニターはレポートを受け取るのみで、マシン/ネットワークアクセスは不要です。スクリプトは設定を読み取りのみ行い(変更なし)、色付きレポートを出力し、コピーを /tmp に書き込みます。
チェック内容:サーバー容量(RAM/CPU/SSD-HDD、スワップネス、共有サーバー検出)・Nextcloud(バージョン、cron、キャッシュ、Redisランタイム、DBタイプ、ログ)・PHP/PHP-FPM(実際のサービングSAPI、OPcacheランタイム、マルチプールメモリ)・Apache(MPM対応ワーカーメモリ)・Nginx・PostgreSQL・MariaDB・セキュリティ衛生(fail2ban または CrowdSec + バウンサー + コミュニティブロックリスト;保留中のアップデート / 再起動 / 古いライブラリ上のサービス)・RAMバジェット調整(InnoDB + FPM + Apache vs 実RAM)・オプションツールが既に存在する場合の詳細分析(mysqltuner、pt-variable-advisor、apache2buddy、sar/iostat)。```bash
curl -fsSL https://gitlab.com/jp.louvel/ncstatuscheck/-/raw/master/tools/nc-audit.sh -o /usr/local/bin/nc-audit.sh chmod 700 /usr/local/bin/nc-audit.sh
sudo nc-audit.sh # auto-detect, dedicated server sudo nc-audit.sh /var/www/nextcloud # explicit path (or NC_PATH=…) sudo NC_RAM_BUDGET_PCT=50 nc-audit.sh # shared host: size to 50% of RAM
**マルチインスタンスホスト**(複数のNextcloud + 共有データベース)。`NC_RAM_BUDGET_PCT`
はスタック全体の**合計**予算です。そのうち`NC_PHP_SHARE_PCT`%(デフォルト60、残りは
DB + Web + OS をカバー — DB負荷の高いサーバーでは低く設定)がPHPの割り当てとなり、
**ウェイト**(相対的な重要度 — パーセンテージでもMBでもない)に基づいてFPMプール全体に分割され、
プールごとにターゲット`pm.max_children`が設定されます。```
target = PHP_share × (weight / Σ weights) / ~50 MB per process
The target is a ceiling the budget allows, not a value you must set (only raise a pool that actually saturates). Weights are your call — the tool never guesses them.```bash
sudo NC_RAM_BUDGET_PCT=70 NC_INSTANCES="poolA:4,poolB:2,poolC:1" nc-audit.sh
sudo NC_RAM_BUDGET_PCT=70 nc-audit.sh --tune-fpm
**レポートのプッシュバック**(オプション、プッシュインフラを再利用): `nc-audit.sh --push
/etc/ncstatuscheck/<slug>.conf` は監査を実行し、レポートをモニターにPOSTします。モニターはそれを保存し、サーバーの詳細ページ(「🩺 サーバー監査」セクション)に表示します。通常は月次cronです。`audit.php` のウェブページ(管理者向け、ベータ版)はスクリプトを配布し(ダウンロード + インライン + GitLab ワンライナー)、そのバージョンを表示します。
> **詳細分析ツールはスクリプットによってインストールされることはありません** — すでに存在する場合のみ実行されます(`curl | bash` や自動インストールはなし)。それぞれ `timeout` で制限されます。
## 🔧 詳細設定
### バージョンルールのカスタマイズ
評価ルールは管理インターフェースから設定可能:
**Nextcloud のステータス:**
- `dev` — 開発版
- `stable` — 現在の安定版
- `oldstable` — 以前のサポート対象安定版
- `deprecated` — 非推奨版
**PHP のステータス:**
- `recommended` — 推奨バージョン
- `supported` — サポート対象バージョン
- `deprecated` — 非推奨バージョン
### 設定変数
`config.php` を編集して設定を調整:```php
// Environment: 'dev' or 'prod'
define('ENV', 'prod');
// Paths and URLs
define('MONITOR_PATH', '/var/www/ncstatuscheck');
define('MONITOR_URL', 'https://monitoring.your-domain.com');
// Main server cache duration
define('CACHE_MAX_AGE', 86400); // 24 hours
// Official Nextcloud versions cache duration
define('VERSIONS_CACHE_AGE', 86400);
// Consecutive failed probes before a server is marked "down" (min 1)
define('UPTIME_FAIL_THRESHOLD', 2);
// Proactive alerts — webhook on a confirmed up/down state change.
// Empty URL = disabled. Format: 'slack' (default, also Mattermost/Google Chat),
// 'discord', or 'raw' (structured JSON). The URL usually carries a secret, so it
// is never logged in full — see config-example.php for details.
define('ALERT_WEBHOOK_URL', '');
define('ALERT_WEBHOOK_FORMAT', 'slack');
// Check alerts on top of up/down (cron-update cadence, 2×/day): SSL expiry
// tiers, vulnerable (below min_secure) or deprecated Nextcloud version, stale
// Push data, critical audit report, blocking apps-audit finding. Edge-triggered with a persisted state
// (cache/alerts_state.json): one alert per NEW condition, no reminders, re-arms
// when resolved (renewed cert, fixed/acked app…). First run arms silently.
define('ALERT_CHECKS', 'ssl,version,push_stale,audit,apps'); // '' = up/down only
define('ALERT_SSL_DAYS', '30,14,7'); // days-left tiers
// Email channel, independent of the webhook (either one arms the alerting).
// One digest mail per batch. Recommended transport: direct SMTP submission to
// your mail server (vendored PHPMailer, lib/phpmailer/ — nothing to set up on
// the host). Without ALERT_SMTP_HOST it falls back to PHP mail() (local MTA).
define('ALERT_EMAIL_TO', ''); // comma list of recipients, '' = off
define('ALERT_EMAIL_FROM', ''); // default: ncstatuscheck@<hostname>
define('ALERT_SMTP_HOST', ''); // e.g. 'mail.example.org', '' = mail() fallback
define('ALERT_SMTP_PORT', 587);
define('ALERT_SMTP_SECURITY', 'starttls'); // 'starttls' | 'tls' | 'none'
define('ALERT_SMTP_USER', '');
define('ALERT_SMTP_PASS', '');
オプションの完全なコメント付きリスト(
DEMO_MODEとPUSH_SCRIPT_VERSIONを含む)についてはconfig-example.phpを参照してください。
lib/csrf-client.js + csrf_require()).htaccess ファイルでミラーリング、servers.json は自動的に 0600 にchmod(内部にトークンあり)X-Frame-Options、nosniff、Referrer-Policyを設定hash_equals() がそのエントリのトークンで成功した場合のみ受け入れられます。これにより、侵害された監視サーバーは他のサーバーのデータを読み取りも上書きもできません。request_push / request_push_all は管理認証とCSRFの背後にあります。侵害されたクライアントシナリオに対してエンドツーエンドで検証済み。config.php はgitignoredであり、サーバーごとに手動編集されるため、徐々にずれていきます。コード内のほぼすべての定数にはフォールバックがあるため、これは静かに発生します。管理ページ上部のバナーは実際に問題がある場合にのみ、問題がある場合のみ報告します:バンプによって残された PUSH_SCRIPT_VERSION、アラートトランスポートがまったく設定されていない、任意の header() の前にバイトを出力する末尾の閉じタグ、書き込み不可能なキャッシュディレクトリ、定数が欠落してデフォルトに静かにフォールバックしていること。
設計上読み取り専用であり、保存アクションはありません。通知タブと同じ理由です:config.php はroot所有であり、秘密情報を含んでいます。ファイルの内容は決して転送されず、それに関する事実のみが転送され、秘密情報は読み取られません。
上記のすべてはアプリケーションレベルです:インターネット上の誰でもモニターに 到達 して調査でき、パスワードだけがそれを防ぎます。IPフィルタリングタブは、アプリの 前に 許可リストを配置するルールを生成するため、未知のホストはまったく通信できません。これは多層防御であり、Basic認証やプッシュトークンの代わりにはなりません — そして、それはレビューして貼り付けるためのテキスト のみを生成し、Webサーバーやファイアウォールの設定を書き込むことはありません。
2つのクラスのソースがあり、意図的に不平等です。これにより、侵害された監視サーバーが管理画面に到達することを防ぎます:
| クラス | 対象 | 到達可能 |
|---|---|---|
push | プッシュモードのみの監視インスタンス | /push-api.php、それ以外はなし |
admin | 踏み台 / VPN / 固定オフィスIP | すべて |
ベーシック/拡張モードでポーリングされるインスタンスは入方向接続を開かず、許可リストエントリも一切取得しません。
アドレスは2つのソースから得られ、その違いは重要です:監視対象ドメインのDNSレコードはその イングレス アドレスですが、プッシュはその エグレス から送信されます。両者が異なる場合、後者のみが機能します。したがって、push-api.php はすべてのプッシュの実際の発信元アドレス(プッシュキャッシュ内の source_ip)を記録し、タブはそれを許可リストに追加し、不一致を報告します。サーバーが一度もプッシュしていない場合は、DNS A+AAAA にフォールバックし、その旨を表示します。
3つの出力:
conf.d ファイル(geo + map)と、vhost内の1行の if ($ncsc_forbidden) { return 403; }。fastcgi ブロックを複製する必要はなく、静的ファイルもカバーされます(admin.html もその一つ)。/.well-known/acme-challenge/ は開いたままなので、証明書の更新が静かに壊れることはありません。<LocationMatch> とプッシュエンドポイント用の <Location> を組み合わせるため、2つのセクションが重なることはなく、Apacheのマージ順序に依存しません。ルールのすべてのアドレスは 1つの Require ip 行にまとめられます:<RequireAll> 内の複数行はAND結合され、誰も満たすことができません。ジェネレーターは、管理アドレスが指定されていない場合に何も出力せず、オペレーター自身のアドレスがカバーされていない場合に警告し、リクエストがプロキシ経由で来た場合にも警告します(geo と Require ip はトランスポートピアを読み取るため、プロキシの背後ではすべてのクライアントが同じに見えます)。生成されたufwスニペットはSSHルールを最初に配置し、HTTP-01チャレンジのためにポート80を開いたままにし、IPv6の落とし穴を明示します:nginxがリストにないv6アドレスを 拒否する のとは異なり、ufwは IPV6=yes が設定されていない限りv6をまったくフィルタリングしません。デュアルスタックホストはそうでなければIPv6で完全に開かれた状態になります。
既知の制限(ページ自体に表示されます):ルールが適用されると、このタブは新しい情報を発見しません。拒否されたプッシュはWebサーバーによってPHPに到達する 前に 拒否されるため、記録されたアドレスは最後に通過したアドレスのままになり、依然として検証済みと見なされます。2つの結果があります:プッシュサーバーを追加するにはルールを再生成して再適用する必要があり、そうしないと最初のプッシュが拒否されます。また、インスタンスのアドレスが変更された場合、新しいアドレスはWebサーバーのアクセスログでのみ確認できます(grep 'push-api.php' access.log | grep ' 403 ')。したがって、タブは観測されたすべてのアドレスの最終確認日を表示し、それが完全なプッシュ未達サイクルよりも古くなるとフラグを立てます。これは push_stale アラートと同じ閾値であり、同じ死角を反対側からカバーします。
フィルタリングの問題とその他の問題を区別する:プッシュエンドポイントへのベア GET は、副作用もトークンも必要とせずにレイヤーをきれいに分離します。評価されるのはそのマシンの送信元アドレスであるため、該当のマシンから 実行してください。```bash
curl -sS -o /dev/null -w '%{http_code}\n' https://your-monitor/push-api.php
| 回答 | 意味 |
|---|---|
| `403` | IPフィルタリングによるブロック |
| `401` | フィルタリング通過、Basic認証が応答中 — 問題は他にある |
| `405` | リクエストがアプリケーションに到達(GETはそこで許可されたメソッドではない) |
| なし / タイムアウト | フィルタリングではない:フィルターが応答する、沈黙はしない |
`-u user:password`で再実行して`403`の疑問を解決する:コードが変わらない場合、実際にフィルタリングである。nginxとApacheの両方で確認済み(`Require valid-user`が有効な場合も含む)、フィルターは認証の*前に*応答する — アプリケーション自体からの`403`は常にボディにJSONを含む。
スニペットロジックは`lib/hardening-rules.php`にあり、これは純粋で`tests/run.php`でカバーされている:スニペットがここでの成果物であり、間違ったものはオペレーターをロックアウトするか、穴を残す。nginxとApacheの両方の出力は動作的に検証済み(実際のサーバー、実際の送信元アドレス、`push`クラスからのパストラバーサル試行を含む)。
### 自動分析(CI `security` ステージ)
依存関係スキャン(`npm/pnpm audit`、Snyk Open Source、Dependabot)はここでは無効:`package.json`も`composer.json`もない — スキャンするものがない。リスクはカスタムコード(PHP約15k行、JS約6k行)と、監視対象インスタンス上で**rootとして**実行されるシェルスクリプト(`tools/*.sh`)にある。パイプラインはそこを対象としている:
| ジョブ | ツール | ブロッキング | スコープ |
|---|---|---|---|
| `secrets_scan` | gitleaks | はい | コミットされたシークレット(ワーキングツリー) |
| `sast_semgrep` | semgrep (`p/php`, `p/javascript`, `p/owasp-top-ten`) | はい | SSRF、認可/CSRFの欠落、XSS |
| `shellcheck` | shellcheck (`--severity=warning`) | はい | `tools/*.sh` — クライアントホスト上でroot |
| `dockerfile_misconfig` | trivy misconfig | はい | `deploy/docker/` |
| `container_cve` | trivy image | いいえ(`allow_failure`) | `deploy/docker`がビルドするイメージ、さらに`nginx:alpine` |
| `ui_tests` | node(依存関係なし) | はい | `lib/ui-common.js`のエスケープ不変条件(過去のXSS回帰の両方) |
| `phpmailer_freshness` | GitHub API | いいえ(`allow_failure`) | ベンダリングされたピン vs アップストリームリリース |
| `deploy_selfcheck` | nc-selfcheck.sh | はい | 出荷されたnginxルールセット(拒否ルール + セキュリティヘッダー)を捨てるコンテナで起動 |
すべてのブロッキングジョブには**ゼロ発見のベースライン**があり、そのため新しいアラートはすべて実際のシグナルである。`.gitlab-ci.yml`にインラインで文書化された、2つの意図的な判断:
- **`php.lang.security.injection.echoed-request`はsemgrepから除外**:これはすべての`echo json_encode()`をXSSとしてフラグするが、ここでのすべてのAPIエンドポイントは正当に行っている(JSON応答、HTMLではない)。初回実行で10件中10件の検出を占め、すべて偽陽性だった。それを残すと誰もがジョブを無視するように訓練されることになる。
- **2つの`allow_failure`ジョブはアップストリームの事実を報告する**(`nginx:alpine`のCVE、または修正がまだAlpineブランチに達していないもの、新しいPHPMailerリリース)。マージリクエストでは修正できない。赤だが許容されるのが正確なシグナル — 「再ビルド/ベンダリングのリフレッシュの時期」 — 無関係な作業をブロックする理由ではない。`phpmailer_freshness`は到達不能またはレート制限されたGitHub APIを*スキップ*として報告し、「古い」として決して報告しない。
- **`container_cve`はビルドするイメージをスキャンし、`FROM`タグはスキャンしない。** Dockerfileは`apk --no-cache upgrade`でベースを強化する(公式PHPイメージはAlpineリポジトリより遅れている — c-ares 1.34.6-r0を出荷した一方で、CVE-2026-33630を修正した1.34.8-r0はすでに公開されていた)。したがってベースタグをスキャンすると、出荷されたイメージにはもはや存在しないCVEが報告される:誰も読まない恒久的にオレンジのジョブになる。
許可リストは意図的に狭い:`.gitleaks.toml`は**リテラル**なプレースホルダー文字列のみ免除し、ドキュメントファイル全体は決して免除しない(`README.md`を許可リストに追加すると、実際のシークレットが貼り付けられた日にスキャンが盲目になる) — したがって、ドキュメント内の新しい例示トークンはそこに追加する必要がある。`.trivyignore`は1つのエントリ`DS-0002`を保持しており、ファイル内で議論されている:php-fpmマスターはrootとして起動し、ワーカーを`www-data`(uid 82)にドロップしなければならない。
### デプロイ後の検証(`nc-selfcheck.sh`)
CIは*出荷された*設定をロックできる(上記の`deploy_selfcheck`ジョブはnginxルールセットをコンテナで立ち上げてプローブする)が、実際にデプロイしたサーバー — 異なるホスト、Basic認証資格情報、ファイルシステム権限 — を検証することはできない。`tools/nc-selfcheck.sh`はそのギャップを埋める。これはスタンドアロンの読み取り専用bashスクリプト(`nc-audit.sh`と同じモデル)で、各デプロイ後に実行する:```bash
# Black-box, no credentials: confirms Basic auth is enforced (401) and that
# sensitive files are blocked (cache/, servers.*, .git, config.php source).
bash tools/nc-selfcheck.sh https://monitoring.example.com
# + security headers behind Basic auth:
bash tools/nc-selfcheck.sh -u user:pass https://monitoring.example.com
# + filesystem checks (run ON the host): servers.json / config.php / CSRF-secret
# permissions, and a stray closing "?>" in config.php.
bash tools/nc-selfcheck.sh --webroot /var/www/ncstatuscheck https://monitoring.example.com
クリティカルな発見(ソース漏洩、ブロック解除された秘密ファイル、ワールドリーダブルなトークンストア、Basic認証の欠落)があった場合、非ゼロで終了するため、ロールアウトのゲートとして機能します。同期/デプロイスクリプトにポストステップとして組み込んでください。WARN/INFOは実行を失敗させません。
// In config.php define('ENV', 'dev');
開発モードでは、追加情報が表示されます(PHPバージョン、Webサーバー)。
### サーバーテスト
管理インターフェースを使用して、URL でサーバーを追加します。サーバーは次のデータ更新時にポーリングされます。
### デバッグログ
`cache/` 内のログファイルを確認してください:
- `monitor.log` — 一般的なアプリケーションログ
- `cron.log` — 完全な収集スクリプトログ(`cron-update.php`)
- `ping.log` — 軽量なアップ/ダウンプローブログ(`cron-ping.php`)
- `alerts.log` — プロアクティブなアラートディスパッチ(Webhook/メール)、Webhook シークレットや SMTP 認証情報をログに記録することはありません
### テストスイート```bash
php tests/run.php # plain-PHP assertions, no framework — exit 0 = all green
純粋なビジネスロジック(バージョン/アプリルール、警告、アップタイム状態マシンと可用性、アラート重複排除/再アーム状態マシン、メールビルダー)をカバーしています。
新しいIssueを開き、以下の情報を記載してください:
このプロジェクトは GNU AGPL v3 の下でライセンスされています。
NcStatusCheck は、オープンソースソリューションを専門とするデジタル協同組合 ézéo によって開発されています。
ヘルプが必要ですか? Issue を確認するか、ézéo チームにお問い合わせください。
| セクション | フィールド |
|---|
| Nextcloudシステム | バージョン、デバッグモード、ローカル/分散memcache、ファイルロック、ディスク容量 |
| PHP | バージョン、memory_limit、upload_max_filesize、max_execution_time、FPM、OPcache |
| Webサーバー | 名前 + バージョン、HTTPプロトコル |
| データベース | タイプ、バージョン、サイズ |
| キャッシュ | Redis、APCuヒット率 |
| アクティブユーザー | 最後の5分、1時間、24時間、7日間 |
latest_ezeo_coop<slug>.conf/etc/cron.d/ncstatuscheck-<slug>targets-<slug>.confncstatuscheck-push-<slug>.log…-<slug>.<md5>.lastDockerで実行中のNextcloud(公式イメージ、compose、AIO): 完全サポート。スクリプトはホストにインストールされ(root cron + Dockerデーモンアクセス)、コンテナ内には決してインストールされません。occ は docker exec を経由します:OCC_CMD=docker exec -u www-data <container> php occ(AIOコンテナ:nextcloud-aio-nextcloud)。管理スクリプトジェネレーターには、これを事前入力する インストールタイプ プリセットがあります。-t を追加しないでください(cron下ではTTY不要)。-u www-data を維持してください(公式イメージはrootでのoccを拒否します)。
フリート展開/更新: コアは単一の同一ファイルであるため、多くのサーバー間でのロジックの更新 = その1つのファイルを置き換えることです(↑ マーカーは古いバージョンを実行しているサーバーを示します)。すぐに使えるプレイブック(または単純な scp ループ)については deploy/ansible/ を参照してください。モニターはパッシブなままです。モニターがフリートにコードを送信することは決してありません。信頼の基点は、モニターではなく、あなた自身のSSHアクセスです。
pre-v4インストールからの移行(モノリシックなインスタンスごとのスクリプト):コア+設定をインストールする前に、古い /usr/local/bin/ncstatuscheck-push-<slug>.sh と /etc/cron.d/ncstatuscheck-<slug> を削除してください(targets-<slug>.conf はそのまま再利用されます)。削除しないと、二重プッシュが発生します。
targets.conf に追加の行があると、すべてのプッシュが第三者に静かにコピーされます。<>"'& を除去し、さらにレンダリング時にエスケープ。try/catch では捕捉できない致命的エラーが発生し、コレクションの実行がループの途中で停止し、それによりフリート全体のすべてのアラートが失われます。