プライベート CA をツールごとに信頼させる
目標
プライベートのルートCAとサーバー証明書を作成して、社内の名前でHTTPSを起動し、OS・Python・Nodeのトラストストアに、それぞれ入れます。どのツールがどのストアを見るのかを、直接測って表に残し、中間CAのチェーンまで、サーバーが送るようにします。
なぜ重要なのか
社内ミラーがHTTPSになった瞬間から、すべてのビルドツールが、そのCAを信頼する必要があります。ところが、トラストストアは、OS、certifi、Nodeの組み込み一覧、JVMに分かれているので、1か所にだけ入れると、一部のツールだけが動きます。動かないツールで、検証をオフにすることが、最もよくある事故です。このラボのPodは、外部に届きません(DNSだけ)。プライベートCAは、エアギャップ環境の中の話だからです。
ステップ
/root/pki/ca.key・/root/pki/ca.crtで、CNがAirgap Internal Root CAのルートCAを作成してください(CA:TRUE、keyCertSign)。/root/pki/server.key・/root/pki/server.crtで、repo.airgap.internalのサーバー証明書を、そのCAで発行してください(SANにDNS名、用途はserverAuth、CAではない)。署名するときに渡した拡張は、/root/pki/server.extとして残します。/etc/hostsに127.0.0.1 repo.airgap.internalを追加して、/root/pki/www/hello.txt(内容はhello-airgap)を、https://repo.airgap.internal:8443/として公開するサーバーを、/root/pki/serve.pyで起動してください。- ルートCAを、OSのストアに、
airgap-root.crtという名前で入れて、更新してください。curlが、--cacertなしで通る必要があります。 /etc/profile.d/airgap-ca.shに、Pythonのrequestsとhttpxが、OSのバンドルを使うようにする環境変数を書いてください。- 同じファイルに、Nodeがプライベートのルートを信頼するようにする環境変数を追加してください。
- 環境変数なしで(OSのストアだけで)、各ツールが通るかを測って、
/root/pki/trust-matrix.txtに、curl・urllib・requests・httpx・node・goの6行を、도구=osまたは도구=envの形式で書いてください(プレースホルダーはツール名です)。 - 中間CA(
/root/pki/int.crt、pathlen 0)を、ルートで発行して、その中間CAで、mirror.airgap.internalの証明書(/root/pki/mirror.crt)を発行してください。/etc/hostsに名前を追加して、リーフと中間をつなげた/root/pki/mirror-chain.crtで、9443ポートに同じサーバーを起動してください。
参考
- 証明書の中身を見る:
openssl x509 -in <파일> -noout -text・名前の確認:openssl x509 -in <파일> -noout -checkhost <이름>(プレースホルダーは、ファイルと名前です) - サーバーが送るチェーン:
openssl s_client -connect 127.0.0.1:8443 -servername <이름> -showcerts </dev/null(プレースホルダーは名前です) - ログインシェルで、profile.dの適用を確認する:
env -i bash -lc 'echo $REQUESTS_CA_BUNDLE' - よくある間違い1: CNだけに名前を入れること。最近のツールは、SANだけを見ます。
- よくある間違い2:
.pem拡張子で入れること。update-ca-certificatesは、.crtだけを読みます。 - よくある間違い3: 動かないツールで、検証をオフにすること(
verify=False、NODE_TLS_REJECT_UNAUTHORIZED=0)。このラボの採点は、検証をオンにしたままでだけ測ります。
プライベートのルートCAを作成する
/root/pki/ca.key・/root/pki/ca.crtで、CN Airgap Internal Root CAのルートCAを作成してください(CA:TRUE、keyCertSign)。
openssl reqに-x509を指定すると、リクエストの代わりに、自己署名証明書がすぐにできます。拡張は-addextで指定でき、ルートCAに必要なのは、基本制約(CA)と、キーの用途(証明書の署名)です。
SANが入ったサーバー証明書を発行する
/root/pki/server.key・/root/pki/server.crtで、repo.airgap.internalのサーバー証明書を、ルートCAで発行してください(SAN DNS、serverAuth、CAではない)。署名するときに渡した拡張は、/root/pki/server.extとして残します。
キーと署名リクエスト(CSR)を作成したあと、CAのキーで署名するときに、拡張ファイルで、subjectAltNameとextendedKeyUsageを指定します。openssl x509 -reqは、リクエストの拡張を、デフォルトでは引き継がないという点に注意してください。
社内の名前でHTTPSを起動する
/etc/hostsに127.0.0.1 repo.airgap.internalを追加して、/root/pki/www/hello.txt(hello-airgap)を、https://repo.airgap.internal:8443/として公開するサーバーを、/root/pki/serve.pyで起動してください。
Pythonの標準ライブラリのhttp.serverを、sslコンテキストで包むと、HTTPSサーバーになります。証明書ファイルとキーファイルを、load_cert_chainに渡します。バックグラウンドで起動して、確認は、--cacertでルートを指定したcurlで行ってください。
OSのストアに入れる
ルートCAを、OSのストアにairgap-root.crtとして入れて、更新してください。curlが、--cacertなしで通る必要があります。
Debian系は、決まったディレクトリに.crt拡張子で置いて、更新コマンドを実行すれば、1つのファイルにつなげたバンドルが、作り直されます。更新コマンドの出力に、いくつ追加されたかが出ます。
Pythonのrequests・httpxに信頼させる
/etc/profile.d/airgap-ca.shに、requestsとhttpxが、OSのバンドルを使うようにする環境変数を書いてください。
requestsとhttpxは、OSのストアではなく、certifiのバンドルを使います。2つのライブラリが従う環境変数は、それぞれ違います。読み物の表を見てください。値は、OSが作成したバンドルファイルです。
Nodeに信頼させる
/etc/profile.d/airgap-ca.shに、Nodeがプライベートのルートを信頼するようにする環境変数を追加してください。
Nodeは、ビルドに組み込まれた一覧を使い、その一覧に証明書を追加する環境変数があります。プロセスが起動するときに一度だけ読むので、新しいシェルで確認してください。
ツールごとの信頼の表を測る
環境変数なしで、各ツールが通るかを測って、/root/pki/trust-matrix.txtに、curl・urllib・requests・httpx・node・goを、도구=osまたは도구=envの形式で書いてください(プレースホルダーはツール名です)。
env -iで環境を空にすれば、profile.dの変数もありません。その状態で通れば、OSのストアだけでできるもの(os)、失敗すれば、環境変数が必要なもの(env)です。Goは、小さなプログラムをgo runで動かしてみてください。
中間CAとチェーンを送る
中間CA/root/pki/int.crt(pathlen 0)と、そのCAが発行したmirror.airgap.internalの証明書/root/pki/mirror.crtを作成して、リーフと中間をつなげた/root/pki/mirror-chain.crtで、9443ポートにサーバーを起動してください。
中間CAもCAなので、基本制約が必要で、pathlenで、その下にCAを置けないようにします。サーバーの証明書ファイルには、リーフ証明書の後ろに、中間CAの証明書をつなげます。クライアントは、ルートだけを知っています。