オンプレミスのサーバーでバーチャルホストしながらSSL対応する


レンタルサーバーだと管理画面で`certbot`を使って自動で組めたり、
`AWS`、`GCP`、`Azure`などのクラウドサービスを利用するとGUIで操作するだけだったりしますが、
オンプレミスの場合はコマンドラインの操作が必要。

今どきオンプレミスの時代じゃないかもしれませんが、
勉強目的ということで初心者向けに記事です。


# 条件
- CentOS 7
- Apache2.4
- mod_ssl
- 予めSSL証明書、秘密鍵、中間証明書が取得できていること

# 注意すべきこと

SSL証明書と秘密鍵、CSRなどのチェックサムを利用して秘密鍵と証明書とCSRのセットの整合性が担保された状態で作業にあたることを推奨します。
これらが間違っている場合や証明書、鍵ファイル自体が間違ったものである場合、
`httpd -t` などのシンタックスチェックでは引っかからず、
再起動などを行うとWebサーバーがエラー停止します。
当然ですが、httpdが停止するとホスティングされている全てのWebサイトに影響が出るため注意を払うべきです。

尚、本稿は前提として以下のいずれかの条件を満たしている必要があります。

- `sudo`を利用する場合そのユーザーが`wheel`グループに入っている(Ubuntuの場合は`sudo`グループ)
- `sudo`にて`httpd` `vim/vi` `service` `systemctl` などが利用可能
- `su`などで`root`ログイン可能

---
# SSL化する方法

1つのサーバーで`VirtualHost`などを利用して多数のWebサイトをホスティングしているケースについて書いています。



## SSL証明書(.crt)、秘密鍵(.key)、中間証明書(.crt)の配置

仮にexample.comに対してSSLの化を行うとします。

証明書、鍵、中間証明書を所定のディレクトリに配置します。
まず`sftp`, `rsync`などでサーバー内に転送する(FileZilla等クライアントアプリでも可)。

```bash
$ rsync -avh [source_directory] user@host:[destination_directory]
```
ホスティングサイトのドキュメントルートに秘密鍵などをコピーするのはやめましょう。



ここでは`~/ssl/example.com`とすると

```bash
$ mkdir -p ~/ssl/example.com
```

以下の通りになるようにファイルを配置

```bash
~/ssl/example.com/example.com.crt
~/ssl/example.com/example.com.key
~/ssl/example.com/cert-chain.crt
```

SSL鍵を置くべきディレクトリに
ローカルから転送してきたSSL証明書をコピー

(ここでは`/etc/httpd/conf/ssl`に配置するものとします)



```bash
$ cp -r ~/ssl/example.com /etc/httpd/conf/ssl
```

このままだと所有者と所有グループが`root`以外になっているはずなので

```bash
$ chown -R root:root /etc/httpd/conf/ssl/example.com
```
で所有者:グループを`root`に変更。

権限が適切でない場合はこのタイミングで読み取り専用に変更。

```bash
$ chmod -R 744 /etc/httpd/conf/ssl/example.com
```

ここまでで下記のようなパスに配置されているはずです。
```bash
#ファイルを置くディレクトリ
/etc/httpd/conf/ssl/example.com/example.com.crt
/etc/httpd/conf/ssl/example.com/example.com.key
/etc/httpd/conf/ssl/example.com/cert-chain.crt


```



## バーチャルホストのconf編集

SSL未導入で新規にSSL化する場合はVirtualHostで80番ポートのみListenされている状態だと思います。

例えば以下のようになっているはずです。

```bash

<VirtualHost *:80>
  ServerName  www.example.com
  ServerAlias example.com
  ServerAdmin info@example.com
  ErrorLog  /var/www/vhosts/example.com/logs/error_log
  CustomLog /var/www/vhosts/example.com/logs/access_log combined
  DirectoryIndex index.html index.php
  AddDefaultCharset UTF-8
  DocumentRoot /var/www/vhosts/example.com/httpdocs
  <Directory "/var/www/vhosts/example.com/httpdocs">
    Options -Indexes +FollowSymLinks
    AllowOverride ALL
    Require all granted
  </Directory>
</VirtualHost>
    
  #ここに443番のポートをListenする設定とSSLの情報を追加
 
```

この状態で以下を追加
(設定内容については割愛)
```bash
<VirtualHost *:443>
    ServerName example.com
    ServerAlias www.example.com
    ServerAdmin info@example.com
    ErrorLog  /var/www/vhosts/example.com/logs/error_ssl_access_logs
    CustomLog /var/www/vhosts/example.com/logs/access_ssl_log combined
    DocumentRoot /var/www/vhosts/example.com/httpdocs
    <Directory /var/www/vhosts/example.com/httpdocs>
        Options -Indexes +FollowSymLinks
        AllowOverride ALL
        Require all granted
    </Directory>
    SSLEngine on
    SSLProtocol All -SSLv2 -SSLv3 -TLSv1
    SSLHonorCipherOrder ON
    SSLCipherSuite EECDH+HIGH:EDH+HIGH:HIGH:MEDIUM:+3DES:!ADH:!RC4:!MD5:!aNULL:!eNULL:!SSLv2:!LOW:!EXP:!PSK:!SRP:!DSS:!KRB5
    SSLCertificateFile /etc/httpd/conf/ssl/example.com/example.com.crt
    SSLCertificateKeyFile /etc/httpd/conf/ssl/example.com/example.com.key
    SSLCertificateChainFile /etc/httpd/conf/ssl/example.com/cert-chain.crt
</VirtualHost>
```

できたファイルは以下のようになります。


```bash

<VirtualHost *:80>
  ServerName  www.example.com
  ServerAlias example.com
  ServerAdmin info@example.com
  ErrorLog  /var/www/vhosts/example.com/logs/error_log
  CustomLog /var/www/vhosts/example.com/logs/access_log combined
  DirectoryIndex index.html index.php
  AddDefaultCharset UTF-8
  DocumentRoot /var/www/vhosts/example.com/httpdocs
  <Directory "/var/www/vhosts/example.com/httpdocs">
    AllowOverride ALL
    Require all granted
  </Directory>
</VirtualHost>
<VirtualHost *:443>
    ServerName example.com
    ServerAlias www.example.com
    ServerAdmin info@example.com
        ErrorLog  /var/www/vhosts/example.com/logs/error_ssl_access_logs
        CustomLog /var/www/vhosts/example.com/logs/access_ssl_log combined
        DocumentRoot /var/www/vhosts/example.com/httpdocs
    <Directory /var/www/vhosts/example.com/httpdocs>
        Options -Indexes +FollowSymLinks
        AllowOverride ALL
        Order allow,deny
        Allow from all
    </Directory>
    SSLEngine on
    SSLProtocol All -SSLv2 -SSLv3 -TLSv1
    SSLHonorCipherOrder ON
    SSLCipherSuite EECDH+HIGH:EDH+HIGH:HIGH:MEDIUM:+3DES:!ADH:!RC4:!MD5:!aNULL:!eNULL:!SSLv2:!LOW:!EXP:!PSK:!SRP:!DSS:!KRB5
    SSLCertificateFile /etc/httpd/conf/ssl/example.com/example.com.crt
    SSLCertificateKeyFile /etc/httpd/conf/ssl/example.com/example.com.key
    SSLCertificateChainFile /etc/httpd/conf/ssl/example.com/cert-chain.crt
</VirtualHost>

```


## シンタックスチェック

```bash
$ httpd -t
Syntax OK
```
となれば設定ファイルの文法ミスは無い。

SSL証明書ファイル、鍵ファイルなどが間違っている場合でも文法がOKであればOKと出ます。
鍵ファイルと証明書ファイルのチェックサムを強く推奨。

これまでの動作にミスが無いことを祈りながら
以下のコマンドでApacheを再起動

```bash
$ systemctl restart httpd
```

---

## .htaccessを使ってリダイレクト

SSL未導入の場合はhttp://からhttps://
へのリダイレクトは適用されていないはずなので追加

```bash
RewriteEngine on
#httpをhttpsにリダイレクト
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R,L]
```

お好みで
```bash
#www無しをwwwありにリダイレクト
RewriteCond %{HTTP_HOST} !^www\.
RewriteRule ^(.*)$ http://www.%{HTTP_HOST}/ [R=301,L]
```

その後ブラウザで確認する。


## 証明書適用を確認

```bash
$ openssl s_client -connect example.com:443
```
などで確認する。



# まとめ

今どきオンプレミス構築なんていうものは流行らないかもしれませんが
そういう構成でずっとやってきているところも結構あると思うし、何よりクラウドサービス内部で何が行われてるを理解する点で勉強目的にはちょうど良いように思います。

Let's Encryptの`certbot`を使うと全て自動でいい感じにやってくれるので、
証明書をわざわざ課金して取得するのはあまり無いかもしれません(企業認証型SSLなどは別ですが)。