【翻訳】MWoS 2015: Let’s Encrypt Automation Tooling の成果報告
この記事は、2016 年 8 月 8 日付で Mozilla Security Blog に投稿された MWoS 2015: Let’s Encrypt Automation Tooling(筆者: J.C. Jones)の翻訳です。この翻訳は公式なものではありません。詳しくはこちらをご覧ください。
Mozilla Winter of Security 2015 が終了し、参加チームの学生はプロジェクトを終えることができました。
Certificate Automation tooling for Let’s Encrypt project は今月終了し、ACME による証明書自動管理プロトコルをサーバ運用と密接に統合させるべく、Nginx web サーバ用の実験的な proof-of-concept のパッチを作成しました。
このプロジェクトにおいて多大なる研究成果を挙げてくださった Klaus Krapfenbauer さん、彼のアドバイザーである Martin Schmiedecker さん、そしてウィーン工科大学の皆さんに対し、MWoS チームとメンターの Richard Barnes、そして同じくメンターの私からお礼を申し上げます。
以下の AirMozilla による動画は、Klaus さんがプロジェクトのまとめとして発表したプレゼンテーションで、このプロジェクトの詳細について紹介しています。
MWoS Let’s Encrypt Certificate Automation のプレゼンテーション(AirMozilla)
Nginx 用 ACME モジュールの開発
筆者: Klaus Krapfenbauer
補足: このモジュールは不完全な proof-of-concept ですが、https://github.com/mozilla/mwos-letsencrypt-2015 から参照できます
2015-2016 Mozilla Winter of Security では、著名な web サーバに ACME クライアントを実装するプロジェクトが採用されました。このプロジェクトの目標は、Let's Encrypt と併用した HTTPS 設定の自動化に対する価値を示すことです。Caddy Server のようなプロジェクトは、多くの使いやすい機能をユーザから利用できるようにしたものですが、今回のプロジェクトでは主要な web サーバの Nginx において、それらの自動化する機能を組み込むことを目指しました。
Nginx のモジュールシステムでは、それぞれが異なる目的を持つ様々なモジュールを利用可能になっています。モジュールとしては、トラフィックを複数のバックエンドサーバに振り分けるロードバランサモジュール、web サイトのデータ変換を行うフィルタモジュール(例えば SSL/TLS モジュールのような暗号化など)、web リクエストに対応するコンテンツを生成するハンドラモジュール(例えば HTML ファイルをディスクから読み出して送り返す HTTP ハンドラなど)が挙げられます。モジュールの目的のみならず、サーバのコア部分へフックをかける方法でもモジュールの種類が異なりますが、この選択は Nginx モジュールを実装し始める際に重要となります。しかし、今回のプロジェクトに適したモジュールの種類はデフォルトで用意されておらず、後述するような困難がいくつか生じました。
ACME モジュール
今回の Nginx モジュールは、ACME Certbot による既存のワークフローを置き換えることが目的です。そのため、このモジュールの機能は Certbot の機能に近いものとなります。その機能とは以下の通りです。
- 鍵対の生成と格納
- ACME サーバに対するアカウントの登録
- ドメインに対する認証情報の作成
- ドメインの認証における HTTP チャレンジの解決
- 他のチャレンジ方式は後日サポートします
- ACME サーバからの証明書取得
- 証明書の更新
- 取得した証明書を Nginx SSL/TLS モジュールに使用させる設定
ACME プロトコルの処理に必要な情報を提供するため、Nginx に以下のような新しい設定ディレクティブを導入することにしました。
他の情報はすべて、Nginx にデフォルトで組み込まれているディレクティブから集められます。例えば、証明書を発行したいドメイン名は "server_name" という Nginx の設定ディレクティブから取得できます。
今回の ACME モジュールは Nginx サーバ自身を拡張するため、モジュールは Nginx というソフトウェアの一部となります。従って、モジュールを設定するために Nginx の設定ファイルを利用し、証明書を Nginx の設定ファイルディレクトリに格納します。その証明書を取得する際、ACME モジュールは ACME サーバ(例えば Let's Encrypt が該当しますが、ACME プロトコルを話せる他のサーバでも構いません)と通信を行い、この証明書を使うよう SSL/TLS モジュールを設定します。そして、設定された SSL/TLS モジュールによtって、web サイトとユーザのブラウザ間における通信が暗号化されます。
ここで、安全な web サイトを立ち上げる際のワークフローを確認してみましょう。ACME を用いない場合、暗号化された web サイトを立ち上げるには以下の手順を踏む必要がありました。
- 必要な情報をすべて揃えて CSR(証明書署名要求)を作成する
- 作成した CSR を CA(認証局)に送る
- 署名付きの証明書を取得するために CA へ料金を支払う
- 証明書が返送されるまで待つ(場合によっては数時間)
- 証明書をダウンロードし、サーバの正しい場所に置く
- その証明書を使用するようにサーバを設定する
ACME プロトコルと Let's Encrypt CA を利用すれば、必要な手順は以下で済みます。
これでも大きな進歩ですが、Nginx 用 ACME モジュールを利用すればもっとシンプルになり、以下の手順だけで済むようになります。
- サーバの設定項目で ACME モジュールを有効にする
他のことはすべて ACME モジュールが処理してくれます。従って、Let's Encrypt クライアントが行っていた処理に加え、サーバ起動時に必要な設定もすべて ACME モジュールが自動的に処理します。このようにすれば、web サイトの管理者が自身のサイトをより安全にしやすくなるはずです。
ACME モジュールを動かすのに必要な最小限の設定は、動作させたい Nginx サーバの設定ファイルにおいて、server コンテキストに "acme" ディレクティブを加えることだけです。つまり、設定の仕方は以下のようになります。
… http { … server { listen 443 ssl; server_name example.com; acme; … <recommended SSL hardening config directives> … location / { … } } } …
立ちはだかった困難
ACME モジュールの設計と開発にはかなりの困難が伴いました。
上で述べたように、Nginx のモジュールには様々な種類があり、モジュールによってサーバのコアを拡張する箇所が異なります。デフォルトとなる Nginx モジュールの種類は、ハンドラモジュール(サーバ上にコンテンツを生成するもの)、フィルタモジュール(SSL/TLS モジュールのように web サイトのデータを変換するもの)、ロードバランサモジュール(リクエストをバックエンドサーバに振り分けるもの)となっています。しかし、これらの種類は ACME モジュールと隠蔽したいワークフローとには当てはまりません。今回のモジュールは、自分自身の設定ディレクティブを有することや、サーバのコアや他のモジュールへのフックを必要とする点において、先程の慣例から外れてしまいます。今回の要件を満たせるように Nginx のモジュールシステムは設計されていないため、ACME プロトコルの通信を行う「タイミング」について自由度はほとんどありませんでした。
ACME モジュールは既存の SSL/TLS モジュールに対して設定を行いますが、SSL/TLS モジュールは web サイトの暗号化を実際に行うものです。ACME で取得した(暗号化に必要となる)証明書を渡すには、ACME モジュールから SSL/TLS モジュールを必要限度でコントロールする必要があります。残念ながら、サーバの起動時に Nginx の設定ファイルがパースされる際、証明書の実在性と正当性が SSL/TLS モジュールによって確認されてしまいます。つまり、設定ファイルのパースが終わる前に ACME モジュールを実行しなければなりません。このような制約があるため、サーバの起動時に "acme" ディレクティブがパースされた段階で、証明書の取得作業をすべて行うように方針決定しました。証明書を取得した後は、メモリに格納されている SSL/TLS モジュールの設定項目を ACME モジュールから更新し、新しい証明書を利用するように設定します。
アーキテクチャに関しては、ACME HTTP チャレンジ・レスポンスを実装する際にもう 1 つ問題が生じました。ACME HTTP チャレンジを用いてドメインを認証させるには、そのドメイン上の well known な URL パスから、サーバが決められたトークンを返信する必要があります。通常このトークンの返信方法は、web サーバがサイトのコンテンツを配信するのと同じ方法でなければなりません。しかし、ACME モジュールが処理を行っている最中に Nginx はまだ起動しておらず、どこにも web サーバは立っていないのです。もし ACME モジュールの処理を中断させ、web サーバの起動に処理を譲ってしまうと(先に SSL/TLS モジュールにおける証明書の制約を解決しておくことに注意)、後から ACME の処理を再開させることは容易にできません。これはアーキテクチャ的に Nginx 側の理に適っていますが、今回のプロジェクトとしては不都合です。このジレンマに直面しつつも proof-of-concept の実現を目指した結果、Nginx 自身が正常に起動する前に ACME チャレンジに応答できる、独立した小規模の web サーバを立てるよう方針を決めました。
まとめ
ここまで議論してきたように、Nginx モジュールの制約を受けたことで、アーキテクチャ設計に関して次善的な意思決定を余儀なくされました。多くのソフトウェアプロジェクトと同じく、利用するフレームワークが設計された際の想定範囲から超えた時に困難は生じます。現時点における ACME モジュールのアーキテクチャ設計は、proof-of-concept と捉えるべきでしょう。
このモジュールのアーキテクチャや、Nginx のコア部分 - SSL/TLS モジュール - ACME モジュール間の通信を改善する余地はまだ残っています。こうした変更案による長所・短所を以下で検討してみましょう。
変更案の 1 つに、証明書を受信するタイミングを、設定ファイルがパースされた後に延期させる方法があります。これを実現するには、新しく受信する証明書の準備が整うまでの間、一時的な証明書で SSL/TLS モジュールをごまかす必要があります。しかしコーナーケースとして、その時点で取得済みの証明書が格納されていない場合に問題が生じ、ちょうどサーバの初回起動時が相当します。
もう 1 つはチャレンジ・レスポンスに変更を加える案です。これは、web サーバの中に web サーバを立てるもので(サードパーティ製ライブラリであるかは関係なく)、あまり綺麗な方法ではありません。こうすることで、ACME プロトコルで採用されている TLS-SNI や他のチャレンジ方式をより組み込みやすくなるかもしれませんし、ACME モジュールの処理中に Nginx を起動させる方法が他にあるかもしれません。
最後にひとつ。SSL/TLS モジュールとのやり取りは非常に hacky です。
プロジェクトの現状と展望
今回開発したモジュールは現状、開発段階の終盤にあたる proof-of-concept とほぼ考えられます。現時点のモジュールでは、一時的な鍵対の生成、ACME サーバへの登録、ドメインに対する認証チャレンジの発行、チャレンジに対する応答の開始までが行えます。proof of concept はまだ実現できていないため、引き続きプロジェクトに取り組んでいく考えです。
多くの方々に感謝を
今回のプロジェクトは、web 全体を安全にしたいという将来像を形にするきっかけとなり、非常に楽しい機会でした。私事になりますが、このプロジェクトにおいて私をサポートしてくださった Mozilla Security Engineering Team の J.C. Jones さんと Richard Barnes さんに特別な感謝を申し上げたいと思います。そして、私の教授でもありメンターであった、オーストリアのウィーン工科大学にある SBA Research の Martin Schmiedecker 氏にも特別な感謝を申し上げます。もちろんながら、Mozilla Winter of Security を開催し、世界中の学生に素晴らしい IT プロジェクトに参加させてくださった Mozilla organization の皆様にもお礼申し上げます。最後となりましたが、一部分ではありながら素晴らしいセキュリティプロジェクトに参加させていただいた Let's Encrypt プロジェクトの皆様にお礼を申し上げます。