独自ドメインを全面的に HTTPS 化するべく、無料で発行してもらえる Let’s Encrypt のサーバ証明書を導入しました。

ドキュメントによるとサーバの root 権限が必要とのことですが、個人的な趣味としてそれは避けたい。

  • Web サーバを実行するユーザに sudo 権限を与えたくない
  • Web サーバの設定ファイルを自動的に書き換えられるのはイヤ(そもそも H2O は非対応ですが)
  • 証明書ファイル (key file / cert file) はローカルに作成できれば十分

というわけで、なにか手を考えないといけません。

Certbot のドキュメントで、 root 権限を使わない ACME client が紹介されています:

README を読むと letsencrypt-nosudo は root 権限での手作業が必要とのことなので、全自動化できそうな simp_le を試しました。

まず、各ドメインのドキュメントルートの直下に .well-known というディレクトリがあり(なければ作り)、そのディレクトリへの書き込み権限があることが前提です。また、そのディレクトリ内のファイル/ディレクトリに対して外部から(Let’s Encrypt のサーバから) TCP 80 番ポートで HTTP GET できなければなりません。 HTTPS ではないので注意。全面的に HTTPS 化する場合、すべての HTTP アクセス(80番ポート)を HTTPS (443番ポート)に 301 リダイレクトすることがあると思いますが、 /.well-known 配下へのアクセスだけはリダイレクトから除外します。 H2O の場合、h2o.conf で次のようにします:

1
2
3
4
5
6
7
8
9
10
11
12
13
hosts:
"example.com:443":
paths:
"/":
file.dir: /path/to/doc-root
"example.com:80":
paths:
"/":
redirect:
url: "https://example.com/"
status: 301
"/.well-known":
file.dir: /path/to/doc-root/.well-known

ちなみに、H2O ではデフォルトで /.well-known 配下がそのまま見えました。 H2O は隠しファイル(名前がドットで始まるもの)を特別扱いしないようです。

さて、ここからは simp_le のインストールの話です。

simp_le の公式ドキュメントでは bootstrap.sh を root 権限 (sudo) で実行するようにと書かれていますが、中身は apt-get install だけなので、手動でやれば十分です(ここだけ root 権限が必要ですすみません)。また、今回は pyenv / pyenv-virtualenv を利用したかったので、 venv.sh も中身を見て手動で実行しました。

python の初期設定については過去記事「Python の導入(root 権限なし)」を参照してください。

まず、bootstrap.sh 相当のことを手動でやる:

1
2
3
4
5
$ sudo apt-get install -y ca-certificates gcc libssl-dev libffi-dev python python-dev
$ cd
$ git clone https://github.com/kuba/simp_le.git
$ cd simp_le
$ pyenv shell 2.7.13

次に、venv.sh 相当のことを手動でやる:

1
2
3
4
5
6
7
8
9
10
11
12
$ pyenv virtualenv --no-site-packages venv-simp_le
$ pyenv virtualenvs
$ pyenv versions
$ pyenv local venv-simp_le
$ pyenv virtualenvs
$ pyenv versions
$ pyenv exec pip list
$ pyenv exec pip install -U setuptools
$ pyenv exec pip install -U pip
$ pyenv exec pip install -U wheel
$ pyenv exec pip install -e .
$ pyenv exec pip list

PATH はすでに通ってるので、設定不要でした。

ヘルプはちゃんと読みましょう:

1
$ simp_le --help

以上で simp_le のインストールは完了です。

続いて、証明書を新規に取得します。

解説記事を読むと account_key.json をあらかじめ用意しないといけないように思えますが、不要です。 -f オプションで指定するファイルはすべて simp_le が生成してくれます。 simp_le を最初に実行するディレクトリは空でよい。

1
2
3
4
5
$ cd ~/h2o
$ mkdir letsencrypt
$ cd letsencrypt
$ pyenv local venv-simp_le
$ simp_le -v --email 'foobar@example.com' -f account_key.json -f cert.pem -f chain.pem -f fullchain.pem -f key.pem -d example.com:../doc-root-1 -d www.example.com:../doc-root-2 -d blog.example.com:../doc-root-3

ここで、エラーになってしまいました。

1
2
3
4
5
DeserializationError: Deserialization error: Wrong directory fields

Unhandled error has happened, traceback is above

Debugging tips: -v improves output verbosity. Help is available under --help.

GitHub の Issue#114 に従って --tos_sha256 オプションを追加してみる:
https://github.com/kuba/simp_le/issues/114#issuecomment-236744611

1
$ simp_le -v --tos_sha256 6373439b9f29d67a5cd4d18cbc7f264809342dbf21cb2ba2fc7588df987a6221 --email 'foobar@example.com' -f account_key.json -f cert.pem -f chain.pem -f fullchain.pem -f key.pem -d example.com:../doc-root-1 -d www.example.com:../doc-root-2 -d blog.example.com:../doc-root-3

しかし変わらず。

ここで、GitHub の Issue を追っていると、 Fork して修正してくださったものを見つけました。感謝!!

https://github.com/zenhack/simp_le

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ cd
$ pyenv virtualenv-delete venv-simp_le
$ rm -rf simp_le
$ git clone https://github.com/zenhack/simp_le.git
$ cd simp_le
$ pyenv virtualenv --no-site-packages 2.7.13 venv-simp_le
$ pyenv virtualenvs
$ pyenv versions
$ pyenv local venv-simp_le
$ pyenv virtualenvs
$ pyenv versions
$ pyenv exec pip list
$ pyenv exec pip install -U setuptools
$ pyenv exec pip install -U pip
$ pyenv exec pip install -U wheel
$ pyenv exec pip install -e .
$ pyenv exec pip list
$ cd ~/h2o/letsencrypt
$ ls -a ←中身は `.python-version` のみ
$ simp_le -v --email 'foobar@example.com' -f account_key.json -f cert.pem -f chain.pem -f fullchain.pem -f key.pem -d example.com:../doc-root-1 -d www.example.com:../doc-root-2 -d blog.example.com:../doc-root-3

今度は成功! インタラクティブな問い合わせとか無くて、全自動で完了です。

カレントディレクトリに -f オプションで指定した5個のファイルが生成されています。

1
2
3
4
5
6
7
$ ls -1
account_key.json
cert.pem
chain.pem
fullchain.pem
key.pem
$

あとは h2o.conf に次のように指定してあげて kill -HUP すれば OK。

1
2
3
4
5
listen:
port: 443
ssl:
certificate-file: letsencrypt/fullchain.pem
key-file: letsencrypt/key.pem

ついでに自動更新スクリプトも作成。 解説記事の update_cert.sh を参考にさせていただきました。

update_cert.sh.20170113a.txt

rotatelogs コマンドが無い人は apt-get install -y apache2-utils で入ります。

Certbot のドキュメントで一日2回やることを推奨しているので、crontab を設定:
https://certbot.eff.org/#ubuntuxenial-other

1
2
3
4
$ crontab -l
13 10 * * * /home/foobar/h2o/letsencrypt/update_cert.sh
37 23 * * * /home/foobar/h2o/letsencrypt/update_cert.sh
$

以上、root 権限なしで Let’s Encrypt のサーバ証明書が取得できました。