注目キーワード

ウェブサーバー構築:Let’s Encrypt で無料のSSL証明書をインストールした方法

今回はこのブログサイトのウェブサーバー構築のお話から

 独自ドメインでウェブサイトを開発・運用するとなると、今や欠かせないのがSSL証明書。
 今回のエントリは、このブログサイトのウェブサーバー構築の時の作業から、Let’s Encryptという無料SSL証明書を提供してくれるサービスを使って、ウェブサーバーにSSL証明書をインストールした際の経験をお伝えします。

SSL証明書というと年間で少なくとも数千円はかかるというイメージを持つ方が多いのではないかと思います。
 ところが、無料でSSL証明書を世の中に提供すると言う志の高いサービス Let’s Encrypt があることを知り、このブログサイトの開発では、それを使おうと思い立ちました。

 Let’s Encrypt の無料SSL証明書を手に入れるには、どのような作業が必要なのか、実際に体験した内容をお届けしたいと思います。

Let’s Encrypt について

  • 公式サイト
  • Let’s Encrypt は、米国サンフランシスコ州に拠点を置く非営利団体によって運営されるサービスで、無料でSSL証明書(TLS/X.509証明書)を発行しています。
  • 証明書の発行作業を自動化することで、この無料のサービスを実現しています。
  • 2018年9月中旬時点で、累計で3億8000万通の証明書を発行しており、現時点ではさらに増えています。
  • 発行される証明書は、90日間だけ有効の期間が短いものですが、後述するcertbotという公式のツールを使って、定期的に証明書を更新するというスタイルで運用していきます。

Let’s EncryptのSSL証明書をインストールする環境について

 このブログサイトのウェブサーバーにLet’s EncryptのSSL証明書を導入したわけですが、そのウェブサーバーの種類やサーバーの環境について書いておきます。   

  • サーバーインフラは、AWSを利用(EC2インスタンス)
  • OS : Ubuntu 18.04.3 LTS
  • ウェブサーバー : Nginx
  • DBサーバー : RDS(AWS)
  • DNSサーバー : Route53(AWS)
  • Python : 3.6.8

 今回のエントリ内容に関連する環境情報は、とりあえず上記です。
 Nginxをインストール&設定済みということを前提に話を進めて参りたいと思います。

certbot のインストール

まず、Let’s Encrypt が提供する証明書管理ツールである certbot を導入します。
これを導入することにより、先ほど述べた証明書の自動更新が可能となるわけです。

OSによってインストール方法が少し変わります。
Debian, Ubuntu の場合、リポジトリの中に certbot が含まれるので、コマンドで簡単にインストールをすることができます。
 それ以外の場合は、Git のリポジトリからソースコードをダウンロードして、インストールします。
 今回は、Ubuntu なので、リポジトリからパッケージを取得して、インストールを進めます。

 リポジトリに含まれるパッケージを検索してみました。

  $ apt search certbot
  Sorting... Done
  Full Text Search... Done
  certbot/bionic-updates 0.27.0-1~ubuntu18.04.1 all
    automatically configure HTTPS using Let's Encrypt

  letsencrypt/bionic-updates 0.27.0-1~ubuntu18.04.1 all
    transitional dummy package
    (・・・中略・・・)
  python3-certbot-nginx/bionic 0.23.0-1 all
  Nginx plugin for Certbot

 
 certbot と python3-certbot-nginx をインストールしました。

  $ sudo apt install certbot python3-certbot-nginx
  Reading package lists... Done
  Building dependency tree
  Reading state information... Done
  The following additional packages will be installed:
    libpython-stdlib libpython2.7-minimal libpython2.7-stdlib python python-minimal python-pyicu python2.7 python2.7-minimal
    python3-acme python3-certbot python3-configargparse python3-future python3-josepy python3-lib2to3 python3-mock python3-parsedatetime
    python3-pbr python3-pyparsing python3-requests-toolbelt python3-rfc3339 python3-tz python3-zope.component python3-zope.event
    python3-zope.hookable
  Suggested packages:
    python3-certbot-apache python-certbot-doc python-doc python-tk python2.7-doc binutils binfmt-support python-acme-doc
    python-certbot-nginx-doc python-future-doc python-mock-doc python-pyparsing-doc
  The following NEW packages will be installed:
    certbot libpython-stdlib libpython2.7-minimal libpython2.7-stdlib python python-minimal python-pyicu python2.7 python2.7-minimal
    python3-acme python3-certbot python3-certbot-nginx python3-configargparse python3-future python3-josepy python3-lib2to3 python3-mock
    python3-parsedatetime python3-pbr python3-pyparsing python3-requests-toolbelt python3-rfc3339 python3-tz python3-zope.component
    python3-zope.event python3-zope.hookable
  0 upgraded, 26 newly installed, 0 to remove and 27 not upgraded.
  Need to get 5216 kB of archives.
  After this operation, 24.1 MB of additional disk space will be used.
  Do you want to continue? [Y/n] y

  (・・・中略・・・)

  update-alternatives: using /usr/bin/python3-pasteurize to provide /usr/bin/pasteurize (pasteurize) in auto mode
  Setting up python (2.7.15~rc1-1) ...
  Setting up python-pyicu (1.9.8-0ubuntu1) ...
  Setting up python3-acme (0.31.0-2~ubuntu18.04.1) ...
  Setting up python3-parsedatetime (2.4-2) ...
  Setting up python3-certbot (0.27.0-1~ubuntu18.04.1) ...
  Setting up certbot (0.27.0-1~ubuntu18.04.1) ...
  Created symlink /etc/systemd/system/timers.target.wants/certbot.timer → /lib/systemd/system/certbot.timer.
  Setting up python3-certbot-nginx (0.23.0-1) ...
  Processing triggers for mime-support (3.60ubuntu1) ...  

 インストール処理は、先ほどのコマンド一発で完了でした。
 続いて、いよいよ certbot を使って、ドメインの登録&SSL証明書の取得の作業に進みます。

certbotを使ったドメインの登録とSSL証明書の取得

さっそく、certbot を使って、Let’s Encrypt にドメインを登録し、SSL証明書を取得しました。
 登録するドメインは yuukou-exp.plus です。
 緊急時の連絡用メールアドレス(恐らく、証明書の有効期限が迫ることを知らせるアラートメール用?)も、求められたので登録しました。
 certbot には、以下のオプションを指定します。

  • Nginxを使っているので、--nginx
  • ドメインには yuukou-exp.plus を指定するので、 -d yuukou-exp.plus

 実行したコマンドと、その出力はこちらです。

  $ sudo certbot --nginx -d yuukou-exp.plus
  Saving debug log to /var/log/letsencrypt/letsencrypt.log
  Plugins selected: Authenticator nginx, Installer nginx
  Enter email address (used for urgent renewal and security notices) (Enter 'c' to
  cancel): **********@gmail.com  # ← ここで緊急連絡用のメールアドレスを入力

  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  Please read the Terms of Service at
  https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf. You must
  agree in order to register with the ACME server at
  https://acme-v02.api.letsencrypt.org/directory
  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  (A)gree/(C)ancel: A  # ← 合意を求められるので A を入力してEnter

  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  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: Y  # ← 合意を求められるので Y を入力してEnter
  Obtaining a new certificate
  Performing the following challenges:
  http-01 challenge for yuukou-exp.plus
  Using default addresses 80 and [::]:80 ipv6only=on for authentication.
  Waiting for verification...
  Cleaning up challenges
  Deploying Certificate to VirtualHost /etc/nginx/sites-enabled/default

  # HTTP リクエストが来た際の振る舞いについての質問
  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): 2  # ← HTTPS 側にリダイレクトする設定を希望したので 2 と入力してEnter
  No matching insecure server blocks listening on port 80 found.

  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  Congratulations! You have successfully enabled https://yuukou-exp.plus

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

  IMPORTANT NOTES:
   - Congratulations! Your certificate and chain have been saved at:
     /etc/letsencrypt/live/yuukou-exp.plus/fullchain.pem
     Your key file has been saved at:
     /etc/letsencrypt/live/yuukou-exp.plus/privkey.pem
     Your cert will expire on 2020-02-07. To obtain a new or tweaked
     version of this certificate in the future, simply run certbot again
     with the "certonly" option. To non-interactively renew *all* of
     your certificates, run "certbot renew"
   - 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.
   - 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
  • 証明書は、/etc/letsencrypt/live/yuukou-exp.plus 以下に保存されます。
    • /etc/letsencrypt/live/(ドメイン名)ということになりますね。
  • 有効期限が来る前に certbot renrew を実行すれば、設定している全ての証明書を更新できます。

certbot で変更された Nginx の設定を確認&修正

SSL用にNGINXの設定を変更したことが画面に表示されていたので、内容を確認しました。
すると、変更が加わっていたのは、 /etc/nginx/sites-enabled/default の方でした。
追加された行は以下の通りです。

  server_name yuukou-exp.plus; # managed by Certbot
  listen [::]:443 ssl ipv6only=on; # managed by Certbot
  listen 443 ssl; # managed by Certbot
  ssl_certificate /etc/letsencrypt/live/yuukou-exp.plus/fullchain.pem; # managed by Certbot
  ssl_certificate_key /etc/letsencrypt/live/yuukou-exp.plus/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  

Nginxの設定ファイルが、以下のような構造になっていれば、大丈夫です。
(ウェブサーバーの設定部分(※1)は、お好みのスタイルで。直接書いても良し、別ファイルに分けてincludeするも良しです)

# HTTPSへのリダイレクト設定(1)
server {
  listen 80;
  server_name yuukou-exp.plus;
  return 301 https://yuukou-exp.plus$request_uri;
}  

# HTTPSへのリダイレクト設定(1)
server {
  listen 80;
  server_name www.yuukou-exp.plus;
  return 301 https://yuukou-exp.plus$request_uri;
}

# HTTPS用ウェブサーバー設定
server {
          client_max_body_size 10m;
          server_name yuukou-exp.plus;

          listen [::]:443 ssl ipv6only=on; # managed by Certbot
          listen 443 ssl; # managed by Certbot

          ssl on;
          ssl_certificate /etc/letsencrypt/live/yuukou-exp.plus/fullchain.pem; # managed by Certbot
          ssl_certificate_key /etc/letsencrypt/live/yuukou-exp.plus/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  

          # 以下、ウェブサーバーの設定(※1)
          ...
  }

DNSレコードにLet’s Encryptを登録する

 certbot を使い、SSL証明書をインストールし、Nginxを再起動すると、ウェブサイト https://yuukou-exp.plus にアクセスすることが出来るようになっていました。
 証明書情報を見ると、有効期限が作業日から約90日後の2020/02/08になっていることを確認できました。
 しかし、完全には信用できないとか条件付きのメッセージが一緒に表示されているのが気になります。

certbot によるドメイン設定時に表示された、証明書確認用URLを叩いてみたところ、 DNS CAA が無いという警告を見つけました。
https://www.ssllabs.com/ssltest/analyze.html?d=yuukou-exp.plus

調べたところ、証明書を発行する認証局をだます余地がある状態という意味であり、DNSサーバーに CAA レコードを追加する必要があることが分かりました。
 DNSには、AWS の Route53 を使っていたので、Route53の管理画面から、以下のレコードを追加しました。

  yuukou-exp.plus. IN CAA 0 issue "letsencrypt.org"

それから5分ぐらい経過してからでしょうか、ブラウザでアクセスしたところ上記問題は解決していました。
 Let’s Encrypt のSSL証明書の組み込みは、以上で完了となります。

certbot を使ったSSL証明書の運用

 Let’s Encrypt を使ってSSL証明書を組み込んでしばらくたってから、SSL証明書の状態を確認しました。
SSL証明書の状態を把握するには、certificates サブコマンドを指定します。

  $ sudo certbot certificates
  Saving debug log to /var/log/letsencrypt/letsencrypt.log

  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  Found the following certs:
    Certificate Name: yuukou-exp.plus
      Domains: yuukou-exp.plus
      # ↓ 日本時間では 2020-02-08 01:00 に有効期限を迎える
      Expiry Date: 2020-02-07 16:00:35+00:00 (VALID: 34 days)
      Certificate Path: /etc/letsencrypt/live/yuukou-exp.plus/fullchain.pem
      Private Key Path: /etc/letsencrypt/live/yuukou-exp.plus/privkey.pem
  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  

 まだまだ、有効期限まで日数がありますね。
後述しますが実は、certbot は自動的に有効期限日数をチェックしており、それが30日を切ると自動的にSSL証明書の更新をしてくれます。
実際に更新される際、どんな出力がされるのか、確かめたいのですが、残り30日を切る絶妙なタイミングを待つというのも苦労が多く、しかもうっかりと見逃がしてしまいそうで懸念があります。
 そんな時は、--dry-run オプションを付けて certbot renew を実行することで、リハーサルを体験することが出来ます。

  $ sudo certbot renew --dry-run
  Saving debug log to /var/log/letsencrypt/letsencrypt.log

  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  Processing /etc/letsencrypt/renewal/yuukou-exp.plus.conf
  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  Cert not due for renewal, but simulating renewal for dry run
  Plugins selected: Authenticator nginx, Installer nginx
  Renewing an existing certificate
  Performing the following challenges:
  http-01 challenge for yuukou-exp.plus
  Waiting for verification...
  Cleaning up challenges

  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  new certificate deployed with reload of nginx server; fullchain is
  /etc/letsencrypt/live/yuukou-exp.plus/fullchain.pem
  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  ** DRY RUN: simulating 'certbot renew' close to cert expiry
  **          (The test certificates below have not been saved.)

  Congratulations, all renewals succeeded. The following certs have been renewed:
    /etc/letsencrypt/live/yuukou-exp.plus/fullchain.pem (success)
  ** DRY RUN: simulating 'certbot renew' close to cert expiry
  **          (The test certificates above have not been saved.)
  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  

SSL証明書が実際に自動更新されているか確かめてみた

 リハーサルもして、あとは certbot に任せれば安全!ということなのですが、何ぶん初めてのことなので、本当にSSL証明書を更新してくれるのか、気になります。
 先ほど確認した有効期限(2020/02/08)まで30日を切った時点で、証明書の状態を確認してみました。
すると・・・ばっちり自動更新されていました!

  $ sudo certbot certificates
  Saving debug log to /var/log/letsencrypt/letsencrypt.log

  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  Found the following certs:
    Certificate Name: yuukou-exp.plus
      Domains: yuukou-exp.plus
      # ↓ 証明書の有効期限が 2020-04-08 05:57 (日本時間)になっている
      Expiry Date: 2020-04-07 20:57:30+00:00 (VALID: 75 days)
      Certificate Path: /etc/letsencrypt/live/yuukou-exp.plus/fullchain.pem
      Private Key Path: /etc/letsencrypt/live/yuukou-exp.plus/privkey.pem
  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  

 次の有効期限が、2020-04-07 20:57:30+00:00 に延びているのが分かります。(日本時間だと、2020-04-07 05:57:30です)
 これはありがたいですね!
 メンテナンス作業いらずの自動化ツールが、しっかり働いているようです。
 certbot の自動チェック処理、どのように実装されているのか気になりますね。
 
 どこかに定期実行される処理を定義するスクリプトがあるはずです。
 各ユーザーおよびrootユーザーの crontab にもcertbot由来の定期実行処理の定義が追加されている痕跡は見つかりませんでした。
 となると、順当に考えて、探す場所は cron の設定ディレクトリ群になります。
 /etc/cron.d, /etc/cron.daily, /etc/cron.hourly, /etc/cron.monthly, /etc/cron.weekly を見て回ったところ、 /etc/cron.d で certbot のスクリプトを発見しました。

  $ cd /etc/cron.d
  $ ll
  total 28
  drwxr-xr-x  2 root root 4096 Nov  9 16:55 ./
  drwxr-xr-x 96 root root 4096 Jan 22 06:17 ../
  -rw-r--r--  1 root root  102 Nov 16  2017 .placeholder
  (・・・中略・・・)
  -rw-r--r--  1 root root  485 Oct  3 19:49 certbot    # ← これがcertbotのスクリプト
  (・・・中略・・・)

その中身を確認したところ、半日に1回、証明書の自動更新処理を実行するものでした。BINGO!
certbot インストール時に一緒に配備されたスクリプトであることが分かります。
 他の定期実行処理への考慮・配慮なのでしょうか。Perl でランダムな待機時間を設けてから、SSL証明書を更新しています。(時間差負荷分散になりますね)

  # /etc/cron.d/certbot: crontab entries for the certbot package
  #
  # Upstream recommends attempting renewal twice a day
  #
  # Eventually, this will be an opportunity to validate certificates
  # haven't been revoked, etc.  Renewal will only occur if expiration
  # is within 30 days.
  SHELL=/bin/sh
  PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

  0 */12 * * * root test -x /usr/bin/certbot -a \! -d /run/systemd/system && perl -e 'sleep int(rand(43200))' && certbot -q renew
最新情報をチェックしよう!