今年のアドベントカレンダー二度めまして、m-ishizukaです。
突然のnginx + pkcs11
VM運用とかしている最中にふとsshの秘密鍵をHyperVisor上のTPMに置いたらVM側が乗っ取られても秘密鍵が漏洩しないのではないか?とか思い立ち、だったら折角なのでTLS証明書もTPMに置いてみたくなりました。 なぜか?と聞かれてもわかりません。天啓でしょうか?
Debian12(bookworm)がリリースされたので、折角なので確認してみましょう。
opensshで利用する方法は以前の記事でもほぼ紹介してしまったので今回は割愛するものとする。(面白みもないので)
目標
以前の記事にもある通り、TPM2はpkcs11経由で扱うことが可能です。 つまり、nginxのTLS証明書を扱う手はずとしては、pkcs11経由で秘密鍵を扱えればそのままTPM2で扱えるということになる。 というわけで、今回の目標としてはnginxでsofthsmの秘密鍵をpkcs11経由で扱うということをゴールに据えました。
環境
ダウンロードはこちらから。sample.tar
とりあえずdockerが動く環境。phase2だけでもよかったが、デバッグとかは圧倒的にphase1環境が便利のため、環境用に残しておく。 なお、Phase1,2の俺俺CA作成時のために秘密鍵を利用している結果、keyIDを03に固定しているが、もちろん実際には別のIDだったりすると思われる。
phase1
検証というか動作確認が目的であったため、nginxのコンテナにsofthsmやら諸々一式が入っているall in one containerになっております。
phase2
色気を出して、以下を実施した。より実際の環境に合わせた変更というべきでしょか。
- nginxコンテナはあくまでnginx用
- softhsmの初期化や証明書は別コンテナに作成させればnginxコンテナはきれいにして無駄を省く
- ついでにcertbotを見据えたスクリプトを追加してみた(未検証)
- RSA -> ECDSA Prime 256v1にしてみた
- HSM等の仕様に合わせてご利用くだしあ。
手順(phase1)
nginx
+ pkcs11
でググると参考文献にあるようにそこそこヒットする。これらと愛と勇気と直観を信じて実施してみました。
解説1-1(shsm周り)
Dockerfileを眺めてわかる通り、docker buildのタイミングでsofthsmの初期化・証明書の生成を実施している。 ECDSAが個人的には好みだが、トラブルを避けるためとりあえずRSAで実施。
解説1-2(nginx / openssl周り)
- nginx.conf: nginxのルートディレクティブでssl_engine pkcs11を入れないといけない。
- 起動オプションで入れる手段もあるが、とりあえずはnginx.confを書き換えて上書きしている。
- tls.conf: pkcs11のURIをふぉい。
- openssl.cnf: dynamic_pathとlibsofthsm2をmodule pathに追記。
手順(phase2)
- nginx.confを修正しないで、起動オプションでssl_engineを食わせる
- nginxコンテナから不要なパッケージを除去
解説2-1(shsm周り)
- /var/lib/softhsmを別コンテナ(shsm)で操作
- pkcs11-tool等やsofthsm2-util等はnginxコンテナから除去
- certbotで動かせるようなサンプルスクリプトを設置。動いたららっきー。
解説2-2(nginx / openssl周り)
- nginx.conf: nginxのルートディレクティブでssl_engine pkcs11を入れないといけない。
- 起動オプションで入れる手段をとってみたが、ほかにも設定を変えるケースの場合、confファイルを上書きのほうがきれいになりそう。
- tls.conf: pkcs11のURIをふぉい。
- openssl.cnf: dynamic_pathとlibsofthsm2をmodule pathに追記。
動かし方。
docker-composeと記載しているが、新しい環境ならdocker composeのほうが良いかも。 まぁ環境に合わせてください。
- phase1: docker-compose up
- phase2:
- (事前に) mkdir -p softhsm; docker-compose run shsm /root/shsm-self-ca.sh
- docker-compose up nginx
確認方法
実際に実施しただけでは本当にTLS通信ができることの確認ができない。 このため、opensslにて確認してみる。Phase1では中間CAやRootCAの証明書を持ってくるのが一手間かかるため、確認はPhase2が良さげ。
$ openssl s_client -tls1_3 -connect localhost:8443 -CAfile <(cat softhsm/root.crt softhsm/inter.crt) < /dev/null | grep Verify
Verify return code: 0 (ok)
続いてcurl
$ curl --resolve leaf:8443:127.0.0.1 --cacert <(cat softhsm/root.crt softhsm/inter.crt) https://leaf:8443
こんな感じでhost側から叩いてエラーが無ければ良さげ。
結び
nginxでpkcs11を経由したTLS証明書を使えるようになった。あわせて検証の方法も提示できた。 これを橋掛かりにほかのpkcs11デバイスをnginxから利用する一助になれば幸いです。