はじめに
諸般の事情からwordpress用のリバプロを作ることになったのですが、ググって調べた内容で設定してもうまく動きません。
当初、mod_substituteで設定をしようとしていましたが、どうもこっちは非推奨になったようなので、かわりにmod_filterを使うことにしました。
リバプロ動作を目的にしているので、www.hogehoge.comにアクセスすると、バックエンドのwww.hohoho.orgへプロキシして応答を返すのが目的です。www.hohoho.orgのコンテンツにはhttp://www.hohoho.org/というリンクがたくさんあり、そのままリバプロを通すとトップページはリバプロ経由になるが、リンク先はバックエンドを直接参照することになります。
そこで、リバプロがクライアントに応答を返す前にコンテンスボディを書き換え、www.hohoho.orgをwww.hogehoge.comに変更する必要があるのです。
mod_proxyの設定
モジュールがインストールされていない場合は、モジュールのインストールをしてください。
RHEL(dnf)の場合、/etc/httpd/modules配下にmod_proxy.soが存在していること、httpd.confのどこかで
LocaModulle mod_proxy ....
が設定されていることが前提です。
リバプロの設定自体はそんなに複雑ではありません。
vhostsのディレクティブか、Locationディレクティブか、httpd.confに
RewriteEngine On
SSLProxyEngine On
SSLProxyCheckPeerCN off
SSLProxyCheckPeerName off
ProxyRequests Off
ProxyPass / https://www.hohoho.org/
ProxyPassReverse / https://www.hohoho.org/
ぐらいを追記していればOKです。
SSLProxyをするために、SSLProxyEngineを有効化、証明書のチェックを無効化しています。
ProxyPassがリバプロのバックエンドサーバの指定、ProxyPassReverseは、Proxyしたコンテンツ(www.hohoho.org)がリバプロ(www.hogehoge.com)から応答しているように見せるための設定です
mod_filterの設定
モジュールがインストールされていない場合は、モジュールのインストールをしてください。
RHEL(dnf)の場合、/etc/httpd/modules配下にmod_filter.soが存在していること、httpd.confのどこかでLocaModulle mod_filter ....が設定されていることが前提です。
ここが一番の問題です。
設定は、vhostsのディレクティブか、Locationディレクティブか、httpd.confに
# mod_filter.so
FilterDeclare deflate_filter CONTENT_SET
FilterProvider deflate_filter DEFLATE "%{Content_Type} =~ m!^text/(html|plain|css|japascript)!i"
FilterProvider deflate_filter DEFLATE "%{Content_Type} =~ m!^application/x-javascript!i"
FilterDeclare gzinflate
FilterProvider gzinflate INFLATE "%{req:Accept-Encoding} =~ m!^gzip!i"
FilterDeclare insert_filter
FilterProvider insert_filter SUBSTITUTE "%{Content_Type} =~ m!^text/(html|css|japascript)!i"
FilterProvider insert_filter SUBSTITUTE "%{Content_Type} =~ m!^application/x-javascript!i"
Substitute "s|https://www.hohoho.org/|http://www.hogehoge.com/|ni"
FilterChain @gzinflate
FilterChain insert_filter
FilterChain deflate_filter
コンテンツボディの書き換えだけであれば、mod_substituteで簡単にできるのですが、最近のWebサーバは、コンテンツを高速に転送するため、圧縮して通信をしている場合はあります。
それを念頭においてconfigを見ていきます。
FilterDeclare deflate_filter CONTENT_SET
FilterProvider deflate_filter DEFLATE "%{Content_Type} =~ m!^text/(html|plain|css|japascript)!i"
FilterProvider deflate_filter DEFLATE "%{Content_Type} =~ m!^application/x-javascript!i"
この部分は出力フィルタの設定です。
deflate_filterという名前でフィルタを生成し、Content_Typeがtext/html、css、javascript、application/javascriptの場合、コンテンツを圧縮して応答します。
FilterDeclare gzinflate
FilterProvider gzinflate INFLATE "%{req:Accept-Encoding} =~ m!^gzip!i"
次に、この部分ですがリバプロがバックエンドにアクセスしたときに、gzip形式で圧縮されたコンテンツを解凍するフィルタです。gzinflateという名前でフィルタを作成しています。
FilterDeclare insert_filter
FilterProvider insert_filter SUBSTITUTE "%{Content_Type} =~ m!^text/(html|css|japascript)!i"
FilterProvider insert_filter SUBSTITUTE "%{Content_Type} =~ m!^application/x-javascript!i"
Substitute "s|https://www.hohoho.org/|http://www.hogehoge.com/|ni"
この部分が書き換えフィルタです。insert_filterという名前で生成しています。
コンテンツの書き換え対象を下の2-3行目に記載しています。
最後にコンテンツの中で置き換える文字列を指定しています。
FilterChain @gzinflate
FilterChain insert_filter
FilterChain deflate_filter
この部分はこれまでに設定したフィルタを適応しています。
@から始めると、そのフィルタは1番最初に登録されます。
他のフィルタはフィルタの属性によって適応順が変わりますので、詳細はmod_filterのソースファイルもしくはドキュメントを参照して下さい。
これでエラーがないことを確認し、httpdをリロードすると動作します。
設定に記述する各条件式や書き換えの文字列には、正規表現を使って記述します。
終わりに
これで目的に動作をさせることができました。
どこに躓いたかというと、バックエンドープロキシ間で取得したコンテンツも圧縮されていることがあるので、フィルタを適応する前にこれを解凍する必要があります。
ここがうまくいかず、curlコマンドでテストすると問題なくコンテンツ内容が書き換わるのに、ブラウザでアクセスするとうまくいかない状態になっていました。
まぁ、この設定をしたサーバを使うのがセキュリティ的にどうかは置いておいて、どうしても外に直接さらしたくないWebサーバを守るには1つの手かな