cert-manager issues certificates through an Issuer or ClusterIssuer of type vault, which calls the PKI secrets engine at <MOUNT>/sign/<ROLE>. Stronghold is API-compatible with Vault, so such an Issuer can be pointed at Stronghold. The <MOUNT>/sign/<ROLE> endpoint accepts the fields that cert-manager sends (csr, common_name, alt_names, ip_sans, uri_sans, other_sans, ttl).

In Deckhouse Platform, cert-manager is installed by the cert-manager module.

Goal

After the setup:

  • cert-manager in the cluster issues and renews TLS certificates for example.com subdomains through the intermediate CA in Stronghold.
  • cert-manager logs in to Stronghold with the Kubernetes auth method, so no long-lived Stronghold credentials are stored in the cluster.

Prerequisites

Before you start, prepare the following:

  • The PKI secrets engine at the pki_int path with an intermediate CA and the example-com role for the example.com domain, as in Internal PKI with Stronghold.
  • The Kubernetes auth method enabled in Stronghold for the cluster.
  • cert-manager installed in the cluster.
  • A Stronghold token with permissions to write policies and Kubernetes auth roles.
  • The CA certificate that signed the Stronghold TLS certificate.

Step 1. Configure Stronghold

Allow cert-manager to sign certificate requests with the example-com role.

  1. Create a policy that allows signing certificate requests for the role:

    d8 stronghold policy write cert-manager-pki - <<'POLICY'
    path "pki_int/sign/example-com" {
      capabilities = ["create", "update"]
    }
    POLICY
  2. Create a Kubernetes auth role for the stronghold-issuer ServiceAccount in the myapp namespace. The audience parameter must match the token audience that cert-manager requests: vault://<NAMESPACE>/<ISSUER_NAME> for an Issuer, vault://<CLUSTER_ISSUER_NAME> for a ClusterIssuer:

    d8 stronghold write auth/kubernetes/role/cert-manager-myapp bound_service_account_names=stronghold-issuer bound_service_account_namespaces=myapp audience=vault://myapp/stronghold policies=cert-manager-pki ttl=20m

    In DP in Automatic mode, the Kubernetes auth method for the current cluster is created by the module at the kubernetes_local path: in this case, use auth/kubernetes_local instead of auth/kubernetes in the commands and in the Issuer mountPath. The auth/kubernetes path applies to a manually enabled method.

Step 2. Configure cert-manager

Create an Issuer that calls Stronghold, and request a certificate.

  1. Create a ServiceAccount and allow cert-manager to request tokens for it:

    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: stronghold-issuer
      namespace: myapp
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
      name: stronghold-issuer-token
      namespace: myapp
    rules:
      - apiGroups: [""]
        resources: ["serviceaccounts/token"]
        resourceNames: ["stronghold-issuer"]
        verbs: ["create"]
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
      name: stronghold-issuer-token
      namespace: myapp
    roleRef:
      apiGroup: rbac.authorization.k8s.io
      kind: Role
      name: stronghold-issuer-token
    subjects:
      - kind: ServiceAccount
        name: cert-manager
        namespace: d8-cert-manager
  2. Create the Issuer:

    apiVersion: cert-manager.io/v1
    kind: Issuer
    metadata:
      name: stronghold
      namespace: myapp
    spec:
      vault:
        server: https://stronghold.example.com
        path: pki_int/sign/example-com
        caBundle: <CA_BUNDLE_BASE64>
        auth:
          kubernetes:
            role: cert-manager-myapp
            mountPath: /v1/auth/kubernetes
            serviceAccountRef:
              name: stronghold-issuer

    <CA_BUNDLE_BASE64> is the base64-encoded CA certificate that signed the Stronghold TLS certificate.

  3. Request a certificate:

    apiVersion: cert-manager.io/v1
    kind: Certificate
    metadata:
      name: www
      namespace: myapp
    spec:
      secretName: www-tls
      commonName: www.example.com
      dnsNames:
        - www.example.com
      duration: 72h
      renewBefore: 24h
      issuerRef:
        name: stronghold
        kind: Issuer

cert-manager stores the certificate and private key in the www-tls Secret and renews the certificate before it expires. The duration value must not exceed the max_ttl of the example-com role.

Other authentication methods

A vault Issuer also supports AppRole authentication (auth.appRole with roleId and a reference to a Secret with secret_id) and token authentication (auth.tokenSecretRef). With these methods, the secret_id or the token is stored in a Secret in the cluster. Kubernetes auth does not require storing long-lived credentials in the cluster.

Verification

Check the result:

  1. Check that the Issuer is ready:

    d8 k -n myapp get issuer stronghold

    The READY column must show True.

  2. Check that the certificate is issued:

    d8 k -n myapp get certificate www

    The READY column must show True.

  3. Check the issuer and the expiry date of the certificate in the Secret:

    d8 k -n myapp get secret www-tls -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -issuer -enddate

    The issuer field must contain the name of the intermediate CA from pki_int.

Cleanup

Delete the objects created in this guide.

  1. Delete the Certificate, its Secret, the Issuer, and the ServiceAccount with its Role and RoleBinding:

    d8 k -n myapp delete certificate www
    d8 k -n myapp delete secret www-tls
    d8 k -n myapp delete issuer stronghold
    d8 k -n myapp delete rolebinding stronghold-issuer-token
    d8 k -n myapp delete role stronghold-issuer-token
    d8 k -n myapp delete serviceaccount stronghold-issuer
  2. Delete the Kubernetes auth role and the policy in Stronghold:

    d8 stronghold delete auth/kubernetes/role/cert-manager-myapp
    d8 stronghold policy delete cert-manager-pki

    Certificates that were already issued stay valid until they expire. To revoke them, use pki_int/revoke, as in Internal PKI with Stronghold.

Additional resources