うまいぼうぶろぐ

linuxとhttpdとperlのメモ

リバースプロキシ環境下のapacheではmod_extract_forwardedよりもやっぱりmod_rpaf?

2011/12/04 追記

このエントリ内でmod_rpaf v0.6 では変換したIPでアクセス制御 (allow, deny) が可能と書いてますが、間違っている可能性大ですすいません。 昔すぎて環境も設定もログも残ってないので何とも言えないのですが、勘違いしていただけかもしれません。

というのも、最近になってdebian 6.0 / apache 2.2.21 でこのエントリと同じことしようとしたら、ログのIP変換は出来てもアクセス制御はreverse proxyのIPでしか出来なかった。ブコメ

masa_matya  httpd, reverse-proxy
mod_extract vs. mod_rpaf。自環境だとmod_rpafのv0.6でもアクセス制限が出来なかっ

ともらっているようにmod_rpaf では出来ないかもしれない。

2012/4/14 さらに追記

ここで気になるのはAPR_HAVE_IPV6です。APR_HAVE_IPV6が定義されていなければ、特に問題なく判定できそうです。APR_HAVE_IPV6が定義されているときはどうでしょうか。 アドレス ファミリがAF_INET(IPv4)であるかの条件文が先にあります。先のmod_rpafのコースコードにはアドレス ファミリを設定しているところはありませんでした。ここが非常に怪しいですね。そこで、mod_rpafにアドレスファミリを設定するように追加してみました。AF_INET決めうちです。

とあるので、昔試したサーバではipv6を設定していなかったから問題なくアクセス制御できた。けど最近作ったサーバはipv6を有効にしていて、かつmod_rpafに問題があったのでアクセス制御できなかった、ということっぽい。というわけなので、本家のmod_rpafは更新が止まっているだけど、githubでforkされているものを使えば、mod_rpafでもいいんじゃないかなという感じ。

追記終わり

2015/7/18 追記

awsのelbもリバースプロキシのようになるので、elb配下のwebサーバにはelbのIPアドレスで届くので、その時にも以下の設定は利用できる。
追記おわり


apacheの前段にproxyサーバ(Nginx,Squid,mod_proxyなど)を置いていると、apacheに届くRemote_AddrはリバースプロキシサーバのIPアドレスになる。この状態だとapacheでは

  • ログ解析が出来ない
  • アクセス制限ができない

という問題が起きるので、これを回避するモジュールがmod_rpafmod_extract_forwarded


両方ともRemote_AddrをX-Forwarded-Forヘッダ(設定で変更できる)で上書きしてくれる。で、mod_rpafの以前のversionでは、アクセスログは変更できるけど、アクセス制限は出来なかったらしい。

バックエンドの apache 2.0 + mod_rpaf な環境で .htaccess によるアクセス制限をかけようとしても、接続元の IP アドレスではなく、pound の IP アドレスで制限がかかってしまう、という現象に悩まされました。

じゃあ、mod_extract_forwardedを使えばいいかなと思ったけど、apache2.2ではそのまま動かないらしい。

Apache 2.2 での mod_proxy の scheme_handler のフック API がちょっと変更になっているので,Apache 2.2 ではおそらく動きません。

これはこれで面倒だなー。どっちにしよう、と悩んだ。

mod_rpaf

今回はNginxをリバースプロキシに使っていて、アクセス制限はNginxで可能。ログにオリジナルのRemote_Addrが残せれば良かったので、とりあえずmod_rpafを使ってダメ元で試したところ最新のmod_rpaf 0.6ではアクセス制限も可能になっていた。

mod_rpaf-0.6のCHANGESを見ると

Move the `change_remote_ip' handler from being APR_HOOK_MIDDLE to
APR_HOOK_FIRST to make the module run before modules like mod_geoip.

とあるので、hookする場所が変わってアクセス制限もできるようになったぽい。

install

Makefile のAPXS2のところを、自分で入れたapache2のapxsに変更する。

#APXS2=$(shell which apxs2)
APXS2=/usr/local/apache2/bin/apxs

そしてmake rpaf-2.0, make install-2.0

apache httpd.conf の設定

LoadModule を追加

LoadModule rpaf_module modules/mod_rpaf-2.0.so
mod_rpafの設定
RPAFenable      On
RPAFsethostname On
RPAFproxy_ips   127.0.0.1 10.0.0.1

RPAproxy_ipsで指定しているホスト以外からはX-Forwarded-Forヘッダを指定してもRemote_addrは変更できない。

ヘッダについて

X-Forwarded-Forではなくて他のヘッダを使う場合はRPAFheaderで指定。例えば、Nginxはproxyのsamplet設定に

proxy_set_header        X-Real-IP       $remote_addr;

というように記述しているので、この通り設定する場合は

RPAFheader      X-Real-IP

とする。

追記

VirtualHostで設定している場合、そのVirtualHostディレクティブ内に書かないと有効にならない。globalのhttpd.confに書いてもダメだった。

追記2

RPAFproxy_ipsにはCIDRで書けない。

ver 0.5からの変更点

CHANGESに書いてる該当箇所。

  • mod_rpaf 0.5
197 static void register_hooks(apr_pool_t *p) {
198     ap_hook_post_read_request(change_remote_ip, NULL, NULL, APR_HOOK_MIDDLE);
199 }
  • mod_rpaf 0.6
240 static void register_hooks(apr_pool_t *p) {
241     ap_hook_post_read_request(change_remote_ip, NULL, NULL, APR_HOOK_FIRST);
242 }

mod_rpaf vs mod_extract_forwarded

似たようなモジュールなので、結局のところどちらを使えばいいかっていう。

アクセス制限について

上にも書いたようにmod_rpaf 0.6からはアクセス制限可能になったので、どちらでもいい。

更新頻度

sourceの日付を見ると

3月 9 2004 mod_extract_forwarded.c
1月 1 2008 mod_rpaf-2.0.c

なので、mod_rpafのほうが新しい。まぁ新しけりゃいいってもんでもないけど

apache 2.2の対応

mod_rpaf
実際にinstallしてみて問題なく動いた。上に書いたようにver. 0.6ではアクセス制限も問題ない。

mod_extract_forwarded
mod_rpaf のかわりに mod_extract_forwarded なるものがあるらしい - daily dayflower 上で引用してるように

Apache 2.2 での mod_proxy の scheme_handler のフック API がちょっと変更になっているので,Apache 2.2 ではおそらく動きません。

などなど、色々詳しく書かれている。patch適用などしないと動かないらしい。

負荷/オーバヘッド

調べてないのでわかりまへん。

まとめ

とりあえず上記の理由から、しばらくmod_rpafを使ってみようということで。

おまけ moduleを使わずに解決する方法

ログ

LogFormatの"%h"を"%{X-Forwarded-For}i"に変更する

アクセス制限

mod_rewriteのRewriteCondとかで%{HTTP:X-Forwarded-For}を判別してごにょごにょする。

おまけの追記

yshh おまけの部分、「ヘッダは偽装可能」ということを意識しないと危険

というブコメもらったけど、clientが直接backendサーバに接続できないなら、ヘッダを偽装されても問題ない。

client <----> nginx <----> apache

この場合、clientがnginxにアクセスするときにX-Forwarded-For(X-Real-IP) を偽装してアクセスしても、nginxがapacheに接続する際のX-Forwarded-For(X-Real-IP)はclientから送信されたrequest headerを使うのではなくてremote_addr を利用するから。(偽装された値をそのまま投げるようならreverse proxy serverとして使いものにならない)

当然clientがapacheに直接接続できる場合にヘッダを偽装した場合、"%{X-Forwarded-For}i" のように直接ヘッダを見ている部分は改ざんされることになる。が、直接接続できるような環境なら、そもそもreverse proxyやmod_rpafの話がいらなくて通常のapacheの設定で良いわけで。