こんにちは!
今回は、Amazon EC2で動かしているDockerにNginxコンテナにLet’s Encryptを使ってSSLを導入する方法を紹介します。
また、certbotを使って自動更新をする方法も合わせて紹介します。
Let’s Encryptとは?
Let’s Encryptとは、Internet Security Research Groupが運営しているサービスです。
Let’s Encryptを使用することで、無料でSSL証明書を取得できます。
SSL化の手順
1.Dockerfileにopenssl,certbotの記述を追加
2.certbotで証明書の発行
3.NginxのSSL設定
4.let’s encryptの更新スクリプトの作成
Dockerfileにopenssl,certbotの記述を追加
まずは、opensslとcertbotをDockerfileに記述します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
FROM nginx:alpine COPY nginx.conf /etc/nginx/ RUN apk update \ && apk upgrade \ #openssl追加 && apk add --no-cache openssl \ #certbot追加 && apk add --no-cache certbot \ && apk add --no-cache bash RUN set -x ; \ addgroup -g 82 -S www-data ; \ adduser -u 82 -D -S -G www-data www-data && exit 0 ; exit 1 EXPOSE 80 443 |
記述ができたら、今度は下記の記述を追加します。
「sites/」以下にある、今回SSLを反映するファイルに下記の部分を追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
server { listen 80; listen [::]:80; # For https # listen 443 ssl default_server; # listen [::]:443 ssl default_server ipv6only=on; # ssl_certificate /etc/nginx/ssl/default.crt; # ssl_certificate_key /etc/nginx/ssl/default.key; server_name twitter-search.test; root /var/www/html/twitter-search-app/public; index index.php index.html index.htm; #追加部分 location ^~ /.well-known/acme-challenge/ { #使用するアプリケーションのルートを指定します。 root /var/www/letsencrypt; } #追加部分 |
記述ができたら、この構成でDokcerを停止しましょう。
1 |
docker-compose stop |
停止したら、buildコマンドでbuildを掛けます。
1 |
docker-compose build |
buildを掛けたら、またDokcerを起動しましょう。
1 |
docker-compose up -d |
これでopensslとcertbotを使用できるようになりました。
certbotで証明書の発行
opensslとcertbotを使用できるようになったら、nginxのワークスペースに下記のコマンドで入り、SSLで使用する証明書を発行します。
1 2 3 |
docker exec -t -i mydocker_nginx_1 bash docker exec -t -i nginxのコンテナ名 bash |
Niginxのコンテナに入ったら、下記のコマンドを使ってSSL証明書を発行します。
1 |
certbot certonly --agree-tos --webroot -w /var/www/html/ -d example.com -d www.example.com |
コマンドの説明
使用しているコマンドのオプションについて紹介します。
certonlyについて
SSL/TLS証明書の取得のみを行いますという意味です。
–agree-tos について
ACME 利用規約に同意するという意味です。
–webrootについて
このオプションを追加することで、稼働中のサービスを落とさずにSSL証明書の発行や更新ができるようになります。
-wについて
ここには、紐付けるルートを指定します。
ドメインが紐付いている場所を指定しましょう。
-dについて
発行したSSL/TLS証明書を使用するドメインを指定します。
もし、下記のようにエラーが表示された場合は指定したドメイン名が有効でない可能性があります。
指定したドメインが有効かどうかを確認するようにしましょう。
Non-ASCII domain names not supported. To issue for an Internationalized Domain Name, use Punycode
うまくコマンドが動作すると下記のように質問が飛んできます。
質問1
How would you like to authenticate with the ACME CA?
– – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – –
1: Spin up a temporary webserver (standalone)
2: Place files in webroot directory (webroot)
– – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – –
Select the appropriate number [1-2] then [enter] (press ‘c’ to cancel):
ここは「2」を選択します。
質問2
Enter email address (used for urgent renewal and security notices) (Enter ‘c’ to
cancel):
ここには、緊急連絡先として送信したいメールアドレスを入力します。
質問3
– – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – –
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.
– – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – –
CertbotやLet’s Encryptに対してのニュースレターを受け取りますか?
というメッセージが表示されます。
Yes、Noで好きなように答えましょう。
もしエラーが出たら
もし、コマンドを入力する中で下記のようなエラーが出た場合は、下記のコマンドで指定したドメインとドキュメントルートにアクセスできるかどうかを確認しましょう。
1 |
certbot certonly --agree-tos --webroot -w /var/www/html/ -d example.com -d www.example.com |
1 2 3 4 5 6 7 8 9 10 11 |
Domain: example.com Type: unauthorized Detail: Invalid response from http:// example.com /.well-known/acme-challenge/68ZVgnqJCO_A8GBfRvlRbBNe9AqdqiNxGfWvccSWJlQ [3.19.208.157]: "<html>\r\n<head><title>404 Not Found</title></head>\r\n<body>\r\n<center><h1>404 Not Found</h1></center>\r\n<hr><center>nginx</center>\r\n" 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. |
NginxのSSL設定
証明書の発行ができたら、Nginxでhttpsで表示するために必要な設定を行っていきます。
sites/以下にある、今回SSL化を設定した「.conf」ファイルの中に下記のように記述が加えられている箇所があると思います。
1 2 3 4 5 6 7 8 9 |
listen 80; listen [::]:80; server_name example.com; # For https # listen 443 ssl default_server; # listen [::]:443 ssl default_server ipv6only=on; # ssl_certificate /etc/nginx/ssl/default.crt; # ssl_certificate_key /etc/nginx/ssl/default.key; |
上記を下記のように変更します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
server { #server_nameのhttpがあればhttpsにリダイレクト listen 80; listen [::]:80; server_name example.com; return 301 https://$host$request_uri; } server { # For https listen 443 ssl default_server; listen [::]:443 ssl default_server ipv6only=on; #sslをonにする ssl on; #letsencryptの認証 ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; #letsencryptの認証 ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; #SSLv3およびTLSプロトコルを使用する場合サーバー暗号がクライアント暗号よりも優先されることを指定 ssl_prefer_server_ciphers on; server_name example.com; #省略 #serverの閉じタグ } |
ちなみに他にも、ここで使えるSSLのmoduleは下記の公式サイトに記載されています。
記述を変更できたら、この構成でDokcerを停止しましょう。
1 |
docker-compose stop |
停止したら、buildコマンドでbuildを掛けます。
1 |
docker-compose build |
buildを掛けたら、またDokcerを起動しましょう。
1 |
docker-compose up -d |
これで「http」にアクセスしたら、「https」にリダイレクトされるようになります。
let’s encryptの更新スクリプトの作成
let’sencryptの証明書は90日で期限があり、それを過ぎるとhttpsでアクセスできなくなります。
そのため最後にlet’sencryptを自動で更新するスクリプトを、cronジョブに仕込んでおきます。
これで、let’secnryptの期限が切れても、自動で更新されるようになります。
certbotでlet’s encryptを自動更新するには、DokcerのNginxコンテナ内で下記のコマンドを入力します。
1 |
certbot renew |
このコマンドを定期的に実行されるようにcrontabでcrongジョブとして登録します。
cronは下記の2つのステップで実行しましょう。
(作業時のユーザーレベルはrootで行います。)
1.DockerのNginxに証明書更新のshファイルを作成
2.AWSで作成したshファイルを実行するように記述
DockerのNginxに証明書更新のshファイルを作成
まずは、下記のコマンドでDockerのnginxのコンテナに入ります。
1 |
docker exec -t -i nginxのコンテナ名 bash |
入ったら、「/var/cronjob/」のディレクトリを作成しましょう。
ディレクトリの作成ができたら、今度はコンテナ内部のPATHを下記のコマンドで確認しておきます。
1 2 3 4 |
echo $PATH #下記のようなものが表示されると思います。 /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin |
表示されたパスは後で使うので、コピーします。
パスの確認ができたら、「/var/cronjob/certbot.sh」という名前でファイルを作ります。
ファイルの中身は次の内容で記述します。
1 2 3 4 5 |
#PATHで確認したもの。 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin #certbotでSSLの証明書更新 certbot renew |
PATH=というようにしておかないとエラーが出てしまうため、PATHを記述するようにします。
AWSで作成したshファイルを実行するように記述
「/var/cronjob/certbot.sh」にファイルが作れたら、今度はEC2側でcronジョブの登録を行います。
まずは、下記のコマンドでcronが動いているかチェックします。
1 |
/etc/rc.d/init.d/crond status |
動いていたら、「crond (pid ) を実行中…」のように表示されると思います。
動いていなかったら、下記のコマンドで起動しましょう。
1 |
/etc/rc.d/init.d/crond start |
今度はcronのプロセスをチェックします。
チェックするには、下記のコマンドを入力します。
1 |
chkconfig --list crond |
下記のように2~5が「on」になっていたらOKっぽいです。
1 |
crond 0:off 1:off 2:on 3:on 4:on 5:on 6:off |
それぞれチェックができたら、cronジョブを登録しましょう。
cronジョブを登録するには、下記のコマンドを入力します。
1 2 |
# Cronの編集 crontab -e |
入力ができたら、下記のように入力します。
1 2 |
#cronで実行する処理の詳細を記述。 00 04 01 * * docker exec -t nginxのコンテナ名 bash /var/cronjob/certbot.sh >> /var/log/certbot_job.log 2>&1 |
上記のコードでは、次のような手順で処理を行っています。
1.docker execでNginxのコンテナに入る。
1 2 |
#コード部分 docker exec -t nginxのコンテナ名 bash |
2.コンテナ内部の「certbot.sh」を実行。
1 2 |
#コード部分 /var/cronjob/certbot.sh |
3.デバッグできるように結果を、EC2内部の「/var/log/certbot_job.log」にログに出力
1 |
>> /var/log/certbot_job.log 2>&1 |
ちなみに「* *」は左から「分」「時」「日」「月」「曜日」となっています。
* * * * * 実行するコマンド
上記のコマンドだと「00 04 01 * *」なので、「00分」「AM4時」「毎月」「曜日していなし」にジョブを実行という意味になります。
ちなみに、テストをする場合は、下記のように設定すると良いかと思います。
1 2 |
#この記事を書いている時が、10月6日の18:56分なので。19時に処理が走るように登録 00 19 06 10 * docker exec -t nginxのコンテナ名 bash /var/cronjob/certbot.sh >> /var/log/certbot_job.log 2>&1 |
設定したcronを削除するには、もう一度下記のコマンドを実行していらないcronジョブを削除するにようにしましょう。
1 2 |
# Cronの編集 crontab -e |
うまく動作していると証明書を作りたての場合、下記のような内容が「certbot_job.log」内に出力されいていると思います。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
Saving debug log to /var/log/letsencrypt/letsencrypt.log - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Processing /etc/letsencrypt/renewal/favoken.com.conf - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Cert not yet due for renewal - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - The following certs are not due for renewal yet: /etc/letsencrypt/live/favoken.com/fullchain.pem expires on 2020-01-04 (skipped) No renewals were attempted. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
まとめ
最後までお読み頂き、ありがとうございます。
今回はDockerで動かしているNginxにLet’s Encryptを使ってSSLを導入する方法を紹介しました。
この記事があなたの役に立てば幸いです。
もし、cronが動かないといった場合はサーバー内部の時間なども確認してみてください。
1 2 |
#サーバー内部の時間を確認。 date |
もしかすると、cronで設定している時間とサーバー内部の時間にズレがあるため、動作しないといったこともありえます。
(時計をみたら14:00だけど、サーバー内部は04:00とか。)