Trusting certificates
If you're using private CAs, clients need to know the CA to be able to connect to servers.
With cert-manager, ca.crt will likely contain the certificate you need to trust,
but do not mount the same Secret as the server to access ca.crt.
This is because:
- That
Secretalso contains the private key of the server, which should only be accessible to the server. You should use RBAC to ensure that theSecretcontaining the serving certificate and private key are only accessible to Pods that need it. - Rotating CA certificates safely relies on being able to have both the old and new CA certificates trusted at the same time. See the FAQ for more details.
When configuring the client you should independently choose and fetch the CA certificates that you want to trust.
Download the CA out of band and store it in a Secret or ConfigMap separate from the Secret containing the server's private key and certificate.
trust-manager can be used to manage these certificates and automatically distribute them to multiple namespaces.
This ensures that if the material in the Secret containing the server key and certificate is tampered with,
the client will fail to connect to the compromised server.
The same concept also applies when configuring a server for mutually-authenticated TLS; don't give the server access to Secret containing the client certificate and private key.
My application expects ca.crt in the same Secret as the certificate
Many Helm charts and operators assume a kubernetes.io/tls Secret with three keys:
tls.crt, tls.key and ca.crt. cert-manager only writes ca.crt when the issuer
reports a CA. The ACME issuer never does, so that Secret has no ca.crt, and the
application refuses to start or the volume mount fails with an error like
references non-existent secret key: ca.crt.
The question to answer first is what the application does with ca.crt. For most
applications it is a trust store: the set of CAs allowed to sign client or peer
certificates. For example, OpenSearch nodes use it to authenticate other nodes and
admin clients, and Redis uses tls-ca-cert-file to
authenticate clients and replicas.
A trust store must contain only CAs you control. If the certificate was issued by a
public CA such as Let's Encrypt, the issuing CA must not be used as a trust store,
because anyone with a certificate from that CA could then authenticate to your
application.
So the CA you want in ca.crt is a decision you make, and its lifecycle is separate
from the leaf certificate. Distribute it with trust-manager and connect the two
Secrets in one of these ways.
Give the application a separate CA Secret
Check whether the application accepts the CA separately. Many of the applications users report do:
- The OpenSearch operator has
caSecret.namenext tosecret.name. - The RabbitMQ cluster operator has
tls.caSecretNamenext totls.secretName. - Istio Gateway reads the CA for mutual TLS from a Secret named
<credentialName>-cacert. - ingress-nginx reads the client CA from the Secret named in the
auth-tls-secretannotation. - Gateway API
BackendTLSPolicyreads CAs from a ConfigMap.
Point those fields at a trust-manager Bundle target. Bundle targets are
ConfigMaps by default; enable Secret targets in trust-manager if the application
only accepts a Secret. See Targets.
Merge the two into one volume
If you write the Pod spec yourself, a
projected volume
presents the cert-manager Secret and the trust-manager ConfigMap as one directory.
The application sees tls.crt, tls.key and ca.crt side by side, and each file is
updated independently by its own controller:
volumes:- name: tlsprojected:sources:- secret:name: my-app-tls # Certificate.spec.secretNameitems:- key: tls.crtpath: tls.crt- key: tls.keypath: tls.key- configMap:name: my-org-roots # trust-manager Bundle nameitems:- key: trust-bundle.pem # Bundle.spec.target.configMap.keypath: ca.crt
Do not write ca.crt into the cert-manager Secret
It is tempting to have a second controller, a mutating webhook or a Job add
ca.crt to the Secret that cert-manager manages. Do not do this. cert-manager
reconciles that Secret and reads it back to decide whether to reissue. For example,
if keystores are enabled, cert-manager treats the presence of ca.crt as proof that
the issuer provided a CA and expects a matching trust store. cert-manager builds
trust stores from the CA returned by the issuer, not from the Secret, and an ACME
issuer returns none. The trust store is never written, so the check fails and
cert-manager reissues on every reconcile.
If the application accepts only a single Secret with all three keys and offers no separate CA option, ask the project to add one, and link to this page. In the meantime, build a derived Secret with a tool such as secret-transform rather than editing the cert-manager Secret.