work.log

元エンジニアの備忘録的ブログ

NginxでLet’s Encryptを使うためのメモ書き

投稿:2017-01-21 13:39  更新:

無料で SSL 証明書を取得できるサービス Let’s Encrypt のメモです。

これまで使っていた無料 SSL 証明書の StartSSL は信頼されない認証局となってしまったので、今後は利用しない方が良さそうという事で、代わりにLet’s Encryptを使っていきたいと思います。

今回は CentOS6 + Nginx の環境で Let’s Encrypt のセットアップ、証明書インストール、自動更新設定をやっていきます。

Let’s Encryptのセットアップ

Let’s Encrypt が発行する証明書は有効期限が90日となっています。

一般的な証明書だと有効期限は1年だと思いますが、これは下記のような理由からだそうです。

  1. 漏洩した秘密鍵や誤発行された証明書が短い期間で失効されるように。(セキュリティ対策の一環)
  2. Let’s Encryptは自動更新運用を前提としている

ちなみに、公式が推奨する更新頻度は60日だそうです。

なので、取得、自動更新のために certbot-auto というツールを別途インストールする必要があります。

以降は全て root 権限での作業です。

EPELリポジトリのインストール

EPEL リポジトリのパッケージを利用するのでインストールがまだなら yum で入れておきます。

# yum -y install epel-release

certbot-autoのインストール

次に certbot-auto をインストールします。

適当なディレクトリに certbot-auto を配置して、これを実行してあげます。

# wget https://dl.eff.org/certbot-auto
# chmod 755 certbot-auto
# ./certbot-auto

必要なパッケージがインストールされ certbot-auto を利用する準備が出来ました。

CentOS6 + Python 2.6 の環境だと certbot-auto を動かす度に、2.6 はサポートを廃止する予定だよと警告が出ますがツール自体は問題なく動作します。(気持ち悪い場合は Python のアップデートを)

pported by the Python core team, please upgrade your Python. A future version of cryptography will drop support for Python 2.6 DeprecationWarning

NginxでLet’s Encryptを使う準備

certbot-auto は Web サーバを無停止で証明書を取得する Webroot プラグイン を利用するので、Nginx に Let’s Encrypt 用の設定を追加します。

certbot-auto を実行するとツールがドメイン名の所有権を確認する認証ファイルを Web 公開用ディレクトリに作成し、それに対し Let’s Encrypt 側が HTTP とか HTTPS でアクセスしてくるのでこれを Nginx で通してあげる必要があります。

認証ファイルを設置するディレクトリは “/home/www/letsencrypt” として、初回はこのように設定します。

場合により、1つのドメイン名を分散構成で使っている場合もあるかと思うのでその例も合わせて書きます。

ドメイン名を1台のWebサーバだけで使っているケース

まず最初は一般的な例です。大大はそのドメイン名を 1 台の Web サーバだけで使っていると思うので、その場合はこちらで大丈夫です。

server {

	listen       80;
	server_name  worklog.be;
	root         /home/blog/worklog.be/htdocs;

	access_log  /home/blog/worklog.be/log/nginx-image-access.log combined;
	error_log   /home/blog/worklog.be/log/nginx-image-error.log warn;

	index  index.html;

	location ^~ /.well-known/acme-challenge/ {
		root /home/www/letsencrypt;
	}

	location = /.well-known/acme-challenge/ {
		return 404;
	}

}

認証用アクセスの場合は Document Root を変更するように設定しています。

Basic認証を利用しているケース

Basic認証を設定しているドメイン名は下記のように、認証用ファイルへのアクセスはBasic認証の対象外とする必要があります。

server {

	listen       80;
	server_name  worklog.be;
	root         /home/blog/worklog.be/htdocs;

	access_log  /home/blog/worklog.be/log/nginx-image-access.log combined;
	error_log   /home/blog/worklog.be/log/nginx-image-error.log warn;

	index  index.html;

	location ^~ /.well-known/acme-challenge/ {
		root /home/www/letsencrypt;
	}

	location = /.well-known/acme-challenge/ {
		return 404;
	}

	location / {
		auth_basic "Please enter your ID and password";
		auth_basic_user_file /home/blog/worklog.be/htdocs/.htpasswd;
	}

}

ドメイン名を複数台のWebサーバで使っているケース

次は複数台環境でドメイン名を使っている場合です。

分散構成だと Let’s Encrypt の認証アクセスがどのサーバに着信するかわからないので、これを Nginx のロードバランス機能を使って処理します。

例えば、”192.168.0.1 ~ 192.168.0.3″ の3台で DNS ラウンドロビンをしている場合はこの様に設定します。

upstream acme-challenge {
	server 192.168.0.1:80;
	server 192.168.0.2:80;
	server 192.168.0.3:80;
}

server {

	listen       80;
	server_name  roundrobin.worklog.be;
	root         /home/blog/roundrobin.worklog.be/htdocs;

	access_log  /home/blog/roundrobin.worklog.be/log/nginx-image-access.log combined;
	error_log   /home/blog/roundrobin.worklog.be/log/nginx-image-error.log warn;

	index  index.html;

	location ^~ /.well-known/acme-challenge/ {
		root /home/www/letsencrypt;
		try_files $uri @acme-challenge;
	}

	location @acme-challenge {
		proxy_set_header Connection "";
		proxy_set_header HOST $host;
		proxy_set_header X-Real-IP $remote_addr;
		proxy_pass http://acme-challenge;
		proxy_next_upstream http_404;
		proxy_intercept_errors on;
	}

}

これを全台に設定しておけば、certbot-auto を実行したサーバを探してアクセスを振り分ける事ができます。

証明書の取得

次に certbot-auto を実行して証明書を取得します。

./certbot-auto \
certonly \
--webroot \
--agree-tos \
-w /home/www/letsencrypt \
-m ssl@example.com \
-d worklog.be

オプションの意味はこんな感じ。

certonly = SSL証明書の取得のみ
–webroot = 認証用ファイルを指定されたディレクトリに生成する。(.well-known ディレクトリ)
–agree-tos = 利用規約に同意する
-w = Document Root のパス、Let’s Encrypt の Bot がアクセスできる場所を指定する
-m = 連絡用のメールアドレス。Let’s Encrypt からのお知らせ等が届く
-d = 利用するドメイン名

問題が無ければ下記に証明書が作成されます。

# ls /etc/letsencrypt/live/worklog.be/
.  ..  cert.pem  chain.pem  fullchain.pem  privkey.pem  README

certbot-auto は一度に複数のドメイン名で証明書を取得する事もできます。

./certbot-auto \
certonly \
--webroot \
--agree-tos \
-w /home/www/letsencrypt \
-m ssl@example.com \
-d worklog.be \
-d www.worklog.be \
-d blog.worklog.be

この場合は最初に指定したドメイン名 “worklog.be” で証明書ファイルが作成されます。

証明書をNginxにインストール

次に Let’s Encrypt の証明書を Nginx にインストールします。

Nginx で SSL を利用するには “–with-http_ssl_module” 付きでコンパイルしてインストールしておく必要があるのでお忘れなく。

まずは DH 鍵交換用の鍵を作っておきます。(環境によっては結構時間がかかる)

# mkdir /etc/nginx/ssl
# cd /etc/nginx/ssl
# openssl dhparam 2048 -out dhparam.pem

そして、対象のドメイン名でこんな感じに設定をします。

ドメイン名を1台のWebサーバだけで使っているケース

server {
	listen       80;
	server_name  worklog.be;
	rewrite ^(.*)$ https://worklog.be$1 permanent;
}

server {

	listen       443;
	server_name  worklog.be;
	root         /home/blog/worklog.be/htdocs;

	access_log  /home/blog/worklog.be/log/nginx-access.log combined;
	error_log   /home/blog/worklog.be/log/nginx-error.log warn;

	ssl                        on;
	ssl_protocols              TLSv1 TLSv1.1 TLSv1.2;
	ssl_prefer_server_ciphers  on;
	ssl_ciphers                'ECDHE+RSAGCM:ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:!aNULL!eNull:!EXPORT:!DES:!3DES:!MD5:!DSS';
	ssl_dhparam                /etc/nginx/ssl/dhparam.pem;
	ssl_certificate            /etc/letsencrypt/live/worklog.be/fullchain.pem;
	ssl_certificate_key        /etc/letsencrypt/live/worklog.be/privkey.pem;

	index  index.html;

	location ^~ /.well-known/acme-challenge/ {
		root /home/www/letsencrypt;
	}

	location = /.well-known/acme-challenge/ {
		return 404;
	}

}

Basic認証を利用しているケース

Basic認証を利用している場合は取得時の設定と同じように、”/.well-known/acme-challenge/” 以下をBasic認証の対象外としておく。

server {
	listen       80;
	server_name  worklog.be;
	rewrite ^(.*)$ https://worklog.be$1 permanent;
}

server {

	listen       443;
	server_name  worklog.be;
	root         /home/blog/worklog.be/htdocs;

	access_log  /home/blog/worklog.be/log/nginx-access.log combined;
	error_log   /home/blog/worklog.be/log/nginx-error.log warn;

	ssl                        on;
	ssl_protocols              TLSv1 TLSv1.1 TLSv1.2;
	ssl_prefer_server_ciphers  on;
	ssl_ciphers                'ECDHE+RSAGCM:ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:!aNULL!eNull:!EXPORT:!DES:!3DES:!MD5:!DSS';
	ssl_dhparam                /etc/nginx/ssl/dhparam.pem;
	ssl_certificate            /etc/letsencrypt/live/worklog.be/fullchain.pem;
	ssl_certificate_key        /etc/letsencrypt/live/worklog.be/privkey.pem;

	index  index.html;

	location ^~ /.well-known/acme-challenge/ {
		root /home/www/letsencrypt;
	}

	location = /.well-known/acme-challenge/ {
		return 404;
	}

	location / {
		auth_basic "Please enter your ID and password";
		auth_basic_user_file /home/blog/worklog.be/htdocs/.htpasswd;
	}

}

ドメイン名を複数台のWebサーバで使っているケース

upstream acme-challenge {
	server 192.168.0.1:443;
	server 192.168.0.2:443;
	server 192.168.0.3:443;
}

server {
	listen       80;
	server_name  roundrobin.worklog.be;
	rewrite ^(.*)$ https://roundrobin.worklog.be$1 permanent;
}

server {

	listen       443;
	server_name  roundrobin.worklog.be;
	root         /home/blog/roundrobin.worklog.be/htdocs;

	access_log  /home/blog/roundrobin.worklog.be/log/nginx-image-access.log combined;
	error_log   /home/blog/roundrobin.worklog.be/log/nginx-image-error.log warn;

	index  index.html;

	location ^~ /.well-known/acme-challenge/ {
		root /home/www/letsencrypt;
		try_files $uri @acme-challenge;
	}

	location @acme-challenge {
		proxy_set_header Connection "";
		proxy_set_header HOST $host;
		proxy_set_header X-Real-IP $remote_addr;
		proxy_pass https://acme-challenge;
		proxy_next_upstream http_404;
		proxy_intercept_errors on;
	}

}

SSL 化後も更新の度に Let’s Encrypt の認証アクセスが来るので、その設定を残しつつハイライト表示にしている箇所を “HTTP => HTTPS” に変更します。

注意事項 Androidで起きる問題

余談ですが、このように証明書を指定してしまうと Android で証明書エラーが出るので、”ssl_certificate” には中間証明書とサーバ証明書が一緒になった “fullchain.pem” を必ず指定した方が良いです。

	## Android だけで起こる NG な例
	ssl_dhparam                /etc/nginx/ssl/dhparam.pem;
	ssl_certificate            /etc/letsencrypt/live/worklog.be/cert.pem;
	ssl_certificate_key        /etc/letsencrypt/live/worklog.be/privkey.pem;
	ssl_trusted_certificate    /etc/letsencrypt/live/worklog.be/chain.pem;

Android の実機はたまに動作確認用でしか使わないから暫くの間エラーを出していました…

後はコンフィグチェックをして問題が無ければリロードすれば無事 SSL 化されます。

# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

# service nginx reload

対象ドメイン名にブラウザからアクセスして SSL が有効 & Let’s Encrypt の証明書が使われていればOKです。

Let’s Encryptの自動更新設定

最後は証明書を自動更新するように cron に設定を仕込みます。

# crontab -e

----- cron ------
0 10 * * * /path/to/certbot-auto renew -q --no-self-upgrade --post-hook "/sbin/service nginx reload" > /dev/null 2>&1
追記: 2017-01-23

“service nginx reload” -> “/sbin/service nginx reload” へ修正。cron ユーザーに Path が通っておらず、下記の様なエラーがログに出ていたため。

HookCommandNotFound: Unable to find post-hook command service in the PATH.
(PATH is /usr/bin:/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin)

Let’s Encrypt の証明書は「毎日 10:00」にチェック&証明書を更新をするように設定しました。

ここは好みですが、夜中にやって万が一が起きた場合わざわざ起きたくありませんので…

オプションの renew は有効期限を確認して対象なら更新。–post-hook は更新後に1回だけ実行されるフックで、更新があった場合のみ Nginx をリロードします。

まだ更新処理が動いていないので経過観察をする必要がありますが、更新作業をしなくて良くなったのはいいですね。

追記: 2017-04-19

自動更新に失敗していると期限切れ前にこのような通知メールが届きます。無料なのに親切ですよね。

Subject: Let’s Encrypt certificate expiration notice
From: Let’s Encrypt Expiry Bot <expiry@letsencrypt.org>

Hello,

Your certificate (or certificates) for the names listed below will expire in
9 days (on 21 Apr 17 14:00 +0000). Please make sure to renew
your certificate before then, or visitors to your website will encounter errors.

worklog.be

(以下略)

という事で、今後新しくサイトを立ち上げる人は Let’s Encrypt で積極的に SSL 化していくと良いと思います。

関連記事

コメント

コメントを残す