Skip to content

Getting started

This page takes you from an empty cluster to a certificate issued by your CMP server in five steps. Every command is meant to be run in order, and each step shows what success looks like so you can tell where you are.

The example uses PasswordBasedMac protection, which needs only a reference and a secret from your CMP administrator. Certificate-signature protection is covered in Message protection once you have the basic flow working.

What you need

  • A Kubernetes cluster and kubectl
  • Helm v3
  • cert-manager already installed and running
  • A CMP server, and from its administrator:
    • the CMP endpoint URL
    • the recipient distinguished name
    • a PasswordBasedMac reference and secret
    • the CA certificate that signs the server's CMP responses

Throughout this page, demo is the namespace your application lives in. Substitute your own.

Step 1: install cmp-issuer

Create the namespace your certificates will live in, then install the controller and name that namespace as one it may read issuer credentials from:

kubectl create namespace demo

helm repo add cmp-issuer https://misiektoja.github.io/cmp-issuer/charts
helm repo update
helm install cmp-issuer cmp-issuer/cmp-issuer \
  --namespace cmp-issuer-system \
  --create-namespace \
  --set 'credentialNamespaces={demo}'

This installs the CRDs, the RBAC and the controller. It also grants cert-manager permission to approve requests for this issuer type, which cert-manager needs before it will act on them at all.

The controller has no cluster-wide Secret access, so each namespace holding issuer credentials is authorized on its own. credentialNamespaces creates that authorization, as a RoleBinding granting get on Secrets in demo and nowhere else. Name every namespace your issuers live in, and create them before you install.

Check the controller is running and the namespace is authorized:

kubectl -n cmp-issuer-system get pods
kubectl get rolebinding -n demo
NAME                                         READY   STATUS    RESTARTS   AGE
cmp-issuer-controller-manager-7d9f8c5b4-x2ktp 1/1     Running   0          30s

NAME                                       ROLE                                      AGE
cmp-issuer-credential-reader-rolebinding   ClusterRole/cmp-issuer-credential-reader   30s

Namespaces you add later, other ways to grant that same access, cert-manager running under a different ServiceAccount, what happens to the CRDs when you uninstall and how to hand approval to approver-policy are all in Installation.

Step 2: store the credentials

Store the PasswordBasedMac credential your CMP administrator gave you:

kubectl create secret generic cmp-credentials \
  --namespace demo \
  --from-literal=reference='<reference>' \
  --from-literal=secret='<shared-secret>'

Store the CA certificate that signs your server's CMP responses. This is the CMP trust anchor, and it is not the same thing as TLS trust:

kubectl create secret generic cmp-trust \
  --namespace demo \
  --from-file=ca.crt=/path/to/cmp-ca.crt

Step 3: create the issuer

kubectl apply -f - <<'EOF'
apiVersion: certmanager.misiektoja.github.io/v1alpha1
kind: CMPIssuer
metadata:
  name: demo-issuer
  namespace: demo
spec:
  endpoint:
    url: http://cmp.example.com:8080/pkix/
  protocol:
    version: 2
    initialEnrollment: P10CR
    recipient: CN=Example CA,O=Example
    confirmation: Explicit
  protection:
    type: PasswordBasedMac
    passwordBasedMac:
      secretRef:
        name: cmp-credentials
  cmpTrust:
    caSecretRef:
      name: cmp-trust
      key: ca.crt
EOF

Check that it became ready:

kubectl get cmpissuers -n demo
NAME          READY
demo-issuer   True

If it reports False, the message names what is missing:

kubectl describe cmpissuer demo-issuer -n demo

A message naming a Secret usually means step 2 was skipped or used a different name.

Step 4: request a certificate

kubectl apply -f - <<'EOF'
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: demo-tls
  namespace: demo
spec:
  secretName: demo-tls
  commonName: workload.example.com
  privateKey:
    algorithm: RSA
    size: 2048
  issuerRef:
    name: demo-issuer
    kind: CMPIssuer
    group: certmanager.misiektoja.github.io
EOF

Watch it complete:

kubectl get certificate demo-tls -n demo
NAME       READY   SECRET     AGE
demo-tls   True    demo-tls   12s

READY True means the CMP exchange finished, the response was authenticated and the certificate was stored.

The controller logs one line for that outcome, which is the quickest account of what the CA issued:

kubectl logs -n cmp-issuer-system deploy/cmp-issuer-controller-manager -c manager | grep "Issued certificate"
{"level":"info","msg":"Issued certificate","CertificateRequest":{"name":"demo-tls-1","namespace":"demo"},
 "issuer":"CMPIssuer/demo/demo-issuer","subject":"CN=workload.example.com","serialNumber":"3b8f2a41",
 "notAfter":"2026-08-02T09:12:31Z","keyType":"RSA","keySize":2048,"duration":"412ms", ...}

Step 5: look at what you got

kubectl get secret demo-tls -n demo -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -subject -issuer -dates
subject=CN=workload.example.com
issuer=CN=Example CA, O=Example
notBefore=...
notAfter=...

The Secret holds tls.crt and tls.key in the usual cert-manager layout, so any workload that already consumes a cert-manager Secret can consume this one unchanged.

If it did not work

What you see What it means
CertificateRequest stays pending with no conditions cert-manager is not permitted to approve these requests, usually because it runs under a different ServiceAccount than the chart default
Issuer Ready False naming a Secret Step 2 was skipped, a name does not match, or demo was not named in credentialNamespaces
Ready False naming response protection or trust cmpTrust does not hold the CA that signs your server's CMP responses
Ready False naming the response sender recipient does not name the authority your server answers as
Connection refused or timeout Wrong endpoint URL, or a NetworkPolicy blocks the controller

Check progress at any time with:

kubectl get cmptransactions -n demo
kubectl describe certificaterequest -n demo
kubectl logs -n cmp-issuer-system deploy/cmp-issuer-controller-manager -c manager -f

Troubleshooting covers these in more detail.

Where to go next