第2の人生の構築ログ

自分の好きなことをやりつつ、インカムもしっかりと。FIRA60 (Financial Independence, Retire Around 60) の実現を目指します。SE を生業としていますが、自分でプログラミングしながら自分が欲しいと思うアプリケーションを作ることが楽しみです。旅行と温泉、音楽と読書は欠かすことができません。

Let's Encrypt を使ってフリーでサーバ証明書を発行する

Webサイトの常時 HTTPS 化のこの時代、小規模なサイト、個人のサイトなど元々 HTTP で充分、というコンテンツを公開していた運営者の中には、サーバ証明書のコストを何とかできたら・・・と思っている方もいるかもしれません。

Let's Encrypt は、フリーで(機能的には他社の有料版と変わらない)サーバ証明書を発行してくれるサービスです。一昔前のサーバ証明書のお値段を考えると嘘のようですよね。
また一方では、最近ではクラウドプラットフォーム/サービス側でサーバ証明書をオプションで利用できるようにしていたりしますので、自身でサーバ証明書を用意して、、、というようなこともなくなってきているかもしれません。(Let's Encrypt が裏で利用されているもの多いようです。) 私の場合は、サーバ証明書がサービスとしてセットで利用できない環境にて Let's Encrypt を使ってサーバ証明書を発行して利用しています。

letsencrypt.org

今年の初めには発行した証明書が10億個を突破したということで話題になりました。

gigazine.net

また、そのよいニュースのすぐ後に Let's Encrypt のソフトウェアのバグにより、一部の発行した証明書を失効しないといけな事態が発生しておりました。

gigazine.net

該当のインシデントは以下の情報ですね。

影響を受けた関係者の方々、お疲れ様でした。

ちなみに、Let's Encrypt が発行するサーバ証明書は、ドメイン検証型の証明書です。サーバ証明書/HTTPS (SSL/TLS) を利用する目的としては、1) サイトの真正性、2) 通信の機密性(秘匿性)、3) データの完全性の担保というものがあります。ここで記載する Let's Encrypt で実現できるのは、2)と3)になり、1) までは担保してくれません。(サイトを運用している組織の証明まで行っているものではありません。)

Let's Encrypt を始めてみる

ではどうやって使い始められるのでしょうか。ホスティング・プロバイダが Let’s Encrypt をサポートしているケースは各サービスの利用ガイドにそって容易に始めえらるのではないかと思います。

ここでは、自分でサーバ証明書を作成して利用する方法の例を記載します。サーバ証明書の発行には、Certbot というコマンドラインツールを利用します。

certbot.eff.org

macOS の場合は、以下で Certbot のインストールを行えます。

$ brew install certbot

それ以外のプラットフォームに関しましては、Get Certbot — Certbot 1.3.0.dev0 documentation を確認ください。

certbot.eff.org

Certbot を使ってサーバ証明書を作成する

Certbotを使った発行にもいろいろとやり方がありますが、ここでは、マニュアルで発行を行い、発行された証明書(と秘密鍵)をサーバに配置するためのpkcs12というファイルフォーマットに変換してサーバに配置する、という流れで記載します。
ちなみに、マニュアルでの発行を行う場合、更新時も全く同じ作業を行うことになります。

証明書の発行は以下のようなコマンドで発行します。管理者権限が必要な処理がありますので、sudo が入っています。

$ sudo certbot certonly --manual -d www.example.com -d test.example.com

上記のコマンドで何が行われると言いますと、以下の2点です。

  1. 証明書に記載されるドメイン/ホスト(FQDN)の検証
  2. サーバ証明書の作成
    CSR の作成、証明書の発行

また、-d オプションの後にFQDNを与えて、2つ指定しています。これは何をやっているかと言いますと、「マルチドメイン(SANs)証明書」と言われるもので、複数の FQDN (上記例では、www.example.comtest.example.comの2つ)をこの1枚のサーバ証明書でまかないますよ、というものになります。最初に書いた FQDN (上記の例の場合はwww.example.com) が Common Name に使われます。先に実際に発行された証明書をみてみます。最初に指定した FQDN の名前(Common Name)で証明書が発行されています。

f:id:dr_taka_n:20200229234820p:plain

マルチドメインの場合には、その Common Name の FQDN、その他指定を行った FQDN は、SAN (Subject Alternative Name) に記載され、Common Name の FQDN だけでなく、SAN に記載された全ての FQDN でこのサーバ証明書が使えることになります。

f:id:dr_taka_n:20200229235201p:plain

実際のコマンドの実行結果をみていきます。

$ sudo certbot certonly --manual -d www.example.com -d test51.example.com 
Password:
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator manual, Installer None
Enter email address (used for urgent renewal and security notices) (Enter 'c' to
cancel): hoge@example.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

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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
Obtaining a new certificate
Performing the following challenges:
http-01 challenge for test51.example.com
http-01 challenge for www.example.com

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NOTE: The IP of this machine will be publicly logged as having requested this
certificate. If you're running certbot in manual mode on a machine that is not
your server, please ensure you're okay with that.

Are you OK with your IP being logged?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: Y

ここまでは Yes or No のレスポンスを行うだけで、特に難しいことはありません。この後ちょっと作業が必要になります。

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Create a file containing just this data:

UehSy-qCCi-5UYcHCoRhe5q1OQpGMxYx-8XnCJ4TMcI.Ca4bn_Go9Bnvznx36-sQL22-kq117wbMGrKqm7llRzI

And make it available on your web server at this URL:

http://test51.example.com/.well-known/acme-challenge/UehSy-qCCi-5UYcHCoRhe5q1OQpGMxYx-8XnCJ4TMcI

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Press Enter to Continue

上記は何かと言いますと、これが Certbot で実行される1番目の処理「1. 証明書に記載されるドメイン/ホスト(FQDN)の検証」になります。

上記では、2つ目に指定した FQDN test51.example.com のサーバに上記のパスで参照できる上記のデータ(UehSy-qCCi-5U...)を含むファイルを配置しておきない、後で確認するからね、ということを言っています。引き続きもう1つ指定したwww.example.com についても同様のことが指示されます。

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Create a file containing just this data:

W1UdvuPTDWcPygFKKXeN_kZ10Ozoiq4twuyLtWPp9N0.Ca4bn_Go9Bnvznx36-sQL22-kq117wbMGrKqm7llRzI

And make it available on your web server at this URL:

http://www.example.com/.well-known/acme-challenge/W1UdvuPTDWcPygFKKXeN_kZ10Ozoiq4twuyLtWPp9N0

(This must be set up in addition to the previous challenges; do not remove,
replace, or undo the previous challenge tasks yet.)

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Press Enter to Continue

コマンド上は上記まではまずは進められます。ここでエンターを押すと検証に入りますので、ここでエンターを押す前に実際に指定された情報の記載されたファイルをサーバに用意しておく必要があります。

準備後、エンターを押すと検証が走り、問題なければ(キチンと参照できれば)正常終了し、証明書が発行されます。

ちなみに、わざとではないのだが、以下はちょっとミスってしまった例です。問題無かったところからリトライができることの確認になりましたので、ミスった版で記載しておきます。

Waiting for verification...
Challenge failed for domain www.example.com
http-01 challenge for www.example.com
Cleaning up challenges
Some challenges have failed.

IMPORTANT NOTES:
 - The following errors were reported by the server:

   Domain: www.example.com
   Type:   unauthorized
   Detail: Invalid response from
   http://www.example.com/.well-known/acme-challenge/W1UdvuPTDWcPygFKKXeN_kZ10Ozoiq4twuyLtWPp9N0
   [xxx.xxx.xxx.xxx]: "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD XHTML 1.0
   Strict//EN\"
   \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n<html
   xmlns=\"http:"

   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.
 - 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.

上記では、test51.example.com は問題なかったのですが、www.example.com で失敗(ファイルの置き場所を誤っていた)しています。再度配置し直し(先にブラウザなどで直接アクセス確認をしておいた方がよいです)、コマンドをやり直します。

既に検証に成功している FQDN は処理をパスされ、失敗したホストだけ再検証されます。

$ sudo certbot certonly --manual -d www.example.com -d test51.example.com
Password:
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator manual, Installer None
Obtaining a new certificate
Performing the following challenges:
http-01 challenge for www.example.com

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NOTE: The IP of this machine will be publicly logged as having requested this
certificate. If you're running certbot in manual mode on a machine that is not
your server, please ensure you're okay with that.

Are you OK with your IP being logged?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: Y

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Create a file containing just this data:

6s6Gr8sY4TV96z3yF9skkfyUVBhOdiH-us66fcAzO2U.Ca4bn_Go9Bnvznx36-sQL22-kq117wbMGrKqm7llRzI

And make it available on your web server at this URL:

http://www.example.com/.well-known/acme-challenge/6s6Gr8sY4TV96z3yF9skkfyUVBhOdiH-us66fcAzO2U

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Press Enter to Continue
Waiting for verification...
Cleaning up challenges

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/www.example.com/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/www.example.com/privkey.pem
   Your cert will expire on 2020-05-25. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot
   again. To non-interactively renew *all* of your certificates, run
   "certbot renew"
 - 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

2020/05/25 に期限切れとなる90日間の証明書が無事発行されていることがわかります。

認証レベルという意味では、サイトの所有者ということだけでしか担保されていない証明書にはなりますが、これまで HTTP だけで公開していたサイトを常時 HTTPS 化に対応さえるという意味においては充分ですね。

発行されたサーバ証明書をサーバに配置する

やり方はいくつかあり、配置の仕方はサーバの仕様に依存します。ここでは、pkcs12 という形式でサーバに配置する方法を記載しておきます。

pkcs12 の作成には openssl を使います。pkcs12 は秘密鍵と証明書の双方を含むファイルです。秘密鍵を含むファイルですので、非常にセンシティブに扱う必要あります。pkcs12 ファイルの防御としてパスワードを指定します(コマンド実行後に入力させられます)。

引数に渡しているのは、サーバ証明書のファイル名と秘密鍵のファイル名です。それぞれコマンド終了後の最後のメッセージに表示されておりますので、それをそのまま指定します。
(コマンド自体に sudo は必要ありません。それぞれのファイルへのアクセスに必要となっています。)

$ sudo openssl pkcs12 -export -in /etc/letsencrypt/live/www.example.com/fullchain.pem -inkey /etc/letsencrypt/live/www.example.com/privkey.pem -out server-cert.pkcs12

上記で作成された server-cert.pkcs12 をサーバに配置します。

例えば、Tomcat 8 などの場合は、server.xml の中で以下のような指定を行い、作成された pkcs12 ファイルをサーバ証明書として利用します。

<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
           maxThreads="150" SSLEnabled="true">
    <SSLHostConfig>
        <Certificate certificateKeystoreFile="/usr/local/tomcat/conf/server-cert.pkcs12"
                     certificateKeystoreType="PKCS12"
                     certificateKeystorePassword="password"
                     type="RSA"
                     clientAuth="false"
                     sslProtocols="TLSv1,TLSv1.1,TLSv1.2"
        />
    </SSLHostConfig>
</Connector>

以上です。