しいしせねっとわーくAmazon.co.jp アソシエイト
[しいしせねっと] [Java] [Tomcat]

Tomcat マルチドメインとLet's Encrypt

いろいろ消えているので復活予定。

mod_jk とか TLS とかMavenで自動配備とか

blog.developer.jp に昔Tomcatを設定した記事があるのですが、ちょっと違うかなと思ったりでこちらに書き直し中。

今回はRaspberryPi OSで管理されているcertbotとTomcatでどうなるのか挑戦。セキュリティ外して動かす野良Tomcatと違ってセキュリティ的な制限がいくつかありそうです。

ヒント的なものなので詳細はぐぐればよいよい。

マルチドメイン管理

conf や webapps などを複数用意することでTLS含むマルチドメイン管理も可能。

Let's encrypt の証明書を使うので certbot をインストールしました。証明書の定期更新は、Tomcatの再起動も必要かもしれません。

本人確認的なものは3つくらいあります

DNSで証明する方法もあるが、まずは手軽にWebで確認する方法で進めることにする。port 80を使う場合、使わない場合で違うかもしれないが、certbotがサーバを勝手に立てて確認する方法は更新の際にWebサーバと競合するのでDNSかサーバの公開ディレクトリを指定するかどちらかにしよう。

conf は /etc/tomcat9/ または /var/lib/tomcat9/conf

/var/lib/tomcat9 に webapps の代わりを用意する。ドメイン名そのままでもいいのかも。アクセス権限もそろえておく。

今回は仮に example.siisise.net ということで進めるがそこは適度に読み替えて。

# mkdir example

# chown tomcat:tomcat example

# chmod 775 example

/lib/systemd/system/tomcat9.service にもReadWritePaths=で追加する ↑セキュリティの確認

conf/server.xml を編集する

まずはポート設定ぐらいかな

有効になっているConnector のport を 8080から 80 に変更するか、もうひとつConnectorを増やす。Connector は同じService内のEngineやHostにアクセスできる、という感じでわかりやすい。

redirectPortも先に8443から443に変更してもいい。

https(TLS)用は証明書を発行していないのであとで設定する。

<Server>
  <Service name="Catalina">
    <Connector port="80" protocol="HTTP/1.1" .... />
    <Engine name=Catalina" defaultHost="example.siisise.net">
      <Host name="example.siisise.net">
        <Value className="..." directory="logs"
           prefix="example.siisise.net_access_log" suffix=".txt"
           pattern="..." />
      </Host>
    </Engine>
  </Service>
</Server>

のような階層になっている。Host以下を増やせばマルチドメインにすることができる。Valueはログの出力設定のようなのでprefixを変更する。

Engine の defaultHost を公開用のドメインに変更しておく。

Tomcat を起動してアクセスできることを確認する。

各ホストのwebapps相当の下に仮で ROOT を作成、certbot を実行しよう。

cd tomcatのwebapps

# mkdir ROOT
# chown tomcat:tomcat ROOT

Let's encrypt

準備が整ったらcertbotでLet's encryptの証明書を取得する。事前に外部からサーバ名でHTTPアクセスできること。IPv4なしでIPv6のみでも可能。

基本的に更新時のことも考えてTomcatを起動したまま更新できる方法をとる。certbotが用意する仮のWebserverは使わないこと。

でも止めないと証明書ファイルが更新できない、かもしれないのでそのあたりはもう少し調べないとわからない。

# certbot certonly

で対話式で進めることもできる

How would you like to authenticate with the ACME CA?
- - - - - - - -
1: Spin up a temporary webserver (standalone)
2: Place files in webroot directory (webroot)

は2を選ぶ。ドメインなどを入力していくとなんとなくおわる。

Tomcatを実行したまま取得する方法。更新のときにも使うのでTomcatが停止した状態の手動設定は避けた方がいいかも。

# certbot certonly --webroot -w /var/lib/tomcat9/example/ROOT -d example.siisise.net -m メールアドレス

DNSにいろいろ追加する場合(手順は略)

# certbot certonly --manual -d example.siisise.net --preferred-challenges dns

サーバのファイルPATH、ドメインとメールアドレスを指定すればいいのかな

本人確認的なもので必要なファイルを置きながら /etc/letsencrypt/ にひととおり作成してくれる。

tomcat からは直接アクセスできないのでコピーして使うなどするのがよさそうか

簡易にであればグループやアクセス権を変更することでも対応できる、今回はとりあえず archive と live に tomcat 的なグループ権限をつける。更新のとき引っかかるかもしれないのでだめならあとで別の方法に変更しよう。

chgrp -R tomcat archive live
chmod 750 archive live
cd archive/example.siisise.net
chmod 640 privkey1.pem

たぶんこのくらい。archive の中のドメイン的なところにもchgrp tomcat付けておくのがよさそう。

renewal-hooks は更新前後に実行できそうなコマンド類を入れるところなのでこちらで更新後のTomcat再起動など対応できるようにしていくのがいいのかも

3カ月に一度の更新

一度作れば証明書の更新は、たぶん自動でやってくれます。たぶん。 systemctl status certbot かなにかで確認して。certbotを手動でインストールなどした場合はcronに登録などいろいろ面倒なので管理されているものがおすすすめ。

# certbot renew

手動でつついてみたい場合は certbot renew で普通の更新確認ができます。--force-renewalをつけると強制更新ができますが、多すぎるとブロックされたりもするので多用しないようにしましょう。

再び /var/lib/tomcat9/conf または /etc/tomcat9 の server.xml

Connector の中で port が8443のものを使う。JDKの証明書形式とPKCS 形式の2例あるが、あとの方はHTTP/2で動く。

HTTP/2のセキュリティアップデートでHTTP/2側が動かないのでHTTP/1.1側にHTTP/2側の<Certificate> を持ってきて使った。問題が解消すればHTTP/2側で使ってもお得かも。

<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol" maxThreads="150" SSLEnabled="true" scheme="https" secure="true">
    <UpgradeProtocol className="org.apache.coyote.http2.Http2Protocol" />
    <SSLHostConfig>
        <Certificate certificateKeyFile="/etc/letsencrypt/live/example.siisise.net/privkey.pem"
                     certificateFile="/etc/letsencrypt/live/example.siisise.net/fullchain.pem"
                     type="RSA" />
    </SSLHostConfig>
</Connector>

単純に書き換えるのであれば<Certificate> で指定されているファイルを変えるだけ。

certificateFile をfullchain.pem にするとchain.pemが省略できます。

Tomcat を再起動してhttpsでport 8443 にアクセスしてみる。うまくいけば443に変更などする。失敗した場合はFirefoxでhttpでアクセスしてみるなど。失敗するパターンとブラウザの組み合わせでアクセスできたりできなかったりする。logs/catalina.out にエラーなどないかよくチェックすればいつか成功するはず。HTTP/2 は謎。

最近のHTTP/2のセキュリティアップデートでTLS接続ができなくなってしまった。原因は調査中。

Firefox で SSL_ERROR_RX_RECORD_TOO_LONG だと TLS化されていない。httpでアクセスすると見える。Chromeはどっちもアクセスできない。

次回の更新

certbot が動いていれば期限の1カ月ぐらい前に新しい証明書を取得する処理が走ります。失敗すればアクセスできなくなっているので対処してみた。

certbot renew を実行してみる。

赤いエラーメッセージが出ていたら最新の証明書はarchive の中にあるが Tomcatがlive側の証明書ファイルをつかんでいるので更新できないという状態かな。一度停止してから再度 certbot renew を実行する。これでTomcatを起動すれば新しいものに更新が完了するはず。

Maven で管理

Maven では warファイルでWebサイトを作成できます。webappsを変更している場合は↑セキュリティの設定は必須です。

Tomcat側は tomcat9-admin 的なパッケージが必要です。アーカイブ版(tar.gz, zip配布のもの)は全部入っているので追加要素はありません。

Mavenでの管理には tomcat7-maven-plugin を使います。tomcat7は7以降ぐらいの意味ですたぶん。

 <build>
  <plugins>
   <plugin>
    <groupId>org.apache.tomcat.maven</groupId>
    <artifactId>tomcat7-maven-plugin</artifactId>
    <version>2.2</version>
    <configuration>
      <url>http://example.siisise.net/manager/text</url>
      <server>1st</server>
      <path>/</path>
    </configuration>
  </plugin>
 </plugins>
</build>

を pom.xml の <plugins> の中に追加していきます。

Tomcat側では manager というものが標準で入っているので使えるようにしていきます。

Tomcat ファイルの編集

tomcatのconf/tomcat-users.xml の編集が必要です。

ユーザ名、パスワード、権限を設定します。

コメントを外しつつ、

<user username="netbeans" password="強そうなパスワード" role="manager-script"/>

的な行を追加します。netbeansのところはツールなどの任意のユーザ名、パスワードは外部から推測しにくい10文字以上くらいのものに変更してください。自分で使わないものでもいいです。

<role rolename="manager-script"/>は追加しなくても大丈夫なようです。

manager-gui だと manager/html が使えます。 manager-script だとスクリプト用管理ページが使えます。maven や netbeans というユーザをつくり manager-scriptをつけておくとよいです。

conf/Catalina/ドメイン名/manager.xml を置いてmanager 機能を有効にします。RaspberryPi の場合は

<?xml version="1.0" encoding="UTF-8"?>
<!--
ライセンス(略)
-->
<Context path="/manager"
    docBase="/usr/share/tomcat9-admin/manager"
    antiResourceLocking="false" privileged="true" />

のようになっているファイルがwebapps か conf/Catalina のどこかにあるので配置します。

さて、pom.xml の<configuration> の中身は。

<configuration>
  <url>http://サーバ/manager/text</url>
  <server>1st</server>
  <path>/</path>
</configuration>

のようになります。url はmanager のpath に /text を加えたものです。https では使えないかもしれません。

<username> と <password> は settings.xml の方に書く方が安全かもしれません。pom.xml の <server>と settings.xml の <id> をそろえて次のようにします。

<servers>
  <server>
    <id>1st</id>
    <username>netbeans</username>
    <password>強そうなパスワード</password>
  </server>
</servers>

初回配備は

mvn tomcat7:deploy

です。2回目以降は

mvn tomcat7:redeploy

とします。