TLS certificate management with cert-manager

When you deploy MinIO Key Management Service (MinKMS) on Kubernetes with the MinKMS Operator, the Operator generates the MinKMS TLS certificate automatically using the Kubernetes Certificate Signing Request (CSR) API: it submits a CSR with the kubernetes.io/kubelet-serving signer, which the Kubernetes API server signs using the cluster’s kubelet CA. (On OpenShift the Operator instead uses the cluster’s service CA; on Amazon EKS it uses the beta.eks.amazonaws.com/app-serving signer.) As an alternative, you can use cert-manager to provision and manage those certificates. cert-manager obtains valid certificates from an Issuer or ClusterIssuer and can automatically renew them prior to expiration.

A ClusterIssuer issues certificates for multiple namespaces. An Issuer only mints certificates for its own namespace.

The following instructions implement a self-signed ClusterIssuer. You can also use any other issuer supported by cert-manager. With other issuers, you must provide that issuer’s CA certificate to every client that connects to MinKMS (for example, AIStor Object Store), instead of the CA used in this guide.

Summary

You complete the following steps to manage your MinKMS TLS certificates with cert-manager:

  1. Install cert-manager.

  2. Create cluster issuers (a self-signed issuer and a CA issuer with a fixed root certificate).

  3. Create a cert-manager certificate for the MinKMS cluster.

  4. Deploy MinKMS using the cert-manager certificate.

  5. Trust the issuing CA from each client that connects to MinKMS.

The details

  1. Install cert-manager. See the cert-manager documentation for details. Version 1.20 is recommended.

  2. Create a ClusterIssuer that can generate self-signed certificates. This is needed to create the initial self-signed certificate used by the CA.

    # selfsigned-issuer.yaml
    apiVersion: cert-manager.io/v1
    kind: ClusterIssuer
    metadata:
      name: selfsigned-issuer
    spec:
      selfSigned: {}
    
  3. Create the self-signed root CA certificate using the selfsigned-issuer, by creating the root-ca certificate in the cert-manager namespace, valid for 10 years:

    # root-ca.yaml
    apiVersion: cert-manager.io/v1
    kind: Certificate
    metadata:
      name: root-ca
      namespace: cert-manager      # change if cert-manager is installed in another namespace
    spec:
      commonName: root-ca
      duration: 87600h        # 10 years
      renewBefore: 720h       # 30 days
      isCA: true
      issuerRef:
        group: cert-manager.io
        kind: ClusterIssuer
        name: selfsigned-issuer
      privateKey:
        algorithm: ECDSA
        size: 256
      secretName: root-ca
    
    Performance Recommendation
    Use an ECDSA or Ed25519 private key. MinKMS logs a warning if it detects an RSA certificate, which can negatively impact performance.
  4. Create a ClusterIssuer that issues certificates signed by the root-ca certificate:

    # ca-issuer.yaml
    apiVersion: cert-manager.io/v1
    kind: ClusterIssuer
    metadata:
      name: ca-issuer
    spec:
      ca:
        secretName: root-ca
    

    This cluster issuer always signs certificates using the root-ca certificate.

  5. Create the certificate for the MinKMS cluster with cert-manager.

    The certificate must be valid for the following DNS names:

    • <name>-minkms.<namespace>
    • <name>-minkms.<namespace>.svc
    • <name>-minkms.<namespace>.svc.<cluster domain>
    • *.<name>-minkms-hl.<namespace>.svc.<cluster domain>

    The first three cover client and API access. The wildcard headless SAN is required because the Operator connects to individual MinKMS pods.

    where:

    • <name> is the name of the MinKMS deployment, defined in the metadata.name field of the MinKMS resource.

    • <namespace> is the namespace where MinKMS is installed, defined in the metadata.namespace field of the MinKMS resource.

    • <cluster domain> is the internal root DNS domain assigned in your Kubernetes cluster. Typically this is cluster.local, but confirm the value by checking your CoreDNS configuration. Different Kubernetes providers manage the root domain differently.

    Create a file called <name>-certificate.yaml (or whatever suits your naming conventions), replacing the placeholders to reflect your cluster and MinKMS configuration:

    # <name>-certificate.yaml
    apiVersion: cert-manager.io/v1
    kind: Certificate
    metadata:
      name: <name>-certmanager-cert
      namespace: <namespace>
    spec:
      dnsNames:
        # Client and API access (required)
        - "<name>-minkms.<namespace>"
        - "<name>-minkms.<namespace>.svc"
        - "<name>-minkms.<namespace>.svc.<cluster domain>"
        # Headless service for Operator-to-pod connections (required)
        - "*.<name>-minkms-hl.<namespace>.svc.<cluster domain>"
      secretName: <minkms-server-tls>
      issuerRef:
        group: cert-manager.io
        kind: ClusterIssuer
        name: ca-issuer
    
    Tip
    <minkms-server-tls> is the name of the secret cert-manager creates for the issued certificate. It can be any name; reference the same name in the MinKMS externalCertSecret field below.

Deploy MinKMS using cert-manager for TLS certificate management

When deploying MinKMS, set the TLS configuration so that:

  • MinKMS does not generate its own certificates (disableAutoCert: true), and

  • MinKMS references the cert-manager secret (externalCertSecret).

This directs the Operator to deploy MinKMS using the cert-manager certificate exclusively.

The following minkms chart values provide a baseline configuration meeting these requirements:

# minkms values.yaml
minkms:
  certificates:
    disableAutoCert: true
    externalCertSecret:
      - name: <minkms-server-tls>

When you renew the certificate, cert-manager updates the <minkms-server-tls> secret in place. MinKMS reloads the updated certificate automatically. See Certificate Management for reload behavior and timing.

Trust the issuing CA from clients

Any client that connects to MinKMS over TLS must trust the issuing CA. cert-manager publishes the issuing CA in the ca.crt key of the issued secret (<minkms-server-tls>), which always reflects the CA that signed the MinKMS server certificate. Distribute that CA certificate to the trust store of each client.

For AIStor Object Store using MinKMS for Server-Side Encryption, that CA must be added to the Object Store’s trusted CAs (objectStore.certificates.trustedCAs); otherwise the Object Store cannot verify the MinKMS endpoint and SSE-KMS operations fail with TLS errors.

  1. Export the issuing CA from the MinKMS server secret:

    kubectl get secret <minkms-server-tls> --namespace <namespace> \
      -o jsonpath='{.data.ca\.crt}' | base64 -d > ca.crt
    
  2. Create a secret in the Object Store namespace containing that CA. The Object Store resolves the key from the secret type:

    • kubernetes.io/tls: CA under tls.crt
    • Opaque (or any other type): CA under public.crt

    For example, as an Opaque secret:

    kubectl create secret generic <minkms-ca> \
      --from-file=public.crt=ca.crt \
      --namespace <object-store-namespace>
    
  3. Reference that secret under objectStore.certificates.trustedCAs in the Object Store Helm chart values:

    # object-store values.yaml
    objectStore:
      certificates:
        trustedCAs:
          - name: <minkms-ca>
    

See the AIStor Object Store network encryption documentation and Server-Side Encryption with AIStor Key Manager for the full encryption configuration.

Known issues

The minkms Helm chart does not expose externalCertSecret on older versions

The minkms Helm chart version 2.3.0 (and earlier) does not render externalCertSecret into the MinKMS resource, so setting it under minkms.certificates in values.yaml has no effect. Newer charts include this field.

Workaround: add the externalCertSecret block to the chart’s templates/minkms.yaml, under the certificates: section, right after disableAutoCert:

    disableAutoCert: {{ .disableAutoCert }}
    {{- with .externalCertSecret }}
    externalCertSecret: {{- toYaml . | nindent 6 }}
    {{- end }}

With that block in place, set the secret through values.yaml as usual:

# minkms values.yaml
minkms:
  certificates:
    disableAutoCert: true
    externalCertSecret:
      - name: <name>-minkms-tls

The Operator does not load externalCertSecret for cluster management on older releases

To manage a MinKMS cluster (for example, to scale it up or down), the Operator connects to the MinKMS service and must trust the server certificate it presents. Recent Operators load the certificate referenced by externalCertSecret into that client’s trust store automatically.

The released Operator RELEASE.2026-04-13T10-27-16Z.operator and earlier do not (on OpenShift, this is the OLM-installed minio-minkms-operator.v2026.4.13102716 CSV and earlier). These releases only trust the certificate found in the secret named <name>-minkms-tls (where <name> is the MinKMS metadata.name). When cert-manager writes the issued certificate to a secret with any other name, the client cannot verify the endpoint and cluster management operations fail with TLS errors.

Workaround: set the Certificate’s secretName to <name>-minkms-tls so cert-manager writes the issued certificate to that secret, and reference the same name in externalCertSecret:

# <name>-certificate.yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: <name>-certmanager-cert
  namespace: <namespace>
spec:
  # ... dnsNames and issuerRef as shown above ...
  secretName: <name>-minkms-tls

cert-manager writes the issued certificate (and the issuing CA in ca.crt) to the <name>-minkms-tls secret. The MinKMS pods serve it from externalCertSecret, while the Operator reads the same secret by its <name>-minkms-tls name to build the client it uses for cluster management. Because disableAutoCert: true stops the Operator from generating its own certificate, it will not overwrite this secret.