カテゴリー: Linux Page 1 of 60

RHEL系LinuxにZabbixサーバをインストール

RHEL系Linux(Rocky Linux 9)にZabbixサーバを構築したときのメモです。

環境

  • Zabbix 7.0
  • PHP-FPM 8.3
  • MySQL 8.0
  • Apache 2.4

前提

  • Linuxの初期設定完了済み
  • 以下のミドルウェアをインストール済み。
  • Apache 2.4
  • MySQL 8.0 (mysql_secure_installation込み)
  • PHP-FPM 8.3

さっくりとした手順

  1. php.iniを修正します。
  2. Zabbixパッケージをインストールします。
  3. DBを作成しスキーマをインポートします。
  4. ZabbixのDBを設定します。
  5. Apacheのバーチャルホストを設定します。
  6. Zabbixサービスを有効化してFirewalldを設定します。
  7. 初期インストールを行います。

PHP 設定の最適化 (php.ini)

Zabbix Web UI の動作要件に合わせて PHP のパラメータを修正します。おそらく多くの方がWebインストールした後に怒られる設定です。

  • php.iniのバックアップ
sudo cp -pi /etc/php.ini /path/to/backup/directory/php.ini.$(date +%Y%m%d)

任意のバックアップディレクトリを指定します。

  • php.iniのバックアップ確認
diff -u /path/to/backup/directory/php.ini.$(date +%Y%m%d) /etc/php.ini 

差分がなければバックアップ成功です。

  • php.ini 修正箇所:
  • post_max_size = 16M (8M から変更)
  • max_execution_time = 300 (30 から変更)
  • max_input_time = 300 (60 から変更)

上記は例です。環境に合わせましょう。

  • php.iniの編集確認
diff -u /path/to/backup/directory/php.ini.$(date +%Y%m%d) /etc/php.ini 

+の箇所に修正した値になっていることを確認します。

  • php-fpmサービス再起動(設定反映)
sudo systemctl restart php-fpm

Zabbix パッケージのインストール

  • レポジトリ追加

2026年4月の最新パッケージである7.0.xを使うため、レポジトリを追加します。

  • Zabbixリポジトリのインストール
sudo rpm -Uvh https://repo.zabbix.com/zabbix/7.0/rocky/9/x86_64/zabbix-release-latest.el9.noarch.rpm
  • キャッシュのクリア
sudo dnf clean all
  • EPEL リポジトリとの競合を避けるため、リポジトリを指定してインストールします。

この指定が地味に詰まりました。

sudo dnf install -y --disablerepo=epel \
zabbix-server-mysql \
zabbix-web-mysql \
zabbix-apache-conf \
zabbix-sql-scripts \
zabbix-selinux-policy \
zabbix-agent2

データベースの作成と初期データの流し込み

MySQL (MariaDB) に Zabbix 用の DB とユーザーを作成し、初期スキーマをインポートします。

  • mysqlログイン
mysql -u root -p
  • DB作成
CREATE DATABASE zabbix CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;
CREATE USER 'zabbix'@'localhost' IDENTIFIED BY 'あなたのパスワード';
GRANT ALL PRIVILEGES ON zabbix.* TO 'zabbix'@'localhost';
SET GLOBAL log_bin_trust_function_creators = 1;
EXIT

SET GLOBAL log_bin_trust_function_creators = 1;を指定しないと、zabbixに必要なスキーマを拒否することがあります。

  • スキーマインポート
zcat /usr/share/zabbix-sql-scripts/mysql/server.sql.gz | mysql --default-character-set=utf8mb4 -uzabbix -p zabbix
  • 設定を元に戻す
mysql -u root -p -e "SET GLOBAL log_bin_trust_function_creators = 0;"

インポート後にこれを行っておかないと、MySQLがインジェクションとなり得るスキーマを許可することがあります。

Zabbix Server の DB 設定

サーバー本体が DB に接続するためのパスワードを設定します。

/etc/zabbix/zabbix_server.conf を以下のように修正します。

修正箇所:

  • DBPassword=あなたのパスワード (コメントアウト # を外して追記)

Apache (httpd) バーチャルホストの設定

上記、dnfで設定した標準設定を無効化し、/etc/httpd/virtual/ 配下で管理するように変更します。

これは、「一つのサーバにWebサーバとZabbixを同時に立てる必要がある」などで重要なテクニックです。

  • ディレクトリ準備
sudo mkdir -p /etc/httpd/virtual
  • 標準設定の退避
sudo mv /etc/httpd/conf.d/zabbix.conf /path/to/backup/direcotry/zabbix.conf.$(date +%Y%m%d)
  • バーチャルホスト設定の作成

/etc/httpd/virtual/zabbix.conf

等として、以下のようなファイルを作ります。

<VirtualHost *:80>
    # 自分の環境に合わせます
    ServerName zabbix.example.com
    DocumentRoot /usr/share/zabbix

    <Directory "/usr/share/zabbix">
        Options FollowSymLinks
        AllowOverride None
        Require all granted
    </Directory>
    # FPM設定
    <FilesMatch \.php$>
        SetHandler "proxy:unix:/run/php-fpm/www.sock|fcgi://localhost"
    </FilesMatch>
    # 任意のログディレクトリを指定します
    ErrorLog /var/log/httpd/zabbix_error.log
    CustomLog /var/log/httpd/zabbix_access.log combined
</VirtualHost>

サービスの起動と Firewalld の設定

全てのコンポーネントを起動し、必要なポートを開放します。これも地味にはまるポイントです。

  • サービスの有効化と起動
sudo systemctl enable --now zabbix-server zabbix-agent2 httpd php-fpm
  • Firewalld の許可
sudo firewall-cmd --permanent --add-service={zabbix-server,zabbix-agent,http}
  • Firewalldのリロード
sudo firewall-cmd --reload

Web セットアップとログイン

  1. ブラウザで http://(ServerName)/ にアクセス。
  2. 全てのチェック項目が OK であることを確認し、DB情報を入力して完了。
  3. 初期ログイン情報:
  • User: Admin (Aは大文字)
  • Password: zabbix

Apacheのインストールと初期設定(RHEL系)

概要

RHEL系(AlmaLinux / Rocky Linux 9等)にWebサーバーApacheをインストールします。最近のトレンドはNginxではあるものの、以下のメリットを考慮してApacheを選択します。

  1. 豊富なモジュールとカスタマイズ: 歴史が長く、情報の蓄積が膨大。
  2. 動的コンテンツの設定のしやすさ: PHP等との親和性が高い。
  3. 運用の手軽さ: 小規模サイトを迅速に立ち上げるのに適している。
  4. 高度なセキュリティ・ログ設定:
    • 自宅等からのアクセスログを除外するなどのログカスタマイズ。
    • 悪質なクローラーの排除。
    • mod_security(WAF)による防御。

さっくりとした手順

  1. firewalldの設定: 外部からのアクセス許可を与えます。
  2. Apacheのインストール: dnfを使用してインストールします。
  3. Apacheの設定: セキュリティとサーバー名の設定を行います。
  4. 設定の反映確認: 正常に動作しているかチェックします。

1. firewalldの設定

サーバー移設などでハマりやすいのが「設定は正しいのにページが表示されない」現象です。RHEL系ではデフォルトで強力なファイアウォール(firewalld)が動作しており、ポート80/443を明示的に開放する必要があります。

大前提

SSH接続(ポート22)は許可されている前提で進めます。設定を誤るとリモート操作ができなくなるため、慎重に行いましょう。

  • HTTP通信を許可する
sudo firewall-cmd --permanent --add-service=http
  • HTTPS通信を許可する
sudo firewall-cmd --permanent --add-service=https
  • 設定を反映させる
sudo firewall-cmd --reload
  • 設定を確認する
sudo firewall-cmd --list-all

services の欄に httphttps が含まれていればOKです。

2. インストールを行います

RHEL系ではApacheのパッケージ名は httpd です。

  • パッケージ全体のアップデート
sudo dnf update -y
  • Apache (httpd) のインストール
sudo dnf install httpd -y
  • バージョン確認
httpd -v

-(表示例)-
Server version: Apache/2.4.57 (AlmaLinux)

  • サービスの起動と自動起動設定
sudo systemctl enable --now httpd
  • サービス稼働確認
systemctl status httpd

enabledactive (running) を確認します。

3. 設定を行います

  • 設定ファイルのバックアップ

RHEL系の設定ファイルは /etc/httpd/conf/httpd.conf です。

sudo cp -pi /etc/httpd/conf/httpd.conf /etc/httpd/conf/httpd.conf.$(date +%Y%m%d)

※任意のバックアップディレクトリを指定してください。

  • 設定ファイルのバックアップ確認
diff -u /etc/httpd/conf/httpd.conf.$(date +%Y%m%d) /etc/httpd/conf/httpd.conf

エラーがないことを確認します。

  • 設定ファイルの書き換え(追記)

セキュリティ向上のため、署名の非表示化とサーバー名を追記します。

sudo bash -c "cat >> /etc/httpd/conf/httpd.conf" << 'EOF'

# Custom Settings
ServerSignature Off
ServerTokens Prod
ServerName example.com:80
EOF

example.com の部分は、ご自身のドメイン名またはホスト名に置き換えてください。

  • 差分の確認
diff -u /etc/httpd/conf/httpd.conf.$(date +%Y%m%d) /etc/httpd/conf/httpd.conf

末尾に指定した3行が追加されていることを確認します。

4. 設定反映を確認します

  • 構文確認
sudo httpd -t

Syntax OK と表示されることを確認します。

  • サービス再起動
sudo systemctl restart httpd
  • 設定の反映確認(ヘッダー確認)
curl -I http://localhost

以下のように、Server ヘッダーが Apache のみ(バージョン情報なし)になっていれば成功です。

HTTP/1.1 200 OK
Date: ...
Server: Apache
...

手なり作業の恐怖。(DNSの特異性)

ちょっとしたミスで地獄行きになるところだったので、自戒を込めてメモを記します。

何をやらかそうになったか?

「DNSの設定変更中、Aレコードとシリアル番号を間違えて登録するところだった」

なお、LinuxのDNS、BINDを用いての作業です。

なぜこれが危険なのか?

DNSレコードの設定は、インターネット上の住所録を書き換えるような作業です。一歩間違えると、メールが届かない、ウェブサイトが見られない、あるいは悪意のある第三者に通信を乗っ取られるといった甚大な被害に直結します。
というか、やらかしかけました。

なぜこれほどまでに慎重さが求められるか? 改めて、「自分に言い聞かせるレベルで」メモをします。

DNS設定ミスが引き起こす主なリスク

DNSは「一度間違えると修正が反映されるまで時間がかかる」という特性があるため、ミスが致命傷になりがちです。

サービス全停止(可用性の喪失)

AレコードCNAMEの記述ミスにより、全世界からサイトやAPIへの接続が断たれます。

メールの不達・消失(機会損失・信用リスク・レピュテーションリスク)

MXレコードの設定ミスは、ビジネス連絡の遮断を意味します。また、SPF/DKIM/DMARCなどの送信ドメイン認証に不備があると、正当なメールが「迷惑メール」として破棄されます。

サブドメイン乗っ取り(Subdomain Takeover)

これがある意味の恐怖です。不要になった外部サービス(SaaS等)へのCNAMEを残し続けると、そのドメインを第三者に取得され、せっかくのブランド名で偽サイトを運営されるリスクがあります。

キャッシュによる影響の長期化

後述するTTLの設定により、間違った設定が世界中のキャッシュサーバーに残り続け、即座に修正しても数時間は復旧しないことがあります。

BINDにおける「シリアル番号」とテキスト管理の罠

BIND(Berkeley Internet Name Domain)のようなテキストベースの管理では、私のようなエンジニアの「うっかり」が原因で同期不全が起きることがよくあります。

シリアル番号(Serial Number)の更新忘れ

BINDのゾーンファイルには、SOA (Start of Authority) レコード内にシリアル番号が存在します。

  • 仕組み:
    • セカンダリサーバーは、プライマリサーバーのシリアル番号が「現在より大きい数字」になった時だけ、新しい設定を同期(ゾーン転送)します。
  • リスク:
    • レコードの内容を書き換えても、シリアル番号を増やし忘れると、セカンダリサーバーには古い設定が残り続けます。 これにより、場所によって新旧の設定が混在する不安定な状態になります。

構文ミスとドット(.)の有無

BINDでは、FQDN(完全修飾ドメイン名)の末尾にドットを付け忘れると、オリジンのドメイン名が自動的に付加されてしまいます。

  • 例:
    -example.com と書くべきところを example.com.(末尾ドットあり)としなかった場合、example.com.example.com. という意図しないレコードとして解釈されます。

ミスを防ぐための4つの鉄則

これに関してはAIと壁打ちしながら設定しました。

1. TTL(Time To Live)を事前に短縮する

作業の数日前から、対象レコードのTTLを短く(例:300秒など)設定しておきます。

  • 理由:
    • 万が一ミスをしても、キャッシュが早く切れるため、被害を最小限に抑えられます。作業完了後に元の値に戻すのを忘れないようにしましょう。

2. BINDの構文チェックコマンドを活用

ファイルを保存した後、サービスを再起動(reload)する前に必ずチェックコマンドを叩く習慣をつけます。

named-checkzone [ドメイン名] [ゾーンファイル名]

`

ゾーンファイルの整合性確認(シリアル番号の形式ミスなども検知できます)

3. シリアル番号の運用ルール化

シリアル番号は YYYYMMDDNN(日付+その日の更新回数)の形式で運用するのが一般的です。

例: 2026年4月16日の1回目の修正 → 2026041601

この、第三者が見ても分かる増分をやりましょう。極端な話

  1. Friends
  2. Romans
  3. Countrymen

のような「ローマ的増分」は分かっているのが自分であっても混乱の元です。

4. 変更後の浸透確認(digコマンド等)

設定を反映したら、自分のPCのブラウザで確認するだけでなく、外部から正しく引けるかを確認します。

  • Google Public DNSで確認
dig @8.8.8.8 example.com A  
  • 同期設定をしたセカンダリDNSで確認
dig @[セカンダリDNSのIP] example.com A 

まとめ

DNS設定は、「間違えた瞬間に世界中にそのミスが拡散し、しかもすぐには消せない」という怖さがあります。

特にBINDなどの手動管理では、「シリアル番号のカウントアップ」と「末尾のドット」を指差し確認するだけでも、トラブルの多くを回避できます。作業前のTTL短縮と、作業後の多角的な検証をセットで行うようにしましょう。

「かんばん」を掲げる意味

拙稿にて、ドメインを掲げる意味として

  • Web上の住所
  • ブランドの顔
  • 信頼の証

の3つを挙げています。また、池波正太郎の

「人間、落ちるところへ落ちてしまっても、なにかこう、この胸の中に、たよるものがほしいのだねえ」
「たよるもの、ねえ…」
「いえば看板みたいなものさ」
「かんばん、かね…?」
「人間、だれしも看板をかけていまさあね。旦那のお店にもかけてござんしょう」
――『にっぽん怪盗伝』

にも感銘を受けています。「ちょっとしたミスによって看板に泥を塗る」事態が起きかけたという話でした。

Linux Webサーバの基本的な設定ミス(カモ)を狙ったログの傾向。

筆者のvpsに訪れる攻撃者。基本や最新のトレンドまで多くのパターンがあります。

そんな中、1分の間に大量の情報略取を試みる攻撃者のログがありました。

これらを紹介します。

ログ抜粋

例によって、テロリストに名前を与えないという哲学の元、アクセス者のグローバルIPは晒しません。

[Tue Apr 14 --:--:-- 2026] [security2:error] [client 192.0.2.10] ModSecurity: Access denied with code 404 (phase 1). [msg "[CUSTOM RULE] Host header is a numeric IP address. Blocked immediately."] [hostname "vps.example.jp"] [uri "/"]
[Tue Apr 14 --:--:-- 2026] [security2:error] [client 192.0.2.20] ModSecurity: Access denied with code 404 (phase 1). [hostname "vps.example.jp"] [uri "/.env"]
[Tue Apr 14 --:--:-- 2026] [security2:error] [client 192.0.2.20] ModSecurity: Access denied with code 404 (phase 1). [hostname "vps.example.jp"] [uri "/sendgrid.env"]
[Tue Apr 14 --:--:-- 2026] [security2:error] [client 192.0.2.20] ModSecurity: Access denied with code 404 (phase 1). [hostname "vps.example.jp"] [uri "/web/.env"]
[Tue Apr 14 --:--:-- 2026] [security2:error] [client 192.0.2.20] ModSecurity: Access denied with code 404 (phase 1). [hostname "vps.example.jp"] [uri "/static//etc/passwd"]
[Tue Apr 14 --:--:-- 2026] [security2:error] [client 192.0.2.20] ModSecurity: Access denied with code 404 (phase 1). [hostname "vps.example.jp"] [uri "/static//home/user/.aws/credentials"]

主な略取対象

どのようなファイルを見ようとしているのか?

言語・フレームワークの特定(Configuration Exploration)

  • 対象:
    • /settings.py (Django),
    • /config.js (Node.js),
  • 攻撃者の意図:
    • サーバの下調べです。フレームワークを特定することでどの脆弱性があるかを調べようとしています。

秘匿情報の取得(Environment Files)

  • 対象:
    • /.env
    • /sendgrid.env
    • /.env.local
    • /application.yml
    • /database.yml (Rails)
  • 攻撃者の意図:
    • 攻撃者がまず狙う情報です。ここにはデータベースの接続パスワード、SendGrid(メール配信サービス)のAPIキー、アプリケーションのシークレットキーがむき出し/平文で置かれていることが多いです。
  • 危険性:
    • ここが突破されれば、サーバのデータベースは私物化され、メール送信機能はスパムメール配信の踏み台にされます。

システムの脆弱性(Path Traversal & LFI)

  • 対象:
    • /static//etc/passwd
    • /static//etc/shadow
    • /static//proc/self/environ
  • 攻撃者の意図:
    • 静的ファイルのディレクトリから、強引にOSの中枢ファイルへ手を伸ばそうとしています。特に shadow ファイルなどは、ログイン情報の心臓部です。
  • 危険性:
    • /etc/passwd が奪われれば、サーバー内のユーザー一覧が露呈し、次の攻撃の正確な座標を与えてしまうでしょう。
    • /etc/shadow も暗号化されているとは言え、ローカル環境でハッシュ値を割り出されてしまいます。
    • 特に /proc/self/environ が読めると、実行中のプロセスの環境変数が丸見えになり、壊滅的な被害に繋がります。
  • 補足:
    • /staticこれは、特定のWAF(Webアプリケーションファイアウォール)や、リバースプロキシの設定(Nginxのエイリアス設定の不備など)をバイパスしようとする試みです。正規化の過程で // が / に変換される挙動を悪用し、本来アクセスできないディレクトリの外側へ飛び出そうとしています。

クラウドの鍵の窃取(Cloud Credentials)

  • 対象:
    • /static//home/user/.aws/credentials
  • 攻撃者の意図:
    • AWS(Amazon Web Services)のアクセスキー。サーバ内にこれを置きっぱなしにしている管理が甘い人たちを狙っています。
  • 危険性:
    • ある意味で最も危険と言えるでしょう。これを奪われれば、aws資産は攻撃者のビットコイン採掘場に変貌し、管理者の元には天文学的な請求書という地獄が待っています。

ここから分かること

彼らは「置き忘れ」や「甘い設定」を狙っています。

  • 初期値だから
  • 便利だからとstaticを使う
  • 管理が楽だから

などは組織の運用であって、攻撃者はそういうところが絶好のカモにしています。これは、私にも跳ね返る言葉ですが:

「ポーカーを始めて30分が過ぎても誰がカモか分からなければ、あなたがカモだ」

のウォーレン・バフェットの言葉はサーバ管理でも通用するというお話しでした。

LAMPサーバの一覧を表示するワンライナー。

UbuntuのLAMPサーバの環境確認に使える一式のワンライナーの紹介です。

 echo -e "| Item | Version / Status |\n|:---|:---|\n| **OS** | $(lsb_release -d | cut -f2) |\n| **Memory** | $(free -h | awk '/^Mem:/ {print $2" (Used: "$3")"}') |\n| **Web Server** | $({ apache2 -v 2>/dev/null || nginx -v 2>&1; } | head -n 1 | sed 's/^[ \t]*//') |\n| **PHP** | $(php -v 2>/dev/null | head -n 1 | cut -d' ' -f1,2 || echo "Not Installed") |\n| **PHP-FPM** | $(systemctl list-units --type=service | grep -o 'php[0-9.]*-fpm' | tr '\n' ' ' | xargs || echo "Not Running") |\n| **DB** | $(mysql -V 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -n1 | sed 's/^/MySQL /' || psql --version 2>/dev/null || echo "Not Installed") |\n| **Node.js** | $(node -v 2>/dev/null || echo "Not Installed") |\n| **Python** | $(python3 -V 2>/dev/null || echo "Not Installed") |\n| **Ruby** | $(ruby -v 2>/dev/null | cut -d' ' -f1,2 || echo "Not Installed") |"

全体の構造

このコマンドは echo -e を使用して、1つの大きな文字列を出力しています。

  • | Item | ... |:Markdownの表ヘッダーを作成しています。
  • $( ... )コマンド置換と呼ばれる仕組みです。カッコ内のコマンドを先に実行し、その結果を文字列の中に埋め込みます。

各項目の詳細解説

項目実行している処理の内容
OSlsb_release -d でOSの説明行を取得し、cut -f2 でタブ以降のOS名(Ubuntu…など)だけを抜き出しています。
Memoryfree -h でメモリ情報を取得。awk を使って「全容量($2)」と「使用量($3)」を抽出して整形しています。
Web Server{ apache2 -v || nginx -v } で両方を試し、見つかった方の1行目を表示。sed で行頭の余計な空白を消しています。
PHPphp -v の1行目から、cut を使って「PHP 8.x」のような名称とバージョンのみを取得しています。
PHP-FPMsystemctl で起動中のサービス一覧から php*-fpm に一致するものを探し、trxargs で横一列に並べています。
DBまず mysql -V を試し、バージョン番号を正規表現で抽出。それがなければ psql(PostgreSQL)を確認します。
Node / Pythonそれぞれ -v または -V オプションでバージョンを確認。インストールされていなければ "Not Installed" を返します。
Rubyruby -v の結果から、最初の2単語(例:ruby 3.x)だけを抜き出しています。

出力イメージ

実行すると、以下のような表がターミナル(またはMarkdown対応のエディタ)に表示されます。

ItemVersion / Status
OSUbuntu 24.04.4 LTS
Memory5.8Gi (Used: 3.8Gi)
Web ServerServer version: Apache/2.4.58 (Ubuntu)
PHPPHP 8.3.30
PHP-FPMphp8.3-fpm
DBMySQL 8.0.45
Node.jsv20.19.2
PythonPython 3.12.3
Rubyruby 3.2.3

サーバー構築直後の確認や、GitHubのIssueに環境情報を貼る際にとても重宝するものです。

Growi v7.5.0のアップデートメモ(nodeのアップデート含む)

Grwoiをv7.5.0にアップデートしたときのメモです。

アップデート概要

  • 対象バージョン: v7.4.7 → v7.5.0
  • 実行環境:
    • Ubuntu 24.04
    • Apache 2.4によるリバースプロキシー

主要な変更点:

  • Node アップデートによる Next.js v16 / Vite v6 / Turbopack への移行
  • リバースプロキシにy-websocket への変更、
  • RegExp.escape() の採用

前提

  • nvmでnodeのバージョンを管理していること。
  • リバースプロキシの変更手順が整っていること。

事前準備・環境更新

最新のGROWI要件に合わせ、Node.jsランタイムをアップデートしました。

-Node.jsの更新:

nvm を使用して v24.14.1 をインストール。 筆者はroot環境で実施しているため

sudo su -

で管理者権限になってから実施しました。

  • nvm バージョンアップ
nvm install 24

bash
nvm use 24

nvm alias default 24
  • ビルドツールの更新:

はまったポイントです。一度バージョンアップするとv24環境で pnpmが消えてしまうので再有効化します。

corepack enable
corepack prepare pnpm@latest --activate

ビルドプロセスの修正

後は基本的に筆者が行っているものに習います。

growiディレクトリに移動します

cd /home/www-data/growi && pwd

自分の環境に合わせます。(筆者環境/home/www-data/growi)

リリースタグを確認します。

  • リリースタグ取得
sudo git fetch --tags
  • リリースタグ確認
sudo git tag -l

スペースで確認していき、上記リリースサイトと同じバージョンがあることを確認します。

チェックアウトとインストールを行います。

  • 変更を一時的に退避
sudo git stash
  • チェックアウト
sudo git checkout 【バージョン】

リリースタグは再確認しましょう。今回は 2026/04/07にリリースされたv7.5.0を選択しました。

メモリ不足の対処

  • pnpm install 時のセキュリティ警告(sharpのビルド停止)およびメモリ不足対策を実施しました。
  • ネイティブバイナリの許可:
    ```bash
    pnpm approve-builds
リストから sharp を選択して許可

bash
pnpm install

- Turbopackによるビルド:

メモリ消費を抑えるため、上限を指定して実行。

bash
NODE_OPTIONS="--max-old-space-size=4096" pnpm run app:build

### リバースプロキシ (Apache) の修正


同時編集プロトコルが `y-websocket` に変更されたことに伴い、`growi.conf` の書き換えルールを厳密化しました。

修正内容: すべてのWS通信ではなく、`/yjs` パスのみをWSプロキシへ流すよう変更。

apache
# リバースプロキシー設定
RewriteEngine on

# 1. /yjs へのアクセスのみ WebSocket プロキシへ飛ばす
RewriteCond %{HTTP:Upgrade} websocket [NC]
RewriteCond %{REQUEST_URI} ^/yjs [NC]
RewriteRule /(.) ws://localhost:3000/$1 [P,L]

# 2. それ以外の通常の HTTP リクエスト
ProxyPass / http://localhost:3000/
ProxyPassReverse / http://localhost:3000/
```

起動スクリプトの修正

2度目のはまりポイントです。RegExp.escape is not a function エラー(Node.jsのバージョン不足)を解消するため、サービスが参照する PATH を更新しました。
(これは筆者環境なので)

growi-start.sh の修正:

# 旧: v20.19.2 -> 新: v24.14.1
export PATH="/root/.nvm/versions/node/v24.14.1/bin:$PATH"

Growiの再起動

  • Growiサービス開始
sudo systemctl restart growi.service
  • サービス開始確認
systemctl status growi.service

active(running)を確認します。

完了確認

  • [x] 管理画面「システム情報」にて GROWI 7.5.0 / Node.js 24.14.1 を確認。
  • [x] ページリストの取得エラー(APIエラー)が解消されたことを確認。
  • [x] ページ複製時の RegExp.escape エラーが解消されたことを確認。
  • [x] Elasticsearch v9 への接続および検索ができることを確認。

AIツールを狙ったログのメモ

筆者の公開VPSにて、昨今のトレンドと言えるようなログを確認しましたので解説です。

攻撃者のIPやホスト名をダミー(例: ATTACKER_IP, TARGET_IP)に置き換えています。わざわざIP晒しをしないのは「テロリストに名前を与えない」の精神からです。

[DATE] [security2:error] [client ATTACKER_IP_1] ModSecurity: Access denied... [id "10004"] [msg "Host header is a numeric IP address"] [hostname "TARGET_IP"] [uri "/"]
[DATE] [security2:error] [client ATTACKER_IP_1] ModSecurity: Access denied... [id "10004"] [msg "Host header is a numeric IP address"] [hostname "TARGET_IP"] [uri "/favicon.ico"]
[DATE] [security2:error] [client ATTACKER_IP_2] ModSecurity: Access denied... [id "10004"] [msg "Host header is a numeric IP address"] [hostname "TARGET_IP"] [uri "/.git/config"]
[DATE] [security2:error] [client ATTACKER_IP_3] ModSecurity: Access denied... [id "10004"] [msg "Host header is a numeric IP address"] [hostname "TARGET_IP"] [uri "/api/tags"]
[DATE] [security2:error] [client ATTACKER_IP_3] ModSecurity: Access denied... [id "10004"] [msg "Host header is a numeric IP address"] [hostname "TARGET_IP"] [uri "/v1/models"]
[DATE] [security2:error] [client ATTACKER_IP_3] ModSecurity: Access denied... [id "10004"] [msg 
[DATE] [security2:error] [client ATTACKER_IP_3] ModSecurity: Access denied... [id "10004"] [msg "Host header is a numeric IP address"] [hostname "TARGET_IP"] [/update_weights_from_tensor]
"Host header is a numeric IP address"] [hostname "TARGET_IP"] [uri "/.well-known/mcp.json"]
[DATE] [security2:error] [client ATTACKER_IP_3] ModSecurity: Access denied... [id "10005"] [msg "Missing Host Header"] [hostname "example.com"] [uri "/"]

解説の前に

  • header is a numeric IP address
  • Missing Host Header

があることに気づく方はいるかと思います。

これは、筆者が導入しているMod_Securityに以下のカスタムルールを設けているからです。

# 1-3. IPアドレス直打ちアクセス対策 
# ポート番号が付いていても即座に拒否する
SecRule REQUEST_HEADERS:Host "@rx ^[\d.]+(:\d+)?$" \
    "id:10004,\
    phase:1,\
    deny,\
    status:404,\
    log,\
    msg:'[CUSTOM RULE] Host header is a numeric IP address (incl port). Blocked immediately.',\
    tag:'application-attack',\
    tag:'PROTOCOL_VIOLATION/INVALID_HREQ'"

# Hostヘッダーが存在しない場合は即ブロック
SecRule &REQUEST_HEADERS:Host "@eq 0" \
    "id:10005,\
    phase:1,\
    deny,\
    status:404,\
    log,\
    msg:'[CUSTOM RULE] Missing Host Header. Blocked immediately.'"

攻撃の全体的な意図:なぜ「IPアドレス」でアクセスしてくるのか?

ログの多くが 「HostヘッダーがIPアドレスである」 という理由でModSecurity(WAF)に遮断されています。これには以下の意図があります。

  • 無差別スキャン:
    • ドメイン名を知らなくても、IPアドレスの範囲(例: 192.168.0.0/16)を片っ端から叩くことで、管理が甘い「生」のサーバーを見つけ出そうとしています。
  • デフォルト設定の探索:
    • 多くのWebサーバーは、不明なドメインやIP直接指定でアクセスされた際に「デフォルトのバーチャルホスト」を表示します。ここにはテストページや管理ツールが放置されていることが多いため、そこを突こうとしています。
  • WAF/CDNの回避:
    • CloudflareなどのCDNを経由せず、サーバーの「本当のIP」に直接攻撃を仕掛けることで、上位の保護層をバイパスしようとする手法です。

各パス(URI)が狙われた理由とケーススタディ

情報漏洩の古典:/.git/config

  • 意図:
    • Gitリポジトリの露出確認。
  • 背景:
    • 開発者が誤って .git ディレクトリを公開サーバーにアップロードしてしまうミスを狙っています。これが取得されると、ソースコード、DBのパスワード、APIキー、過去の全変更履歴が盗まれます。

AI・LLM時代の新潮流:/v1/models, /api/tags, /api/version

  • 意図:
    • OllamaやLocalAI、OpenAI互換API などのエンドポイント探索。
  • 背景:
    • 昨今、自宅サーバー等でローカルLLMを動かす人が増えています。これらのツールはデフォルトで /v1/models などのパスを使用するため、認証なしでAIリソースを悪用(AI寄生)したり、モデル情報を盗んだりするためにスキャンされています。

2026年での最新トレンドの追跡:/.well-known/mcp.json, agent.json

  • 意図:
    • Model Context Protocol (MCP) 設定ファイルの探索。
  • 背景:
    • MCPは2024年末〜2025年にかけて普及し始めた、AIエージェントとツールを接続するための規格です。これがあるサーバーは「AIエージェントが操作可能なリソース」を持っている可能性が高いため、攻撃者は踏み台やデータ奪取の対象としてリストアップしようとしています。

AI学習モデルの改竄:/update_weights_from_tensor

  • 意図:
    • 学習済みモデルの「重み(Weights)」を外部から直接書き換えようとしています。
  • 背景:
    • もし成功すれば、攻撃者はサーバにインストールされているAIの判断を密かに操作できます。例えば「特定の条件の時だけ誤判定させる(バックドア)」や「特定の顔をスルーさせる」といった、AIジャックが可能です。

監視・インフラの隙:/metrics, /queue/status

  • 意図:
    • Prometheusや監視ツールの露出確認。
  • 背景:
    • /metrics にはサーバーの負荷、プロセス一覧、時には内部ネットワークの構成情報がプレーンテキストで出力されます。攻撃者にとっては、攻撃の戦略を立てるための「設計図」を手に入れるようなものです。

ここから得られること

総括

「攻撃者はドメイン名で選んでいるのではない。露出している機能(Git, AI API, 監視ツール)をポートスキャンとパス探索で機械的に探り当てている」

今後の教訓:

  • ホワイトリスト運用:
    • /api//metrics などのパスは、特定のIP以外からは404にしましょう。
  • 「隠しファイル」の禁止:
    • .git.env など、ドットで始まるディレクトリへのアクセスはWebサーバーレベルで一括拒否しましょう。

いくら最新の(今回のようなAIサーバ)を狙ったところで

  • 「対策の甘いところから狙われる」
  • 公開されたらヤバいところは共有される

という普遍性は同じ。それこそ『緋色の研究』でホームズが言う

「日の下に新しきものなし」 (Nihil novi sub sole)

ぐらいの勢いで、基本的な対策をしていきましょうという話でした。

NAS移行後の展望。

  • 初期設定
  • NAS構築後のHDD初期不良

を乗り越え、無事に移行も完了。次のフェイズに関する簡単なメモです。

宅内サーバ環境の刷新。

さすがにUbuntu 20.04が動き続けるというのは忍びないので、これもHWを変えたいです。昨今、ミニPCも高くなってきているのでその辺は様子を見ながら。

それこそ転がっているノートPCあたりをベースにするのも視野に入れています。

旧NASの外部ディスク転用。

2015年に購入したNASですが、メインのストレージではなく、先のサーバ環境の外部ディスクとして用いるには十分。(さすがにこの際はディスクを変えないとですが)

いずれにしても、これまでのデータを複数取り込むことができたのはありがたい限り。

余談

膨大な写真データのなから、コーンウォールを訪れた際のこういう写真を発掘できたのは割と収穫です。

Growi環境:axiosサプライチェーンの影響確認。

2026年3月31日に発覚したaxiosのサプライチェーン攻撃

内容を聞くだけでゾッとする話だったので、調べつつ筆者がインターネット上に設置しているgrowiサーバでの影響を調べました。

筆者環境

  • Growi v7.4.7
  • npm 11.4.0
  • Apache 2.4によるリバースプロキシー
  • Ubuntu 24.04

自環境の白黒確認

まずは、動いている環境のaxiosバージョンを特定することが最優先。筆者はこのライブラリを恥ずかしながら聞くまで知らなかったのですが 、JavaScriptのデファクトスタンダード的なHTTPクライアントライブラリ と聞いたので「絶対に入っているだろう」の断定で動きました。

調査コマンド

  • Growiのルートディレクトリに移動
cd /path/to/growi

自分の環境に合わせます(筆者環境/home/www-data/growi)

  • インストール済みaxiosの調査 
npm ls axios

または

 cat pnpm-lock.yaml |grep axios
  • 調査結果
axios@1.11.0

axios@0.26.1

axios@0.21.4

判定:安全

影響を受けるバージョンは

  • axios@1.14.1
  • axios@0.30.4

そのため、攻撃対象となったaxiosを利用していない(つまり、攻撃コードは混入していない)ものとなっています。

念のための確認

ほぼないとは思いますが「npm updateを実行してしまったかも」の場合は、今回のケースで攻撃者が埋め込んだマルウェアが生成するディレクトリの有無を調べます。

find node_modules -name "plain-crypto-js"

結果、何もなければ物理的にもクリーンです。

また、筆者は該当vpsでBookStackやsnipe-itなどのLaravelライブラリを動かしていますが

cd /path/to/BookStack && pwd
npm ls axios
BookStack@ /home/www-data/BookStack

└── (empty)

と、いずれも(PHPメインであるためか)動いていない状態が分かりました。

サプライチェーン攻撃の危険さ

開発者が信頼している公式ツールをそのまま使っただけで感染する状態であった点にあります。供給者のツールに攻撃ツールを仕込んでいたということで「サプライチェーン攻撃」だそうで。(発覚後、npm が悪性 axios バージョンを削除)

シーケンス

これをmermaid.jsにまとめたのがこちら。

sequenceDiagram participant Dev as 開発者 / サーバー participant NPM as npm レジストリ participant Mal as 悪意のあるパッケージ<br/>(plain-crypto-js) participant C2 as 攻撃者サーバー<br/>(C2サーバー) Note over Dev, NPM: 1. 汚染された axios@1.14.1 を要求 Dev->>NPM: npm install (axios要求) NPM-->>Dev: axios と依存の plain-crypto-js を配信 Note over Dev: 2. インストール直後にフックが発動 Dev->>Dev: postinstall: node setup.js を自動実行 Note over Dev, C2: 3. OSを判別し、マルウェアを落とし込む Dev->>C2: 難読化解除 + OS情報送信 + 通信開始 C2-->>Dev: 各OS用ペイロード (Mac/Win/Linux) を送信 rect rgb(240, 240, 240) Note right of Dev: Linuxの場合: /tmp/ld.py を実行<br/>Windowsの場合: 隠しPowerShellを実行 end Note over Dev: 4. 証拠隠滅 (セルフデストラクト) Dev->>Dev: setup.js を削除 Dev->>Dev: package.json をクリーンな状態に書き換え Note over Dev: node_modules 内は<br/>一見シロに見える

恐ろしさのポイント

  1. 自動実行: npm install した瞬間に、何も操作せずともウイルス(RAT)が仕込まれます。
  2. OS別の狙撃: Linux、Windows、macOSそれぞれに最適な攻撃コードが送り込まれます。
  3. 証拠隠滅: 実行後に自分自身の痕跡(setup.js)を消去し、package.json を書き換えて「何事もなかったかのように」振る舞います。

今後の対策と教訓

たまたまGrowiのアップデートタイミングとずれていた、そして、 pnpm-lock.yamlがあるおかげで、意図しない汚染版の混入を防ぐことができました。

なお、今回のケースで「黒」だった場合は、該当axiosの除去に留まりません。

VPS乗の全ての認証情報

  • SSH鍵
  • APIトークン

などが漏洩されたものとして作り直す必要があります。

以上、4月1日に公開すべき内容ではないものでした。

TLSの矛盾で読み解くエージェント偽装の対応。

筆者はUbuntu環境のApache設定で

  • 不審なIP/NWをブロック
  • 過剰にアクセスしてくるクローラーをエージェントで判別してブロック

というセキュリティ対策を取っています。(詳細)

しかし、これは相手もその辺りの呼吸をわきまえていて、

  • まだブロックされていない/もしくは攻撃者がよく使うASN「ではない」アクセス元から
  • 正常なアクセスを装って
  • 情報を袖手したり攻撃の糸口をつかもうとする

パターンが割とあります。今回、それを検知した時のお話。

不自然に見えたログ

例によってURLとIPアドレスはダミーですが、以下のような奇妙なログを見つけました。

192.0.2.10 - - [27/Mar/2026:10:28:03 +0900] "GET /images/?1770681132 HTTP/1.1" 404 5506 "-" "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.4601.1280 Mobile Safari/537.36"

一見、ただの404エラーではありますが、強烈な違和感を覚えました。

違和感1「古いAndroid端末」

Android 5.0は、2018年頃に公式セキュリティサポート終了。GooglePlay開発者サービスも2024年7月には終わっています。

違和感2「古いChrome」

同じくChrome60。これも2017年と古いバージョンです。

違和感3「TLS1.3貫通」

そんな古いAndroidが筆者のサイトにアクセスできるということはまず、あり得ません。種を明かしてしまうと、筆者のWebサイトは

#SSL対応
  SSLEngine on
    Protocols h2 http/1.1
SSLProtocol             all -SSLv3 -TLSv1 -TLSv1.1 -TLSv1.2

として、TLS1.3未満のアクセスができないようになっています。 この暗号化形式をサポートするようになったのはAndroid10以降。

結論:エージェント偽装。

古いAndroidが新しいSSL暗号が施されたサイトには、そもそもアクセス不可能です。リクエストの段階でハンドシェイクが拒否されるため、404エラーどころかWebサイトそのものが見られません。

しかし、上記のアクセスログは「古いAndroidのバージョンからTLS1.3のSSLログが残る」。つまり、残る結論はほぼ「エージェントを偽装してくるクローラー」に限られます。

アプリ開発者が Conscrypt(Google製のセキュリティライブラリ)などをアプリに同梱している場合は、Android 4.4以降の古い端末でもアプリ単位でTLS 1.3通信を行える場合がありますが、そんな回りくどい方法はないでしょう

措置:新たなエージェント拒否を追加。

筆者の「厄介なエージェントを拒否する」仕組みがこちら。

  • apache側
(省略)
    # botのアクセスリストを外部ファイルから読み込む
    Include /etc/apache2/conf-enabled/bad-bot-list.conf
(省略)
        <RequireAll>
            # bad_bot変数がセットされていたらアクセスを拒否
            Require not env bad_bot
            # それ以外は許可
            Require all granted
        </RequireAll>
  • bad-bot-list.conf
SetEnvIfNoCase User-Agent "facebookexternalhit/1\.1" bad_bot dontlog
SetEnvIfNoCase User-Agent "SemrushBot/7~bl" bad_bot dontlog
SetEnvIfNoCase User-Agent "AhrefsBot" bad_bot dontlog
SetEnvIfNoCase User-Agent "MJ12bot" bad_bot dontlog
SetEnvIfNoCase User-Agent "DotBot" bad_bot dontlog
SetEnvIfNoCase User-Agent "Baiduspider" bad_bot dontlog
SetEnvIfNoCase User-Agent "YandexBot" bad_bot dontlog
SetEnvIfNoCase User-Agent "Sogou web spider" bad_bot dontlog

等。この、SetEnvIfNoCase User-Agentの箇所に、以下を加えます。

# Android 10.0未満  を排除する
SetEnvIfNoCase User-Agent "Android [1-9]\." bad_bot dontlog

# 10年以上前の古いOS(Windows XP/Vista等)を装うボットも排除
SetEnvIfNoCase User-Agent "Windows NT [3-5]\." bad_bot dontlog

# その他、不自然な挙動を示すUA群
SetEnvIfNoCase User-Agent "^$" bad_bot dontlog

後は

sudo apache2ctl configtest
sudo systemctl reload apache2.service

で、上記、不自然なログのアクセスはピタリと止まりました。

まとめ

不正アクセスにはユーザーのみならず

  • IPアドレス
  • ドメイン

はもとより、OSやエージェントを偽装してくる輩も多いので。

だが…………
マヌケは見つかったようだな

ぐらいの勢いでアクセスログを観察していきましょうというお話でした。

Page 1 of 60

Powered by WordPress & Theme by Anders Norén