Apache をバージョンアップしたら mod_rewrite によるリバースプロキシが動かなくなった話
最近、気が付いたら Apache によるリバースプロキシが動かなくなっていた。
うちは由緒正しい「DMZ を持つファイアウォール」を構成してあり、ルータの内側に FreeBSD の GW を置いて宅内 N/W と DMZ を分離している。一方、web サーバは宅内 N/W 上にあるので、GW 上で Apache を動かし、これを mod_rewrite によりリバースプロキシとして動作させることにより、宅内 N/W の web サーバを WAN 側へ公開している。
ところが、いつの間にかこのリバースプロキシがまともに動作しなくなり、全てのアクセスが 404 や 403 になっていた。そもそもはちゃんと動作していたわけで、特に設定を変えていないのに動かなくなったのが意味不明。
Apache を何度か pkg(8) でアップデートしていたのが原因であることはまず間違いないので、最初は設定ファイルが上書きされていないかを確認。しかし、そのような事実はなかった。
次に、「実は非推奨になっている旧い記述を行なっていた」可能性を確認。しかし、これもそんな様子はない。RewriteCond を書くのをさぼっていたので真面目に書いてみたりもしたのだが、やはり効果なし。
仕方がないので、設定ファイルに「LogLevel rewrite:trace5」を指定して /var/log/httpd-error.log を確認。すると、以下の出力が見つかった(行が長いので、タイムスタンプや IP アドレスなどの部分は省略)。
[rewrite:trace4] mod_rewrite.c(505): RewriteCond: input='wiki.tetsudou.jp' pattern='^wiki\\.[^.]+\\.jp$' => matched
[rewrite:trace2] mod_rewrite.c(505): rewrite '/dokuwiki/' -> 'wiki.tetsudou.jp/dokuwiki/'
[rewrite:trace3] mod_rewrite.c(505): add root prefix: wiki.tetsudou.jp/dokuwiki/ -> /wiki.tetsudou.jp/dokuwiki/
[rewrite:trace2] mod_rewrite.c(505): setting lastsub to rule with output %{HTTP_HOST}$1
ちなみに、このログは「https://wiki.tetsudou.jp/dokuwiki/」でアクセスしたときのもの。これを、httpd-vhosts.conf(からインクルードしているファイル)に記述した以下のルール(「RewriteCond を真面目に書いてみた」もの)で処理していた。なお、宅内 N/W のドメイン名を「example.jp」と表しているが、これは説明用のものであり、実際には別のドメイン名を使用している。
RewriteCond %{HTTP_HOST} ^www\.agt\.ne\.jp$
RewriteRule ^(.+)$ http://www.example.jp$1 [P,END,QSA]
RewriteCond %{HTTP_HOST} ^wiki\.agt\.ne\.jp$
RewriteRule ^(.+)$ https://www.agt.ne.jp/dokuwiki$1 [R,L,QSA]
RewriteCond %{HTTP_HOST} ^.*\.agt\.ne\.jp$
RewriteRule ^(.+)$ %{HTTP_HOST}$1 [C]
RewriteRule ^(.+)\.agt\.ne\.jp(.*) http://www.example.jp/$1$2 [P,END,QSA]
RewriteCond %{HTTP_HOST} ^wiki\.[^.]+\.jp$
RewriteRule ^(.+)$ %{HTTP_HOST}$1 [C]
RewriteRule ^wiki\.([^.]+)\.jp/dokuwiki(.*)$ http://$1-www.example.jp/dokuwiki$2 [P,END,QSA]
RewriteRule ^wiki\.([^.]+)\.jp(.*)$ http://$1-www.example.jp/dokuwiki$2 [P,END,QSA]
RewriteCond %{HTTP_HOST} ^(.+)\..+\.jp$
RewriteRule ^(.+)$ %{HTTP_HOST}$1 [C]
RewriteRule ^(.+)\.([^\.]+)\.jp(.*)$ http://www.example.jp/$2/$1$3 [P,R,END,QSA]
上記ログでは、agt.ne.jp ドメインに関する部分を省略し、4 つ目の RewriteCond にマッチするところからを抜き出してある。
リバースプロキシは複数のドメインで共有しており、宅内サーバへリバースプロキシをかけるにあたってはそれぞれのドメインごとに個別の URI(こっちはこっちで VirtualHost だったりするのだが)へ転送するため、RewriteCond 直後の RewriteRule でアクセス時のホスト名をパスへ連結し、それをさらに以降の RewriteRule でパターンに従って加工して転送するという形。ちなみに、ルールの 13・14 行目は転送先が同じだが、これは外部からのアクセス時にパスの「/dokuwiki/」を省略してもいいようにするためのもの。dokuwiki が生成するリンクには「/dokuwiki/」が含まれていたりするので、どちらでも同じ場所にアクセスできるようにしている。
ここで、上記のログを見ていくと、2 行目でホスト名をパスに連結していることが判る(上記ルールの 12 行目の処理)。しかし、その次の行(=「add root prefix:」の行)におかしなことが書かれており、先頭へ勝手に「/」が付加されている。これにより、ルールの 13・14 行目のパターンにマッチしなくなってしまい、最終的に、最後のルールに従って意図しない URI が生成されてしまっていた。
https://access.redhat.com/solutions/7082162 によると、この変更はセキュリティ上の要件だったようなのだが、今後恒久的なものとなるかどうか若干不透明な気もする。そこで、もし突然元の挙動に戻ってしまっても問題が発生しないよう、先頭に「/」が付加されるか否かに関わらず動作する以下のルールに変更した。
RewriteCond %{HTTP_HOST} ^www\.agt\.ne\.jp$
RewriteRule ^(.+)$ http://www.example.jp$1 [P,END,QSA]
RewriteCond %{HTTP_HOST} ^wiki\.agt\.ne\.jp$
RewriteRule ^(.+)$ https://www.agt.ne.jp/dokuwiki$1 [R,L,QSA]
RewriteCond %{HTTP_HOST} ^.*\.agt\.ne\.jp$
RewriteRule ^(.+)$ %{HTTP_HOST}$1 [C]
RewriteRule ^/?(.+)\.agt\.ne\.jp(.*) http://www.example.jp/$1$2 [P,END,QSA]
RewriteCond %{HTTP_HOST} ^wiki\.[^.]+\.jp$
RewriteRule ^(.+)$ %{HTTP_HOST}$1 [C]
RewriteRule ^/?wiki\.([^.]+)\.jp/dokuwiki(.*)$ http://$1-www.example.jp/dokuwiki$2 [P,END,QSA]
RewriteRule ^/?wiki\.([^.]+)\.jp(.*)$ http://$1-www.example.jp/dokuwiki$2 [P,END,QSA]
RewriteCond %{HTTP_HOST} ^(.+)\..+\.jp$
RewriteRule ^(.+)$ %{HTTP_HOST}$1 [C]
RewriteRule ^/?(.+)\.([^\.]+)\.jp(.*)$ http://www.example.jp/$2/$1$3 [P,R,END,QSA]
これにより、以下の通りこれまでと同じく動作するようになった。
[rewrite:trace4] mod_rewrite.c(505): RewriteCond: input='wiki.tetsudou.jp' pattern='^wiki\\.[^.]+\\.jp$' => matched
[rewrite:trace2] mod_rewrite.c(505): rewrite '/dokuwiki/' -> 'wiki.tetsudou.jp/dokuwiki/'
[rewrite:trace3] mod_rewrite.c(505): add root prefix: wiki.tetsudou.jp/dokuwiki/ -> /wiki.tetsudou.jp/dokuwiki/
[rewrite:trace2] mod_rewrite.c(505): setting lastsub to rule with output %{HTTP_HOST}$1
[rewrite:trace3] mod_rewrite.c(505): applying pattern '^/?wiki\\.([^.]+)\\.jp/dokuwiki(.*)$' to uri '/wiki.tetsudou.jp/dokuwiki/'
[rewrite:trace2] mod_rewrite.c(505): rewrite '/wiki.tetsudou.jp/dokuwiki/' -> 'http://tetsudou-www.example.jp/dokuwiki/'
[rewrite:trace2] mod_rewrite.c(505): forcing proxy-throughput with http://tetsudou-www.example.jp/dokuwiki/
[rewrite:trace2] mod_rewrite.c(505): setting lastsub to rule with output http://$1-www.example.jp/dokuwiki$2
[rewrite:trace1] mod_rewrite.c(505): go-ahead with proxy request proxy:http://tetsudou-www.example.jp/dokuwiki/ [OK]
というわけで、ReWriteCond を書かない設定ファイルに戻し、上記同様にホスト名連結後の文字列に対する RewriteRule でパターンの先頭に「/?」をつけてやったところ、従来通り動作するようになった。やれやれ。
ちなみに、Apache のバージョンアップ自体をあまりマメに行なっていなかったし、アクセス自体はもっとマメではなかったので、どのバージョンからこうなったのかは不明。少なくとも 2.4.62 ではこうなっている。
