Apache-Tomcat環境のhttps対応

:::note warn
個人メモ
:::

## 前提環境
+ ハードウェア：RaspberryPi4
+ OS：RaspberryPi OS
+ Apache/2.4.54, Tomcat/10.1.7はインストール、httpでのアクセスを確認済
+ オレオレ証明書でのSSL/TLS対応

## 手順
### SSL/TLS証明書を作成する
#### 0. 事前準備
##### opensslのインストール（インストール済みであれば不要）
```Shell:実行コマンド
>sudo apt install openssl
```
#### 1. 秘密鍵の生成
認証局が発行するSSL/TLS証明書には、認証局が持つ秘密鍵による署名がされており、これを装うため同じように秘密鍵を生成する

```Shell:実行コマンド
>openssl genrsa -des3 -out server.key 2048
Generating RSA private key, 2048 bit long modulus (2 primes)
.................+++++
...+++++
e is 65537 (0x010001)
Enter pass phrase for server.key: <秘密鍵のパスワード>
Verifying - Enter pass phrase for server.key: <秘密鍵のパスワード>
```
コマンド実行後、秘密鍵用のパスワード入力を２回（新規と確認用）求められる

:::note info
コマンドの説明
RSA暗号（公開鍵暗号）における公開鍵と秘密鍵のペアのうち秘密鍵を生成し、
パスワードを使用し、暗号化アルゴリズム：トリプルDESで暗号化する
（本来は秘密鍵の平文が欲しいがこのコマンドだけではできないため、一旦トリプルDESで暗号化する）
なお、公開鍵のほうは使用しないためここでは生成しない
| オプション | 説明 |
|:--|:--|
| genrsa | RSA暗号（公開鍵暗号）における公開鍵と秘密鍵を生成する |
| -des3  | 秘密鍵の暗号アルゴリズムをトリプルDESで作成する |
| -out  | 秘密鍵の出力先の指定（ここでは実行ディレクトリにserver.keyファイルを生成） |
| 2048 | 生成する秘密鍵のビット数＝2048bit |
:::

#### 2. 秘密鍵の平文化
トリプルDESで暗号化した秘密鍵を使用してapacheを設定すると、apache起動時にパスワードの入力が必要になるため
ここで暗号化を解除し、平文の秘密鍵を取得する

```Shell:実行コマンド
>openssl rsa -in server.key -out server.key
Enter pass phrase for server.key:
writing RSA key
```
:::note info
コマンドの説明
入力、出力以外のオプションを設定しないことで、復号化した秘密鍵をそのまま出力する（＝平文化する）

| オプション | 説明 |
|:--|:--|
| rsa | RSAキーの処理 |
| -in  | 秘密鍵の入力 |
| -out  | 秘密鍵の出力（同ファイル名を指定しているため上書きされる） |
:::

#### 3. SSL/TLS証明書の生成

```Shell:実行コマンド
>openssl req -new -days 3650 -key server.key -out server.csr
Ignoring -days; not generating a certificate
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:<入力１>
State or Province Name (full name) [Some-State]:<入力２>
Locality Name (eg, city) []:<入力３>
Organization Name (eg, company) [Internet Widgits Pty Ltd]:<入力４>
Organizational Unit Name (eg, section) []:<入力５>
Common Name (e.g. server FQDN or YOUR name) []:<入力６>
Email Address []:<入力７>

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:<入力８>
An optional company name []:<入力９>
```

:::note info
コマンドの説明
CSR（証明書署名要求）を生成する
CSR（証明書署名要求）には、サーバの実在性を確認するための下記９点の情報＋サーバ側で生成した公開鍵をもつ
実際は、このCSRを認証局に送り、認証局の秘密鍵による署名を追加したものが証明書となる。（オレオレ証明書の場合はこの認証局の部分はない）

| オプション | 説明 |
|:--|:--|
| req | 証明書の署名要求（CSR）の作成を行う |
| -new | 新規にCSR（証明書署名要求）を作成する |
| -days | 有効期限の日数、ここでは3650日（10年） |
| -key | CSR（証明書署名要求）に含める公開鍵を生成するのに使用する秘密鍵ファイル |
| -out | 出力するCSR（証明書署名要求）ファイル |

入力内容
| # | 項目名 | 項目名（英語） | 備考 |
|:--|:--|:--|:--|
| 入力１ | 国名 | Country Name | 例：JP,EN |
| 入力２ | 都道府県名 | State or Province Name |   |
| 入力３ | 市区町村名 | Locality Name |   |
| 入力４ | 組織名 | Organization Name |   |
| 入力５ | 組織内の部署名 | Organizational Unit Name |   |
| 入力６ | ホスト名（FQDN） | Common Name |   |
| 入力７ | Eメールアドレス | Email Address |   |
| 入力８ | チャレンジパスワード | A challenge password | オプション[^1] |
| 入力９ | 会社名略称 | An optional company name | オプション[^2] |
:::

#### 4. 証明書の作成

```Shell:実行コマンド
>openssl x509 -in server.csr -out server.crt -req -signkey server.key -days 3650
Signature ok
subject=C = xx, ST = xx, L = xx, O = xx, CN = xx
Getting Private key
```

:::note info
コマンドの説明
CSR（証明書署名要求）に秘密鍵の署名を追加し証明書を作成する

| オプション | 値 | 説明 |
|:--|:--|:--|
| x509 |  | 証明書を作成するオプション |
| -in | server.csr | 入力となるCSR（証明書署名要求） |
| -out | server.csr | 出力される証明書ファイル |
| -signkey | server.key | CSR（証明書署名要求）に追加する署名、本来は認証局（CA）にて認証局（CA）独自の秘密鍵を使用するがここではRSA作成時の秘密鍵で代用 |
| -days | 3650 | 証明書の有効期限、3650日（10年） |
:::

### SSL/TLS証明書をapacheに組み込む

#### 1. 証明書関連ファイルの移動

格納場所はどこでもOK（権限的にapacheが参照できる場所）
以下ではapache関連ファイルであることが分かりやすいように、apache設定ファイルが格納されている`/etc/apache2`に`ssl`というディレクトリを作成し格納する

```Shell:実行コマンド
>sudo mkdir /etc/apache2/ssl
>sudo mv server.* /etc/apache2/ssl/
```

#### 2. 証明書ファイルの所有者、権限変更

```Shell:実行コマンド
>sudo chmod 444 /etc/apache2/ssl/server.*
>sudo chown tomcat:tomcat /etc/apache2/ssl/server.*
```

#### 3. apacheでSSLを使用できるようにする

```Shell:実行コマンド
>sudo a2enmod ssl
Considering dependency setenvif for ssl:
Module setenvif already enabled
Considering dependency mime for ssl:
Module mime already enabled
Considering dependency socache_shmcb for ssl:
Enabling module socache_shmcb.
Enabling module ssl.
See /usr/share/doc/apache2/README.Debian.gz on how to configure SSL and create self-signed certificates.
To activate the new configuration, you need to run:
  systemctl restart apache2
```

#### 4. 証明書ファイルをapacheから参照するようにする

```Shell:実行コマンド
>sudo vi /etc/apache2/sites-available/default-ssl.conf
```

```diff_conf:default-ssl.conf
<IfModule mod_ssl.c>
        <VirtualHost _default_:443>
                ServerAdmin webmaster@localhost

                DocumentRoot /var/www/html

                # Available loglevels: trace8, ..., trace1, debug, info, notice, warn,
                # error, crit, alert, emerg.
                # It is also possible to configure the loglevel for particular
                # modules, e.g.
                #LogLevel info ssl:warn

                ErrorLog ${APACHE_LOG_DIR}/error.log
                CustomLog ${APACHE_LOG_DIR}/access.log combined

                # For most configuration files from conf-available/, which are
                # enabled or disabled at a global level, it is possible to
                # include a line for only one particular virtual host. For example the
                # following line enables the CGI configuration for this host only
                # after it has been globally disabled with "a2disconf".
                #Include conf-available/serve-cgi-bin.conf

                #   SSL Engine Switch:
                #   Enable/Disable SSL for this virtual host.
+               SSLEngine on

                #   A self-signed (snakeoil) certificate can be created by installing
                #   the ssl-cert package. See
                #   /usr/share/doc/apache2/README.Debian.gz for more info.
                #   If both key and certificate are stored in the same file, only the
                #   SSLCertificateFile directive is needed.
                # SSLCertificateFile    /etc/ssl/certs/ssl-cert-snakeoil.pem
                # SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key
+               SSLCertificateFile    /etc/apache2/ssl/server.crt
+               SSLCertificateKeyFile /etc/apache2/ssl/server.key

                #   Server Certificate Chain:
                #   Point SSLCertificateChainFile at a file containing the
                #   concatenation of PEM encoded CA certificates which form the
                #   certificate chain for the server certificate. Alternatively
                #   the referenced file can be the same as SSLCertificateFile
                #   when the CA certificates are directly appended to the server
                #   certificate for convinience.
                #SSLCertificateChainFile /etc/apache2/ssl.crt/server-ca.crt

                #   Certificate Authority (CA):
                #   Set the CA certificate verification path where to find CA
                #   certificates for client authentication or alternatively one
                #   huge file containing all of them (file must be PEM encoded)
                #   Note: Inside SSLCACertificatePath you need hash symlinks
                #                to point to the certificate files. Use the provided
                #                Makefile to update the hash symlinks after changes.
                #SSLCACertificatePath /etc/ssl/certs/
                #SSLCACertificateFile /etc/apache2/ssl.crt/ca-bundle.crt

                #   Certificate Revocation Lists (CRL):
                #   Set the CA revocation path where to find CA CRLs for client
                #   authentication or alternatively one huge file containing all
                #   of them (file must be PEM encoded)
                #   Note: Inside SSLCARevocationPath you need hash symlinks
                #                to point to the certificate files. Use the provided
                #                Makefile to update the hash symlinks after changes.
                #SSLCARevocationPath /etc/apache2/ssl.crl/
                #SSLCARevocationFile /etc/apache2/ssl.crl/ca-bundle.crl

                #   Client Authentication (Type):
                #   Client certificate verification type and depth.  Types are
                #   none, optional, require and optional_no_ca.  Depth is a
                #   number which specifies how deeply to verify the certificate
                #   issuer chain before deciding the certificate is not valid.
                #SSLVerifyClient require
                #SSLVerifyDepth  10

                #   SSL Engine Options:
                #   Set various options for the SSL engine.
                #   o FakeBasicAuth:
                #        Translate the client X.509 into a Basic Authorisation.  This means that
                #        the standard Auth/DBMAuth methods can be used for access control.  The
                #        user name is the `one line' version of the client's X.509 certificate.
                #        Note that no password is obtained from the user. Every entry in the user
                #        file needs this password: `xxj31ZMTZzkVA'.
                #   o ExportCertData:
                #        This exports two additional environment variables: SSL_CLIENT_CERT and
                #        SSL_SERVER_CERT. These contain the PEM-encoded certificates of the
                #        server (always existing) and the client (only existing when client
                #        authentication is used). This can be used to import the certificates
                #        into CGI scripts.
                #   o StdEnvVars:
                #        This exports the standard SSL/TLS related `SSL_*' environment variables.
                #        Per default this exportation is switched off for performance reasons,
                #        because the extraction step is an expensive operation and is usually
                #        useless for serving static content. So one usually enables the
                #        exportation for CGI and SSI requests only.
                #   o OptRenegotiate:
                #        This enables optimized SSL connection renegotiation handling when SSL
                #        directives are used in per-directory context.
                #SSLOptions +FakeBasicAuth +ExportCertData +StrictRequire
                <FilesMatch "\.(cgi|shtml|phtml|php)$">
                                SSLOptions +StdEnvVars
                </FilesMatch>
                <Directory /usr/lib/cgi-bin>
                                SSLOptions +StdEnvVars
                </Directory>

                #   SSL Protocol Adjustments:
                #   The safe and default but still SSL/TLS standard compliant shutdown
                #   approach is that mod_ssl sends the close notify alert but doesn't wait for
                #   the close notify alert from client. When you need a different shutdown
                #   approach you can use one of the following variables:
                #   o ssl-unclean-shutdown:
                #        This forces an unclean shutdown when the connection is closed, i.e. no
                #        SSL close notify alert is send or allowed to received.  This violates
                #        the SSL/TLS standard but is needed for some brain-dead browsers. Use
                #        this when you receive I/O errors because of the standard approach where
                #        mod_ssl sends the close notify alert.
                #   o ssl-accurate-shutdown:
                #        This forces an accurate shutdown when the connection is closed, i.e. a
                #        SSL close notify alert is send and mod_ssl waits for the close notify
                #        alert of the client. This is 100% SSL/TLS standard compliant, but in
                #        practice often causes hanging connections with brain-dead browsers. Use
                #        this only for browsers where you know that their SSL implementation
                #        works correctly.
                #   Notice: Most problems of broken clients are also related to the HTTP
                #   keep-alive facility, so you usually additionally want to disable
                #   keep-alive for those clients, too. Use variable "nokeepalive" for this.
                #   Similarly, one has to force some clients to use HTTP/1.0 to workaround
                #   their broken HTTP/1.1 implementation. Use variables "downgrade-1.0" and
                #   "force-response-1.0" for this.
                # BrowserMatch "MSIE [2-6]" \
                #               nokeepalive ssl-unclean-shutdown \
                #               downgrade-1.0 force-response-1.0
        </VirtualHost>
</IfModule>

# vim: syntax=apache ts=4 sw=4 sts=4 sr noet
```

#### 5. 設定ファイルの有効化＆apacheのリロードし設定を読み込む

```Shell:実行コマンド
>sudo a2ensite default-ssl
Enabling site default-ssl.
To activate the new configuration, you need to run:
  systemctl reload apache2
>sudo systemctl reload apache2
```

#### 6. httpsでのアクセス確認

下記イメージでは192.168.11.3がraspberrypiのIP

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3492164/b9200dc2-1126-d55f-d89c-3799c983aa60.png)


### httpアクセスをhttpsにリダイレクトする

#### 1. apacheにrewriteモジュールを追加

```Shell:実行コマンド
>sudo a2enmod rewrite
```

#### 2. apache設定ファイルの編集

※ここでは、既存のapache設定ファイルをdefault.confに変えている
※初期状態では、000-default.confになっているかも

```Shell:実行コマンド
>sudo vi /etc/apache2/sites-available/default.conf
```

```diff_conf:default.conf
<VirtualHost *:80>
        # The ServerName directive sets the request scheme, hostname and port that
        # the server uses to identify itself. This is used when creating
        # redirection URLs. In the context of virtual hosts, the ServerName
        # specifies what hostname must appear in the request's Host: header to
        # match this virtual host. For the default virtual host (this file) this
        # value is not decisive as it is used as a last resort host regardless.
        # However, you must set it for any further virtual host explicitly.
        #ServerName www.example.com

        ServerAdmin webmaster@localhost
        DocumentRoot /var/www/html

        # Available loglevels: trace8, ..., trace1, debug, info, notice, warn,
        # error, crit, alert, emerg.
        # It is also possible to configure the loglevel for particular
        # modules, e.g.
        #LogLevel info ssl:warn

        ErrorLog ${APACHE_LOG_DIR}/errorl.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined

        # For most configuration files from conf-available/, which are
        # enabled or disabled at a global level, it is possible to
        # include a line for only one particular virtual host. For example the
        # following line enables the CGI configuration for this host only
        # after it has been globally disabled with "a2disconf".
        #Include conf-available/serve-cgi-bin.conf

+       # Redirect Setting
+       # http(80) -> https(443) Redirect
+       RewriteEngine On
+       RewriteCond %{HTTPS} off
+       RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
</VirtualHost>

# vim: syntax=apache ts=4 sw=4 sts=4 sr noet
```

#### 3. apacheの設定リロード

```Shell:実行コマンド
>sudo systemctl reload apache2
```
#### 4. 確認

`http://～`でアクセスし、httpsアクセス時と同じURL、画面になればOK

### apache→tomcatの設定

#### 1. apacheにproxyモジュールの追加

```Shell:実行コマンド
>sudo a2enmod proxy
```

#### 2. apacheの設定編集

```Shell:実行コマンド
>sudo vi /etc/apache2/sites-available/default-ssl.conf
```

```diff_conf:default-ssl.conf
<IfModule mod_ssl.c>
        <VirtualHost _default_:443>
                ServerAdmin webmaster@localhost
                （途中省略）
                
+               <Location />
+                       ProxyPass ajp://localhost:8009/
+                       ProxyPassReverse ajp://localhost:8009/
+               </Location>
        </VirtualHost>
</IfModule>

# vim: syntax=apache ts=4 sw=4 sts=4 sr noet
```

#### 3. tomcatの設定編集

```Shell:実行コマンド
>sudo vi /opt/tomcat/conf/server.xml
```

既に下記記述があれば不要

```diff_xml:server.xml
<?xml version="1.0" encoding="UTF-8"?>

（途中省略）

<Server port="8005" shutdown="SHUTDOWN">

（途中省略）

  <Service name="Catalina">

（途中省略）

    <!-- A "Connector" using the shared thread pool-->
+   <!--
    <Connector executor="tomcatThreadPool"
               port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />
+   -->

（途中省略）

+   <!-- Define an AJP 1.3 Connector on port 8009 -->
+   <Connector protocol="AJP/1.3"
+              address="::1"
+              port="8009"
+              secretRequired="false"
+              redirectPort="8443" />

（途中省略）

  </Service>
</Server>
```

#### 4. apache/tomcatの再起動

```Shell:実行コマンド
>sudo systemctl reload apache2
```

```Shell:実行コマンド
>sudo systemctl restart tomcat
```

#### 5. 確認

`http://～`でアクセスし、下記画面が出ればOK
`https://～`になってること
tomcatの管理画面が出てること

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3492164/7431e7e9-161d-f14f-f879-ec5225dccd9b.png)



[^1]:チャレンジパスワードは、認証局（CA）が、証明書発行依頼者が正しいことを確認するために使用されるパスワード（例：見知らぬ誰かがその証明書の削除を請求した際、その人にチャレンジパスワードを確認することで、その人が正しい依頼者かどうかを判断する等）
ただし、現状このチャレンジパスワードを使用しているCAはないため空白を推奨しているところが多い
参考：https://security.stackexchange.com/questions/77028/whats-the-use-of-challenge-password-in-build-key-server-and-build-key-from-easy
[^2]:会社名の略称は、認証局（CA）がその会社の実在を確認するために組織名と合わせて使用すると思われる、認証局（CA）が別途使用に関して指定できるオプションらしい
参考：https://stackoverflow.com/questions/18711236/openssl-what-is-the-difference-between-organizationname-and-unstructuredname