Apache HTTP Server を「デフォルト設定値」で動かす方法


NRI OpenStandia Advent Calendar 2020の8日目は、Apache HTTP Serverを題材に取り上げます。

始めに

Apache HTTP Server(以下Apache)は昔からよく使われるWebサーバですが、慣れていない人にとっては設定が難しいと思われるようです。
私はApacheの「デフォルト設定ファイル」が複雑で、これが多くの人に苦手意識を生んでいるのではないか?と考えています。そこで今回は、このデフォルト設定ファイルではなく「デフォルト設定値」でApacheを動かしてみよう、という記事を今回書いてみました。

基礎知識

デフォルト設定ファイル vs デフォルト設定値

Apacheの設定の「デフォルト値」としてよく言われるのは、2種類あります。

  1. インストール直後の設定ファイルの値(デフォルト設定ファイルの値)
  2. 設定ファイルに設定を記載しなかった場合の値(デフォルト設定値)

前者はインストール直後に見ることが出来るためその存在は分かりやすいですが、後者を多くの人が知らず知らずのうちに使っています。何しろApacheには695種類の設定ディレクティブが存在し、デフォルト設定ファイルに全てが書かれている訳ではないので、ほとんどの人はその存在すら知らずに2のデフォルト設定値のまま使っているのです。

しかし2のデフォルト設定値があるなら、1のデフォルト設定ファイルの値は何の意味があるのでしょうか?これはスタンスの違いなのかなと思います。
デフォルト設定値はアクセス制御も含め"余計な"機能が一切設定されておらず使いづらいところもありますが、デフォルト設定ファイルはアクセス制御が設定されており"Secure by default"の考え方があったり程よく設定されておりすぐに使えるというところがあります。
しかし、もしそれで多くの人がApacheを苦手に感じているとしたら残念なことです。

デフォルト設定ファイルの大量の設定

Apacheを開発元のソースコードからインストールした場合でも、各種Linuxディストリビューション付属のパッケージをインストールした場合でも、インストール時に置かれる設定ファイルは最初から大量の設定が記載してあり、とても複雑です。

例えば、以下の説明のために、公式のDockerイメージを起動して中に入ってみます。

# docker run -t -i --rm httpd:2.4.46 bash

Dockerではなく公式ソースコードからインストールしても基本的には同様です。

インストール直後の設定ファイルの行数を数えてみます。

# wc -l /usr/local/apache2/conf/httpd.conf
551 httpd.conf

551行! これは設定だけでなく説明のためのコメントが大半ですが、それにしても多いですね。

デフォルト値の実例(LogLevel)

デフォルト設定ファイルの324行目付近に以下のとおりLogLevelディレクティブがあります。これはログの出力レベルの設定です。

# LogLevel: Control the number of messages logged to the error_log.
# Possible values include: debug, info, notice, warn, error, crit,
# alert, emerg.
#
LogLevel warn

仮にこの設定をしなかった場合は「デフォルト設定値」が使用されます。LogLevelのデフォルト値を見てみましょう。マニュアルに記載があります。

デフォルト: LogLevel warnと記載があるので、LogLevelを記載しなかった場合はwarnとなるようです。
これがあるのであれば、(warn以外にする必要がなければ)設定ファイルに書く必要はなく、思い切って消してしまうのも考え方の一つです。

デフォルト設定値で動かしてみよう

ようやくタイトルの話に入っていきます。
LogLevelに限らず、デフォルト設定値があるのだから、まずはそれで動かして、デフォルト設定値ではダメな場合に設定を追加していけば良いのではないか?ということで、やってみました。

まず設定ファイルを思い切って空にしてみます。

# echo > /usr/local/apache2/conf/httpd.conf

これで、デフォルト設定値でApacheが問題なく動くのでしょうか?起動してみます。

# apachectl start
AH00534: httpd: Configuration error: No MPM loaded.

残念、完全に空の設定ファイルで起動することは出来ませんでした。
そもそも完全に空の設定ファイルにしてしまうと、コア的なモジュール以外は一切存在しない状態になるので、MPMも存在しません[^1]。MPMは動作に必要なので、エラーとなってしまいます。
解決は簡単で、ロードする設定を追加すれば良いだけです。今回はEvent MPMをロードして再び起動を試みます。

[^1]: コンパイル時にモジュールを組み込むオプションを指定した場合は、この限りではありません。公式Dockerイメージではモジュールは全く組み込まれておらず、必要なものはLoadModuleでロードする必要があります。

# echo "LoadModule mpm_event_module modules/mod_mpm_event.so" >> httpd.conf
# apachectl start
AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 172.17.0.2. Set the 'ServerName' directive globally to suppress this message
no listening sockets available, shutting down
AH00015: Unable to open logs

またダメでした。
no listening sockets availableだそうですが、なぜでしょうか?

答えはListenディレクティブのマニュアルにきちんと記載がありました。
http://httpd.apache.org/docs/2.4/mod/mpm_common.html#listen

Listen ディレクティブは 現在は必須のディレクティブとなりました。 もし設定ファイルになければ、サーバは起動に失敗します。

デフォルトで80番で起動してくれるわけではないのですね。早速設定を追加して起動します。

# echo "Listen 80" >> httpd.conf
# apachectl start
AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 172.17.0.2. Set the 'ServerName' directive globally to suppress this message

おお、起動できたか?

# ps aux | grep http[d]
# 

起動できてない!エラーログを見てみましょう。

# cat ../logs/error_log
[Sun Dec 06 16:40:51.544435 2020] [core:crit] [pid 17:tid 140427119510656] AH00136: Server MUST relinquish startup privileges before accepting connections.  Please ensure mod_unixd or other system security module is loaded.
AH00016: Configuration Failed

mod_unixdか何かをロードしろと言っています。mod_unixdのマニュアルを見てもモジュールの説明は特にないので前知識が無いとなんだこれはというところですが、存在するディレクティブにUserGroupがあるのがポイントです。このモジュールは大雑把に言うと、ApacheをUnix系OSで使う場合に、root権限で起動した後に動作するユーザーとグループを変更(権限を落とす)するモジュールです。

ではmod_unixdをロードしてみましょう。

# echo "LoadModule unixd_module modules/mod_unixd.so" >> httpd.conf
# apachectl start
AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 172.17.0.2. Set the 'ServerName' directive globally to suppress this message

今度は起動したでしょうか?

# ps aux | grep http[d]
root       599  0.0  0.0   5284  3320 ?        Ss   17:08   0:00 /usr/local/apache2/bin/httpd -k start
root       600  0.0  0.0 1996392 11284 ?       Sl   17:08   0:00 /usr/local/apache2/bin/httpd -k start
root       601  0.0  0.0 1996392 11348 ?       Sl   17:08   0:00 /usr/local/apache2/bin/httpd -k start
root       602  0.0  0.0 1996392 11320 ?       Sl   17:08   0:00 /usr/local/apache2/bin/httpd -k start

おお、今度は起動しました。では早速アクセスしてみましょう。

500エラー…
以下のエラーが出ています。

[Sun Dec 06 17:13:19.243024 2020] [core:crit] [pid 12:tid 140695037785856] [client 172.17.0.1:59724] AH00025: configuration error:  couldn't check user: /

ユーザーをチェックできない…?
Apacheではmod_authz_coreというモジュールでユーザーがアクセスして良いかどうかチェックしています。ロードしてみましょう。

# echo "LoadModule authz_core_module modules/mod_authz_core.so" >> httpd.conf
# apachectl stop
AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 172.17.0.2. Set the 'ServerName' directive globally to suppress this message
# apachectl start
AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 172.17.0.2. Set the 'ServerName' directive globally to suppress this message

アクセスしてみると…

404エラーになりました。これは、デフォルト設定値では、ディレクトリを指定した場合にindex.htmlを表示してくれないためです。そこでURLにindex.htmlを付けると…

無事に表示されました。

まとめ

Apacheをデフォルト設定値で動かすには、次の4行の設定が必要でした。

httpd.conf
LoadModule mpm_event_module modules/mod_mpm_event.so
Listen 80
LoadModule unixd_module modules/mod_unixd.so
LoadModule authz_core_module modules/mod_authz_core.so

このうちLoadModuleの3行は、モジュールをロードしているのみですので、Listenのみが唯一必要な設定と言えそうです。これ以外は全てデフォルト設定値で動いています。
ここから必要な設定を追加していくことで、自分だけのApache設定を作っていくことが出来ます。是非皆さんも試してみて下さい。