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

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

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

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

Let’s Encryptのセットアップ

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

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

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

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

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

EPELリポジトリの追加

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

EPEL リポジトリのパッケージを利用するので yum で CentOS に入れておきます。

# yum -y install epel-release

certbot-autoのインストール

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

/usr/local/bin に certbot-auto を配置してこれを実行してあげます。

# wget -q -P /usr/local/bin/ https://dl.eff.org/certbot-auto
# chmod 700 /usr/local/bin/certbot-auto
# certbot-auto

依存パッケージのリストが表示されるので “y” を押してそのまま yum インストールします。

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Would you be willing to share your email address with the Electronic Frontier
Foundation, a founding partner of the Let's Encrypt project and the non-profit
organization that develops Certbot? We'd like to send you email about our work
encrypting the web, EFF news, campaigns, and ways to support digital freedom.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: N <= "N"を入力してエンター

最初に、電子フロンティア財団 (Electronic Frontier Foundation) とメールアドレスを共有するか聞かれますので「YかN」で回答します。

メーリングリストみたいなものなので関連するニュースが知りたい方は登録してください。

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: Apache Web Server plugin (apache)
2: Nginx Web Server plugin (nginx)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate number [1-2] then [enter] (press 'c' to cancel): c <= "c"を入力してエンター

対話式のウィザードが始まるので “c” でキャンセルします。

これで certbot-auto を利用する準備が出来ました。

NginxでLet’s Encryptを使う準備

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

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

認証ファイルを設置するディレクトリは /home/www/letsencrypt として設定するので、mkdir でディレクトリを作っておきます。

以降で Nginx 側の設定例を書いていきますが、Basic 認証や負荷分散構成等の設定例も合わせて記載します。

ドメイン名を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;
	return 301 https://worklog.be$request_uri;
}

server {

	listen       443 ssl;
	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.2 TLSv1.3;
	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;
	return 301 https://worklog.be$request_uri;
}

server {

	listen       443 ssl;
	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.2 TLSv1.3;
	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;
	return 301 https://roundrobin.worklog.be$request_uri;
}

server {

	listen       443 ssl;
	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 に設定を仕込みます。

■ CentOS 6

# crontab -e
----- cron ------
0 10 * * * /usr/local/bin/certbot-auto renew -q --post-hook "/sbin/service nginx reload" > /dev/null 2>&1

■ CentOS 7

# crontab -e
----- cron ------
0 10 * * * /usr/local/bin/certbot-auto renew -q --post-hook "systemctl reload nginx" > /dev/null 2>&1

CentOS6 と CentOS7 でデーモンの起動方法が変わっているので間違えないように注意。

Let’s Encrypt の SSL 証明書は「毎日 10:00」にチェック&証明書を更新をするように cron を設定しました。何時に更新するかは好みですが、Nginx のサービス断は無いのでいつやってもいいと思います。

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

これで煩わしい証明書の更新作業をしなくて良くなったので楽になりました。

証明書にドメインを追加したい場合

サブドメインを同じ証明書に追加したい場合は –expand オプションを付けて下記のように実行します。

※ 元のドメイン worklog.be、追加したいドメイン sub.worklog.be

certbot-auto \
certonly \
--webroot \
--agree-tos \
--expand \
-w /home/www/letsencrypt \
-d worklog.be \
-d sub.worklog.be

証明書を失効&削除したい場合

証明書を失効させ削除したい場合は証明書ファイルを指定して下記のように実行します。

# certbot-auto revoke --cert-path=/etc/letsencrypt/live/<コモンネーム>/cert.pem
# certbot-auto revoke --cert-path=/etc/letsencrypt/live/worklog.be/cert.pem
Saving debug log to /var/log/letsencrypt/letsencrypt.log

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Would you like to delete the cert(s) you just revoked?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es (recommended)/(N)o: Y <= "Y" を入力してエンター

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Deleted all files relating to certificate worklog.be.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Congratulations! You have successfully revoked the certificate that was located
at /etc/letsencrypt/live/worklog.be/cert.pem

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

これで関連ファイルが letsencrypt 内のディレクトリから削除されます。

あとは Nginx の設定を修正して reload しておけば OK です。

自動更新に失敗するとメールが届く

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

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 化していくと良いと思います。

コメント

  1. 匿名 より:

    勉強なりました

コメントを残す

よく読まれている記事

  • 本日
  • 週間
  • 月間