動機
以前、こちらの記事で普段使いのDocker生活を提唱しました。事実、私は普段の業務でいくつかの作業はdocker-compose化してソースコードに含めて配布していますが、今回は毎回ググってる感のある証明書作成の作業をDocker化してみたらどうだろうと思いやってみました。
ソース
使い方
0.前提知識
こちらのサイトを参考にさせていただきました。
クライアント証明書はサーバー証明書と同じで
- 秘密鍵を作る
- 秘密鍵からCSRを作る
- CSRにCAが署名することで証明書となる
という手順です。
が、最終的なファイル形式がサーバー証明書で一般的なX509形式ではなくp12形式となります。(なんでクライアント証明書はp12が一般的なのですかね・・・という疑問)
X509形式
- BASE64エンコードされたASCII文字で形成されるテキストファイル
- 秘密鍵と証明書が別ファイル
p12形式
- バイナリーファイル
- 秘密鍵と証明書が1ファイルにまとまっている
1. CAとCRLを作る
さて、クライアント証明書を発行するには署名する発行者としてのCA証明書が必要になります。
このCA証明書はクライアント証明書専用というわけではなく、サーバー証明書に署名するものと同じでも構いません。
署名者の信頼度が求められる場合には外部の認証局に署名してもらうことになりますが、今回は自前で認証局を運営する形態とします。いわゆるオレオレ認証局というやつで、自分で自分に署名したROOT認証局証明書になります。
$ docker-compose run create_ca_cert
./ca
ディレクトリ直下に以下のファイルが出来ます。
- ca.pem ... CA用の秘密鍵
- ca.crt ... CAの自己署名証明書
- ca.crl ... 失効リスト
証明書の期限、サブジェクトはca.jsonに設定します。
2. create client cert
次に、お待ちかねのクライアント証明書を作成します。
$ docker-compose run create_client_cert yoshiko
上記で生成された./ca/ca.crt
で署名されたクライアント証明書が./client/out
ディレクトリにp12ファイルとして出来ます。
パスワードは空文字が設定されています。1
- 証明書の期限、サブジェクトは./client/seed/yoshiko.json で設定します
- seedを増やすことでクライアント証明書を増産できます
3. create server cert
本題ではないですが、クライアント証明書を使う際には通信経路の暗号化、サーバー認証のためにサーバーもほぼセットで使用されることになると思われるので、サーバー証明書についても用意しておきました。
最近Google Chromeでは無いと注意されるSANsの設定も対応しています。
$ docker-compose run create_server_cert site.kinoboku.net
上記で生成された./ca/ca.crt
で署名されたサーバー証明書が./server/out
ディレクトリにファイルとして出来ます。
- 証明書の期限、サブジェクトは./server/seed/site.kinoboku.net.json で設定します
- seedを増やすことでサーバー証明書を増産できます
- seedのJSONファイル名はサーバーのFQDNを指定します
4. nginxで試してみる
これで、
- クライアント証明書(p12ファイル)
- サーバー証明書一式(pem, crtファイル)
- 認証局証明書一式(pem,crtファイル)
が出来たので、動作検証をするためのNginxコンテナも用意しました。
$ SERVER=site.kinoboku.net docker-compose up test_nginx
これで試せるのですが、以下の2つの理由で怒られる事になるでしょう。
- 署名に使ったCAが信頼できない
- ドメイン名が証明書のものと違う(localhostだから)
1の問題は当然です。オレオレ認証局なのですから。ブラウザの「信頼するルート証明書」として先ほど作ったca.crtを登録して信頼しましょう。(Macの場合はキーチェーンツールによる登録ですが手順はややこしいので省略します)
2.の問題は「証明書のサブジェクト名がsite.kinoboku.netなのに、いまアクセスしているのはlocalhostだから違うぞ」ということです。
そこで、/etc/hosts
に以下を登録しましょう。
127.0.0.1 site.kinoboku.net
これで、証明書のエラーは出ずにNo required SSL certificate was sent
というNginxの400エラー画面が出ることでしょう。
「クライアント証明書を提示していないため、クライアント証明書が必要だよ」ということです。
あとはクライアント証明書をブラウザに登録することで、Welcome to nginx!
ページが表示されることでしょう。
メモ的なところ
自分はクライアント証明書を少し勘違いしていました。
認証するユーザーのクライアント証明書を1つ1つサーバー側に登録する必要があるのだと思っていましたが、そうではなく、
- サーバー側は認証局を信頼する
- 認証局が発行したもので期限内であれば妥当と判断される
- ただし、CRLに載っているものは失効したものとして拒否される
という挙動でした(少なくともここで設定したNginxでは)
-
Macのキーチェーンツールではパスワード無しのp12ファイルの取り込みに失敗してしまったのでMacな人はssl_create_client.shを改変してパスワードをつけたほうが良いかもしれません。 ↩