人間とウェブの未来

「ウェブの歴史は人類の歴史の繰り返し」という観点から色々勉強しています。

Apache httpd 2.4系をバックエンドに置く場合のクライアントIPアドレスの扱いとハマりどころ

nginxやその他のリバースプロキシをフロントにおいて、バックエンドにApache httpdを置くという構成をとることがあります。

その場合に何も対処しないと、アクセスログやアプリが認識するクライアントIPアドレスがリバースプロキシのIPアドレスになってしまうので、モジュールを入れることでそれを対処していると思います。

バックエンドに置くApacheを2.4系でクライアントIPアドレスの変換を試していたのですが、どうもハマりどころが幾つかあったので、今回のエントリではそれを共有しておこうと思います。

クライアントIPアドレス変換モジュール

バックエンドのApacheで受け取るクライアントIPアドレスを、リバースプロキシのIPアドレスから実際のクライアントIPアドレスへと適切に変換するモジュールで代表的なものを以下に列挙します。

モジュール名 Apache対応バージョン 特徴
mod_rpaf 2.2.x 2.4系に対応していない。IPv6回りで問題があってアクセス制御できなかったが今は改修したものがGitHubにあるらしい。
mod_extract_forwarded 2.2.x 2.4系に対応していない。普通にアクセス制御もできる。
mod_remoteip 2.4.xの標準モジュール 2.4系の標準モジュールなのでこれ使えば良い?

上記を見ると、2.4系で使う上では選択肢などあってないようなもので、mod_remoteip使えば良いという話なのですが、mod_remoteipはできない事がありました。

mod_remoteipを使った場合ホスト名でアクセス制御できない

mod_remoteipを使った場合、allowdenyrequireディレクティブでホスト名によるアクセス制御ができませんでした。というわけで、実際にmod_access_compatやmod_authz_host、及び、mod_remoteipのコードを読んでみました。

すると、denyallowディレクティブではIPアドレスの場合、mod_remoteipによって、リクエストヘッダから渡された本来のクライアントIPアドレスを入れておくr->useragent_addrr->useragent_ipを使用してアクセス制御を行います。ここで、r->useragent_ip等は、Apache2.4系から追加された構造体のメンバで、用途としてはリバースプロキシから渡された実際のクライアントIPアドレスを保存しておく場所です。

一方で、HOST名によるアクセス制御場合は、本来のクライアントIPアドレスではなく、ロードバランサやプロキシ等のコネクションのIPアドレス情報r->connection->client_addrr->connection->client_ip、あるいは、r->connection->remote_hostからホスト名を取得しようとします。

つまり、allowdenyrequireディレクティブを使ったホスト名によるアクセス制御は、依然としてr->connection->client_ipという、バックエンドに直接アクセスのあったIPアドレス、すなわち、リバースプロキシのIPアドレスから逆引きを行いホスト名を解決していたからでした。

うーん、これは困った。

mod_extract_forwardedを2.4系に対応させた

ということで、例のごとくこれらの処理をApache2.4系で実現できるモジュールを作りました。

matsumoto-r/mod_extract_forwarded_for_2.4 · GitHub

といってもmod_extract_forwardedは開発がかなり昔に止まっているようで、連絡をとっていいかも分からなかったので、mod_extract_forwardedのコードをベースに、Apache2.4系に対応させつつmod_remoteipできなかった事をできるように実装しました。

また、2.4系対応時に注意しなければならない点として、wordpressといったアプリケーションは管理画面でコメントやアクセスのあったIPアドレスをREMOTE_ADDRという環境変数から取得する事が多いです。2.2系の時の仕様ではr->connection->client_ipREMOTE_ADDRにセットするため、クライアントIPアドレス変換モジュールではr->connection->client_ipをリバースプロキシのIPアドレスから一時的に本来のクライアントIPアドレスに書き換える必要がありました。

しかし、2.4系では実装がかわり、REMOTE_ADDRにはr->useragent_ipをセットするようになっています。つまり、mod_extract_forwardedを改修する際には、IPアドレスやホスト名でのアクセス制御が適切にできるようにr->connection->client_ipあたりを対応するだけでなく、2.4系で新たに追加されたr->useragent_ipにも本来のクライアントIPアドレスを入れておくようにしないと、アプリケーションからはリバースプロキシのIPアドレスとして見えたままになってしまいます。

まとめ

というわけで、長々とApacheにおけるクライアントIPアドレスの扱いや2.2系と2.4系での違いを述べましたが、Apache2.4用にそれらを考慮したクライアントIPアドレス変換モジュールを実装したので、お困りの際は是非お使いください。

matsumoto-r/mod_extract_forwarded_for_2.4 · GitHub