Apacheのhttpd.confをじっくり読んでみた

apache-httpd

はじめに

AWSのインフラエンジニアとして従事して2年以上経過するが、実はApacheというものが何か、というは知っているが、これまでhttpd.confをゼロから作ったことがなく、既に出来上がったものを引き継いで運用していたため、この機会に各パラメータの意味を調べてみようと思いました。

Apacheとは

Webサーバソフトのことです。

PCにApache (httpd)というソフトを入れるとWebサーバとして機能します。

今あなたが見ているこのサイトもリクエストを受けて、Webサーバにあるファイルの内容をレスポンスしてブラウザを通してWebページを見ることができるのです。

httpd.confとは

Apacheの動作を定義する設定ファイルです。

各パラメータについて見ていきましょう

ServerRoot

ServerRoot "/etc/httpd"

Apacheが各種関連ファイルを探すときの基準となるディレクトリです。

このhttpd.conf内にInclude conf.d/*.confと記載されていれば、/etc/httpd/conf.d/配下にある設定ファイルをApache起動時に読み込みに行きます。

以下のようなファイルがあれば順番に読みに行きます。

/etc/httpd/conf.d/00-base.conf
/etc/httpd/conf.d/00-mpm.conf

User apache
Group apach

User apache
Group apache

Apacheがどのユーザー権限で操作を行うかを指定する設定。

権限を設定しておかなとすべてをroot権限で動かすことになるため、仮に、脆弱性をつかれてWebサーバーに侵入された場合、root権限でWebサーバ全体を操作可能になってしまい非常に危険です。

ServerName

Apacheをコンテナで起動している場合、そのコンテナの正式な名前が何か?を定義する場所です。

例えば以下のように定義されている場合、このhttpd.confのファイルを読み取って起動するコンテナの正式名称はdev.example.comであり、ポートは8080で接続する、という設定となります。

ServerName dev.example.com:8080

ServerTokens

ひとことで言えばバージョン情報を外部へ見せないための設定。

Apacheサーバに対して、HTTPリクエストが送信されると、HTTPレスポンスヘッダにApacheのバージョン情報が表示されるため、その情報を出力しないように以下を指定している。

ServerTokens: Prod

何も指定しない場合はHTTPレスポンスヘッダは以下のように情報を表示する

HTTP/1.1 200 OK
Server: Apache/2.4.68 (Red Hat)

Prodを指定することでバージョン情報を出力しないため不要な情報を外部に公開しないためのセキュリティ対策の一つとしての設定です。

ServerSignature

ServerSignatureに関してもServerTokensと似たような設定で、例えば利用者がhttps://example.comでアクセスした場合は正常にブラウザにページが表示されたとします。

しかし、https://example.com/hogeへリクエストし、/hogeというページが見つけられない場合404 Not Foundが返されます。

その際、この設定をonに設定しておくと先ほどの404を返す際に以下のようなエラーページを返します。

404 Not Found

The requested URL was not found on this server.

Apache/2.4.68 Server at example.com/hoge Port 8080

つまり、これもバージョン情報やPort番号など余計な情報を返すことになります。

これをoffに設定しておけば以下のように余計な情報を返しません。

404 Not Found

The requested URL was not found on this server.

これもセキュリティ対策の一つの設定と思えばよいかと思います。

TraceEnable

HTTP TRACEメソッドの利用可否を制御する設定。

そもそもTRACEメソッドとは?

HTTPにはGET(ページ取得), POST(データ送信)などのメソッドは知っているかと思いますが、それ以外にもHEAD, PUT(更新), DELETE(削除), OPTIONS, TRACE, CONNECTなどのメソッドが存在します。

ここでは徳丸本で有名な徳丸さんが書かれた記事から引用しますが、

実はそんなに怖くないTRACEメソッド

Cross-Site Tracing(XST)という化石のような攻撃手法があります。「化石」と書いたように、既に現実的な危険性はないのですが、XSTに関連して「TRACEメソッドは危険」というコメントを今でも見ることがあります。 このエントリでは、XSTという攻撃手法について説…

TRACEメソッドは、HTTPリクエストを「オウム返しに」HTTPレスポンスとして返すもので、以下のようにGET等の代わりにTRACEとしてWebサーバーにリクエストします。

TRACE /auth/index.php HTTP/1.1
Host: example.jp
User-Agent: Mozilla/5.0 (Windows NT 6.0; rv:18.0) Gecko/20100101 Firefox/18.0
Cookie: PHPSESSID=4lel0hml53u2tbhcd9pmo7pkc4
Authorization: Basic eWFtYWRhOnBhc3N3b3Jk
Connection: keep-alive

上記リクエストに対して以下のようなレスポンスが返ります。

HTTP/1.1 200 OK
Date: Tue, 22 Jan 2013 13:51:09 GMT
Server: Apache/2.2.14 (Ubuntu)
Keep-Alive: timeout=15, max=100
Connection: Keep-Alive
Content-Type: message/http
Content-Length: 198

TRACE /auth/index.php HTTP/1.1
Host: example.jp
User-Agent: Mozilla/5.0 (Windows NT 6.0; rv:18.0) Gecko/20100101 Firefox/18.0
Cookie: PHPSESSID=4lel0hml53u2tbhcd9pmo7pkc4
Authorization: Basic eWFtYWRhOnBhc3N3b3Jk
Connection: keep-alive

HTTPリクエストの内容がそのまま返されているため、Cookieヘッダ、Autorizationヘッダの内容まで見えてしまっています。

TRACEメソッドはサーバが受信したHTTPリクエストをそのまま返す診断用機能です。

過去にはXSS (Cross-Site Scripting)と組み合わせたXST (Cross-Site Tracing)攻撃に利用されたため、無効化が推奨されることが多い。

ただ、現在の主要ブラウザではXST対策が実装されており、TRACE自体が重大な脆弱性というわけではない。

それでも通常のWebシステムでは利用用途がほとんどなく、無効化によるデメリットも少ないためTraceEnable Off設定がされていると考えられる。

Header unset X-Powered-By

まず、X-Powered-Byとは、HTTPレスポンスヘッダの一種です。

X-Powered-Byによってレスポンスされるヘッダ情報をunsetとすることで、削除する設定です。

例えばcurl -I https://example.comを実行すると、以下のような情報が返ってくることがあります。

X-Powered-By PHP/8.2.10

つまり、このサイトはPHPで動いている、バージョンは8.2.10かということがわかり、対象バージョンの脆弱性をついて攻撃されるリスクがあるということです。

あえてそんなリスクを取る必要はないので、Header unset X-Powered-Byとしておくことで、余計な情報を表示しないようにするわけです。

Time out

Time outについては例えば60秒と設定していた場合、ユーザからリクエストを受信後、60秒経過しても処理が完了しない場合はタイムアウトとして接続を終了する設定です。

例えば以下のような構成でリクエストを受け取る場合、Apache が後続のアプリケーションサーバへトラフィックを転送し、アプリケーションサーバからの応答を待ちます。

その際、アプリケーションサーバがなんらかの原因で60秒経過しても応答がない場合、Apacheがユーザに対してエラーを返します。

ユーザ -> Apache Webサーバ -> アプリケーションサーバ

KeepAlive

HTTP通信を行う際に、TCP 3-way handshakeによって接続が確立されます。

KeepAliveを有効化しておくと、HTTPリクエストごとにTCP接続を切断せず、確立済みのTCP接続を利用して複数回HTTP通信を行うことができます。

具体的にどういうことか見ていきましょう。

KeepAliveがOffの場合

TCP 3way handshake
↓
HTTP GET /index.html
↓
HTTPレスポンス
↓
TCP切断

TCP 3way handshake
↓
HTTP GET /index.html
↓
HTTPレスポンス
↓
TCP切断

毎回TCP接続を張り直します。

KeepAlive Onの場合

TCP 3way handshake
↓
HTTP GET /index.html
↓
HTTP レスポンス

HTTP GET /style.css
↓
HTTPレスポンス

HTTP GET /image.jpg
↓
HTTPレスポンス

TCP切断

上記のようにTCP接続が使いまわされます。

この設定はデフォルトではKeepAlive onになっています。

RemoteIPProxyProtocol

一言でいうとApacheがアクセス元のIPアドレスを確認するための設定です。

どういうことかというと、例えば以下のように間にALBを挟む場合、ユーザのIPアドレスが203.0.113.10だった場合、ApacheとしてはユーザのIPアドレスである203.0.113.10からのアクセスだということを知りたいわけですが、この場合だとALBを経由してApacheに来るため、Apacheで確認できるIPアドレスはALBのIPアドレスになります。

ユーザ
↓
ALB
↓
Apache Webサーバ

この元のアクセスIPを知りたい場合にRemoteIPProxyProtocolをOnにしておくことでApacheはProxy Protocol付の通信を受け取ることができるようになります。

RemoteIPHeader X-Forwarded-For

一言で言うとHTTPヘッダーの一種で、X-Forwarded-ForはALBやリーバスプロキシを経由した際に、元のクライアントIPアドレスを後続のサーバ伝えるために付与されるヘッダーのことです。

例えば以下のようにX-Forwarded-Forヘッダを付与します。

GET / HTTP/1.1
Host example.com
X-Forwarded-For: 203.0.113.10

DocumentRoot “/var/www/html”

一言で言うと、Apacheが公開するWebコンテンツの配置場所です。

一般的にはDocumentRootは/var/www/htmlで設定し、この配下配置されたファイルがWebコンテンツとなる。

/var/www/html/index.htmlを配置し、ユーザがhttps://example.comにアクセスするとindex.htmlが返される。

DirectoryInex index.html

この設定は正確には以下のディレクティブのようになっています。

<IfModule dir_module>
    DirectoryIndex index.html
</IfModule>

DirectoryIndexはディレクトリへのアクセス時にデフォルトで表示するファイルを指定する設定です。

流れとしては、ユーザがhttps://example.com にアクセスするとDocumentRoot/var/www/htmlが参照され、その後DirectoryIndex index.htmlにより/var/www/html/index.htmlが返される。

ErrorLog

Apacheのエラーログ出力先を指定します。

例えば以下のように設定の場合、

/var/log/httpd/error_log

/var/log/httpd/配下にerror_logが出力されます。

CustomLog

Apacheのアクセスログの出力先を指定します。

エラーログと同様に以下のような設定になります。

/var/log/httpd/access_log

ProxyRequests ProxyPass ProxyPassReverse

ProxyRequests: フォワードプロキシにする設定、Onならフォワードプロキシとして機能、Offならリバースプロキシとして機能。

フォワードプロキシとは

ユーザの代わりにが外部サイトへアクセスする

会社のネットワークなどは以下の流れでインターネットに接続します。

ユーザ
↓
社内Proxy
↓
外部サイト

https://google.comへアクセスすると、上記の流れになるため、Googleからみたら、アクセス元はProxyサーバからのアクセスということになります。

これがどういうことか?

  • データをキャッシュできる
  • 許可されていない不正サイトへのアクセスをブロックする
  • アクセス元を一元管理するため、誰がどのサイトにアクセスしたのかを記録できる

リバースプロキシとは

Webサーバの代わりにリクエストを受ける

ユーザ
↓
Apache
↓
ALB
↓
バックエンドサーバ

上記のような構成で、ALBがリバースプロキシとして動作し、ユーザからのリクエストを後続へ振り分ける役割を担います。

負荷分散やアクセスログの取得、ヘルスチェックなどの機能も提供します。

フォワードプロキシとリバースプロキシをまとめると

フォワードプロキシ
ユーザ (クライアント)の前段に配置され、ユーザに変わって外部サイトへアクセスする。

リバースプロキシ

サーバの前段に配置され、ユーザ (クライアント)からのアクセスを受け付けて、後続のサーバへ転送する。

ここまでを理解した上で、ProxyPassProxyPass / http://internal-alb-123456.ap-northeast-1.elb.amazoaws.comのように設定されていたら、Apacheに到着したリクエストを後続のALBへ転送する設定と理解します。

ユーザ
↓
Apache
↓
ALB

実際にローカル環境にApacheコンテナとバックエンドコンテナを構築して試してみましょう。

以下のディレクトリ構成でファイルを準備します。

apache-sandbox
├ apache
│   └ httpd.conf
├ backend
│   └ index.html
└ docker-compose.yaml
ServerName localhost

LoadModule mpm_event_module modules/mod_mpm_event.so

LoadModule authz_core_module modules/mod_authz_core.so

LoadModule log_config_module modules/mod_log_config.so

LoadModule unixd_module modules/mod_unixd.so

LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so


User daemon
Group daemon

ErrorLog /proc/self/fd/2

LogFormat "%h %l %u %t \"%r\" %>s %b" common
CustomLog /proc/self/fd/1 common

Listen 80

<VirtualHost *:80>

    ProxyPass / http://backend/
    ProxyPassReverse / http://backend/

</VirtualHost>
<h1>Backend Server</h1>
services:
  apache:
    image: httpd:2.4
    container_name: apache

    ports: 
      - "8080:80"
    
    volumes:
      - ./apache/httpd.conf:/usr/local/apache2/conf/httpd.conf
    
    depends_on:
      - backend
  
  backend:
    image: nginx:latest
    container_name: backend
  
    volumes:
      - ./backend/index.html:/usr/share/nginx/html/index.html

上記ファイルを準備し、以下コマンドでコンテナを立ち上げます。

docker compose up -d

[+] Running 14/14
 ✔ apache Pulled                                                                                                                         12.0s 
 ✔ backend Pulled                                                                                                                        10.3s 
                                                                                                                                       
[+] Running 3/3
 ✔ Network apache-sandbox_default  Created                                                                                                0.1s 
 ✔ Container backend               Started                                                                                                0.6s 
 ✔ Container apache                Started 

コンテナが問題なく起動しhttp://localhost:8080にアクセスするとBackend Serverと画面に表示されれば、Apacheが後続のコンテナへ転送できているということになります。

クライアント
↓
Apache
↓
Backend

ProxyPassについてにはこれで理解できたかと思います。

次にProxyPassReverseについてですが、これはなぜ必要なのでしょうか?これが非常にわかりづらいのですが、

上記で構築したbackendコンテナがリダイレクトを返すケースが問題になってきます。

例えば、http://localhost:8080/loginにアクセスした場合、以下のように転送します。

ブラウザ
↓
localhost::8080/login

Apache
↓
backend/login

Backendが仮に以下を返したとします。

HTTP/1.1 302 Found
Location: http://backend/top

ProxyPassReverseがない場合

ブラウザは

Location: http://backend/top

上記を受け取ります。

ブラウザは

http://backend/top

へアクセスしようとします。

ただ、backendはDockerネットワーク内部の名前なのでブラウザから上記でアクセスしてもエラーとなります。

ProxyPassReverseがある場合

Apacheはレスポンスを見て、

Location: http://backend/top

上記を以下に書き換えます。

Location: http://localhost:8080/top

その結果ブラウザは以下へアクセスします。

http://localhost:8080/top

まとめると、

ProxyPassReverseは後続サーバから返却されたリダイレクト先のURLを利用者向けのURLへ書き換えるための設定です。

ここまでhttpd.confについて諸々調べて書き起こしてみました。

いまだにApacheを使っているシステムはまだまだあるので、今後保守案件などで必要になった際の備忘として使用できればと思います。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です