Apache ssl化まとめ(Dockerでも使えます)

https://qiita.com/Diwamoto/items/08a46096743cd89c6d06
の続きです。

#やりたかったこと

・セキュリティの勉強をして、httpsとは何かを学んだ。
・ちょうどDockerで環境作りをしていたので、ついでに自分のローカル環境もssl化するいいタイミングだった。
・ワイルドカードでVirtualHost設定してるしsslもワイルドカード証明書あるっぽいからワイルドカード証明書作ってそれさえあればどのプロジェクトでも自動でssl化できる様にしよう！！

####しかし、世間はそんなに甘くなかったのです。。。

ローカル環境の完全ssl化は長く辛い物語でした。

# つまづきその1

まずやろうと思ったのがDockerfileにてopensslコマンドでbuild時にワイルドカード証明書を作成し、それで運用しようとしたのですが

```Dockerfile
RUN openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/ssl/private/ssl-cert-snakeoil.key -out /etc/ssl/certs/ssl-cert-snakeoil.pem -subj "/C=AT/ST=Vienna/L=Vienna/O=Security/OU=Development/CN=*.localhost"
```

ブラウザにアクセスしてみると、、、

`NET::ERR_CERT_AUTHORITY_INVALID`
(証明書の発行元が無効です)

####動かんのじゃあ！！！

理由としてはローカルの認証局ではなくdockerコンテナ内部でのオレオレ証明書ですので、macのキーチェーンアクセスに登録しなくてはならないという事でした。

うーーーーん、めんどくさい。。。

そこは自動にしたいなあ。。。

(そこのあなた...mkcertを使うのです...)

神からお告げが聞こえてきたので早速google先生に聞いてみました

(もちろんお使いのpcは正常ですし、私の精神はイっちゃってません)

#つまづきその2

神からmkcertを使えと言われたので調べてきました。

###mkcertとは？

https://github.com/FiloSottile/mkcert
mkcertはすごく短いコマンドでローカルCA(認証局)の構築、証明書の発行まで行ってくれる優れものです。

opensslコマンドに絶望していた僕はこれにめちゃめちゃ食いつきました。

これでワイルドカード証明書発行して設定すればいけるやろ！！！

mkcertはhomebrewからinstallできます。ほんと便利。

```bash
$ brew install mkcert
$ mkcert -install
$ mkcert *.localhost
```

実行すると気になる文が。

`Warning: many browsers don't support second-level wildcards like "*.localhost"`

ん？まあ、warningやしいいやろ〜

**ダメです。**

いざブラウザにアクセスすると、

`NET::ERR_CERT_COMMON_NAME_INVALID`

####mkcert使えんのじゃあ！！！

どうやらsubjectaltnameが設定されてない証明書はchromeから弾かれるようです。

...設定してるんだけど...

mkcertはもちろん正常に使える鍵を発行してくれていました。

なのにできない。。。

ここで僕はめちゃめちゃつまずきました。

vhostでワイルドカードが設定できるんだから*.localhostも絶対にできるはず、と思って2週間ほどいろんなサイトを巡っていました。 

**が、**

ある日、わからなすぎてエンジニアの質問ができる数サイトに投稿しました。

https://teratail.com/questions/218121

知恵袋にも投稿したし、stack overflowも覗いたけど返答帰って来てない。。。

その時！！


>"多くのブラウザは、ドメイン名のセカンドレベルでのワイルドカードははじくようです。
(*.comとかの怪しいワイルドカード証明書を排除するためっぽい)"


天才が現れました。`*.localhost`はそもそも使えなかったのです。

例を挙げると`*.com`で証明書を取得すると`facebook.com`も`google.com`もマッチできてしまうのです。なのでブラウザで弾くような仕様になっているのです。

ここで、あのwarning文を見返してみましょう。


`Warning: many browsers don't support second-level wildcards like "*.localhost"`

####いや思いっきり書いとるやないかい!!!!

前提を飛ばして重要な部分のみを見ようとする僕の悪い癖が出ました。GG。

ですが、コマンドで証明書を発行するのはめちゃめんどい。。。なのでファイルで管理するようにして、コマンドの引数として呼び出すようにしました

```hostname.txt
aaa.loclahost
sample.localhost *.sample.localhost
```
これを

```bash
$ mkcert -cert-file ./keys/server.crt -key-file ./keys/server.key $(cat ./hostnames.txt)
```

こうして

```bash
Using the local CA at "/Users/daiki/Library/Application Support/mkcert" ✨

Created a new certificate valid for the following names 📜
 - "sample.localhost"
 - "*.sample.localhost"
 - "aaa.localhost"

Reminder: X.509 wildcards only go one level deep, so this won't match a.b.sample.localhost ℹ️

The certificate is at "./keys/server.crt" and the key at "./keys/server.key" ✅

```

こうじゃ！
コマンドラインに絵文字が出てくるのは中華臭くて個人的に嫌いだけど。。。。

もちろんこれを毎回実行するのはめんどいのでinitssl.shにまとめました。


これをapacheの設定に加えてvhostを設定します。

```ssl.conf
<VirtualHost _default_:443>
    SSLEngine on
    SSLCertificateFile	/etc/apache2/ssl/keys/server.crt
    SSLCertificateKeyFile /etc/apache2/ssl/keys/server.key
</VirtualHost>
<VirtualHost *:443>
    ServerName localhost
    ServerAlias *.localhost
    VirtualDocumentRoot "/var/www/html/%1"
    <Directory "/var/www/html">
        AllowOverride All
    </Directory>
</VirtualHost>
```
443ポートから入って来たアクセスはまずdefaultを通り鍵の設定を受け取って、その後下のvhostに到達し、指定されたdocumentrootへと飛ばれます。

(*.aaa.localhost)を受け取りたければ別途設定が必要です。。。

`*.aaa.localhost`でテストをしてみると。。。

:lock:aaa.aaa.localhost

鍵ついた！！！！！

これにて終了です。

http->httpsのリダイレクトですが、ローカルだと鍵つかないほうが都合がいい時もあるので強制リダイレクトはしません。

ありがとうございました。

長い最強のローカル環境構築の幕が閉じました。