trust-manager
trust-manager は、Kubernetes および OpenShift クラスターでトラストバンドルを管理する最も簡単な方法です。
主に TLS ハンドシェイク中に証明書を検証するために使用される信頼できる X.509 証明書のバンドルを調整しますが、他の状況でも使用できます。
概要
trust-manager は、クラスターでの TLS トラストバンドルの管理のオーバーヘッドを削減することを目的とした、小さな Kubernetes オペレーターです。
さまざまなソースからの入力を読み取り、結果の証明書をアプリケーションで使用できるバンドルに結合できる、Bundle
カスタム Kubernetes リソース (CRD) を追加します。
trust-manager は、信頼できる証明書を迅速かつ簡単に最新の状態に保つことを保証し、クラスター管理者が信頼ストアを更新するためにコンテナーを再構築することを心配することなく、セキュアなバンドルの提供を簡単に自動化できるようにします。
これは cert-manager を補完するように設計されており、cert-manager Issuer
または ClusterIssuer
から CA 証明書を使用する場合にうまく機能しますが、必要に応じて cert-manager とは完全に独立して使用できます。
インストール
trust-manager のインストール方法については、インストールガイドを参照してください。
使用方法
trust-manager は意図的にシンプルで、新しい Kubernetes CustomResourceDefintion
を 1 つだけ追加します。Bundle
。
Bundle
は、クラスター全体に分散する必要がある X.509 証明書のセットを表します。
すべての Bundle
は、クラスタースコープです。
Bundle
は、trust-manager が最終的なバンドルを組み立てる sources
のリストと、結果のバンドルを書き込む方法と場所を記述する target
で構成されます。
Bundle
の例は次のようになります。
apiVersion: trust.cert-manager.io/v1alpha1kind: Bundlemetadata:name: my-org.com # The bundle name will also be used for the targetspec:sources:# Include a bundle of publicly trusted certificates which can be# used to validate most TLS certificates on the internet, such as# those issued by Let's Encrypt, Google, Amazon and others.- useDefaultCAs: true# A Secret in the "trust" namespace; see "Trust Namespace" below for further details- secret:name: "my-db-tls"key: "ca.crt"# Here is another Secret source, but this time using a label selector instead of a Secret's name.- secret:selector:matchLabels:fruit: applekey: "ca.crt"# A ConfigMap in the "trust" namespace; see "Trust Namespace" below for further details- configMap:name: "my-org.net"key: "root-certs.pem"# Here is another ConfigMap source, but this time using a label selector instead of a ConfigMap's name.- configMap:selector:matchLabels:fruit: applekey: "ca.crt"# A manually specified string- inLine: |-----BEGIN CERTIFICATE-----MIIC5zCCAc+gAwIBAgIBADANBgkqhkiG9w0BAQsFADAVMRMwEQYDVQQDEwprdWJl....0V3NCaQrXoh+3xrXgX/vMdijYLUSo/YPEWmo-----END CERTIFICATE-----target:# Sync the bundle to a ConfigMap called `my-org.com` in every namespace which# has the label "linkerd.io/inject=enabled"# All ConfigMaps will include a PEM-formatted bundle, here named "root-certs.pem"# and in this case we also request binary formatted bundles in JKS and PKCS#12 formats,# here named "bundle.jks" and "bundle.p12".configMap:key: "root-certs.pem"additionalFormats:jks:key: "bundle.jks"pkcs12:key: "bundle.p12"namespaceSelector:matchLabels:linkerd.io/inject: "enabled"
Bundle
リソースは現在、いくつかのソースタイプをサポートしています。
configMap
- trust-manager 名前空間のConfigMap
リソースsecret
- trust-manager 名前空間のSecret
リソースinLine
- 少なくとも 1 つの証明書を含む手動で指定された文字列useDefaultCAs
- 通常、公開的に信頼された証明書のバンドル
ConfigMap
がデフォルトのターゲットタイプですが、v0.7.0 以降、trust-manager はターゲットとして Secret
リソースもサポートしています。
Secret
ターゲットのサポートは、trust-manager コントローラーで明示的に有効にする必要があります。「Secret ターゲットを有効にする」の下の詳細を参照してください。
ConfigMap
と Secret
の両方で、ラベルセレクターを指定して複数のリソースを一度に選択することもできます。これは、ConfigMap
または Secret
の名前が実行時にのみわかっている動的な環境で役立ちます。ソースを追加するとき、ConfigMap
または Secret
のいずれかのタイプの場合、フィールド name
と selector
は相互に排他的です。どちらか一方を必ず設定する必要があり、両方を設定することはできません。
すべてのソースおよびターゲットオプションについては、trust-manager API リファレンスドキュメントに記載されています。
ターゲット
すべての Bundle
ターゲットは、ConfigMap
(および/または Secret
) に書き込まれます。その名前は Bundle
の名前と一致し、すべてのターゲットには PEM 形式のバンドルが含まれています。
ユーザーは、JKS/PKCS#12 形式のバイナリトラストストアをターゲットに書き込むこともオプションで選択できます。JKS は v0.5.0 以降、PKCS#12 は v0.7.0 以降サポートされています。
JKS および PKCS#12 トラストストアを使用するアプリケーションでは、多くの場合、レガシーな理由でパスワードを設定する必要があります。これらのパスワードは、非常に弱い暗号化を使用するか、暗号化するファイルの隣にプレーンテキストでパスワードが提供され、パスワードを持つという目的が無効になるため、多くの場合、セキュリティの偽りです。
トラストバンドルには秘密鍵が含まれていないため、ほとんどのユースケースでは、トラストバンドルを暗号化してもセキュリティ上のメリットはありません。そのため、トラストストアのパスワードは、JKS の場合はデフォルトで changeit
に、PKCS#12 の場合は ""
(空の文字列または「パスワードなし」) に設定されています。
最近のリリースでは、バンドル YAML ファイル spec.target.additionalFormats.jks.password
および spec.target.additionalFormats.pkcs12.password
を設定することで、そのパスワードを変更できます。
古いリリースには、現在のデフォルト値がハードコードされており、変更できません。詳細については、パスワードが役に立たない理由をお読みください。
名前空間セレクター
ターゲットの namespaceSelector
は、Bundle
のターゲットを同期する名前空間を制限するために使用されます。
namespaceSelector
は、フィールド matchLabels
をサポートしています。
ラベルセレクターの設定方法については、Kubernetes ドキュメントを参照してください。
namespaceSelector
が空の場合、Bundle
のターゲットはすべての名前空間に同期されます。
⚠️ trust-manager の今後のアップデートでは、この動作が変更され、空の名前空間セレクターはデフォルトで trust-manager 名前空間のみに同期するようになります。
クイックスタート例
独自の Bundle
を作成する例から始めましょう!
まず、デモクラスターを作成します。
git clone https://github.com/cert-manager/trust-manager trust-managercd trust-managermake demo
クラスターが実行されたら、trust-manager の起動時に構成されたデフォルトの CA を使用して Bundle
を作成できます。Helm を使用して trust-manager をインストールしたため、デフォルトの CA パッケージには Debian コンテナーから派生した公開的に信頼された証明書が含まれています。
kubectl --kubeconfig ./bin/kubeconfig.yaml apply -f - <<EOFapiVersion: trust.cert-manager.io/v1alpha1kind: Bundlemetadata:name: example-bundlespec:sources:- useDefaultCAs: truetarget:configMap:key: "trust-bundle.pem"EOF
簡単でしたね。次に、すべてが正常に同期され、ConfigMap
が書き込まれていることを確認しましょう。
kubectl --kubeconfig ./bin/kubeconfig.yaml get bundle example-bundle | lesskubectl --kubeconfig ./bin/kubeconfig.yaml get configmap example-bundle -o "jsonpath={.data['trust-bundle\.pem']}" | less
素晴らしい - トラストバンドルを取得しました。コンテナーにすぐに使用できますが、もう少し進んで、Bundle
に含めるダミーの「組織 CA」を作成してみましょう。
cert-manager を使用してダミー組織証明書を生成します。
kubectl --kubeconfig ./bin/kubeconfig.yaml apply -f - <<EOFapiVersion: cert-manager.io/v1kind: ClusterIssuermetadata:name: trust-manager-selfsigned-issuerspec:selfSigned: {}---apiVersion: cert-manager.io/v1kind: Certificatemetadata:name: trust-manager-example-canamespace: cert-managerspec:isCA: truecommonName: trust-manager-example-casecretName: trust-manager-example-ca-secretprivateKey:algorithm: ECDSAsize: 256issuerRef:name: trust-manager-selfsigned-issuerkind: ClusterIssuergroup: cert-manager.ioEOF
次に、cert-managerが作成したSecret
を確認しましょう。
kubectl --kubeconfig ./bin/kubeconfig.yaml get -n cert-manager secret trust-manager-example-ca-secret -o"jsonpath={.data['tls\.crt']}" | base64 -d# tls.crt will contain a PEM certificate, starting with -----BEGIN CERTIFICATE-----
🤔 なぜ
ca.crt
ではなくtls.crt
を使ったのか疑問に思われるかもしれません。詳細は以下をご覧ください。
最後に、新しいプライベートCAを含めるようにBundle
を更新します。
kubectl --kubeconfig ./bin/kubeconfig.yaml apply -f - <<EOFapiVersion: trust.cert-manager.io/v1alpha1kind: Bundlemetadata:name: example-bundlespec:sources:- useDefaultCAs: true- secret:name: "trust-manager-example-ca-secret"key: "tls.crt"target:configMap:key: "trust-bundle.pem"EOF
これで完了です!example-bundle
ConfigMap
はすでに更新されているはずです。
ConfigMap
を再度確認すると、リストの最後に新しいダミーCAが表示されるはずです。
trust-managerインストールの安全な維持
いずれかのBundle
リソースでuseDefaultCAs
ソースを選択した場合、デフォルトのCAパッケージイメージを最新の状態に保つことが重要です。そうしないと、Debianコンテナにパブリックトラストバンドルをインストールする際にapt-get upgrade ca-certificates
を実行しないのと同等になります。
trust-managerは、デフォルトCAをサポートするtrust-managerの任意のバージョン(v0.4.0
以降)で、デフォルトCAパッケージのどのバージョンでも動作するように設計されています。アップグレードによるtrust-managerの安定性へのリスクはないはずです。
公式のcert-manager提供のDebian CAパッケージ(デフォルト)を使用している場合は、お持ちのバージョンを確認し、最新のパッケージバージョンと比較する必要があります。
このバージョンは、Helmチャートの.defaultPackageImage.tag
値で設定できます。また、このバージョンは、デフォルトCAパッケージを使用する同期されたBundleリソースのstatus
フィールドにも書き込まれます。
Helmを使用したデフォルトCAパッケージのアップグレード
trust-managerをアップグレードせずに、デフォルトのCAパッケージをタグ付きバージョンXYZ
にインプレースアップグレードしたいとしましょう。
Helmリリースが「trust-manager」と呼ばれ、cert-manager
名前空間にインストールされていると仮定します。
⚠️ このアップグレードプロセスは、他のものが実行されていないことを前提としています。このプロセス中に別のユーザーまたはプロセスがHelmの値を変更すると、作業内容が上書きされる可能性があります。
まず、現在のHelmの値をダンプして、失われないようにします。
helm get values -n cert-manager trust-manager -oyaml > values.yaml
次に、values.yaml
にdefaultPackageImage.tag
がすでに設定されている場合は、それを更新します。それ以外の場合は、追加します。利用可能なタグは、quay.io
で確認できます。
# values.yaml...defaultPackageImage:tag: XYZ
デフォルトパッケージイメージタグのこれらのバージョンは、Debianのca-certificates
パッケージのバージョンから直接派生しています。
最後に、変更を適用し、デフォルトのCAパッケージと同時にtrust-managerコントローラーも更新しないように、インストールされているtrust-managerのバージョンを手動で指定するようにしてください。
# Get the currently installed version. You could do this manually if you find that easier.TRUST_MANAGER_VER=$(helm list --filter "^trust-manager$" -n cert-manager -ojson | jq -r ".[0].app_version")# Check the version makes senseecho $TRUST_MANAGER_VER# Run the upgradehelm upgrade -f values.yaml -n cert-manager trust-manager jetstack/trust-manager --version $TRUST_MANAGER_VER
誤ったタグが使用された場合、デプロイメントは失敗し、helm rollback
を使用して作業状態に戻す必要がある可能性があります。
本番環境の準備
TLSは複雑で、TLS証明書を誤用する方法はたくさんあります。
trust-managerを本番環境で実行する前に注意すべきいくつかの潜在的な落とし穴を次に示します。
trust-managerを本番環境で実行する予定で、デフォルトのCAパッケージだけでなく、他のものも使用している場合は、このセクションを読んで理解することを強くお勧めします。後で停止を引き起こすのを防ぐことができます。
ℹ️ これらの落とし穴はtrust-managerに固有のものではなく、TLSトラストを管理する任意の方法で遭遇する可能性があります!
中間証明書のバンドル
CertbotなどのLet's Encryptクライアントを使用したことがある場合は、cert.pem
、chain.pem
、fullchain.pem
など、複数の証明書ファイルが生成されるのを見たことがあるでしょう。
これらのさまざまなファイルは、証明書とチェーンを個別に提供する必要がある可能性のあるさまざまなアプリケーションをサポートするために提供されています。ほとんどのユーザーおよびアプリケーションの場合、fullchain.pem
が唯一の正しい選択肢です。
残念ながら、これらのファイルが存在することで、fullchain.pem
が正しい場合でも、cert.pem
が正しい選択肢であると誤って想定してしまうという残念な副作用があります。これは、証明書が使用されるときに、チェーンの残りの部分が送信されないことを意味します。
多くの場合、この問題を解決するように見える簡単な修正は、クライアントがチェーンをトラストストアに追加することです。これにより、短期的に証明書エラーが修正されたように見えます。この種の「修正」が、他の人が従うことができるソリューションとしてどこかに埋め込まれることは簡単です。
この「修正」は危険です。つまり、それを含んでいるすべてのトラストストアを最初に更新しない限り、中間証明書を安全にローテーションできないことを意味します。
この場合、中間証明書は事実上ルート証明書になり、中間証明書を持つことの目的を完全に無効にします。
含める必要があると確信している場合を除き、可能な限り、トラストストアで中間証明書を使用することは避けてください。それがOKになる可能性のある例としてはクロス署名がありますが、一般的なケースでは必要になる可能性は低いです。
中間証明書を信頼するのではなく、ルート証明書だけを新しいConfigMap
にコピーし、それをソースとして使用する方が良いでしょう。
cert-managerとの統合:ca.crt
とtls.crt
trust-managerにcert-manager発行の証明書を含むSecret
を指定している場合、ca.crt
とtls.crt
の2つの関連フィールドが表示されます。(tls.key
は無視します。trust-managerはそれにアクセスする必要はありません)
これにより、明らかな疑問が生じます。ca.crt
とtls.crt
のうち、trust-managerで使用すべきなのはどちらですか?
残念ながら、どちらのフィールドを使用するのが正しいかは一般的なケースでは言うことができませんが、ガイドラインを提供できます。
tls.crt
には、一般的に複数の証明書が含まれており、そのすべてが発行者ではない可能性があり、中間証明書である可能性が高いものもあります。そうである場合、tls.crt
をソースとして使用しないでください。(詳細については、上記の「中間証明書のバンドル」を参照してください。)
したがって、ca.crt
の方が一般的に正しい選択のように思えるかもしれませんが、ca.crt
の内容は、ベストエフォートベースでのみ設定できることを念頭に置いておくことが重要です。ca.crt
の内容は、Issuer
が正しく設定されているかどうかに依存しており、一部の発行者タイプでは、このフィールドに有用または正しいエントリを提供できない場合があります。
原則として、Bundles
はルート証明書のみを使用して作成することを推奨します(上記参照)。そのため、単一のルート証明書を持つフィールドのみを使用してください。cert-managerが発行した証明書に直接依存することを避けるべき理由については、以下をお読みください。
cert-managerとの統合:意図的にCA証明書をコピーする
Kubernetesの世界では、インフラストラクチャの自動化を難しくするようなステップを意図的に追加することは非常に奇妙に聞こえるかもしれませんが、TLSトラストストアの場合には賢明な選択となることがあります。
例えば、ca.crt
の中に信頼したいルート証明書を持つcert-managerのIssuer
があるとします。Secret
を直接使用してca.crt
を指定したくなりますが、ベストプラクティスとしては、そのルートを別のConfigMap
(またはSecret
)にコピーすることです。
その理由は、多くのTLSの落とし穴と同様に、証明書のローテーションにあります。新しいルート証明書から発行されるようにIssuerをローテーションした場合、trust-managerはSecret
が更新されたことを検出し、自動的にトラストバンドルを更新して新しいルートを含めます。これにより、古いルートはすぐに信頼されなくなります。
つまり、古いルートによって発行された証明書をまだ使用しているサービスがある場合、それらは信頼されなくなり、壊れてしまいます。
ローテーションでは、両方のルート証明書が一定期間同時に信頼されるか、または発行されたすべての証明書が古いルートと同時に、またはそれよりも前にローテーションされる必要があります。
既知の問題
kubectl describe
useDefaultCAs
オプションは、kubectl describe
内でコーナーケースに当たり、Use Default C As: true
と表示されます。これは純粋に見た目の問題です。