徒労日記のhttps化 ~ Let’s Encryptの無料証明書でnginx上のwordpressを常時SSLに対応させる

徒労日記のhttps化 ~ Let’s Encryptの無料証明書でnginx上のwordpressを常時SSLに対応させる

正直やりたくないなぁ・・・と逃げていたらGoogle Chromeに真綿で首をくすぐられ続けていよいよ重い腰をあげたお話。

「サイトの安全性を高める」なんて崇高な目的よりも、個人Blogに至っては完全に「Google村八分が怖いから」。どうにも手段が目的化するパターンに陥っている気がしてなりません。

はじめに&レジュメ

よくある「https化とはなにか」や「SSL証明書とは」については他著名サイトに譲り、ここでは割愛します。

証明書の手配についてSSL証明書ならさくらのSSLあたりを使っておけばもっとスマートだったとは思うけれど、価値0円サイトにかかるコストはなるべく下げるべき。と思って無料のLet’s Encryptを利用させていただきました。種類としては一番カンタンなDV証明書(ドメイン認証型)となります。

構築環境は

  • Ubuntu 16.04.4 LTS
  • Python 2.7.12
  • nginx/1.10.3 (Ubuntu)

作業は大まかにいって以下手順をふみます。

  1. Let’s Encryptクライアント(certbot)のインストール
  2. SSL証明書の発行
  3. nginxとcronの変更
  4. WordPressへのSSL化プラグイン導入
  5. 動作テスト

今回の作業で参考にさせていただいた以下サイトには大変感謝しております。

Let’s Encryptクライアントのインストール

早速インストールからはじめます。

Let’s Encryptにはやり取りを自動化してくれるCertbotというクライアントがあるのでgitからとってきます

$ cd /tmp
$ git clone https://github.com/letsencrypt/letsencrypt
$ cd letsencrypt

ここから特にどこかへインストールされたりしなかったため、後ほど/tmp/letsencryptから/usr/local/bin/letsencryptへ移動しました。

証明書の発行

ウィザード形式(?)でやってくれるスクリプトがついてくるので実行してみる。

$ ./letsencrypt-auto

Eメールアドレスやnewsletterに参加するかなど聞かれたあと・・・

Obtaining a new certificate
Performing the following challenges:
http-01 challenge for dolls.tokyo
Cleaning up challenges
An unexpected error occurred:
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe3 in position 2: ordinal not in range(128)
Please see the logfiles in /var/log/letsencrypt for more details.

IMPORTANT NOTES:
- Your account credentials have been saved in your Certbot
configuration directory at /etc/letsencrypt. You should make a
secure backup of this folder now. This configuration directory will
also contain certificates and private keys obtained by Certbot so
making regular backups of this folder is ideal.

はい失敗してるううう

UnicodeDecodeErrorとは?でググってみると、コミッタとおぼしき方のこんなコメントを発見。

Crash on starting certbot-nginx with a clean nginx install UnicodeDecodeError: ‘ascii’ codec can’t decode byte 0xc2 in position 10453: ordinal not in range(128) · Issue #5657 · certbot/certbot · GitHub


I think this is a duplicate of #5646 or one of the other Unicode issues mentioned there. This is definitely a bug in Certbot.

A likely workaround is to remove non-ASCII Unicode characters such as ö or ñ from comments and/or filenames in your nginx configuration file. In the future, Certbot should be fixed so it doesn’t crash when encountering these. Sorry for the inconvenience!

See also my comment related to this at

https://community.letsencrypt.org/t/certbot-unicodedecodeerror/52020/2

Certbotのバグだから、Unicode独自のasciiじゃない文字を消してみてね、ゴメン! との事。

Python3.5で実行してもダメ。更に調べると、Python自体のデフォルトエンコーディングをasciiからutf-8に変えてしまえばいいそうな。

$ python
Python 2.7.12 (default, Dec 4 2017, 14:50:18) 
[GCC 5.4.0 20160609] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.getdefaultencoding()
'ascii'

これをutf-8に変える。

それにはpython*.*/site-packagesへsitecustomize.pyを置けばいい、と複数サイトに書いてあったのだけれど効果がなくてしばらくハマりました。
結局、findで見つけた/usr/lib/python2.7/sitecustomize.pyを書き換えたら動きました。

$ sudo vi /usr/lib/python2.7/sitecustomize.py
# install the apport exception handler if available
try:
import apport_python_hook
except ImportError:
pass
else:
apport_python_hook.install()

import sys
sys.setdefaultencoding('utf-8')

再実行。

$ ./letsencrypt-auto
(前略)
Obtaining a new certificate
Performing the following challenges:
http-01 challenge for dolls.tokyo
Waiting for verification...
Cleaning up challenges
Deploying Certificate to VirtualHost /etc/nginx/sites-enabled/dolls.tokyo.conf

Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: No redirect - Make no further changes to the webserver configuration.
2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for
new sites, or if you're confident your site works on HTTPS. You can undo this
change by editing your web server's configuration.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate number [1-2] then [enter] (press 'c' to cancel):1

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Congratulations! You have successfully enabled http://dolls.tokyo

You should test your configuration at:
https://www.ssllabs.com/ssltest/analyze.html?d=dolls.tokyo
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

IMPORTANT NOTES:
- Congratulations! Your certificate and chain have been saved at:
/etc/letsencrypt/live/dolls.tokyo/fullchain.pem
Your key file has been saved at:
/etc/letsencrypt/live/dolls.tokyo/privkey.pem
Your cert will expire on 2018-10-31. To obtain a new or tweaked
version of this certificate in the future, simply run
letsencrypt-auto again with the "certonly" option. To
non-interactively renew *all* of your certificates, run
"letsencrypt-auto 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

動いた!
うまくいくと全部のhttpアクセスをhttpsにリダイレクトするか聞かれます。準備なしでは怖いのでいま時点は1: No redirectを選択。

今後はwebroot経由での証明書発行を行いたい為、単体での証明書発行もテスト。

$ nslookup dolls.or.hm -> OK
$ /usr/local/bin/letsencrypt/certbot-auto certonly --webroot -w /usr/share/nginx/hogehoge -d dolls.tokyo --agree-tos --force-renewal -n
$ /usr/local/bin/letsencrypt/certbot-auto renew

エラーは無いけれど、「まだ新しいからスキップするね」と出ます。90日後がちょっと心配。

nginxとcronの設定変更

nginx

リダイレクトは拒否しましたが、証明書についてはcertbotのnginxプラグインがserverディレクティブへ勝手に追記してくれていました。

$ sudo cat /etc/nginx/sites-enabled/dolls.tokyo.conf
server {

(中略)

location ^~ /.well-known/acme-challenge/ {
default_type "text/plain";
root /usr/share/nginx/hogehoge;
}

listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/dolls.tokyo/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/dolls.tokyo/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

}

最後の5行が発行された証明書ファイルへのシンボリックリンク。
locationディレクティブは参照したサイトさんの真似。Let’s Encryptの定期更新プロセスがhttpsではなくhttpを利用するため、そこだけ例外するためのもの。

2018/10/14 追記

設定不備のため修正。
location ^~ /.well-known/acme-challenge/のrootを{{root}}ではなく、public_htmlの実パス指定に変更。

301のhttpリダイレクトは後述のプラグインとバッティングしたため採用していません。

cron

定期的に証明書を更新する必要があるため、cronへジョブを追加します。nginxのリスタートも行うためrootのcrontabへ追記。

$ sudo crontab -l
0 4 1 * * /usr/local/bin/letsencrypt/certbot-auto renew && service nginx restart

毎月1日の朝4時に再取得&リスタート。

WordPressをReally Simple SSLで一気に

転ばぬ先のバックアップ

何かする前にはフルバックアップ。

$ tar cvzf ~/20180804fullbackup.tar.gz /usr/share/nginx/hogehoge --exclude backwpupfolder
$ sudo service nginx stop
$ mysqldump -u mariaDBuser -p --all-databases > ~/20180804sql.dump
$ sudo service nginx start

WordPressは設定画面とプラグイン

ちょっと緊張しながら管理画面の設定→一般にて「サイトアドレス (URL)」をhttps://してみました。
無事表示されて一安心。

問題は小ページやそれに対する既存のリンク。DB内文字列をすべて書き換える方法もありますが、今回は様子見もかねてReally Simple SSL | WordPress.orgを使いました。JavaScriptや内部処理を使って動的にリンクをhttps化してくれるそうです。

有効化するとこんな確認画面が表示されます。

「.cssでhttp://リンクがないか?」とか一瞬戸惑いましたが、目で確認すればいいやと思い切って有効化ボタンを押します。

結果、あっさりと全ページhttpsアクセスとなりました。「ワンクリックで SSL を有効化」の言葉に偽りなし。

参照しているサイト側も

自分を見ている側もわかる範囲で変えておきます。
思い当たるのはGoogle アナリティクスSearch Console

そもそもGoogleにSSL化を認識してもらいたいわけですから、抜かりなくいきましょう。

動作確認と見栄え

ちゃんと表示されるか複数のブラウザで確認しつつ、見え方がどう変わったのかチェックして行きます。

対応前

Chrome バージョン: 68.0.3440.84(Official Build) (64 ビット)。
アドレスバーの先頭にiマークがつき、保護されてない事を訴えます。おのれ。

Windows Edge。文章は柔らかいものの、同じ様な表示。

IE11は平常運転。

iOS版Chrome(今日時点の最新版)。こちらは特に警告表示が出ません。

Android版Chrome(今日時点の最新版)。端末のOSは6.0.1。情報のあるiマークだけが表示されます。

対応後

Chrome。緑のカギがつき「保護された通信」と表示されます。この表示が欲しかった!努力は報われました。

IEは見慣れた鍵マーク付きに。Windows10にインストールしたESETのフィルターも太鼓判。

IEはぱっと見変化がないものの、URL欄の末尾に鍵マークが付きます。

iOS版chromeも目立つ緑の鍵付きになりました。

Android版はiマークが消えて緑の鍵付きに。

おわりに

面倒くさい、お金かかるのいやだ、と逃げ回っていたSSL化が無事完了しました。粘った怪我の功名か、無料の証明書サービスが登場し簡単に利用できたのはラッキーです。支援団体や先人たちには感謝しきり。

SSLの安全性チェックではSSL Server Test (Powered by Qualys SSL Labs)にてA判定となりました。ひとまず安心してみなさんに提供できそうです。

2018/08/07 追記
トップページやカテゴリページは問題無いのに、個別記事のページだけ警告が出てしまう。プラグインが悪いのか?と思ったらh3見出しに使っているピンク色の鉛筆マーク。これの画像がstyle.css内でhttpで呼び出されていたのが原因でした。

やっぱりCSSでhttp://使ってましたよ!というオチ。
Chromeだと分かりづらかったけれど、Firefoxなら違反メッセージの中から詳細情報を呼び出し、リンク一つ一つのURIが確認できて調べやすかった。

2018/10/14 追記
Let’s Encrypt Expiry Botからメールがきてました。

Your certificate (or certificates) for the names listed below will expire in 20 days (on 01 Nov 18 22:39 +0000). Please make sure to renew your certificate before then, or visitors to your website will encounter errors.

このままいくと、後20日であなたの証明書は失効しますよと。

cron動いてないのかな?と思って/usr/local/bin/letsencrypt/certbot-auto renewしてみると、そもそも更新が出来ていません。

前略
1 renew failure(s), 0 parse failure(s)

IMPORTANT NOTES:
 - The following errors were reported by the server:

   Domain: dolls.tokyo
   Type:   unauthorized
   Detail: Invalid response from
   http://dolls.tokyo/.well-known/acme-challenge/CzCCr8hcNXLxxxxxxxxxxxxxxxxxxxxxxxxxx:
   "<html>\r\n<head><title>404 Not Found</title></head>\r\n<body
   bgcolor=\"white\">\r\n<center><h1>404 Not
   Found</h1></center>\r\n<hr><center>"

   To fix these errors, please make sure that your domain name was
   entered correctly and the DNS A/AAAA record(s) for that domain
   contain(s) the right IP address.

テスト用のURLへアクセスできないので更新ができないとのこと。事前にサイト認証用にhttpアクセスを開けておいたはずなのに。
多くの人が同じことしているみたいなので、2つ修正。

  1. ドキュメントルート配下に.well-known/acme-challenge/を作成。
    cd public_html
    mkdir .well-known
    mkdir acme-challenge
    chown -R nginx:nginx .well-known/acme-challenge/
  2. nginxのVirtual Host用.confを修正
    参考サイトに従ってrootのパスを{{root}};で書いていたのだけど当方の環境では動かず。結局直接ドキュメントルートの実パスに修正。

これにて無事更新できました。

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Congratulations, all renewals succeeded. The following certs have been renewed:
/etc/letsencrypt/live/dolls.tokyo/fullchain.pem (success)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

コメントを残す