Support subject, literalSubject, and usages for CA and self-signed certificates. - #813
Support subject, literalSubject, and usages for CA and self-signed certificates.#813MartinWeindel wants to merge 12 commits into
subject, literalSubject, and usages for CA and self-signed certificates.#813Conversation
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
Signed-off-by: Martin Weindel <martin.weindel@sap.com>
Signed-off-by: Martin Weindel <martin.weindel@sap.com>
Signed-off-by: Martin Weindel <martin.weindel@sap.com>
Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Martin Weindel <martin.weindel@sap.com>
…objects Signed-off-by: Martin Weindel <martin.weindel@sap.com>
Signed-off-by: Martin Weindel <martin.weindel@sap.com>
Signed-off-by: Martin Weindel <martin.weindel@sap.com>
…tificates Extend the CA and self-signed issuance paths to honor the structured Subject, LiteralSubject and Usages fields of the Certificate spec: - subjectFromInput builds the pkix.Name from either the parsed literal subject (LDAP DN) or CommonName plus the structured Subject attributes, falling back to the CA-derived attributes otherwise. - buildKeyUsages maps api.KeyUsage values to x509 key/extended-key usages, always including KeyCertSign for CA certificates and defaulting to server auth when no usages are requested. - NewSelfSignedCertInPEMFormat and the CA/self-signed constructors now return the resulting common name so the reconciler can reconstruct the original spec and derive the status common name from a literalSubject. - Promote the common name (including one extracted from a literalSubject) to a DNS SAN for leaf certificates. Add unit and integration test coverage for these paths. Signed-off-by: Martin Weindel <martin.weindel@sap.com>
Go 1.25+ deprecates direct access to the raw ecdsa.PublicKey coordinate fields (X/Y). Use the Equal method implemented by all standard library public key types instead, which compares the underlying values without touching the deprecated fields and safely returns false for mismatched key types. The (bool, error) contract and error-on-unknown-type behavior are preserved; ed25519 keys are now handled as well. Signed-off-by: Martin Weindel <martin.weindel@sap.com>
ToKeyUsages split the usages annotation on "," without trimming, so a natural value like "server auth, client auth" produced " client auth", which failed the known-usage check and was silently dropped. Trim each token before the lookup so surrounding whitespace is tolerated. Signed-off-by: Martin Weindel <martin.weindel@sap.com>
Signed-off-by: Martin Weindel <martin.weindel@sap.com>
…roller The certman2 source controller (common.CertInput / CreateSpec) handled issuer, preferredChain, privateKey and renewBefore annotations but not the literal-subject and usages annotations that the legacy source controller already syncs onto the Certificate spec. As a result, a Gateway/Service/Ingress annotated with cert.gardener.cloud/literal-subject or cert.gardener.cloud/usages produced a Certificate that silently dropped these fields. Add the AnnotLiteralSubject and AnnotUsages constants, extend CertInput with LiteralSubject and Usages, read them in augmentFromCommonAnnotations, and map them into the CertificateSpec in CreateSpec, mirroring the legacy controller. Extend the ingress and service reconciler tests accordingly. Signed-off-by: Martin Weindel <martin.weindel@sap.com>
80d399f to
62f3761
Compare
|
/cla |
| if spec.LiteralSubject != nil { | ||
| h.Write([]byte("literalSubject")) | ||
| h.Write([]byte{0}) | ||
| h.Write([]byte(*spec.LiteralSubject)) | ||
| h.Write([]byte{0}) | ||
| } | ||
| if spec.Subject != nil { | ||
| s := spec.Subject | ||
| for _, v := range s.Organizations { | ||
| h.Write([]byte("O=")) | ||
| h.Write([]byte(v)) | ||
| h.Write([]byte{0}) | ||
| } | ||
| for _, v := range s.Countries { | ||
| h.Write([]byte("C=")) | ||
| h.Write([]byte(v)) | ||
| h.Write([]byte{0}) | ||
| } | ||
| for _, v := range s.OrganizationalUnits { | ||
| h.Write([]byte("OU=")) | ||
| h.Write([]byte(v)) | ||
| h.Write([]byte{0}) | ||
| } | ||
| for _, v := range s.Localities { | ||
| h.Write([]byte("L=")) | ||
| h.Write([]byte(v)) | ||
| h.Write([]byte{0}) | ||
| } | ||
| for _, v := range s.Provinces { | ||
| h.Write([]byte("ST=")) | ||
| h.Write([]byte(v)) | ||
| h.Write([]byte{0}) | ||
| } | ||
| for _, v := range s.StreetAddresses { | ||
| h.Write([]byte("STREET=")) | ||
| h.Write([]byte(v)) | ||
| h.Write([]byte{0}) | ||
| } | ||
| for _, v := range s.PostalCodes { | ||
| h.Write([]byte("PC=")) | ||
| h.Write([]byte(v)) | ||
| h.Write([]byte{0}) | ||
| } | ||
| if s.SerialNumber != "" { | ||
| h.Write([]byte("SN=")) | ||
| h.Write([]byte(s.SerialNumber)) | ||
| h.Write([]byte{0}) | ||
| } | ||
| } | ||
| for _, u := range spec.Usages { | ||
| h.Write([]byte("usage=")) | ||
| h.Write([]byte(u)) | ||
| h.Write([]byte{0}) | ||
| } |
There was a problem hiding this comment.
Given the length of this function at this point, we could add a little helper function that takes an optional prefix, writes the payload, and adds the 0 trailer. What do you think?
| for usage := range strings.SplitSeq(value, ",") { | ||
| if set.Has(string(usage)) { | ||
| usages = append(usages, api.KeyUsage(usage)) | ||
| } | ||
| } |
There was a problem hiding this comment.
Currently, it doesn't allow for extraneous spaces in the key usages. Do we want to allow that and implicitly trim the spaces before appending the key usage?
| email: some.user@mydomain.com | ||
| precheckNameservers: | ||
| - 8.8.8.8 | ||
| - 8.8.4.4 No newline at end of file |
There was a problem hiding this comment.
nit: there was a trailing newline here before
There was a problem hiding this comment.
I haven't verified whether it actually happens, but it might be that the current tests don't capture this case: I think there could be a hash mismatch scenario with a CSR-based Certificate that would cause it to be reprovisioned constantly.
The hash is using the CommonName from the spec:
cert-management/pkg/controller/issuer/certificate/reconciler.go
Lines 1078 to 1081 in 62f3761
nil)
When reconstructing the spec, it populates CommonName from the LiteralSubject now. This should only happen when CSR is nil:
cert-management/pkg/controller/issuer/certificate/reconciler.go
Lines 401 to 403 in 62f3761
| certInput.LiteralSubject = value | ||
| } | ||
| if value := annotations[AnnotUsages]; value != "" { | ||
| certInput.Usages = shared.ToKeyUsages(value) |
There was a problem hiding this comment.
minor: shared.ToKeyUsages silently drops unrecognized values (whereas this is highlighted when having them in the resource spec
|
|
||
| var usages []api.KeyUsage | ||
| if value, ok := resources.GetAnnotation(objData, AnnotUsages); ok && value != "" { | ||
| usages = shared.ToKeyUsages(value) |
There was a problem hiding this comment.
minor: shared.ToKeyUsages silently drops unrecognized values (whereas this is highlighted when having them in the resource spec
| if ptr.Deref(spec.LiteralSubject, "") != info.LiteralSubject { | ||
| if info.LiteralSubject != "" { | ||
| s := info.LiteralSubject | ||
| spec.LiteralSubject = new(s) | ||
| } else { | ||
| spec.LiteralSubject = nil | ||
| } | ||
| mod.Modify(true) | ||
| } | ||
|
|
||
| if !reflect.DeepEqual(spec.Usages, info.Usages) { | ||
| spec.Usages = info.Usages | ||
| mod.Modify(true) | ||
| } |
There was a problem hiding this comment.
minor: LiteralSubject and Usages are correctly set in the updateEntry path. However, in createEntryFor, they are not set when building the spec
cert-management/pkg/cert/source/reconciler.go
Lines 322 to 389 in 62f3761
| // +optional | ||
| IsCA *bool `json:"isCA,omitempty"` | ||
| // Usages defines the requested key usages and extended key usages. | ||
| // If unset, defaults to `digital signature` and `key encipherment`. |
There was a problem hiding this comment.
minor: The comment is slightly off; key encipherment is only added for RSA keys:
cert-management/pkg/shared/legobridge/pki.go
Lines 482 to 484 in 62f3761
What about:
| // If unset, defaults to `digital signature` and `key encipherment`. | |
| // If unset, defaults to `digital signature` (and `key encipherment` for RSA keys). |
| if src.LiteralSubject != "" { | ||
| spec.LiteralSubject = &src.LiteralSubject | ||
| } |
There was a problem hiding this comment.
Earlier in the function CommonName is set, but there's no exclusivity check here to use either CommonName or LiteralSubject
cert-management/pkg/certman2/controller/source/common/certinput.go
Lines 187 to 195 in 62f3761
How to categorize this PR?
/kind enhancement
What this PR does / why we need it:
Support subject, literalSubject, and usages for CA and self-signed certificates.
subjectFromInputbuilds the pkix.Name from either the structured subject or a parsed literalSubject LDAP DN.literalSubject, the parsed RDN sequence is DER-encoded and set as RawSubject on the CSR/certificate template, preserving exact attribute order and non-structured attribute types (e.g. DC) that Go would otherwise reorder or drop.Notes
Tests
Which issue(s) this PR fixes:
Fixes #794
Special notes for your reviewer:
Release note: