mod_proxyとmod_filterを使ってリバプロを構成する

## はじめに
　諸般の事情から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つの手かな