社内検証環境用にプライベート認証局立ててみた

こんにちは、GMOインターネット ネットワークチームの梅崎です。
今回は弊社ネットワークチームの検証環境にあるWebサーバー群のために、プライベート認証局を立てた話です。

※構築には easy-rsa を利用させていただきました。
 作成されたファイルのパスなどはデフォルトのものを記載しております、適宜読み替えてください。
※本文中の設定/コマンドはそのままコピペできなかったらごめんなさい。
 (気をつけてはいますが、ワードプレスに慣れていないもので変な空白が……)

先に最終的になにをしたのかを

準備

easy-rsagit clone

openssl-easyrsa.cnfを編集して [ easyrsa_ca ]セクションを変更
(中間認証局を作らない場合はpathlen は0でも大丈夫だと思います)

- basicConstraints = CA:true
+ basicConstraints = critical,CA:true,pathlen:1

[ easyrsa_ca ]セクションに下記追加

nameConstraints=critical,@name_constraints

下記セクションも追加
192.0.2.0/255.255.255.0 はご自身の環境に合わせてください

[name_constraints]

permitted;IP.1 = 192.0.2.0/255.255.255.0
permitted;DNS.1 = invalid.invalid

認証局を作る

初期化と認証局の秘密鍵や証明書作成は下記コマンドで
鍵長などはお好みで、2022年現在だと4096bitもあれば大丈夫だと思います

./easyrsa init-pki
./easyrsa build-ca

できた認証局の証明書(./pki/ca.crt)をダウンロードして「信頼されたルート証明機関」にインポートすればOKです

サーバー用の証明書

サーバー用の証明書は下記コマンドで
./easyrsa gen-reqは実際に秘密鍵を利用するサーバーで実行する方がおすすめです
※192.0.2.1の箇所はご自身のサーバーのIPアドレス に合わせてください

./easyrsa gen-req hogehoge nopass
./easyrsa --subject-alt-name='DNS:invalid.invalid,IP:192.0.2.1' sign-req server hogehoge

後はコマンドの実行結果に出ているディレクトリに秘密鍵(./pki/private/hogehoge.key)と証明書(./pki/issued/hogehoge.crt)があるので実際に使用するだけです
※Nginx等の利用の場合はサーバーの証明書へ認証局自身の証明書をcatかなにかでくっつける必要があります

以上で内部向けページも緑鍵に

目指せ「この接続ではプライバシーが保護されません」の撲滅!

本編

はじめに

昨今では常時TLS化もわりかし行われていますが
社内向け環境だと自己署名証明書、いわゆオレオレ証明書を利用していることが多いのかなと思っております。

もしサーバーがグローバルIPアドレスを持っていればLet’s Encrypt等の利用も考えられるのですが、プライベートIPのみの環境ですとそれもできないです。

これを解決するためにはサーバーの証明書を個別にPCに登録するか、個人的に認証局を立てそれをPCに登録するかの二択だと思います。

ただ、サーバーの証明書を個別に登録するのはサーバーが増えたり減ったりすることも考えるとかなり手間です。
かといって認証局を立てるとその秘密鍵が漏洩した場合、最悪すべての通信に中間者攻撃の恐れがあるため、気軽に立てるにはかなり荷が重い仕事になってしまいます。

そんなこんなで仕方なくセキュリティエラーを無視する日々でした……

そんなある日

証明書に載せられる情報について色々調べていたある日のこと、なんの気なしにプロファイルを眺めていると
https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.10
制限するための方法が普通に書いているではありませんか!

ということで、このオプションはどうやって適用できるのか、何ができてどんな挙動なのかを調べてみました。

認証局の証明書へ制限事項を追加

下記の設定を追加すれば認証局の証明書を作る際に、証明書へ制限事項の情報を追加できました。

nameConstraints=critical,@name_constraints


[name_constraints]

permitted;IP.1 = 192.0.2.0/255.255.255.0
permitted;DNS.1 = invalid.invalid

nameConstraintsというのが、この認証局が発行するサーバー証明書に含むことができる
Subject Alternative Name(以降SAN)のブラック/ホワイトリストとなっております。
※昨今ではブラウザはホストと証明書の組み合わせが正しいかの確認にSubject Alternative Nameを利用しています。

ざっくりいうと「このIPのサーバーにしか証明書は発行できません」という感じです。

設定中のcriticalは「この項目を理解できない実装はとりあえず証明書エラー扱いにしてくれ」というフラグです。

設定中のIPDNSは値の形式を表していて内容は見たままなのですが、存在しない 値の形式は全許可扱いになるため要注意です。
(例えばDNSの制限/許可がないとデフォルトで全許可です、危ない!)

とあるブラウザの不思議な挙動

上記のような設定でいろいろな認証局とサーバー証明書を作って試したのですが、Chromeの挙動が意図したようにはなっていなかったのです。

具体的には、特定のIPアドレスのみ許可、DNS表記は不可としたく
認証局の証明書に「すべてのDNS表記は不許可、IPは特定のプレフィックスのみ許可」や「この特定のDNS表記は許可、 IPは特定のプレフィックスのみ許可 」
サーバーの証明書のSANはIPアドレス表記のみとした際

証明書自体は問題なしと出るのですが、Chromeの画面上だと証明書エラーとなる事象が起きてしまいました。
※Firefoxだと問題なくページが表示される。

他にもいろいろ試していると、どうにもChromeはDNS表記の制限がある場合に、サーバー証明書へDNS表記のSANが無いとエラーと判断しているのではと思います。

ただ、検証環境ではIPでサーバーへアクセスできれば十分ですし、権威DNSサーバーを立てるのも面倒なので困ってしまいました。

その回避策として

DNS表記のSANが無いとエラーになるのなら
 ・サーバー証明書 へは適当なドメインのSANを追加
 ・認証局の証明書へはそれへの許可のみ
をすれば実質IPだけの運用ができるのではと考えました。

とは言うのものの、勝手に変なドメイン名を利用するのも趣味ではなく、いい感じのTLDが無いか調べてみると
https://datatracker.ietf.org/doc/html/rfc6761#section-6.4
名前解決を失敗するためのTLDがあり、自由に使っていいとも書いていたので
こいつを使っちゃおうということで

認証局の証明書はIPアドレスと「DNS invalid.invalid 」を併記で
サーバー証明書にもとりあえずSANへ 「DNS invalid.invalid 」 を入れておこうということで落ち着きました。

まとめ

まとめると下記を書けば大丈夫です。

nameConstraints=critical,@name_constraints


[name_constraints]

permitted;IP.1 = 192.0.2.0/255.255.255.0
permitted;DNS.1 = invalid.invalid

(実はちょっぴり不安なので、やっていること間違えていればご教授ください。特にセキュリティ的にあかんことになっていると最悪なので!)

最後に

これにてプライベートな検証環境にも鍵マークが現れました💮

今後はLet’s EncryptのようにACMEに対応したいなーという野望を……!
ACMEに対応できれば構築中のおうちk8sの証明書の管理がはかどりそうですし

ACMEの認証局側の実装はOSSとして公開してくれているようなので、今後は検証環境への導入を頑張るぞい

ということで、以上「社内検証環境用にプライベート認証局立ててみた」でした。
ここまで読んでいただきありがとうございました。

付録

試した証明書の組み合わせと結果を下に貼っておきます。

 ・縦が認証局の証明書のnameConstraints
 ・横がサーバー証明書のSAN
です。

10.0.12.36でアクセスした場合
test.example.comでアクセスした場合

ブログの著者欄

梅崎 皓太

GMOインターネット株式会社

ネットワークエンジニア。GMOインターネットのバックボーンから商材までのネットワーク設計・構築・運用を担当しています。 写真はマイニング事業で北欧に飛んでいたときにブリザードに遭遇した後の写真です。冷たすぎてiPhone電源落ちちゃってビックリ

採用情報

関連記事

KEYWORD

採用情報

SNS FOLLOW