docker

Dockerで公開しているWebサイトをLet's Encryptを使ってSSL対応する

More than 1 year has passed since last update.

無料でSSLを導入できるLet's Encryptを使って、Dockerコンテナで立ち上げているサイトのSSL対応について簡単に説明します。環境はCentOS7です。

アプリケーションの起動

適当なコンテナを立ち上げます。特に立ち上げられるコンテナがなければ、いろいろと完成しているWordPressイメージを立ち上げるといいです。WordPressを立ち上げる場合は以下みたいにします。

docker run -e WORDPRESS_DB_HOST=192.168.0.1:3306 -e WORDPRESS_DB_USER=root -e WORDPRESS_DB_NAME=example -e WORDPRESS_DB_PASSWORD=password --name blog -d -p 8080:80 wordpress

http://example.com:8080にアクセスするとWordPressが立ちがっていると思います。

SSL証明書の取得

Let's EncryptはいつのまにかDockerで設定ができるようになっているみたいです。下記の説明がとても分かりやすく参考にさせてもらいました。

今回はざっくり下記のようなコマンドでSSL証明書の取得を取得しました。随分前にやったことを思い出しながら書いているのでうろ覚えなんですが、確かstandaloneでやれば面倒な作業なく発行できるんですが、それがオプションで指定したのか、途中の対話式の画面で選択したのか忘れてしまいました。とりあえず自然にできていた覚えがあります。

docker run -it --rm -p 443:443 --name letsencrypt -v "/etc/letsencrypt:/etc/letsencrypt" -v "/var/lib/letsencrypt:/var/lib/letsencrypt" quay.io/letsencrypt/letsencrypt:latest certonly

完了すると以下のようなメッセージが出ます

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at
   /etc/letsencrypt/live/example.com/fullchain.pem. Your cert will
   expire on 2016-08-10. To obtain a new or tweaked version of this
   certificate in the future, simply run certbot again. To
   non-interactively renew *all* of your certificates, run "certbot
   renew"
 - If you like Certbot, 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/example.com/fullchain.pem
/etc/letsencrypt/live/example.com/cert.pem
/etc/letsencrypt/live/example.com/chain.pem
/etc/letsencrypt/live/example.com/privkey.pem

Nginxの設定

Nginxをインストールして設定します。そのままではインストールできないのでepelを入れます。

yum -y install epel-release

インストールしたら/etc/yum.repos.d/epel.repoを編集して、epelのenabledを0にしてデフォルトを無効にしておきます。その後下記のようにしてNginx本体をインストール。

yum install --enablerepo=epel nginx

自動起動をONにしてNginxの起動を確認しておきます。

systemctl enable nginx
systemctl start nginx

続けてNginxのSSLの設定とDockerコンテナへのプロキシを行います。

  1. 今回は全ページをSSL対応するため、80番ポートへのアクセスは全てhttpsにリダイレクト
  2. httpsに来たアクセスの実体はhttp://localhost:portにプロキシすることで表側からSSL対応

その設定が下記になります。

nginx.conf
server {
  listen 80;
  server_name example.com;
  listen [::]:80;
  return 301 https://$host$request_uri; 
}
server {
  listen 443 ssl;
  listen [::]:443 ssl;
  ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

  server_name example.com;

  proxy_set_header Host $host;
  proxy_set_header X-Real-IP $remote_addr;
  proxy_set_header X-Forwarded-Proto https;
  proxy_set_header X-Forwarded-Host $host;
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

  proxy_set_header Host $http_host;
  location / {
    proxy_pass http://127.0.0.1:8080;
  }
}

proxy_set_headerでいろいろとやってますが、アプリケーション側が対応していればこの設定はいらなかったりします。いろいろ端折ってますが大体こんなイメージでいけます。実際の運用時にまだまだ設定しなきゃいけないことがありますが、ざっくりこんな感じ。

(追記) proxy_set_headerについて

proxy_set_header Host $host;という記述を(覚えてはいけないけど)WordPress対策のために設定したことを忘れ、この設定のままLaravelのアプリケーションを立ち上げたらエラーになりました。Dockerでアプリケーションを運用する場合はproxy_set_headerの値に注意したいです。

また、nginx自体の設定ファイルや再起動を自動化しても良いかもしれません。proxy_set_headerの値をjsonで定義しておき動的に書き換えられるようにすれば、意識付けもできますし、個人用Docker運用環境ならそれくらいで良いかもしれません。

(追記) 定期更新について

Let's Encryptには有効期限があります。上記の手順で再度ファイルを作成します。ファイルの作成先は同様の場所になるのですが、別のディレクトリになることがありますので、古いものを削除して同様の場所に配置してあげれば良いです。Nginxを起動していると443番ポートで更新用のコンテナが立ち上がらないので一度Nginxを停止して上げる必要があります。どこか別の場所で生成して持ってきても良いかもしれない。

(追記) 定期更新について2

あれから定期更新や新規サービスの追加毎にSSL証明書の再発行を行っています。毎度作成する時にはよくわからない場所に証明書が設定されて、シンボリックリンクが更新されないという問題にぶち当たり、Nginx側から調査してしまって時間をかけるという悪循環なので自分が忘れないように、作成日の時間を確認しながら最新ファイルの実態がどこに作成されたか確認をしてシンボリック先を変更するの忘れないようにという気持ちを込めてもう一度書いておきました。

参考
- http://qiita.com/ywatai@github/items/a179186a458a42b3c7f0
- http://kido0617.github.io/wordpress/2014-11-03-wordpress-redirect-loop/
- http://postd.cc/secure-web-deployment-with-lets-encrypt-and-nginx/
- http://qiita.com/sawanoboly/items/9fdde1707de5e975dd15