新着: プロジェクトの更新情報をTwitterMastodonで入手してください。

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/v1alpha1
kind: Bundle
metadata:
name: my-org.com # The bundle name will also be used for the target
spec:
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: apple
key: "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: apple
key: "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 ターゲットを有効にする」の下の詳細を参照してください。

ConfigMapSecret の両方で、ラベルセレクターを指定して複数のリソースを一度に選択することもできます。これは、ConfigMap または Secret の名前が実行時にのみわかっている動的な環境で役立ちます。ソースを追加するとき、ConfigMap または Secret のいずれかのタイプの場合、フィールド nameselector は相互に排他的です。どちらか一方を必ず設定する必要があり、両方を設定することはできません。

すべてのソースおよびターゲットオプションについては、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-manager
cd trust-manager
make demo

クラスターが実行されたら、trust-manager の起動時に構成されたデフォルトの CA を使用して Bundle を作成できます。Helm を使用して trust-manager をインストールしたため、デフォルトの CA パッケージには Debian コンテナーから派生した公開的に信頼された証明書が含まれています。

kubectl --kubeconfig ./bin/kubeconfig.yaml apply -f - <<EOF
apiVersion: trust.cert-manager.io/v1alpha1
kind: Bundle
metadata:
name: example-bundle
spec:
sources:
- useDefaultCAs: true
target:
configMap:
key: "trust-bundle.pem"
EOF

簡単でしたね。次に、すべてが正常に同期され、ConfigMap が書き込まれていることを確認しましょう。

kubectl --kubeconfig ./bin/kubeconfig.yaml get bundle example-bundle | less
kubectl --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 - <<EOF
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: trust-manager-selfsigned-issuer
spec:
selfSigned: {}
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: trust-manager-example-ca
namespace: cert-manager
spec:
isCA: true
commonName: trust-manager-example-ca
secretName: trust-manager-example-ca-secret
privateKey:
algorithm: ECDSA
size: 256
issuerRef:
name: trust-manager-selfsigned-issuer
kind: ClusterIssuer
group: cert-manager.io
EOF

次に、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 - <<EOF
apiVersion: trust.cert-manager.io/v1alpha1
kind: Bundle
metadata:
name: example-bundle
spec:
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.yamldefaultPackageImage.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 sense
echo $TRUST_MANAGER_VER
# Run the upgrade
helm 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.pemchain.pemfullchain.pemなど、複数の証明書ファイルが生成されるのを見たことがあるでしょう。

これらのさまざまなファイルは、証明書とチェーンを個別に提供する必要がある可能性のあるさまざまなアプリケーションをサポートするために提供されています。ほとんどのユーザーおよびアプリケーションの場合、fullchain.pemが唯一の正しい選択肢です。

残念ながら、これらのファイルが存在することで、fullchain.pemが正しい場合でも、cert.pemが正しい選択肢であると誤って想定してしまうという残念な副作用があります。これは、証明書が使用されるときに、チェーンの残りの部分が送信されないことを意味します。

多くの場合、この問題を解決するように見える簡単な修正は、クライアントがチェーンをトラストストアに追加することです。これにより、短期的に証明書エラーが修正されたように見えます。この種の「修正」が、他の人が従うことができるソリューションとしてどこかに埋め込まれることは簡単です。

この「修正」は危険です。つまり、それを含んでいるすべてのトラストストアを最初に更新しない限り、中間証明書を安全にローテーションできないことを意味します。

この場合、中間証明書は事実上ルート証明書になり、中間証明書を持つことの目的を完全に無効にします。

含める必要があると確信している場合を除き、可能な限り、トラストストアで中間証明書を使用することは避けてください。それがOKになる可能性のある例としてはクロス署名がありますが、一般的なケースでは必要になる可能性は低いです。

中間証明書を信頼するのではなく、ルート証明書だけを新しいConfigMapにコピーし、それをソースとして使用する方が良いでしょう。

cert-managerとの統合:ca.crttls.crt

trust-managerにcert-manager発行の証明書を含むSecretを指定している場合、ca.crttls.crtの2つの関連フィールドが表示されます。(tls.keyは無視します。trust-managerはそれにアクセスする必要はありません)

これにより、明らかな疑問が生じます。ca.crttls.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と表示されます。これは純粋に見た目の問題です。