続 ACME

2018年11月14日 水曜日


【この記事を書いた人】
田口 景介

社会人生活の半分をフリーランス、半分をIIJで過ごすエンジニア。元々はアプリケーション屋だったはずが、クラウドと出会ったばかりに半身をインフラ屋に売り渡す羽目に。現在はコンテナ技術に傾倒中だが語りだすと長いので割愛。タグをつけるならコンテナ、クラウド、ロードバイク、うどん。

「続 ACME」のイメージ

前回の記事を公開してからというもの、たくさんのコメントをいただきました。
正直なところ、日本文化ではLet’s Encryptに対して保守的な声が強いのではと考えていたのですが、エンジニアの皆さんからのコメントは肯定的な意見がかなりの割合を占めており、ほっとした次第です。
また、やれ盛大なブーメランだ、自虐的だ、身内の批判はよくないだ、たくさんの温かいコメントも合わせていただきました。
むしろこっちの反応が盛況だったのは言うまでもありませんが、どのような反響であろうとも読者からのレスポンスを糧に生きるのがブロガーというものですからキニモナラナカッタノデスガ、やはりネタは回収してこそ肥しになるというもの。
こちらをご覧ください。

before

after

どうです。
これがガイアツというやつです(嘘です。本当は元々予定されていたんですよ)。

それはさておき。
今回はLet’s Encryptを利用して、サーバ証明書を取得して、サーバへ適用するまでを実践してみましょう。
前回の記事ではブラウザベンダによってLet’s Encryptへの信頼レベルには温度差がありそうだと書きましたが、その後MicrosoftにもLet’s EncryptのルートCA証明書であるISRG Root X1が認められ、これで主要なすべてのブラウザで直接信頼されるルートCAになったそうなので、これまで以上に安心して使ってもよさそうですからね。

https://letsencrypt.org/2018/08/06/trusted-by-all-major-root-programs.html

ACMEクライアント lego

ACMEに対応したクライアントとサーバはボチボチ増えてきましたが、利用されているツールはおそらくcertbotとlegoが大多数を占めるのではないかと思います。python文化圏ならばcertbot、golang文化圏ならばlegoが使われていることでしょう。
この二つのツールを比較すると、EFFによる“the official Let’s Encrypt client”であるcertbotを利用している方が多いと思いますが、我々開発者としては”ACME library written in Go”でもあるlegoに親近感を覚えるところです。
また、legoをライブラリとして採用するプロダクトが増えるにつれ、ACMEの認証に利用される対応DNSプロバイダへのコントリビューションが世界中から集まってくるなど、着々と利便性が増しています。
かくいうIIJでも弊社のDNSサービスであるIIJ DNSアウトソースサービス用のDNSプロバイダを実装し、アップストリームに取り込んでもらうことができたので、legoをツールとして利用するだけでなく、内部的にlegoを利用しているtraefikやterraformといったプロダクトを利用する場合、弊社サービスを利用して認証できるようになりました。

https://github.com/xenolf/lego

せっかくなので、legoとIIJ DNSアウトソースサービスを使ってサーバ証明書を発行してみましょう。
また、せっかくDNSプロバイダを実装したので、ワイルドカード証明書を取得することにします。
Let’s Encryptで普通のサーバ証明書を発行するだけならば、証明書のCommonNameおよびSANに指定されたドメイン名でアクセスできるWebサーバを用意するだけでよいのですが、ワイルドカードサーバ証明書を発行するには、指定されたゾーンに認証用のTXTレコードを書き込む必要があるためです。

発行のために、以下の準備を整えます。

  • go get -u github.com/xenolf/lego
  • IIJ DNSアウトソースサービスのAPIキーなどを環境変数IIJ_API_ACCESS_KEY, IIJ_API_SECRET_KEY, IIJ_DO_SERVICE_CODEに設定

Let’s Encryptの利用にユーザー登録は不要なので、以下のコマンドを1回実行すればサーバ証明書を取得できます。
本番用の証明書を発行するならばserverの設定は不要ですが、今回はテスト目的なので検証用ACMEサーバのURLを指定しています。

$ lego --dns iij --server "https://acme-staging-v02.api.letsencrypt.org/directory" --domains '*.ike.iij.jp' --email keisuk-t@iij.ad.jp run
2018/07/25 12:28:49 No key found for account keisuk-t@iij.ad.jp. Generating a curve P384 EC key.
2018/07/25 12:28:49 Saved key to /home/keisuk-t/.lego/accounts/acme-staging-v02.api.letsencrypt.org/keisuk-t@iij.ad.jp/keys/keisuk-t@iij.ad.jp.key
2018/07/25 12:28:50 Please review the TOS at https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf
2018/07/25 12:28:50 Do you accept the TOS? Y/n
2018/07/25 12:28:58 [INFO] acme: Registering account for keisuk-t@iij.ad.jp
2018/07/25 12:28:59 !!!! HEADS UP !!!!
2018/07/25 12:28:59
                Your account credentials have been saved in your Let's Encrypt
                configuration directory at "/home/keisuk-t/.lego/accounts/acme-staging-v02.api.letsencrypt.org/keisuk-t@iij.ad.jp".
                You should make a secure backup of this folder now. This
                configuration directory will also contain certificates and
                private keys obtained from Let's Encrypt so making regular
                backups of this folder is ideal.
2018/07/25 12:28:59 [INFO] [*.ike.iij.jp] acme: Obtaining bundled SAN certificate
2018/07/25 12:28:59 [INFO] [*.ike.iij.jp] AuthURL: https://acme-staging-v02.api.letsencrypt.org/acme/authz/B0JyhvsRjW6YlX-hJ-bUyqDgveJyKVcgjXGQS1-_0Vw
2018/07/25 12:28:59 [INFO] [ike.iij.jp] acme: Trying to solve DNS-01
Present: _acme-challenge.ike.iij.jp. IN TXT iCWMLyuuKqkaqr5csXg3mQBwTI0lGPx5xsgU1JfDnKc
2018/07/25 12:29:05 [INFO] [ike.iij.jp] Checking DNS record propagation using [10.131.16.20:53 10.131.208.20:53]
2018/07/25 12:29:27 [INFO] [ike.iij.jp] The server validated our request
CleanUp: _acme-challenge.ike.iij.jp. IN TXT iCWMLyuuKqkaqr5csXg3mQBwTI0lGPx5xsgU1JfDnKc
2018/07/25 12:29:31 [INFO] [*.ike.iij.jp] acme: Validations succeeded; requesting certificates
2018/07/25 12:29:32 [INFO] [*.ike.iij.jp] Server responded with a certificate.
$ find .lego
.lego
.lego/accounts
.lego/accounts/acme-staging-v02.api.letsencrypt.org
.lego/accounts/acme-staging-v02.api.letsencrypt.org/keisuk-t@iij.ad.jp
.lego/accounts/acme-staging-v02.api.letsencrypt.org/keisuk-t@iij.ad.jp/keys
.lego/accounts/acme-staging-v02.api.letsencrypt.org/keisuk-t@iij.ad.jp/keys/keisuk-t@iij.ad.jp.key
.lego/accounts/acme-staging-v02.api.letsencrypt.org/keisuk-t@iij.ad.jp/account.json
.lego/certificates
.lego/certificates/_.ike.iij.jp.crt
.lego/certificates/_.ike.iij.jp.issuer.crt
.lego/certificates/_.ike.iij.jp.key
.lego/certificates/_.ike.iij.jp.json

これで.lego/certificatesの下にサーバ証明書と秘密鍵のファイルが生成されました。

あとは通常通りWebサーバへ設定すればhttpsでサイトを公開できるわけですが、やはりWebサーバへの適用まで自動化してこそのACMEでしょう。
しかし、証明書と鍵の設定方法はサーバごとにまちまちで、環境に依存せず自動化するのは今のところ少々難しい課題になります。そこで、直接Webサーバへ証明書を設定するのではなく、その前段に置くロードバランサにACMEクライアントの役割を担ってもらうことにしましょう。

The Cloud Native Edge Router Traefik

今回はそのロードバランサにtraefikを利用します。国内ではあまり馴染みのないプロダクトかもしれませんが、docker/Kubernetes界隈では著名なロードバランサの一つであり、legoを利用したACMEクライアント機能を内蔵したエッジルータ(ロードバランサ)でもあります。

https://github.com/containous/traefik

例えば、こんな感じでtraefikを設定します(一部省略してあります)。

defaultEntryPoints = ["https"]
[entryPoints]
  [entryPoints.http]
  address = ":80"
    [entryPoints.http.redirect]
    entryPoint = "https"
  [entryPoints.https]
  address = ":443"
    [entryPoints.https.tls]
    MinVersion = "VersionTLS12"
[kubernetes]
[acme]
email = "keisuk-t@iij.ad.jp"
storage = "/traefik/acme/account"
entryPoint = "https"
acmeLogging = true
onHostRule = true
[acme.httpChallenge]
entryPoint = "http"
[acme.dnsChallenge]
provider = "iij"
acmeLogging = true
[[acme.domains]]
  main = "*.ike.iij.jp"

すると、

  • CommonName ‘*.ike.iij.jp’ のワイルドカードサーバ証明書が自動的に取得され、これにマッチするサイトに適用される
  • ロードバランサにFrontEnd(apacheのVirtualHost、nginxのserverのようなもの)を設定すると、これを検知してそのホスト名でサーバ証明書が自動的に取得される
  • 証明書の有効期限は定期的にtraefikによってチェックされ、必要に応じて自動的に更新される

こんな具合でサーバ証明書の管理は完全にtraefikによって自動化されます。

ちなみに、ここでは詳しく触れませんが、この仕組みをKubernetes上で利用すると、Ingressリソースを宣言するだけでロードバランサに設定が入り、サーバ証明書の取得、設定、更新が自動化されます。
Let’s Encryptの証明書では都合が悪いサイトは、Secretリソースで明示的に証明書を設定することも可能です。Kubernetesへのオペレーションですべて完結するのがいいですね。
この辺りは機会があればまたいずれ。

ところで、ここまで読んでいただいた読者諸氏は当ブログの証明書が当然Let’s Encryptで発行されたものだと思っていますよね…?
(続きません)

田口 景介

2018年11月14日 水曜日

社会人生活の半分をフリーランス、半分をIIJで過ごすエンジニア。元々はアプリケーション屋だったはずが、クラウドと出会ったばかりに半身をインフラ屋に売り渡す羽目に。現在はコンテナ技術に傾倒中だが語りだすと長いので割愛。タグをつけるならコンテナ、クラウド、ロードバイク、うどん。

Related
関連記事