../ |
---|
VPSサーバー(CentOS8.2)にTomcat9をインストールしている。インターネットからhttp(80ポート)やhttps(443ポート)で受けたリクエストを、Tomcat9で動的に制御できるようにしたい。AJPプロトコル(Apache Jserv Protocol)の実装であるmod_proxy_ajpを利用したい。その手順をメモしておく。
Tomcat9では、初期設定で、8080ポートで HTTP 1.1 の通信を受け付け、8009ポートで AJP 1.3 の通信を受け付けるようになっている。私の環境では、80ポートや443ポートで受け付けた通信は、バーチャルホストを使ってサイトごとに制御し、動的サイトはすべてTomcat9で処理するようにしようと考えている。例えば、kankeri.comはLet's Encyptでサーバー証明し、xxx.comはプライベートのサーバー証明とする。サーバー認証は、httpd側で行い、Tomcat9側では行わないようにする。
- kankeri.com/ --> ようこそkankeriへ (Let's Encypt証明)
- xxx.com/ --> ようこそxxxへ (プライベートサーバー証明)
バージョンの確認
$ httpd -version
Server version: Apache/2.4.37 (centos)
Server built: Sep 15 2020 15:41:16
$ ${CATALINA_HOME}/bin/version.sh
...
Server version: Apache Tomcat/9.0.40
Server built: Nov 12 2020 15:35:02 UTC
Server number: 9.0.40.0
OS Name: Linux
OS Version: 4.18.0-193.28.1.el8_2.x86_64
Architecture: amd64
JVM Version: 11.0.9+11-LTS
JVM Vendor: Red Hat, Inc.
httpd:プロキシモジュールの確認
まず、00-proxy.conf に以下の設定が含まれていることを確認する。
$ vi /etc/httpd/conf.modules.d/00-proxy.conf
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_ajp_module modules/mod_proxy_ajp.so
httpd:プロキシの設定
ProxyPassとProxyPassReverseの記述は、/etc/httpd/conf/httpd.confや/etc/httpd/conf.d/ssl.confにはしない。それぞれのバーチャルホストのconfファイルを作成し、そこに記述する方針とした。
- vhost-02-kankeri.conf (kankeri.com:80用)
- vhost-02-kankeri-le-ssl.conf (kankeri.com:443用)
- vhost-03-xxx.conf (xxx.com:80用)
- vhost-03-xxx-ssl.conf (xxx.com:443用)
vhost-02-kankeri.conf(kankeri.com:80用)
このconfファイルは、手作業で作成したもの。Rewriteの4行はCertbotで追記される。http:/のリクエストはhttps:/に置換される。
$ vi /etc/httpd/conf.d/vhost-02-kankeri.conf
<VirtualHost kankeri.com:80>
ServerName kankeri.com
ServerAlias www.kankeri.com
ServerAdmin webmaster@kankeri.com
DocumentRoot "/opt/tomcat9/webapps/kankeri"
<Directory "/opt/tomcat9/webapps/kankeri">
Options FollowSymLinks
AllowOverride All
Require all granted
</Directory>
ErrorLog logs/kankeri-error_log
CustomLog logs/kankeri-access_log combined
RewriteEngine on
RewriteCond %{SERVER_NAME} =kankeri.com[OR]
RewriteCond %{SERVER_NAME} =www.kankeri.com
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
</VirtualHost>
vhost-02-kankeri-le-ssl.conf(kankeri.com:443用)
Certbotで自動生成されたconfファイルを編集する。Let's EncyptのSSLサーバー証明を使う。 SSL証明書(Let's Encrypt)をセットアップする手順で作成する。そこに、mod_proxy_ajpで遷移できるようにProxyPassとProxyPassReverseを追記する。
$ vi /etc/httpd/conf.d/vhost-02-kankeri-le-ssl.conf
<IfModule mod_ssl.c>
<VirtualHost kankeri.com:443>
ServerName kankeri.com
ServerAlias www.kankeri.com
ServerAdmin webmaster@kankeri.com
DocumentRoot "/opt/tomcat9/webapps/kankeri"
<Directory "/opt/tomcat9/webapps/kankeri">
Options FollowSymLinks
AllowOverride All
Require all granted
</Directory>
ProxyPass / ajp://localhost:8009/kankeri/
ErrorLog logs/kankeri-error_log
CustomLog logs/kankeri-access_log combined
SSLCertificateFile /etc/letsencrypt/live/kankeri.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/kankeri.com/privkey.pem
Include /etc/letsencrypt/options-ssl-apache.conf
</VirtualHost>
</IfModule>
ProxyPassの末尾には/
が必要だが、ProxyPassReverseの末尾には/
は不要らしい。リダイレクト時にLocationヘッダを書き換える必要がないからだそうだ。
vhost-03-xxx.conf(xxx.com:80用)
このconfファイルは、手作業で作成したもの。vhost-02-kankeri.confをコピーして修正したものになる。
$ vi /etc/httpd/conf.d/vhost-03-xxx.conf
<VirtualHost xxx.com:80>
ServerName xxx.com
ServerAlias www.xxx.com
ServerAdmin webmaster@xxx.com
DocumentRoot "/opt/tomcat9/webapps/xxx"
<Directory "/opt/tomcat9/webapps/xxx">
Options FollowSymLinks
AllowOverride All
Require all granted
</Directory>
ErrorLog logs/xxx-error_log
CustomLog logs/xxx-access_log combined
RewriteEngine on
RewriteCond %{SERVER_NAME} =xxx.com [OR]
RewriteCond %{SERVER_NAME} =www.xxx.com
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
</VirtualHost>
vhost-04-xxx-ssl.conf(xxx.com:443用)
xxx.comはプライベートのSSLサーバー証明を使う。 SSL証明書(プライベート認証局)をセットアップする手順で行う。そこに、mod_proxy_ajpで遷移できるようにProxyPassとProxyPassReverseを追記する。
$ vi /etc/httpd/conf.d/vhost-04-xxx-ssl.conf
<IfModule mod_ssl.c>
<VirtualHost xxx.com:443>
ServerName xxx.com
ServerAlias www.xxx.com
ServerAdmin webmaster@xxx.com
DocumentRoot "/opt/tomcat9/webapps/xxx"
<Directory "/opt/tomcat9/webapps/xxx">
Options FollowSymLinks
AllowOverride All
Require all granted
</Directory>
ProxyPass / ajp://localhost:8009/xxx/
ErrorLog logs/xxx-error_log
CustomLog logs/xxx-access_log combined
SSLCertificateFile /etc/pki/tls/xxx.com/server.crt
SSLCertificateKeyFile /etc/pki/tls/xxx.com/server.key
</VirtualHost>
</IfModule>
Tomcat9:server.xmlの設定
続いて、Tomcat側の設定をする。server.xmlを編集するだけである。実は、結構苦戦させられたのだが、解決には、https://qiita.com/polarbear08/items/f016a0675e6c9637e7b8 の記事が参考になった。「Tomcat9では9.0.31以降からAJPでの通信をするためにはTomcat側で初期設定が必要となる」とのことだった。
$ vi ${CATALINA_HOME}/conf/server.xml
<Connector protocol="AJP/1.3"
address="::1"
port="8009"
redirectPort="8443" />
↓
<Connector protocol="AJP/1.3"
address="localhost"
secretRequired="false"
tomcatAuthentication="false" URIEncoding="UTF-8"
port="8009"
redirectPort="8443" />
address属性は、localhost
にしておく。0.0.0.0
を設定すると、全てのIPアドレスを許容するようになる。また、secretRequired="false" の属性を追加する必要がある。httpd側でパスワードを設定して、Tomcat9側で認証することもできるようだが、それは行わないことにした。
再起動と確認
それぞれのDocumentRootにダミーのHTMLを配置し、Apache httpdとTomcat9のサービスを再起動し、ブラウザからアクセスしてみる。
$ ls -la ${CATALINA_HOME}/webapps/kankeri
-rw-r--r-- 1 tomcat9 tomcat9 98 2 11:58 index.html
$ ls -la ${CATALINA_HOME}/webapps/xxx
-rw-r--r-- 1 tomcat9 tomcat9 98 2 11:58 index.html
$ systemctl restart httpd
$ systemctl restart tomcat9
http://xxx.com/
http://www.xxx.com/
https://xxx.com/
https://www.xxx.com/
補足1: Tomcatのログ情報の使い方
当初、AJPの設定がよく分からず、以下のようなエラーがよく出ていた。
Service Unavailable
The server is temporarily unable to service your request
due to maintenance downtime or capacity problems. Please try again later.
httpdの出力ログを見ると、以下のような感じである。
$ tail -50 /var/log/httpd/kankeri-error_log | more
[Sun Dec 06 11:57:10.385924 2020] [proxy:error]
[pid 400345:tid 140206267074304] (111)Connection refused: AH00957: AJP: attempt to connect to 127.0.0.1:8009 (localhost) failed
[Sun Dec 06 11:57:10.386019 2020] [proxy_ajp:error]
[pid 400345:tid 140206267074304] [client 27.xx.xx.xx:51397] AH00896: failed to make connection to backend: localhost
この「AJP: attempt to connect to 127.0.0.1:8009 (localhost) failed」には、しばらく悩まされた。Tomcatのログ(catalina.out)で詳細をみるために、ログレベルをINFOからDEBUGに変更して、Tomcatを再起動し、再現させ、ログを眺めてみた。
$ vi ${CATALINA_HOME}/conf/logging.properties
INFO
↓
DEBUG
$ systemctl restart tomcat9
$ tail -50 ${CATALINA_HOME}/logs/catalina.out | more
06-Dec-2020 13:11:09.262 情報 [main] org.apache.catalina.util.LifecycleBase.handleSubClassException
コンポーネント[Connector[AJP/1.3-8009]]の開始に失敗しました。
org.apache.catalina.LifecycleException:
プロトコルハンドラの起動に失敗しました
Caused by: java.lang.IllegalArgumentException:
AJP コネクタは secretRequired="true" として構成されていますが、
secret 属性は null または空文字列が設定されています。
この組み合わせは有効ではありません
この情報を元にsecretRequired="true"あたりの問題だとたどり着くことができた。また、ログレベルがDEBUGだと、server.xmlの記述ミスがあれば、詳細が出力され、分析が容易になる。
補足2: AJP:8009の前に8080ポートでの接続確認
また、ajp:8009ポートで接続できない場合、まず、http:8080ポートで接続できるかを先に確認した方がよい。原因の切り分けにはなるだろう。
ProxyPass / http://localhost:8080/kankeri
↓
ProxyPass / ajp://localhost:8009/kankeri
以上
../ |
---|