見出し画像

無料の SSL証明書を取得する

さくらインターネットのさくらの VPS に FreeBSD 13.0-RELEASE をインストールして 13.1-RELEASE にアップデートファイアーウォールを設定したので、次はウェブサーバとして動作させるため httpサーバ、Apache をセットアップしたい。もちろん、ウェブサーバが不要であればこの作業は必要ない。

ウェブは長い歴史の中で、平文(ひらぶん)ですべてをやりとりする、いわゆる http を当たり前に使ってきたが、フィッシング詐欺などの不正行為が跡を絶たず、各社が対策する中で出てきたコンセンサスとして、2022年現在、ウェブは SSL で暗号化すべし、という流れになってきている。

ここではよく使われている用語として SSL(Secure Socket Layer)を使っていくが、正確には TLS(Transport Security Layer)という規格で、誤用を承知の上でわかりやすさを優先して SSL と呼んでいくので予めご承知いただきたい。なお、元々 SSL は http で通信の暗号化のために使われていた技術だったが、汎用的に使えるぞ、ということで http 以外のプロトコルでも同様の仕組みで通信の暗号化を行うようになってきて、http以外の場合を TLS と呼んでいたが、本家の http も規格が新しくなる際に他のプロトコルで使っていた新規格に嵌合し、TLS になってきた、という歴史がある。


SSL証明書を取得する、とは


通信を暗号化する目的というのは、やり取りしている通信内容を第三者に盗み見られないため第三者に改ざんされないため、であるがこれは通信の途中の話である。

盗聴や改ざんは、通信相手でも起こりうることであり、通信の暗号化を行う上で通信相手が信頼すべき相手であるかを確認する手続き、というのが必要になる。

たまに通信の暗号化するだけならオレオレ証明書でいいよ、という人がいるがこれは間違っていてオレオレ証明書ではそもそも偽物と通信している可能性を排除できないので、そもそも通信を暗号化する意味すらなくなってしまう。

しかし通信を行う際に相手が信頼すべき相手であるかを確認すると、言葉で言ってもそれが難しいことは容易にわかると思う。

なぜなら、誰しも自分が自分であることを自分で証明することはできないからである。正規の者であっても、不正の者であっても、「オレオレ、俺だよ」と言ってるわけでこちらはその判別を付けることができない。

そこで、身分を保証する第三者が、「この人が本人に間違いない」と太鼓判を押し、その御札を持参することで「オレは俺」を相手に示すことができる。

この身分を保証する第三者を証明機関と呼び、御札を SSL証明書(正確には TLS証明書)と呼ぶ。

同じように、第三者である証明機関をどうやって信頼するのか、という問題が出てくるが証明機関は全世界でも数えるほどしか無いので、これは予めリストアップしてしまい、OS(Windows や Android、FreeBSD など)やブラウザ(Firefox)を作るベンダ(Microsoft、Google など)でリストを吟味し、ユーザに提供する形になっている。ちなみに、Windows 10 21H2 には、組織の重複があるものののべ 55 の証明機関が登録済みだった。

もちろん、Microsoft や Google の作ったリストなんか信頼できない、というポリシーもアリだが、一般的なユーザであれば Microsoft や Google が(今後もこの世界でビジネスをやっていくうえで)不正を働くことは無いだろう、というモヤッとしたコンセンサスの上でこのリストを信頼することにしている。

第三者である証明機関は、公営のもの、民営のもの、さまざまあるが証明機関を運用する上でコストが掛かってくるので、これを証明書の発行を希望するユーザに対して利用料という形で転嫁している。

証明書が必要なユーザは、証明機関に利用料を払い(概ね、1~3年単位で支払う)、自身の存在を証明してもらい、証明書を発行してもらう。


無料の Let's Encrypt


いままでは SSL証明書は取得する必要がある人たちが、自腹で発行手数料を支払って証明機関から証明書を取得していたが、本来はインターネットを使用する全ユーザで負担すべきコストである、という考え方もある。(閲覧する側が負担ゼロで良い、という合理的な理由がない)

SSL やインターネットに関わるあらゆる団体(非営利法人や、Google などの営利企業)が集まって、ISRG(Internet Security Research Group) という非営利団体を設立し、スポンサー企業(Cisco、Google、Akamai など)からの支援一般ユーザからの寄付を使って証明機関を運用し、発行手数料無料で SSL証明書を発行する仕組みが作られた。

この証明機関が Let's Encrypt で、個人だけでなく一般企業も、ユーザに対してホスティングサービスを提供するプロバイダー企業であっても、利用料が不要で証明書の発行を受けることができる。

ただし、Let's Encrypt では運用コストを最大に下げるため、発行に必要な自信を証明するための作業(検証作業)を全自動で行っており、取得を希望するユーザ側は Let's Encrypt の仕様にそって検証作業を行わないといけない。

この作業はベンダーに手数料を払って代行してもらうこともできるし、もし手数料を払ってまで無料にこだわらない、ということであればこれらの作業を手取り足取りやってくれる旧来型の証明機関もある(DigiCert、Symantec(旧VeriSign)、SECTIGO(旧comodo)、GoDaddy など)ので、ユーザは自分にあった証明書を選べば良い。

なお手数料は無料ではあるが、技術的にはいままでの有償で発行された SSL証明書とまったく同じものであり、使用する上でなんらの制約も発生しない。


Let's Encrypt の検証作業


Let's Encrypt では証明を希望するユーザの身元を確認するため、独自ドメイン名(及び、そこから生成される FQDN)の管理権を有しているか、を自動的に確認してくる。これを ACMEプロトコルを呼ぶ。

具体的には、ウェブサーバを管理しているか、もしくは DNSサーバを管理しているか、を確認する。どちらか片方でも動作すれば良い。

逆にウェブサーバも DNS も使っていない独自ドメイン名の場合は、Let's Encrypt の証明書を発行してもらうことができない。

どちらの場合も、Let's Encrypt に証明書の発行依頼を出すと、ランダムな文字列(nonce)を規定の方法で設定するように求めてくる。

具体的にはウェブサーバを使った検証(HTTP-01 チャレンジ)の場合、FQDN のウェブサーバ配下の、/.well-known/acme-challenge/ 以下に nonce のファイル名のファイルを置き、この中に規定のユーザアカウントから生成されるフィンガープリントを記述しておく。

DNSサーバを使った検証(DNS-01 チャレンジ)の場合、FQDN に _acme-challenge という TXT レコードを追加、このレコードのレスポンスに nonce を記述しておく。

どちらを使っても結果は同じだが、今回は設定が容易な DNSサーバを使った検証で証明書を取得する。


Dynamic DNS を使う


DNS-01チャレンジ(以下、acme-challenge)では、リクエストを送った後に Let's Encrypt から返される返答を確認し、DNS にレコードを追加する作業が必要になる。

もちろん、さくらインターネットのネームサーバサービスを使って手作業でレコードを追加しても良いし、自前でセットアップした bind にレコードを追加しても良い。

ただ、発行する Let's Encrypt証明書の数が増えてくると、これらの作業は何度も繰り返すことになるので私の場合は bind の Dynamic DNS という仕組みを使って作業を自動化している。

通常、bind ではレコードの追加や削除などは、masterファイルを書き換えるなどの作業を行う必要があるが、Dynamic DNS を使うと、nsupdate というコマンドからこの作業を行うことができる。

nsupdate は UDP で bind とやりとりするので、bind の動作しているホストだけでなく、リモートのホストから作業することもできる。

具体的には、acme-challenge で使う専用の Dynamic DNS を作成しておいて、ここへ TXTレコードを追加。検証する FQDN からは CNAME で参照する、という流れで設定してある。

まず、acme-challenge で使う専用の Dynamic DNS を作成する。

これはさくらの VPS で作成したインスタンス上で作っても良いが、うちの場合はすでに運用している bind があるので、ここに設定した。FreeBSD 13.1-RELEASE ではデフォルトで bind はインストールされないので、pkg で bind916 か bind918 をインストールする。

昔の FreeBSD では bind は OS にデフォルトでインストールされていたので設定ファイルも /var/namedb 以下に置かれていたと思うが、pkg からインストールした bind の場合は、pkg の作法に従い、/usr/local/etc/namedb 以下に置かれている。

named.conf の細かい記述方法は割愛するが、Dynamic DNS に対応したゾーンは、以下のように作る。


zone "dynamic.example.net" {

        type master;
        file "dynamic/dynamic.example.net";

        allow-update {
                127.0.0.1;
                ::1;
                fe80::%2/64;
        };
};

通常の masterファイルと同じような記述方法で、allow-update を付ける必要がある。ここでリストした IPアドレスからの update だけが適用される。127.0.0.1 は IPv4 のローカルホスト、::1 は IPv6 のローカルホスト、fe80::%2/64 は IPv6 のリンクローカルで、vtnet1(スコープIDが 0x2 = %2)に接続したローカルネットのホストからの接続だけを許可している。

初期の master ファイル(dynamic/dynamic.example.net)の中身は通常の masterファイルと同じにする。

上位のドメインの設定で、dynamic.example.net に対する NSレコードを設定する。

nsupdateコマンドは、bind-tools に含まれているのでこちらも pkg installコマンドでインストールしておく。

レコードを追加する場合は、

% nsupdate
> update add some.dynamic.example.net. IN A 192.168.0.1
> send
> ^D

とコマンドを打つだけだ。これにより、some.dynamic.exmaple.net という Aレコードが追加される。

TXTレコードを追加することもできるので、これを使って DNS-01チャレンジで指定される nonce を追加する。

Dynamic DNS にレコードを追加しただけでは検証したい FQDN と結びつかないので、検証したい FQDN には、以下の構文で CNAME を設定しておく。

例えば、今回、SSL証明書を取得しようと思っている FQDN が、secure.example.org だった場合、DNS-01チャレンジでは、_acme-challenge.secure.exmaple.org の TXTレコードをチェックする。


画面はさくらのネームサーバサービスのレコード編集画面だが、

エントリ名 _acme-challenge.secure(先頭がアンダースコアで始まっているので忘れず!)、タイプ CNAME、データ secure.example.org.dynamic.example.net.(最後がドットで終わっているので忘れず!)、とする。


certbot


ACMEチャレンジを行うツールはいくつか公開されているが、よく使われているのが certbot というツールだろう。

Python で書かれたコマンドベースのツールで、pkg から py38-certbot でインストールできる。

インストールしたが、まずはユーザ登録が必要なので、


certbot register コマンドを実行する。


電子メイルアドレスを聞かれ、規約に合意するか確認されるので入力して進めていく。ここで登録した電子メイルアドレス宛に、取得した証明書の有効期限が迫ってきているときなどにお知らせが送られてくる。

certbotコマンドはリクエストの実行の際に、任意のスクリプトを動かす、hook という仕組みがあるので、ここに自前のスクリプトを指定して、そこで nsupdate を行うことにする。


これが私が使っている hook で、環境変数 ${CERTBOT_DOMAIN} に証明書をリクエストしている FQDN、${CERTBOT_VALIDATION} に nonce が入ってくる。

上記は実際に運用しているスクリプトのままなので、.acme-challenge.v.zero52.net. のところを、上の例で言えば、.dynamic.example.net. に置き換えて読んでいただければよい。

3秒の sleep を置いているのは、いくら Dynamic DNS と言えど secondaryサーバへのレコードの転送など、反映には少し時間が掛かるのでそのために入れてある。

このスクリプトを、/usr/local/etc/letsencrypt/dns-manual-auth-hook.sh として置いておく。(実行権限を付与するのを忘れずに!)


こちらは検証後、不要になったレコードを削除するための hook スクリプト。

このスクリプトを、/usr/local/etc/letsencrypt/dns-manual-cleanup-hook.sh として置いておく。(実行権限を付与するのを忘れずに!)


certbot の実行


準備が整ったので、certbot を実行して SSL証明書の取得をリクエストする。


certbotコマンドの構文は、

# certbot \
  certonly \
  --manual \
  -d (FQDN) \
  --preferred-challenges dns \
  --manual-auth-hook    /usr/local/etc/letsencrypt/dns-manual-auth-hook.sh \
  --manual-cleanup-hook /usr/local/etc/letsencrypt/dns-manual-cleanup-hook.sh

で、-d は複数指定することができる。複数指定した FQDN は 1枚の証明書にまとめられるので、IPアドレスを共有するときに便利に使うことができる。

個別の証明書として欲しい時(もちろん、IPアドレス( + ポート)は専用に割り当てる必要がある)は、必要なだけでコマンドを繰り返して実行する。

正常に実行が終わると、/usr/local/etc/letsencrypt/archive以下に FQDN名のディレクトリが作成され、certN.pem(証明書)、chainN.pem(中間証明書)、privkeyN.pem(秘密鍵)といったファイルが作成されている。

N のところは世代で、更新するごとにインクリメントされていく。

最新の証明書に対して、/usr/local/etc/letsencrypt/live以下にシンボリックリンクが張られるので運用ではこちらを使えば良い。


作成された証明書は、openssl x509コマンドで確認できる。

% openssl x509 -noout -text -in cert.pem


Let's Encrypt から発行される SSL証明書は有効期限が 90日程度と短いので、更新についても自動化してしまったほうが良い。

FreeBSD なら /etc/rc.conf に weekley_certbot_enable="YES" の行を追加しておくと、weekley periodic で更新作業を実行してくれる。

次回の記事で、今回作成した SSL証明書を使い、ウェブサーバをセットアップする。


さいごに


技術的な小難しい話題でしたが、最後まで読んでいただいてありがとうございました。

よければ ↓ にある、♡ をクリックしていただけると励みになります。質問のコメントも歓迎です。

この記事が気に入ったらサポートをしてみませんか?