スマートフォン・タブレットからインターネットサーバーオペレーション

APPW.jp
 
error code / triage

同じエラーコード、違う原因
AH コードは分類であって原因ではない

AH01071 を2か月で3回見ました。毎回まったく違う問題でした。 存在しない .php、プラグインの権限エラー、プラグインの型エラー。 コードで検索して出てくる解決策は、たいてい自分のケースではありません。 原因は本文にあるという話です。

Apache 2.4 error.log AH01071 切り分け

[ § 0 ] 導入

同じコードで、3回別のことが起きた

error.log を集計するようになって、AH コードごとに件数を数えられるようになりました。 すると AH01071 が何度も出てくる。同じコードなのだから同じ問題だろう—— と思っていたら、中身が毎回違いました

同じ AH01071、3つの中身
# 1回目
[proxy_fcgi:error] AH01071: Got error 'Primary script unknown'

# 2回目
[proxy_fcgi:error] AH01071: PHP message: PHP Warning:
  file_put_contents(...): Failed to open stream: Permission denied

# 3回目
[proxy_fcgi:error] AH01071: PHP message: PHP Warning:
  Cannot use a scalar value as an array in ... on line 242

1回目はApache の設定の問題。2回目はファイルの権限。 3回目はプラグインのバグ。 直す場所も、直し方も、全部違います。

[ § 1 ] 仕組み

AH コードは、どこで振られるか

AH コードはモジュールごとに割り当てられた通し番号です。 coreproxy_fcgimod_dirmod_ssl などが、 それぞれ自分の番号帯を持っています。

つまりコードが示すのは「どのモジュールが、どの種類の状況を報告したか」だけです。 原因そのものではありません。

AH01071 の正体

proxy_fcgi が「FastCGI から何かエラーを受け取った」と言っているだけです。何を受け取ったかは書いていません。それが本文に続きます。だから同じコードで中身が3通りにも4通りにもなります。

ログ行の構造を見ると、はっきりします。

ログ行の構造
[時刻] [モジュール:レベル] [pid …] [client …] AHxxxxx: 本文
        ~~~~~~~~~~~~~~~~~~~~                    ~~~~~~~~~  ~~~~
        どこが報告したか                          見出し     中身
[ § 2 ] 実測

実際に見た8種類

自分のサーバーで観測したものを並べます。

コードモジュール中身対応
AH01071proxy_fcgiPrimary script unknown要対応
AH01071proxy_fcgiPHP Warning: 権限エラー要対応
AH01071proxy_fcgiPHP Warning: 型エラー要対応
AH00124core内部リダイレクトの上限超過要対応
AH10244core不正な URI パスを拒否対処不要
AH01276dirindex が無く一覧も禁止対処不要
AH02032sslSNI なし接続で別 Host を要求対処不要
AH00489mpm_event起動・再開の記録対処不要

注目すべきは半分が「対処不要」だということです。 Apache が正しく仕事をした記録が、エラーレベルで記録されている。

コードだけでは、直すべきかどうかすら分かりません。 AH01071 は3つとも要対応ですが、 AH10244AH01276AH02032何もしなくていい。 件数の多さで優先順位をつけると、対処不要なものに時間を使うことになります。
[ § 3 ] 切り分け①

「対処不要」を、見分ける

いちばん役に立った判断の軸は、これでした。

サーバーが困っているのか、相手を断ったのか。 断れているなら、それは成功の記録です。

拒否できているか

AH10244 は「不正な URI だったので受理しなかった」という記録です。 /cgi-bin/.%2e/.%2e/bin/sh のようなパストラバーサル探索が来て、 Apache がそれを弾いた。困っているのは攻撃者のほうです。

同じく AH01276 は 403 で、AH02032 は 421 で拒否しています。 いずれも応答を返し終えている

自分の設定か、相手の行為か

状況判断
壊れたリクエストが来た相手の行為。拒否できていれば不要
設定が矛盾している自分の問題。要対応
アプリが例外を投げた自分の問題。要対応
起動・停止・再読み込み運用上の記録

AH00489(起動)や AH00169(停止)は notice レベルですが、エラーを探しているときには目に入ります。 異常ではありません

[ § 4 ] 切り分け②

AH01071 の3回を、分ける

では同じコードの3つを、どう見分けるか。決め手は1つです

この文字列があるか
PHP message:

これがあれば、PHP-FPM は動いています。 スクリプトが起動して、その中で警告や致命的エラーが出た。 それが Apache に転送されてきただけです。問題はアプリ側にあります

無ければ、PHP-FPM に届く手前で失敗していますPrimary script unknown は「そんなスクリプトは無い」という意味で、 存在しない .php を渡してしまっている。 実在チェックで防げる、Apache 側の問題です。

本文の形動いた層見る場所
Primary script unknownFPM の手前Apache の受け渡し設定
PHP message: PHP WarningPHP(起動済み)アプリ・プラグイン
PHP message: PHP Fatal errorPHP(起動済み)同上。より深刻
2回目と3回目は、同じプラグインが原因でした。 キャッシュ系のプラグインが、ログを書けずに警告を出し(権限)、 さらに設定値の型が壊れて警告を出していた(Cannot use a scalar value as an array)。 設定を詰めるより外すほうが早いと判断して削除したら、両方とも消えました。
[ § 5 ] 体感デモ

ログ行を、切り分ける

実際に観測した8行です。選ぶと、3段階で切り分けが進みます。 上のトグルで「コードだけで判断」に切り替えると、何が起きるか見てください。

Interactive · log triage
コードだけ見るか、本文まで読むか
自サーバーの error.log から採取した実際の行です(パスとホスト名は一部を伏せています)。

「コードだけで判断」にすると、AH01071 の3行がすべて同じ説明になります。 3つとも別の場所を直さなければならないのに、区別がつきません。 辞書を引く方式では、ここが限界です

[ § 6 ] まとめ

コードは、見出しにすぎない

AH01071 を3回見て、3回とも別の場所を直しました。 コードは「どのモジュールが報告したか」を示す見出しであって、 原因ではありませんでした。

そして半分は、直す必要すらなかった。 Apache が攻撃を弾いた記録や、起動の記録が、エラーを探す目には同じ「AH〜」として映ります。 これを見分けられるようになると、error.log はずっと静かになります

この記事のまとめ

  • AH コードはモジュールごとの通し番号。原因ではなく分類。
  • 同じ AH01071 で、Apache の設定・ファイル権限・プラグインのバグと3通りあった。
  • 決め手は PHP message: の有無。あれば PHP は動いている=アプリ側の問題。
  • 実測8種類のうち半分は対処不要。拒否できた記録や、起動の記録。
  • 判断の軸は「サーバーが困っているのか、相手を断ったのか」。
  • 件数の多さで優先順位をつけると、対処不要なものに時間を使う
  • 検索するなら、コードではなくメッセージ本文の特徴的な部分で。他人の AH01071 と自分の AH01071 は別物かもしれない。

関連記事

『同じエラーコード、違う原因 — AH コードは分類であって原因ではない』を公開しました。