Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥

Kubernetes でもJava アプリでTLS 接続を終端したい

Kubernetes でもJava アプリでTLS 接続を終端したい

2024年6月16日 (日) に開催されたJJUG CCC Spring 2024ランチセッション(6月16日 (日) 12:00 - 12:45)の発表資料です。

Cybozu

June 16, 2024
Tweet

More Decks by Cybozu

Other Decks in Programming

Transcript

  1. Kubernetes でも Java アプリで TLS 接続を終端したい JJUG CCC 2024 Spring

    #jjug_ccc_b 2024年6⽉16⽇ サイボウズ株式会社 齋藤 耕平 1
  2. はじめまして︕ ▌齋藤 耕平 (さいとう こうへい) l サイボウズ株式会社 l 2022年中途⼊社 l

    バックエンドエンジニア l グローバル向け AWS 版 kintone の開発/運⽤ ▌GitHub: github.com/sightseeker ▌Xアカウント: @SightSeekerTw 2 Tw は Twitter の 識別⽤につけてた名残り
  3. プライベートネットワークのプライベートなドメインの認証局は︖ ▌オレオレ認証局 (独⾃に発⾏したキーペアを利⽤) ▌Private PKI サービスを利⽤ l DigiCert Private PKI

    l Venafi Zero Touch PKI l HashiCorp Vault ▌クラウドベンダー提供のプライベートCAサービス l AWS Private Certificate Authority l Microsoft Active Directory Certificate Service l Google Cloud Certificate Authority Service 26
  4. cert-manager とは ▌Cloud Native Computing Foundation のプロジェクトの⼀つ ▌Kubernetesクラスタ内でアプリケーション⽤の TLS 証明書の発⾏と管理を⾃動化するためのツール

    (K8s の カスタムコントローラ/CRD で構成される) ▌証明書は Secret リソースとして発⾏され、 Pod にマウントして利⽤できる 28
  5. Issuer とは ▌意味: 〔株式や本などの〕発⾏⼈[者・社] (英辞郎 on the WEB より) ▌

    証明書を発⾏するための設定を定義する cert-manager のカスタムリソース ▌ 証明書を発⾏するための具体的な⽅法や 認証局(CA)の情報が含まれている ▌ ただの Issuer はデプロイされてる Namespace 内でのみ利⽤可能 ▌ ClusterIssuer は Namespace 横断で使える Issuer 29 証明書を発⾏するよ︕ Issuer さん
  6. Issuer には多様な選択肢がある ▌⾃前で⽤意した CA ⽤のキーペアを利⽤したものや、 各種ベンダー提供の PKI, プライベート CA サービスを

    Issuer として設定可能 ※各種PKI, プライベートCAサービス を Issuer として利⽤するには 別途対応するカスタムコントローラ, CRD をデプロイが必要な場合がある 30
  7. TLS証明書を利⽤するまでの流れ ▌(cert-manager を Kubernetes にデプロイ)今回は説明を省略 ▌Issuer リソースを作成 ▌Issuer に証明書を発⾏してもらう l

    Certificate リソースを作成 l Issuer が Certificate リソースに対応する証明書を発⾏ (証明書の Secret が作成される) ▌発⾏された証明書の Secret を アプリのコンテナの所定のパスにマウントして利⽤する 31
  8. Issuer を作成する (1/2) ▌ openssl コマンドで CA ⽤のキーペアを作成 (Private PKI

    サービスなどを使わない場合) 33 # CA のプライベートキーの作成 (RSA 2048 bit) openssl genrsa -out ca.key 2048 # プライベートキーを使って CA の証明書の発⾏ openssl req -x509 -new -nodes -key ca.key ¥ -subj "/CN=sandbox" -days 3650 -out ca.crt ▌ ⽣成したキーペアの Secret を作成 # キーペアの Secret を作成 kubectl create secret tls ca-key-pair --cert=ca.crt --key=ca.key
  9. Issuer に証明書を発⾏してもらう 34 ▌ Certificate リソースのマニフェストを作成 ▌ Keystore のパスワードの Secret

    を作成する kubectl create secret generic keystore-password ¥ --from-literal=password=changeit apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: demo-cert spec: secretName: sandbox-cert-tls duration: 24h renewBefore: 12h subject: organizations: - My Organization commonName: demo dnsNames: - demo.sandbox.svc.cluster.local keystores: jks: create: true passwordSecretRef: name: keystore-password key: password issuerRef: name: sandbox-issuer kind: Issuer group: cert-manager.io kubectl apply -f demo-cert.yaml demo-cert.yaml 有効期間, 再発⾏時期 誰のものか TLS接続を終端するドメイン名 証明書のフォーマット (ここではJKSのみ定義) ※PKCS12については省略してます 発⾏者 (Issuer) の情報
  10. ⽣成された証明書の Secret の中⾝ 35 apiVersion: v1 type: kubernetes.io/tls kind: Secret

    metadata: annotations: cert-manager.io/alt-names: demo.sandbox.svc.cluster.local cert-manager.io/certificate-name: demo-cert cert-manager.io/common-name: demo cert-manager.io/ip-sans: "" cert-manager.io/issuer-group: cert-manager.io cert-manager.io/issuer-kind: Issuer cert-manager.io/issuer-name: sandbox-issuer cert-manager.io/subject-organizations: My Organization cert-manager.io/uri-sans: "" labels: controller.cert-manager.io/fao: "true" name: demo-cert-tls namespace: sandbox data: ca.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tL (省略) keystore.jks: /u3+7QAAAAIAAAACAAAAAQAL (省略) tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS (省略) tls.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSB (省略) truststore.jks: /u3+7QAAAAIAAAABAAAAAgACY2EA (省略) truststore.p12: MIIEFgIBAzCCA+IGCSqGSIb3DQEH (省略) Certificate や Issuer に関する注釈 PEM形式の TLS 証明書キーペア (tls.crt, tls.key) PEM形式の Issuer の CA 証明書 (ca.crt) JKS形式の TLS 証明書 (keytstore.jks) JKS形式の Issuer の CA 証明書 (truststore.jks) PKCS12形式の TLS 証明書 (keystore.p12) PKCS12形式の Issuer のCA 証明書 (truststore.p12) ※Base64 Encoded
  11. 証明書の中⾝の確認 36 Subject: O=My Organization CN=demo Subject Alt Name: DNS:demo.sandbox.svc.cluster.local

    Issuer: CN=sandbox Infoaccess: undefined Validfrom: Jun 10 07:30:24 2024 GMT Validto: Jun 11 07:30:24 2024 GMT Fingerprint: 4D:BA:29:C2:9D:91:47:A7:7E:24:43:90:B0:F8:F0:9A:94:7B:56:84 Fingerprint256: A1:9F:94:E7:1A:8B:C7:9A:DF:A7:A1:C6:C9:03:E8:81:6D:E6:EF:72:6D:0D:7A:BC: 0D:02:2B:46:1A:1D:17:7B Keyusage: undefined Serialnumber: d14d6498d92f7e63ef57724579212a7a 有効期間 (Validfrom, Validto) 誰のものか (Subject) TLS接続を終端するドメイン名 (Subject Alt Name) 発⾏者 (Issuer) の情報 発⾏された証明書の識別情報 Fingerprint(256), Serialnumber
  12. Spring Boot でのマウントされた証明書の設定例 (JKS形式) 38 # サーバ証明書が⼊ってる keystore ファイルのパス spring.ssl.bundle.jks.demo.keystore.location=/path/to/cert/keystore.jks

    # keystore のパスワード spring.ssl.bundle.jks.demo.keystore.password=changeit # keystore の種別 (JKS) spring.ssl.bundle.jks.demo.keystore.type=JKS # keystore 内の証明書のエイリアス spring.ssl.bundle.jks.demo.key.alias=certificate # 使⽤する SSL Bundle の名前 server.ssl.bundle=demo # LISTEN ポート server.port=8443 application.properties
  13. Spring Boot でのマウントされた証明書の設定例 (PKCS12形式) 39 # サーバ証明書が⼊ってる PKCS12 形式のファイルのパス spring.ssl.bundle.jks.demo.keystore.location=/path/to/cert/keystore.p12

    # keystore のパスワード spring.ssl.bundle.jks.demo.keystore.password=changeit # keystore の種別 (PKCS12) spring.ssl.bundle.jks.demo.keystore.type=PKCS12 # keystore 内の証明書のエイリアス spring.ssl.bundle.jks.demo.key.alias=1 # 使⽤する SSL Bundle の名前 server.ssl.bundle=demo # LISTEN ポート server.port=8443 application.properties 最近は Java 界隈も JKS から PKCS12 に移⾏しつつあるよね︕
  14. Spring Boot でのマウントされた証明書の設定例 (PEM形式) 40 # 証明書のパス spring.ssl.bundle.pem.demo.keystore.certificate=/path/to/cert/tls.crt # 証明書の秘密鍵のパス

    spring.ssl.bundle.pem.demo.keystore.private-key=/path/to/cert/tls.key # 使⽤する SSL Bundle の名前 server.ssl.bundle=demo # LISTEN ポート server.port=8443 application.properties Tomcat と Netty 限定︕
  15. 42

  16. PEM や PKCS12 などアプリやコンテナによって必要な形式が違うし 何よりも様々なアプリのコンテナ内の 信頼するCA証明書が統⼀されてることを保証しにくい Issuer の CA証明書 がインポートされている必要があるけれど

    ▌コンテナイメージをビルドするときに証明書をインストールする ▌initContainer で証明書をインストールして emptyDir 経由でアプリ のコンテナに共有する 45 課題
  17. 信頼するCA証明書は ConfigMap や Secret で配布して 各Podがマウントすればよいのでは︖ 46 それでも PEM と

    PKCS12 と JKS それぞれの形式で中⾝が同じ信頼するCA証明書 を同じものを作るのツラい
  18. 信頼するCA証明書の設定 (Bundle) 49 apiVersion: trust.cert-manager.io/v1alpha1 kind: Bundle metadata: name: mybundle

    spec: sources: - useDefaultCAs: true - secret: name: ca-cert-secret key: ca.crt target: configMap: key: "ca-certificates.crt" additionalFormats: jks: key: "bundle.jks" pkcs12: key: "bundle.p12" namespaceSelector: matchLabels: sandbox: "true" パブリックな CA のルート証明書など useDefaultCAs -> Debian コンテナにデフォで⼊ってるやつ Issuer の CA 証明書 の Secret (Secret 以外にも In-line で PEMの内容を書いたりもできる) 発⾏する証明書のフォーマット (configMap, additinalFormats) ※keystoreのパスワード設定は省略してます 信頼するCA証明書のConfigMapを配布先 Namespace の指定 合成した CA 証明書ファイルを
  19. こんな ConfigMap を作ってくれる 50 apiVersion: v1 binaryData: bundle.jks: /u3+7QAAAAIAAACCAAAAAgCkMDJlZDBlYjJ8Y249ZW50cnVzd (省略)

    bundle.p12: MIMCtEUCAQMwgwK0PQYJKoZIhvcNAQcBoIMCtC0EgwK0KDCD (省略) data: ca-certificates.crt: | -----BEGIN CERTIFICATE----- MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UE (省略) -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIFgzCCA2ugAwIBAgIPXZONMGc2yAYdGsdUhGkHMA0GCSqGSIb3DQEBCwUAMDsx (省略) ----END CERTIFICATE----- . . . kind: ConfigMap metadata: annotations: trust.cert-manager.io/hash: (省略) labels: trust.cert-manager.io/bundle: mybundle name: mybundle JKS 形式の CA 証明書 PKCS12 形式の CA 証明書 PEM 形式の CA 証明書 信頼できる CA 証明書が 各フォーマットですべて⼊っていて 中⾝が揃っている
  20. あとは Pod にマウントして使うだけ ▌PEM形式は適切なパスにマウントするのが良さそう (Linux はディストリビューションによってパスが異なる) 52 /etc/ssl/certs/ca-certificates.crt // Debian/Ubuntu/Gentoo

    etc. /etc/pki/tls/certs/ca-bundle.crt // Fedora/RHEL 6 /etc/ssl/ca-bundle.pem // OpenSUSE /etc/pki/tls/cacert.pem // OpenELEC /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem // CentOS/RHEL 7 /etc/ssl/cert.pem // Alpine Linux ▌Java で使いたい場合は適当なところに置いてオプションで指定が良さそう︖ java –Djavax.net.ssl.trustStore=/app/resources/bundle.p12 ¥ -Djavax.net.ssl.trustStorePassword=changeit ¥ -Djavax.net.ssl.trustStoreType=PKCS12 -jar /app/app.jar
  21. cert-manager / trust-manager 最⾼︕ ▌証明書を作るのに、クセ強めな openssl コマンドを駆使しなくていい ▌証明書の発⾏に必要な定義ファイル (Certificate のマニフェスト)

    は YAML で可読性が⾼い︕ ▌プライベートでも TLS 証明書, CA 証明書の発⾏、管理、利⽤が楽︕ ▌PEM だけでなく Java でも使える JKS, PKCS12 形式もサポート︕ 54 Kubernetes でも Java アプリで TLS 接続を終端するのは難しくないよね︕
  22. 紹介しきれなかった話 ▌ cert-manager / trust-manager のインストール⽅法 (helmで簡単に⼊れられるよ︕) ▌ 証明書のローテーション l

    cert-manager は Certificate に有効期間を定義できて、 ⾃動的に対応する証明書の Secret をローテーションしてくれる l Secret の値が更新されても、Pod にマウントされてる証明書は更新されないので、Pod の置き換えが必要 l Secret や ConfigMap の更新をトリガーに rollout restart してくれるコントローラ “wave-k8s/wave” とかを⼊れておくのが良さそう (Deployment や StatefulSet, DaemonSet リソースの annotation で対象を識別する) ▌ 1枚の TLS 証明書 の Secret を複数の replicas Pod で使い回すのどうなの︖ っていう気持ちにも対応できる 公式 CSI Driver もあるよ ▌ Issuer (CA) の階層化, 証明書発⾏の承認プロセス, 証明書のRBAC ▌ Replicas Pod 間の通信で IP SANs を使いたいとき 56