cmp-issuer is a vendor-neutral cert-manager external issuer for Certificate Management Protocol (CMP) servers.
Point a cert-manager Certificate at a CMPIssuer and the certificate is enrolled over CMPv2 with the resulting certificate written to the usual TLS Secret.
CMP message protection is mandatory. HTTP and HTTPS are both supported.
Initial enrollment uses P10CR. A renewal re-enrolls with P10CR by default or uses certificate-authenticated KUR with CRMF proof of possession when the CMP profile requires a true key update.
This repository is under active initial development. The API group is served at
v1alpha1and may change.
- Kubernetes cluster v1.31 or newer, verified on v1.34-1.36
- cert-manager with external issuer support, verified on v1.19-1.21
- Helm v3
- Kubernetes container runtime like Docker, containerd or CRI-O
- A CMP server (so far tested with Nokia NCM 26.7 and EJBCA Community Edition 9.3.7)
demo is the namespace your certificates are issued into. Substitute your own.
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}'That installs the CRDs, the controller and the permission cert-manager needs before it will approve
requests for this issuer type. credentialNamespaces lets the controller read the issuer credentials in
demo and nowhere else, so name every namespace your issuers live in.
A packaged chart, a self-contained installer manifest and an air-gapped bundle are attached to every release. Those paths, and the settings this quick start leaves at their defaults, are in Installation.
The short version is below. Getting started explains each step, shows the expected output and lists what to check when something does not work.
Store the credential your CMP administrator gave you and the CA certificate that signs the server's CMP responses:
kubectl create secret generic cmp-credentials --namespace demo \
--from-literal=reference='<reference>' \
--from-literal=secret='<shared-secret>'
kubectl create secret generic cmp-trust --namespace demo \
--from-file=ca.crt=/path/to/cmp-ca.crtCreate 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
renewal: 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
kubectl get cmpissuers -n demoNAME READY
demo-issuer True
renewal decides what a cert-manager renewal sends. It defaults to another P10CR enrollment. Set it to
KUR when the CMP profile requires a true key update, as described in
Renewal with P10CR or KUR.
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
kubectl get certificate demo-tls -n demoNAME READY SECRET AGE
demo-tls True demo-tls 12s
Enrollment. cert-manager creates the private key and the PKCS #10 request. The controller picks up
the approved CertificateRequest, sends the request to your CMP server in a protected P10CR and hands
the issued chain back to cert-manager. initialEnrollment accepts only P10CR for now.
Renewal. A renewal sends another P10CR unless you set spec.protocol.renewal to KUR.
KUR authenticates the request using the current valid certificate and proves possession of the key that
cert-manager requested. This meets the expectations of CMP profiles requiring a true key update. Renewals
can go to their own CMP alias with spec.endpoint.renewalUrl.
Slow or queued requests. When the CA cannot issue right away it answers waiting and the issuer
polls until the certificate arrives. Every transaction is recorded in a CMPTransaction before the
first message goes out, so a controller restart resumes that transaction instead of enrolling a second
time. Follow them with kubectl get cmptransactions -A.
Private keys and secrets. P10CR never reads workload private keys. KUR reads only the current and
staged keys of the certificate being renewed, after checking the controlling cert-manager Certificate,
its owner UID, revision, issuer reference and Secret ownership. The controller reads Secrets only in the
namespaces you name at install time. TLS trust and CMP response trust are configured separately and
every response must come from the authority set as recipient.
CMP server differences. spec.protocol.validationProfile defaults to Interoperable, which tolerates
the deviations real CMP servers show. It accepts certReqId -1 or 0 in a P10CR response, echoes the
received value back in certConf and treats KUP caPubs as untrusted chain candidates rather than
trust anchors. Choose RFC9483 for the strict checks or pin a single behavior you already know, such as
spec.protocol.p10crResponseCertReqId.
Logs and metrics. Each completed enrollment writes one Issued certificate log line with the
subject, serial, validity and issuing CA. Prometheus metrics count enrollments, durations and classified
failures per issuer, with renewals kept separate from first enrollments. See
Metrics.
Built on cert-manager's own library. Approval, denial, retry classification, Ready conditions and Events come from issuer-lib, maintained by the cert-manager project and pinned to an exact version. See ADR 0002.
Real-world interoperability. cmp-issuer is tested against real PKI and CMP server implementations to verify that it works beyond synthetic test environments. This includes interoperability testing with EJBCA and Nokia NCM, with results and known compatibility details published in Tested PKIs.
Full documentation is at misiektoja.github.io/cmp-issuer.
- Getting started
- Installation
- Support matrix
- Enrollment guide
- Renewal with P10CR or KUR
- Message protection
- CMPIssuer reference
- Tested PKIs
- Metrics
- Troubleshooting
- Known limitations
- Threat model
SUPPORT.md directs usage questions, bug reports, feature requests and security reports. Check the documentation and gather the sanitized version, resource status and controller log before posting.
Original cmp-issuer code is licensed under Apache-2.0. Dependencies retain their own licenses. See THIRD_PARTY_NOTICES.md.