Apache2.4とTomcatをWindowsServerで連携させてみる

# 目的
これまでApache2.2系 + OpenSSLでTomcatと連携するWebサーバーを構築していましたが、Apacheを2.4系 + OpenSSL1.1.0に変える必要があったため、主にApacheの設定ファイルについて調べたことをまとめました。

# やりたいこと
* クライアントからのリクエストをTomcatに転送すること。
* 静的コンテンツ(画像,css,js)はTomcatに転送せずにApacheで返すこと。
* 初回アクセスしたときのURLに付加されるjsessionid=xxxを除去して転送すること。
* SSLで接続できること

#### やらなくていいこと
* 負荷分散

# 環境
* OS：Windows Server 2012 R2
* Apache : 2.4.27 + OpenSSL 1.1.0　[Apache Lounge](https://www.apachelounge.com/download/)を使用。[^1]
* Tomcat : 7.0.40 

*Apache LoungeのバイナリはVisualStudio2017でコンパイルしています。利用するためにはC++再配布可能パッケージVC15向けを事前にインストールしてください。ダウンロードは上記Apache Loungeのリンクからできます*

# クライアントからのリクエストをTomcatに転送する
mod_proxyを有効にして、プロキシ機能でTomcatに転送します。
ApacheとTomcat間の接続プロトコルですが、一般的にはmod_proxy_ajp、またはmod_jkを使うと思いますが、今回はmod_proxy_httpを利用します。[^2]

``` httpd.conf
# 以下の#を除去してモジュールを有効にします。
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so

# 以下のようにProxyPassを追加し、Tomcat(port:8080)へ転送します
ProxyPass /app http://localhost:8080/app
ProxyPassReverse /app http://localhost:8080/app
```

# 静的コンテンツ(画像,css,js)はTomcatに転送せずにApacheで返す
静的コンテンツはapacheのインストールフォルダ下に以下のように配置し、Tomcatへ転送しないようにします。

```
Apache24
└ htdocs
  └ app
     ├ images
     ├ jss
     └ styles
```

``` httpd.conf
# 以下のようにProxyPassを追加し転送しないようにします。(転送設定よりも前に記述することで有効になります。)
ProxyPass /app/images !
ProxyPass /app/jss !
ProxyPass /app/styles !
ProxyPass /app http://localhost:8080/app
ProxyPassReverse /app http://localhost:8080/app
```

# 初回アクセスしたときのURLに付加されるjsessionid=xxxを除去して転送する
リクエストURLに`jsessionid`が付いていると静的ファイルが正常に取得できないため、mod_rewriteモジュールを利用してjsessionidを除去します。

``` httpd.conf
# 以下の#を除去してモジュールを有効にします。
LoadModule rewrite_module modules/mod_rewrite.so

# 以下のようにrewite定義を追記します。(proxyPassの前でも後でも大丈夫なようです)
RewriteEngine On
RewriteRule ^(.*);jsessionid=(.*)$ $1 [R,L]

ProxyPass /app/images !
ProxyPass /app/jss !
ProxyPass /app/styles !
ProxyPass /app http://localhost:8080/app
ProxyPassReverse /app http://localhost:8080/app
```

# SSLで接続する

#### SSLの証明書を格納するディレクトリ

```
Apache24  
└ conf
  └ ssl
```

#### 秘密鍵に関する考察

* 秘密鍵のbit長は、2048bitとする
* パスフレーズは設定しない。
  Apacheの起動時にパスフレーズの入力が必要になるため。  
  現状もパスフレーズを解除しているから、元々設定しない。  
  秘密鍵だからサーバから鍵が漏えいされない限りは安全な筈。

#### SSLサーバ証明書の設定

①OpenSSLの設定ファイルを環境変数に定義しておく
`set OPENSSL_CONF=E:/Apache24/conf/openssl.cnf`
※OpenSSLコマンド実行時にファイルが無いと怒られるため。

②暗号鍵とCSRを作成する
`openssl req -nodes -new -newkey rsa:2048 -keyout ../conf/ssl/server.YYYY.key -out ../conf/ssl/server.YYYY.csr`

③サーバー証明書を入手する
本番はCAに発行してもらいますが、今回は自己証明書を用意しました
`openssl x509 -in ../conf/ssl/server.YYYY.csr -days 365 -req -signkey ../conf/ssl/server.YYYY.key -out ../conf/ssl/server.YYYY.crt`

##### httpd-ssl.confの設定
* 作成した暗号鍵とサーバー証明書を定義しました。
* ログローテーションを実行（error.log/access.log/ssl_request.log）
* リバースプロキシを定義して、Tomcatにhttpで転送します。(ApacheとTomcat間はhttpとします。)

*証明書の注意事項*
中間証明書はこれまでSSLCertificateChainFileに定義していましたが、今後はサーバ証明書と中間証明書をつなげたファイルを作成し、SSLCertificateFileで定義するようです。（未確認）

``` httpd-ssl.conf
<VirtualHost _default_:443>
DocumentRoot "E:/Apache24/htdocs"
#ServerName www.example.com:443
#ServerAdmin admin@example.com

# ログファイルローテーション
#ErrorLog "E:/Apache24/logs/error.log"
ErrorLog "|bin/rotatelogs.exe logs/ssl_error_%Y%m%d.log 86400 540"
#TransferLog "E:/Apache24/logs/access.log"
CustomLog "|bin/rotatelogs.exe logs/ssl_access_%Y%m%d.log 86400 540" common

SSLEngine on

# サーバー証明書と秘密鍵のパスを定義します
SSLCertificateFile "E:/Apache24/conf/ssl/server_2017.crt"
SSLCertificateKeyFile "E:/Apache24/conf/ssl/server.2017.key"

<FilesMatch "\.(cgi|shtml|phtml|php)$">
    SSLOptions +StdEnvVars
</FilesMatch>
<Directory "E:/Apache24/cgi-bin">
    SSLOptions +StdEnvVars
</Directory>

BrowserMatch "MSIE [2-5]" \
         nokeepalive ssl-unclean-shutdown \
         downgrade-1.0 force-response-1.0
#CustomLog "E:/Apache24/logs/ssl_request.log" \
#          "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"
CustomLog "|bin/rotatelogs.exe logs/ssl_request_%Y%m%d.log 86400 540" \
          "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"


RewriteEngine On
RewriteRule ^(.*);jsessionid=(.*)$ $1 [R,L]

# リバースプロキシを設定してTomcatへ転送します。
SSLProxyEngine on
ProxyPass /app/images !
ProxyPass /app/jss !
ProxyPass /app/styles !
ProxyPass /app http://localhost:8080/app
ProxyPassReverse /app http://localhost:8080/app

</VirtualHost>
```

#### ApacheのSSLを有効にする

従来のmod_ssl.soだけでなく、mod_socache_shmcb.soモジュールも有効にする必要がありました。

``` httpd.conf
LoadModule socache_shmcb_module modules/mod_socache_shmcb.so
LoadModule ssl_module modules/mod_ssl.so
```

#### http(80)からのリクエストをhttps(443)にリダイレクトする

SSLを有効にしたので、クライアントからのhttpリクエストをhttpsにリダイレクトするように変更しました。

``` httpd.conf
#ewriteEngine On
#RewriteRule ^(.*);jsessionid=(.*)$ $1 [R,L]

#ProxyPass /app/images !
#ProxyPass /app/jss !
#ProxyPass /app/styles !
#ProxyPass /app http://localhost:8080/app
#ProxyPassReverse /app http://localhost:8080/app

#httpでのリバースプロキシはやめ、httpsでリダイレクトするように変更しました。
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R,L]
```


# その他に設定したこと

#### mod_access_compatの無効化
Apache2.4系からアクセス制御のためのAllowディレクティブとDenyディレクティブが利用できなくなりました。
このモジュールを有効にしているとこのディレクティブが使えるようになるとのことだが、そもそも現行のWebサーバーでAllowやDenyを定義していなかったので今のうちに無効化しました。

``` httpd.conf
# 以下に#を追加してモジュールを無効にします。
#LoadModule access_compat_module modules/mod_access_compat.so
```

#### htdocsのindexの無効化
アプリケーションのhtdocsに対するディレクトリリスティングを無効にします。

``` httpd.conf
<Directory "E:/Apache24/htdocs">
    # 以下のOptionsのIndexesに(-)を付けて無効にします。FollowSymLinksは有効にするため(+)を付けます。
    Options -Indexes +FollowSymLinks
    AllowOverride None
    Require all granted
</Directory>
```

#### ログローテーション
標準だとAccess.logとerror.logはローテーションを行わないので、rotatelogs.exeを利用してログファイルを日別に出力します。
※毎日0:00になると新しいlogファイルが作られます。

``` httpd.conf
# error.logのファイル名に_yyyymmddを付ける
ErrorLog "|bin/rotatelogs.exe logs/error_%Y%m%d.log 86400 540"

# access.logのファイル名にyyyymmddを付ける
<IfModule log_config_module>
    CustomLog "|bin/rotatelogs.exe logs/access_%Y%m%d.log 86400 540" common
</IfModule>
```

#### チューニング
チューニングとは言っても現行のWebサーバの設定をApache2.4でも引き継いだだけです。
実際にベストな設定値かどうかはテストする必要があります。

``` httpd.conf
# 外部定義ファイル　httpd-mpm.confとhttpd-default.confを有効にする
Include conf/extra/httpd-mpm.conf
nclude conf/extra/httpd-default.conf
```

``` httpd-mpm.conf
# WinNT MPM
<IfModule mpm_winnt_module>
    # ThreadsPerChildを250に変更する（デフォルトは150）
    ThreadsPerChild        250
    MaxConnectionsPerChild   0
</IfModule>
```

※httpd-default.confは、オリジナルのままなので割愛します。

## 感想
これで現行のWebサーバーと同じ様に動かすことができました。
ですがApacheについて改めて調べたてみたことで、セキュリティやパフォーマンスに対する設定をもっと良くできそうだなと感じましたのでこれを機会にApacheについてもう少し触れてみようかとも思いました。

## 参考
* [Apache モジュール mod_proxy](https://httpd.apache.org/docs/2.4/ja/mod/)
* [Apacheのリバースプロキシで特定のパスをプロキシしない。](http://takuya-1st.hatenablog.jp/entry/20100511/1273577034)
* [ProxyPass と RewriteRule を同時に使用する場合の適応順番について Apache2.2](http://takuya-1st.hatenablog.jp/entry/20100511/1273577034)
* [jsessionid が付加された場合に CSS ファイル等が正常に取得できない場合の対処](http://a4dosanddos.hatenablog.com/entry/2013/06/07/011428)
* [Apache2.4からのSSL](http://ume.macoron.net/archives/6610)
* [apache 2.4におけるSSL証明書の設定](https://qiita.com/bageljp@github/items/6b9876b7571852284ead)
* [Apacheで簡単にリバースプロキシ構築](https://blog.neet-shikakugets.com/construct-reverse-proxy-using-apache)
* [Apache モジュール mod_access_compat](https://httpd.apache.org/docs/2.4/ja/mod/mod_access_compat.html)
* [ディレクトリリスティングを無効](http://tech.withsin.net/2017/05/23/httpd-indexes/)
* [Apache2.4での.htaccessによるアクセス制御](http://neoblog.itniti.net/htaccess-apache24/)

## 注釈
[^1]: [Apache Haus](https://www.apachehaus.com/cgi-bin/download.plx)も検討しましたが、こちらは設定内容に若干のカスタマイズが入っていたので、できるだけオリジナルに近いApache Loungeのバイナリ―を選びました。SSLの設定などApache Hausの内容も参考にしています。
[^2]: mod_proxy_httpを選択した理由は現行のWebサーバーが採用しているためです。また、現行のWebサーバーで採用した理由はmod_poxy_ajpを利用していて接続関係の障害が発生したためと聞いていますが具体的なことは不明です。