Known limitations¶
Behavior to plan around in the current release. Each item states what is not covered and the failure it can produce.
Transaction durability¶
cmp-issuer records the operation, transaction identifier, issuer and Secret configuration identity plus the issued chain in CMPTransaction, so an asynchronous enrollment survives a controller restart and an interrupted attempt resumes under its original transaction identifier rather than starting a new one. The gaps below remain.
A lost enrollment response cannot be recovered¶
If the controller stops after sending an enrollment and the response never arrives, retrying under the recorded transaction identifier reliably prevents a second certificate but does not retrieve the first one. Neither tested server answers the repeat from its existing transaction: Nokia NCM 26.7 refuses it with transactionIdInUse and EJBCA CE 9.3.7 refuses it with badRequest. The CertificateRequest fails and cert-manager enrolls again under a new transaction identifier, which succeeds. The certificate the server issued for the lost response is orphaned and counts against any issuance quota or audit trail the certificate authority keeps.
Coarse progress reporting¶
CMPTransaction.status.phase reports Enrolling, Polling, Confirming or Issued, so kubectl get cmptransactions shows less detail than the underlying message flow.
Completed transactions are retained¶
A transaction that obtained a certificate keeps its record, including the issued chain, so that a restart before cert-manager stores the certificate does not enroll a second one. The record is removed only when the owning CertificateRequest is garbage collected, so kubectl get cmptransactions lists completed transactions next to in-flight ones.
Protocol and product scope¶
| Limitation | Status |
|---|---|
IR for a hand-written CertificateRequest |
Unsupported. IR proves possession with the key cert-manager staged for a Certificate. Use P10CR for a direct request |
spec.protocol.certProfile |
Reserved until its CMP encoding is implemented. A non-empty value makes the issuer NotReady |
| KUR with a changed subject or SAN | Unsupported by design. Use P10CR where the server permits re-enrollment or wait for planned CR support |
| KUR after the old certificate expires or its Secret is deleted | Unsupported, because KUR authenticates with the current certificate. Renew while it is still valid or set renewalFallback: InitialEnrollment, see Missing or expired current certificate |
| KUR from a CA that keeps the old validity | Refused, because cert-manager would renew again at once. Configure the CA to compute a new validity on key update or use renewal: P10CR |
| PBMAC1 | Planned |
| mTLS to the CMP endpoint | Planned. Setting the reserved spec.transport.tls.clientCertificateSecretRef makes the issuer NotReady |
| CMPv3 beyond ML-DSA certificate confirmation | Planned |
| Composite ML-DSA | Unsupported. cert-manager rejects composite CSRs and Go cannot validate composite certificate chains |
| Revocation over CMP | Planned |
| Kubernetes CSR signing | Unsupported by design |
| Broad CMP compatibility | Not claimed |
See Support matrix.
ML-DSA¶
cmp-issuer supports ML-DSA-44, ML-DSA-65 and ML-DSA-87. Enrollment requires a CMP server that accepts the chosen algorithm and a cert-manager build that admits ML-DSA CSRs.
- cert-manager v1.21 is built with Go 1.26 and rejects ML-DSA CSRs. Go 1.27 provides the required cryptography, but admission also depends on the cert-manager build and its feature gates. The upstream ML-DSA design proposal introduces an alpha
MLDSAPrivateKeysgate for the controller and webhook. Follow the instructions for your build. - If the build admits ML-DSA CSRs but cannot manage ML-DSA keys, submit a direct
CertificateRequestfor P10CR enrollment. Renew by submitting another request. Automatic issuance and renewal throughCertificateresources require cert-manager support for ML-DSA key generation, storage and matching. A Go rebuild alone does not provide that support. - KUR requires cert-manager to supply the current and requested ML-DSA keys in seed-only PKCS #8 form. cmp-issuer reads the keys and CSR independently of the algorithm name used by the cert-manager API.
- Go reads only seed-only ML-DSA private keys. OpenSSL writes the seed and the expanded key by default, so convert an ML-DSA CMP signature credential before storing it:
openssl pkey -in key.pem -provparam ml-dsa.output_formats=seed-only -out seed-key.pem. - A
kubectlbuild without ML-DSA support may printWarning: tls: failed to parse private keyduringkubectl create secret tls. Check whether the Secret was created. - The
certConffor a certificate signed with ML-DSA carries a SHA-512certHashand names SHA-512 inhashAlg. The OpenSSL CMP mock server ignoreshashAlgand checks the hash with SHA-256, so explicit confirmation fails against it. Use implicit confirmation with that server.
Key usages¶
Set usages: [digital signature] for an ML-DSA request and configure the CA profile accordingly. Add extended usages such as server auth when needed. Keep the CSR extensions consistent with the CertificateRequest.spec.usages values. The classical default includes key encipherment, which is invalid for ML-DSA.
RFC 9881 section 5 restricts the keyUsage extension for ML-DSA subject keys to signature usages. If present, it must contain at least one of digitalSignature, nonRepudiation, keyCertSign or cRLSign. It must not contain keyEncipherment, dataEncipherment, keyAgreement, encipherOnly or decipherOnly.
cmp-issuer enforces this rule for issued chains, CMP response signer chains and signature credentials, including stored certificates used after a restart. It refuses a non-compliant issued certificate and sends a rejecting certConf when explicit confirmation is required. RSA or ECDSA subject keys retain their usual usages when signed by an ML-DSA CA.
Dependencies¶
The CMP library is pre-v1. ADR 0001 describes the dependency boundary and validation requirements.
API stability¶
certmanager.misiektoja.github.io/v1alpha1 may change before a stable version.