2015年12月21日月曜日

DockerでLet's EncryptのSSLを導入する

無料でSSL証明書を自動発行するサービス Let's Encrypt を利用して、nginxにSSL証明書を組み込む手順を紹介します。
通常、Let's Encryptの証明書を発行するには、Pythonやモジュールをインストールして、Lets's Encrypt Clientを使えるようにする必要がありますが、これらの環境構築を簡素化するためにDockerを利用します。
以下導入手順です。OSはCentOS 6を使用しています。

Dockerをインストール
# yum install docker-io
# service docker start
# service docker status
docker (pid  2257) is running...
※ もし。Dockerが起動できず。以下のようなメッセージが出るときは、カーネルのバージョンを上げる必要があります。
# cat /var/log/docker
\nWed Aug 17 15:27:36 JST 2016\n
time="2016-08-17T15:27:36.884624234+09:00" level=warning msg="You are running linux kernel version 2.6.32-431.17.1.el6.x86_64, which might be unstable running docker. Please upgrade your kernel to 3.10.0."
/usr/bin/docker: relocation error: /usr/bin/docker: symbol dm_task_get_info_with_deferred_remove, version Base not defined in file libdevmapper.so.1.02 with link time reference
yumで最新のカーネルをインストールして、サーバをリブートします。
# yum clean all
# yum -y upgrade

Let's Encryptで証明書を発行
コマンドを実行すると、--webroot-path で指定したパス以下に、.well-known/acme-challenge/* ディレクトリを作って認証ファイルが設置されます。
Let's Encryptの認証クローラが、このファイルにアクセスして認証処理をします。
Dockerコンテナから、外部ファイルへアクセスする必要があるので、 -v オプションに --webroot-path と同じパスを指定しておきます。

以下の例では、--webroot-path に /usr/share/nginx/html を指定。
同じく、-v "/usr/share/nginx/html":"/usr/share/nginx/html"を指定。
-d にドメイン historia.hitokoto.co を指定。
-m にメールアドレスを指定します。
# docker run -it --rm --name letsencrypt \
            -v "/etc/letsencrypt:/etc/letsencrypt" \
            -v "/var/lib/letsencrypt:/var/lib/letsencrypt" \
            -v "/usr/share/nginx/html":"/usr/share/nginx/html" \
            quay.io/letsencrypt/letsencrypt:latest certonly \
            --agree-tos \
            --webroot \
            --webroot-path /usr/share/nginx/html \
            -d historia.hitokoto.co \
            -m yako.takeshi@googlemail.com
IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at
   /etc/letsencrypt/live/historia.hitokoto.co/fullchain.pem. Your cert
   will expire on 2016-03-20. To obtain a new version of the
   certificate in the future, simply run Let's Encrypt again.
 - If you like Let's Encrypt, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le
証明書ができた旨が示されます。
この例では、/etc/letsencrypt/live/historia.hitokoto.co/ 以下にファイルができています。 確認してみましょう。
# ls -al /etc/letsencrypt/live/historia.hitokoto.co
total 8
drwxr-xr-x 2 root root 4096 Dec 21 13:48 .
drwx------ 3 root root 4096 Dec 21 13:48 ..
lrwxrwxrwx 1 root root   44 Dec 21 13:48 cert.pem -> ../../archive/historia.hitokoto.co/cert1.pem
lrwxrwxrwx 1 root root   45 Dec 21 13:48 chain.pem -> ../../archive/historia.hitokoto.co/chain1.pem
lrwxrwxrwx 1 root root   49 Dec 21 13:48 fullchain.pem -> ../../archive/historia.hitokoto.co/fullchain1.pem
lrwxrwxrwx 1 root root   47 Dec 21 13:48 privkey.pem -> ../../archive/historia.hitokoto.co/privkey1.pem

OpenSSLのサーバコマンドで証明書をチェック
s_server を起動。オプションで証明書を指定します。さらに、-wwwオプションを付けて HTTPS サーバをエミュレートします。
# cd /etc/letsencrypt/live/historia.hitokoto.co
# openssl s_server -cert cert.pem -key privkey.pem -CAfile chain.pem -www
Using default temp DH parameters
Using default temp ECDH parameters
opensslのs_serverがポート 4433 番で接続待ちをしている状態になりました。

クライアントを起動。
$ openssl s_client
CONNECTED(00000003)
depth=2 O = Digital Signature Trust Co., CN = DST Root CA X3
verify return:1
depth=1 C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X1
verify return:1
depth=0 CN = historia.hitokoto.co
verify return:1
---
Certificate chain
 0 s:/CN=historia.hitokoto.co
   i:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X1
 1 s:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X1
   i:/O=Digital Signature Trust Co./CN=DST Root CA X3
---
Server certificate
以下略
ハンドシェイクの様子が流れて、入力待ちとなります。

Curlで確認してみます。
# curl https://historia.hitokoto.co:4433/ -H 'Host: historia.hitokoto.co' -v
* About to connect() to historia.hitokoto.co port 4433 (#0)
*   Trying 153.126.157.62... connected
* Connected to historia.hitokoto.co (153.126.157.62) port 4433 (#0)
* Initializing NSS with certpath: sql:/etc/pki/nssdb
*   CAfile: /etc/pki/tls/certs/ca-bundle.crt
  CApath: none
* SSL connection using TLS_DHE_RSA_WITH_AES_256_CBC_SHA
* Server certificate:
*  subject: CN=historia.hitokoto.co
*  start date: Dec 21 03:49:00 2015 GMT
*  expire date: Mar 20 03:49:00 2016 GMT
*  common name: historia.hitokoto.co
*  issuer: CN=Let's Encrypt Authority X1,O=Let's Encrypt,C=US
> GET / HTTP/1.1
> User-Agent: curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.19.1 Basic ECC zlib/1.2.3 libidn/1.18 libssh2/1.4.2
> Accept: */*
> Host: historia.hitokoto.co
>
* HTTP 1.0, assume close after body
< HTTP/1.0 200 ok
< Content-type: text/html
<
以下略
SSLで通信ができていることが確認できました。

nginxにLet's Encryptの証明書を組み込む
DH鍵交換に使用するパラメータファイルを作成。
# openssl dhparam 2048 -out dhparam.pem
nginx設定ファイルを編集。serverディレクティブを以下のようにしました。
nginxをリスタート。
# service nginx restart
Stopping nginx:                                            [  OK  ]
Starting nginx:

Qualys SSL LabsでSSLの評価を試してみます。
https://www.ssllabs.com/ssltest/index.html

A+となりました。

SSL証明書を更新
証明書を発行する時とほぼ同じコマンドです。--renew-by-defaultオプションを付けています。
状況によって、--interactive=false --tty=false オプションも付けます。
# docker run --rm --name letsencrypt \
            -v "/etc/letsencrypt:/etc/letsencrypt" \
            -v "/var/lib/letsencrypt:/var/lib/letsencrypt" \
            -v "/usr/share/nginx/html":"/usr/share/nginx/html" \
            quay.io/letsencrypt/letsencrypt:latest certonly \
            --webroot \
            --webroot-path /usr/share/nginx/html \
            -d historia.hitokoto.co \
            --renew-by-default
証明書の期限が90日と短いです。そのため、cronなどでSSL証明書を自動更新するようにしておくと良いでしょう。

See Also.
Title image from Let's Encrypt - Overview
User Guide — Let's Encrypt 0.2.0.dev0 documentation
https://letsencrypt.readthedocs.org/en/latest/using.html#running-with-docker
Let's Encrypt を支える ACME プロトコル - Block Rockin’ Codes
http://jxck.hatenablog.com/entry/letsencrypt-acme
Linux - Let's Encrypt で証明書を取得した時の手順備忘録 - Qiita
http://qiita.com/TsutomuNakamura/items/4166423699061e38d296
letsencrypt - DockerでLet’s Encryptしよっか - Qiita
http://qiita.com/sawanoboly/items/9fdde1707de5e975dd15
Let's EncryptのSSL証明書で、Qualys SSLTestでA+評価を獲得するには - Qiita
http://qiita.com/dseg/items/bab80f6f14349fcd9c22
Dockerを導入する(CentOS版) - Qiita
http://qiita.com/zwirky/items/991f61a231f4e198a320