From fd0c3717f23a4ab5b6340944308f48605e3cd746 Mon Sep 17 00:00:00 2001 From: Martin Weindel Date: Thu, 24 Feb 2022 14:13:50 +0100 Subject: [PATCH] delegated domains --- charts/cert-management/templates/crds-v1.yaml | 5 ++ .../templates/crds-v1beta1.yaml | 5 ++ examples/30-cert-simple.yaml | 6 ++ examples/40-ingress-echoheaders.yaml | 1 + examples/40-service-loadbalancer.yaml | 1 + go.mod | 2 +- go.sum | 10 +-- .../cert.gardener.cloud_certificates.yaml | 5 ++ pkg/apis/cert/crds/zz_generated_crds.go | 5 ++ pkg/apis/cert/v1alpha1/certificate.go | 3 + .../cert/v1alpha1/zz_generated.deepcopy.go | 5 ++ pkg/cert/legobridge/certificate.go | 2 + pkg/cert/legobridge/dnscontrollerprovider.go | 9 +++ pkg/cert/source/controller.go | 2 + pkg/cert/source/defaults.go | 8 ++- pkg/cert/source/interface.go | 7 ++- pkg/cert/source/reconciler.go | 3 + pkg/cert/utils/dns_utils.go | 33 +++++++++- .../issuer/certificate/reconciler.go | 5 ++ pkg/controller/source/ingress/handler.go | 8 ++- .../controller-manager-library/hack/run-in.sh | 0 .../go-acme/lego/v4/acme/api/api.go | 2 +- .../go-acme/lego/v4/acme/api/certificate.go | 4 +- .../v4/acme/api/internal/sender/useragent.go | 2 +- .../go-acme/lego/v4/acme/commons.go | 61 ++++++++++++------- .../github.com/go-acme/lego/v4/acme/errors.go | 6 +- .../go-acme/lego/v4/certcrypto/crypto.go | 2 +- .../lego/v4/certificate/certificates.go | 12 +++- .../go-acme/lego/v4/challenge/challenges.go | 6 +- .../challenge/dns01/dns_challenge_manual.go | 2 +- .../v4/challenge/http01/domain_matcher.go | 4 +- .../challenge/http01/http_challenge_server.go | 31 +++++++--- .../challenge/tlsalpn01/tls_alpn_challenge.go | 4 +- .../tlsalpn01/tls_alpn_challenge_server.go | 2 +- .../go-acme/lego/v4/lego/client_config.go | 52 ++++++++++++---- vendor/modules.txt | 2 +- 36 files changed, 243 insertions(+), 74 deletions(-) mode change 100644 => 100755 vendor/github.com/gardener/controller-manager-library/hack/run-in.sh diff --git a/charts/cert-management/templates/crds-v1.yaml b/charts/cert-management/templates/crds-v1.yaml index 5d02db63..785c0ec4 100644 --- a/charts/cert-management/templates/crds-v1.yaml +++ b/charts/cert-management/templates/crds-v1.yaml @@ -305,6 +305,11 @@ spec: this date. format: date-time type: string + followCNAME: + description: FollowCNAME if true delegated domain for DNS01 challenge + is used if CNAME record for DNS01 challange domain `_acme-challenge.` + is set. + type: boolean issuerRef: description: IssuerRef is the reference of the issuer to use. properties: diff --git a/charts/cert-management/templates/crds-v1beta1.yaml b/charts/cert-management/templates/crds-v1beta1.yaml index 702e8f9f..9ce5ff4c 100644 --- a/charts/cert-management/templates/crds-v1beta1.yaml +++ b/charts/cert-management/templates/crds-v1beta1.yaml @@ -311,6 +311,11 @@ spec: this date. format: date-time type: string + followCNAME: + description: FollowCNAME if true delegated domain for DNS01 challenge + is used if CNAME record for DNS01 challange domain `_acme-challenge.` + is set. + type: boolean issuerRef: description: IssuerRef is the reference of the issuer to use. properties: diff --git a/examples/30-cert-simple.yaml b/examples/30-cert-simple.yaml index 3f2223d3..821b93ea 100644 --- a/examples/30-cert-simple.yaml +++ b/examples/30-cert-simple.yaml @@ -21,3 +21,9 @@ spec: secretRef: name: cert-simple-secret namespace: default + + # If delegated domain for DNS01 challenge should be used. This has only an effect if a CNAME record is set for + # either '_acme-challenge.cert1.mydomain.com' or '_acme-challenge.cert1.my-other-domain.com'. + # For example: If a CNAME record exists '_acme-challenge.cert1.mydomain.com' => '_acme-challenge.writable.domain.com', + # the DNS challenge will be written to '_acme-challenge.writable.domain.com'. + #followCNAME: true diff --git a/examples/40-ingress-echoheaders.yaml b/examples/40-ingress-echoheaders.yaml index 1827d62e..b853c3bc 100644 --- a/examples/40-ingress-echoheaders.yaml +++ b/examples/40-ingress-echoheaders.yaml @@ -12,6 +12,7 @@ metadata: #dns.gardener.cloud/class: garden # needed on Gardener shoot clusters for managed DNS record creation #cert.gardener.cloud/commonname: "*.demo.mydomain.com" # optional, if not specified the first name from spec.tls[].hosts is used as common name #cert.gardener.cloud/dnsnames: "" # optional, if not specified the names from spec.tls[].hosts are used + #cert.gardener.cloud/follow-cname: "true" # optional, same as spec.followCNAME in certificates spec: tls: - hosts: diff --git a/examples/40-service-loadbalancer.yaml b/examples/40-service-loadbalancer.yaml index 8cac6d6b..fd5775a5 100644 --- a/examples/40-service-loadbalancer.yaml +++ b/examples/40-service-loadbalancer.yaml @@ -12,6 +12,7 @@ metadata: #dns.gardener.cloud/class: garden # needed on Gardener shoot clusters for managed DNS record creation #cert.gardener.cloud/commonname: "*.demo.mydomain.com" # optional, if not specified the first name from dns.gardener.cloud/dnsnames is used as common name #cert.gardener.cloud/dnsnames: "" # optional, if specified overrides dns.gardener.cloud/dnsnames annotation for certificate names + #cert.gardener.cloud/follow-cname: "true" # optional, same as spec.followCNAME in certificates name: test-service namespace: default spec: diff --git a/go.mod b/go.mod index 0671a4df..25474d37 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/ahmetb/gen-crd-api-reference-docs v0.2.0 github.com/gardener/controller-manager-library v0.2.1-0.20220127132236-5167be276ff5 github.com/gardener/external-dns-management v0.11.4 - github.com/go-acme/lego/v4 v4.5.3 + github.com/go-acme/lego/v4 v4.6.0 github.com/miekg/dns v1.1.44 github.com/onsi/ginkgo v1.16.4 github.com/onsi/gomega v1.15.0 diff --git a/go.sum b/go.sum index 9b4b7345..c11f6d10 100644 --- a/go.sum +++ b/go.sum @@ -257,8 +257,8 @@ github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= -github.com/go-acme/lego/v4 v4.5.3 h1:v5RSN8l+RAeNHKTSL80eqLiec6q6UNaFpl2Df5x/5tM= -github.com/go-acme/lego/v4 v4.5.3/go.mod h1:mL1DY809LzjvRuaxINNxsI26f5oStVhBGTpJMiinkZM= +github.com/go-acme/lego/v4 v4.6.0 h1:w1rQtE/YHY5SupCTRpRJQbaZ6bkySJJ0z+kl8p6pVJU= +github.com/go-acme/lego/v4 v4.6.0/go.mod h1:v19/zU0bumGNzvsbx07zQ6c9IxAvy55XIKhXCZio3NQ= github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs= github.com/go-cmd/cmd v1.0.5/go.mod h1:y8q8qlK5wQibcw63djSl/ntiHUHXHGdCkPk0j4QeW4s= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= @@ -846,6 +846,8 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.287/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.287/go.mod h1:CuOaLxOQr477GhMWAQPYQFUJrsZbW+ZqkAgP2uHDZXg= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= @@ -864,7 +866,7 @@ github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyC github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= -github.com/vinyldns/go-vinyldns v0.0.0-20200917153823-148a5f6b8f14/go.mod h1:RWc47jtnVuQv6+lY3c768WtXCas/Xi+U5UFc5xULmYg= +github.com/vinyldns/go-vinyldns v0.9.16/go.mod h1:5qIJOdmzAnatKjurI+Tl4uTus7GJKJxb+zitufjHs3Q= github.com/vultr/govultr/v2 v2.7.1/go.mod h1:BvOhVe6/ZpjwcoL6/unkdQshmbS9VGbowI4QT+3DGVU= github.com/wacul/ptr v0.0.0-20170209030335-91632201dfc8/go.mod h1:BD0gjsZrCwtoR+yWDB9v2hQ8STlq9tT84qKfa+3txOc= github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= @@ -1052,7 +1054,6 @@ golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985 h1:4CSI6oo7cOjJKajidEljs9h+uP0rRZBPPPhcCbj5mw8= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1578,3 +1579,4 @@ sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZa sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= +software.sslmate.com/src/go-pkcs12 v0.0.0-20210415151418-c5206de65a78/go.mod h1:B7Wf0Ya4DHF9Yw+qfZuJijQYkWicqDa+79Ytmmq3Kjg= diff --git a/pkg/apis/cert/crds/cert.gardener.cloud_certificates.yaml b/pkg/apis/cert/crds/cert.gardener.cloud_certificates.yaml index 521a407f..1722667f 100644 --- a/pkg/apis/cert/crds/cert.gardener.cloud_certificates.yaml +++ b/pkg/apis/cert/crds/cert.gardener.cloud_certificates.yaml @@ -86,6 +86,11 @@ spec: this date. format: date-time type: string + followCNAME: + description: FollowCNAME if true delegated domain for DNS01 challenge + is used if CNAME record for DNS01 challange domain `_acme-challenge.` + is set. + type: boolean issuerRef: description: IssuerRef is the reference of the issuer to use. properties: diff --git a/pkg/apis/cert/crds/zz_generated_crds.go b/pkg/apis/cert/crds/zz_generated_crds.go index 69ff5299..ba86b862 100644 --- a/pkg/apis/cert/crds/zz_generated_crds.go +++ b/pkg/apis/cert/crds/zz_generated_crds.go @@ -393,6 +393,11 @@ spec: this date. format: date-time type: string + followCNAME: + description: FollowCNAME if true delegated domain for DNS01 challenge + is used if CNAME record for DNS01 challange domain `+"`"+`_acme-challenge.`+"`"+` + is set. + type: boolean issuerRef: description: IssuerRef is the reference of the issuer to use. properties: diff --git a/pkg/apis/cert/v1alpha1/certificate.go b/pkg/apis/cert/v1alpha1/certificate.go index f3c6aebe..6ef64734 100644 --- a/pkg/apis/cert/v1alpha1/certificate.go +++ b/pkg/apis/cert/v1alpha1/certificate.go @@ -68,6 +68,9 @@ type CertificateSpec struct { // EnsureRenewedAfter specifies a time stamp in the past. Renewing is only triggered if certificate notBefore date is before this date. // +optional EnsureRenewedAfter *metav1.Time `json:"ensureRenewedAfter,omitempty"` + // FollowCNAME if true delegated domain for DNS01 challenge is used if CNAME record for DNS01 challange domain `_acme-challenge.` is set. + // +optional + FollowCNAME *bool `json:"followCNAME,omitempty"` } // IssuerRef is the reference of the issuer by name. diff --git a/pkg/apis/cert/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/cert/v1alpha1/zz_generated.deepcopy.go index a3e94d2a..c72b7fe4 100644 --- a/pkg/apis/cert/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/cert/v1alpha1/zz_generated.deepcopy.go @@ -371,6 +371,11 @@ func (in *CertificateSpec) DeepCopyInto(out *CertificateSpec) { in, out := &in.EnsureRenewedAfter, &out.EnsureRenewedAfter *out = (*in).DeepCopy() } + if in.FollowCNAME != nil { + in, out := &in.FollowCNAME, &out.FollowCNAME + *out = new(bool) + **out = **in + } return } diff --git a/pkg/cert/legobridge/certificate.go b/pkg/cert/legobridge/certificate.go index 5dbd8ee3..05c93f68 100644 --- a/pkg/cert/legobridge/certificate.go +++ b/pkg/cert/legobridge/certificate.go @@ -75,6 +75,8 @@ type DNSControllerSettings struct { AdditionalWait time.Duration // PropagationTimeout is the propagation timeout for the DNS challenge. PropagationTimeout time.Duration + // FollowCNAME if true checks and follows CNAME records for DNS01 challenge domains. + FollowCNAME bool } // ObtainOutput is the result of the certificate obtain request. diff --git a/pkg/cert/legobridge/dnscontrollerprovider.go b/pkg/cert/legobridge/dnscontrollerprovider.go index 07ba7d60..e127e852 100644 --- a/pkg/cert/legobridge/dnscontrollerprovider.go +++ b/pkg/cert/legobridge/dnscontrollerprovider.go @@ -112,6 +112,15 @@ func (p *dnsControllerProvider) Present(domain, token, keyAuth string) error { atomic.AddInt32(&p.count, 1) fqdn, value := dns01.GetRecord(domain, keyAuth) + if p.settings.FollowCNAME { + var err error + orgfqdn := fqdn + fqdn, err = utils.FollowCNAMEs(fqdn, p.settings.PrecheckNameservers) + if err != nil { + return fmt.Errorf("following CNAME for DNS01 challenge for %s failed: %w", orgfqdn, err) + } + } + values := p.addPresentingDomainValue(domain, value) setSpec := func(e *dnsapi.DNSEntry) { diff --git a/pkg/cert/source/controller.go b/pkg/cert/source/controller.go index 031fcb1a..6a865b3e 100644 --- a/pkg/cert/source/controller.go +++ b/pkg/cert/source/controller.go @@ -37,6 +37,8 @@ const ( AnnotCommonName = "cert.gardener.cloud/commonname" // AnnotCertDNSNames is the annotation for explicitly specifying the DNS names (if not specified, values from "dns.gardener.cloud/dnsnames" is used) AnnotCertDNSNames = "cert.gardener.cloud/dnsnames" + // AnnotFollowCNAME is the annotation for allowing delegated domains for DNS01 challenge + AnnotFollowCNAME = "cert.gardener.cloud/follow-cname" // OptClass is the cert-class command line option OptClass = "cert-class" diff --git a/pkg/cert/source/defaults.go b/pkg/cert/source/defaults.go index 9220b1ba..793be414 100644 --- a/pkg/cert/source/defaults.go +++ b/pkg/cert/source/defaults.go @@ -8,6 +8,7 @@ package source import ( "fmt" + "strconv" "strings" "sync" @@ -182,7 +183,12 @@ func (s *DefaultCertSource) GetCertsInfo(logger logger.LogContext, obj resources issuer = &annotatedIssuer } - info.Certs[secretName] = CertInfo{SecretName: secretName, Domains: annotatedDomains, IssuerName: issuer} + followCNAME := false + if value, ok := resources.GetAnnotation(obj.Data(), AnnotFollowCNAME); ok { + followCNAME, _ = strconv.ParseBool(value) + } + + info.Certs[secretName] = CertInfo{SecretName: secretName, Domains: annotatedDomains, IssuerName: issuer, FollowCNAME: followCNAME} return info, nil } diff --git a/pkg/cert/source/interface.go b/pkg/cert/source/interface.go index 2ae4a172..cde8a14f 100644 --- a/pkg/cert/source/interface.go +++ b/pkg/cert/source/interface.go @@ -20,9 +20,10 @@ import ( // CertInfo contains basic certificate data. type CertInfo struct { - SecretName string - Domains []string - IssuerName *string + SecretName string + Domains []string + IssuerName *string + FollowCNAME bool } // CertsInfo contains a map of CertInfo. diff --git a/pkg/cert/source/reconciler.go b/pkg/cert/source/reconciler.go index 3c0a8527..705555a5 100644 --- a/pkg/cert/source/reconciler.go +++ b/pkg/cert/source/reconciler.go @@ -315,6 +315,9 @@ func (r *sourceReconciler) createEntryFor(logger logger.LogContext, obj resource } else { cert.Namespace = r.namespace } + if info.FollowCNAME { + cert.Spec.FollowCNAME = &info.FollowCNAME + } e, _ := r.SlaveResoures()[0].Wrap(cert) diff --git a/pkg/cert/utils/dns_utils.go b/pkg/cert/utils/dns_utils.go index e8b88f6b..45f9d92c 100644 --- a/pkg/cert/utils/dns_utils.go +++ b/pkg/cert/utils/dns_utils.go @@ -8,9 +8,10 @@ package utils import ( "fmt" + "time" + "github.com/go-acme/lego/v4/challenge/dns01" "github.com/miekg/dns" - "time" ) const defaultPath = "/etc/resolv.conf" @@ -91,6 +92,36 @@ func checkTXTValue(answer []dns.RR, values []string) bool { return true } +// FollowCNAMEs follows the CNAME records and returns the last non-CNAME fully qualified domain name +// that it finds. Returns an error when a loop is found in the CNAME chain. The +// argument fqdnChain is used by the function itself to keep track of which fqdns it +// already encountered and detect loops. +// Method copied from https://github.com/cert-manager/cert-manager/pkg/issuer/acme/dns/util/wait.go +func FollowCNAMEs(fqdn string, nameservers []string, fqdnChain ...string) (string, error) { + r, err := dnsQuery(fqdn, dns.TypeCNAME, nameservers, true) + if err != nil { + return "", err + } + if r.Rcode != dns.RcodeSuccess { + return fqdn, err + } + for _, rr := range r.Answer { + cn, ok := rr.(*dns.CNAME) + if !ok || cn.Hdr.Name != fqdn { + continue + } + // Check if we were here before to prevent loops in the chain of CNAME records. + for _, fqdnInChain := range fqdnChain { + if cn.Target != fqdnInChain { + continue + } + return "", fmt.Errorf("Found recursive CNAME record to %q when looking up %q", cn.Target, fqdn) + } + return FollowCNAMEs(cn.Target, nameservers, append(fqdnChain, fqdn)...) + } + return fqdn, nil +} + // The following methods are copied from github.com/go-acme/lego/v3/challenge/dns01/nameserver.go // dnsTimeout is used to override the default DNS timeout of 10 seconds. diff --git a/pkg/controller/issuer/certificate/reconciler.go b/pkg/controller/issuer/certificate/reconciler.go index d65575c2..daeee69e 100644 --- a/pkg/controller/issuer/certificate/reconciler.go +++ b/pkg/controller/issuer/certificate/reconciler.go @@ -445,6 +445,10 @@ func (r *certReconciler) obtainCertificateAndPendingACME(logctx logger.LogContex } var dnsSettings *legobridge.DNSControllerSettings if issuer.Spec.ACME.SkipDNSChallengeValidation == nil || !*issuer.Spec.ACME.SkipDNSChallengeValidation { + followCNAME := false + if cert.Spec.FollowCNAME != nil && *cert.Spec.FollowCNAME { + followCNAME = true + } dnsSettings = &legobridge.DNSControllerSettings{ Cluster: r.dnsCluster, Namespace: cert.Namespace, @@ -452,6 +456,7 @@ func (r *certReconciler) obtainCertificateAndPendingACME(logctx logger.LogContex PrecheckNameservers: r.precheckNameservers, AdditionalWait: r.additionalWait, PropagationTimeout: r.propagationTimeout, + FollowCNAME: followCNAME, } if r.dnsNamespace != nil { dnsSettings.Namespace = *r.dnsNamespace diff --git a/pkg/controller/source/ingress/handler.go b/pkg/controller/source/ingress/handler.go index 9723f46c..626d2af4 100644 --- a/pkg/controller/source/ingress/handler.go +++ b/pkg/controller/source/ingress/handler.go @@ -8,6 +8,7 @@ package ingress import ( "fmt" + "strconv" "strings" networkingv1 "k8s.io/api/networking/v1" @@ -56,6 +57,11 @@ func (s *CIngressSource) GetCertsInfo(logger logger.LogContext, obj resources.Ob return info, nil } + followCNAME := false + if value, ok := resources.GetAnnotation(obj.Data(), source.AnnotFollowCNAME); ok { + followCNAME, _ = strconv.ParseBool(value) + } + cn, _ := resources.GetAnnotation(obj.Data(), source.AnnotCommonName) cn = strings.TrimSpace(cn) var issuer *string @@ -83,7 +89,7 @@ func (s *CIngressSource) GetCertsInfo(logger logger.LogContext, obj resources.Ob } else { domains = mergeCommonName(cn, tls.Hosts) } - info.Certs[tls.SecretName] = source.CertInfo{SecretName: tls.SecretName, Domains: domains, IssuerName: issuer} + info.Certs[tls.SecretName] = source.CertInfo{SecretName: tls.SecretName, Domains: domains, IssuerName: issuer, FollowCNAME: followCNAME} } return info, err } diff --git a/vendor/github.com/gardener/controller-manager-library/hack/run-in.sh b/vendor/github.com/gardener/controller-manager-library/hack/run-in.sh old mode 100644 new mode 100755 diff --git a/vendor/github.com/go-acme/lego/v4/acme/api/api.go b/vendor/github.com/go-acme/lego/v4/acme/api/api.go index b4a3eeb9..69dda009 100644 --- a/vendor/github.com/go-acme/lego/v4/acme/api/api.go +++ b/vendor/github.com/go-acme/lego/v4/acme/api/api.go @@ -70,7 +70,7 @@ func (a *Core) post(uri string, reqBody, response interface{}) (*http.Response, } // postAsGet performs an HTTP POST ("POST-as-GET") request. -// https://tools.ietf.org/html/rfc8555#section-6.3 +// https://www.rfc-editor.org/rfc/rfc8555.html#section-6.3 func (a *Core) postAsGet(uri string, response interface{}) (*http.Response, error) { return a.retrievablePost(uri, []byte{}, response) } diff --git a/vendor/github.com/go-acme/lego/v4/acme/api/certificate.go b/vendor/github.com/go-acme/lego/v4/acme/api/certificate.go index 70bddaf3..baf5a385 100644 --- a/vendor/github.com/go-acme/lego/v4/acme/api/certificate.go +++ b/vendor/github.com/go-acme/lego/v4/acme/api/certificate.go @@ -39,7 +39,7 @@ func (c *CertificateService) GetAll(certURL string, bundle bool) (map[string]*ac certs := map[string]*acme.RawCertificate{certURL: cert} // URLs of "alternate" link relation - // - https://tools.ietf.org/html/rfc8555#section-7.4.2 + // - https://www.rfc-editor.org/rfc/rfc8555.html#section-7.4.2 alts := getLinks(headers, "alternate") for _, alt := range alts { @@ -92,7 +92,7 @@ func (c *CertificateService) getCertificateChain(cert []byte, headers http.Heade // The issuer certificate link may be supplied via an "up" link // in the response headers of a new certificate. - // See https://tools.ietf.org/html/rfc8555#section-7.4.2 + // See https://www.rfc-editor.org/rfc/rfc8555.html#section-7.4.2 up := getLink(headers, "up") issuer, err := c.getIssuerFromLink(up) diff --git a/vendor/github.com/go-acme/lego/v4/acme/api/internal/sender/useragent.go b/vendor/github.com/go-acme/lego/v4/acme/api/internal/sender/useragent.go index 5ceb83f4..bd6fea27 100644 --- a/vendor/github.com/go-acme/lego/v4/acme/api/internal/sender/useragent.go +++ b/vendor/github.com/go-acme/lego/v4/acme/api/internal/sender/useragent.go @@ -5,7 +5,7 @@ package sender const ( // ourUserAgent is the User-Agent of this underlying library package. - ourUserAgent = "xenolf-acme/4.5.3" + ourUserAgent = "xenolf-acme/4.6.0" // ourUserAgentComment is part of the UA comment linked to the version status of this underlying library package. // values: detach|release diff --git a/vendor/github.com/go-acme/lego/v4/acme/commons.go b/vendor/github.com/go-acme/lego/v4/acme/commons.go index 9635c6bd..b37fa07d 100644 --- a/vendor/github.com/go-acme/lego/v4/acme/commons.go +++ b/vendor/github.com/go-acme/lego/v4/acme/commons.go @@ -1,5 +1,5 @@ // Package acme contains all objects related the ACME endpoints. -// https://tools.ietf.org/html/rfc8555 +// https://www.rfc-editor.org/rfc/rfc8555.html package acme import ( @@ -7,20 +7,37 @@ import ( "time" ) -// Challenge statuses. -// https://tools.ietf.org/html/rfc8555#section-7.1.6 +// ACME status values of Account, Order, Authorization and Challenge objects. +// See https://www.rfc-editor.org/rfc/rfc8555.html#section-7.1.6 for details. const ( - StatusPending = "pending" - StatusInvalid = "invalid" - StatusValid = "valid" - StatusProcessing = "processing" StatusDeactivated = "deactivated" StatusExpired = "expired" + StatusInvalid = "invalid" + StatusPending = "pending" + StatusProcessing = "processing" + StatusReady = "ready" StatusRevoked = "revoked" + StatusUnknown = "unknown" + StatusValid = "valid" +) + +// CRL reason codes as defined in RFC 5280. +// https://datatracker.ietf.org/doc/html/rfc5280#section-5.3.1 +const ( + CRLReasonUnspecified uint = 0 + CRLReasonKeyCompromise uint = 1 + CRLReasonCACompromise uint = 2 + CRLReasonAffiliationChanged uint = 3 + CRLReasonSuperseded uint = 4 + CRLReasonCessationOfOperation uint = 5 + CRLReasonCertificateHold uint = 6 + CRLReasonRemoveFromCRL uint = 8 + CRLReasonPrivilegeWithdrawn uint = 9 + CRLReasonAACompromise uint = 10 ) // Directory the ACME directory object. -// - https://tools.ietf.org/html/rfc8555#section-7.1.1 +// - https://www.rfc-editor.org/rfc/rfc8555.html#section-7.1.1 type Directory struct { NewNonceURL string `json:"newNonce"` NewAccountURL string `json:"newAccount"` @@ -32,7 +49,7 @@ type Directory struct { } // Meta the ACME meta object (related to Directory). -// - https://tools.ietf.org/html/rfc8555#section-7.1.1 +// - https://www.rfc-editor.org/rfc/rfc8555.html#section-7.1.1 type Meta struct { // termsOfService (optional, string): // A URL identifying the current terms of service. @@ -65,8 +82,8 @@ type ExtendedAccount struct { } // Account the ACME account Object. -// - https://tools.ietf.org/html/rfc8555#section-7.1.2 -// - https://tools.ietf.org/html/rfc8555#section-7.3 +// - https://www.rfc-editor.org/rfc/rfc8555.html#section-7.1.2 +// - https://www.rfc-editor.org/rfc/rfc8555.html#section-7.3 type Account struct { // status (required, string): // The status of this account. @@ -112,7 +129,7 @@ type ExtendedOrder struct { } // Order the ACME order Object. -// - https://tools.ietf.org/html/rfc8555#section-7.1.3 +// - https://www.rfc-editor.org/rfc/rfc8555.html#section-7.1.3 type Order struct { // status (required, string): // The status of this order. @@ -165,7 +182,7 @@ type Order struct { } // Authorization the ACME authorization object. -// - https://tools.ietf.org/html/rfc8555#section-7.1.4 +// - https://www.rfc-editor.org/rfc/rfc8555.html#section-7.1.4 type Authorization struct { // status (required, string): // The status of this authorization. @@ -207,8 +224,8 @@ type ExtendedChallenge struct { } // Challenge the ACME challenge object. -// - https://tools.ietf.org/html/rfc8555#section-7.1.5 -// - https://tools.ietf.org/html/rfc8555#section-8 +// - https://www.rfc-editor.org/rfc/rfc8555.html#section-7.1.5 +// - https://www.rfc-editor.org/rfc/rfc8555.html#section-8 type Challenge struct { // type (required, string): // The type of challenge encoded in the object. @@ -241,23 +258,23 @@ type Challenge struct { // It MUST NOT contain any characters outside the base64url alphabet, // and MUST NOT include base64 padding characters ("="). // See [RFC4086] for additional information on randomness requirements. - // https://tools.ietf.org/html/rfc8555#section-8.3 - // https://tools.ietf.org/html/rfc8555#section-8.4 + // https://www.rfc-editor.org/rfc/rfc8555.html#section-8.3 + // https://www.rfc-editor.org/rfc/rfc8555.html#section-8.4 Token string `json:"token"` - // https://tools.ietf.org/html/rfc8555#section-8.1 + // https://www.rfc-editor.org/rfc/rfc8555.html#section-8.1 KeyAuthorization string `json:"keyAuthorization"` } // Identifier the ACME identifier object. -// - https://tools.ietf.org/html/rfc8555#section-9.7.7 +// - https://www.rfc-editor.org/rfc/rfc8555.html#section-9.7.7 type Identifier struct { Type string `json:"type"` Value string `json:"value"` } // CSRMessage Certificate Signing Request. -// - https://tools.ietf.org/html/rfc8555#section-7.4 +// - https://www.rfc-editor.org/rfc/rfc8555.html#section-7.4 type CSRMessage struct { // csr (required, string): // A CSR encoding the parameters for the certificate being requested [RFC2986]. @@ -267,8 +284,8 @@ type CSRMessage struct { } // RevokeCertMessage a certificate revocation message. -// - https://tools.ietf.org/html/rfc8555#section-7.6 -// - https://tools.ietf.org/html/rfc5280#section-5.3.1 +// - https://www.rfc-editor.org/rfc/rfc8555.html#section-7.6 +// - https://www.rfc-editor.org/rfc/rfc5280.html#section-5.3.1 type RevokeCertMessage struct { // certificate (required, string): // The certificate to be revoked, in the base64url-encoded version of the DER format. diff --git a/vendor/github.com/go-acme/lego/v4/acme/errors.go b/vendor/github.com/go-acme/lego/v4/acme/errors.go index 3d6a5ab1..acaea5f6 100644 --- a/vendor/github.com/go-acme/lego/v4/acme/errors.go +++ b/vendor/github.com/go-acme/lego/v4/acme/errors.go @@ -11,8 +11,8 @@ const ( ) // ProblemDetails the problem details object. -// - https://tools.ietf.org/html/rfc7807#section-3.1 -// - https://tools.ietf.org/html/rfc8555#section-7.3.3 +// - https://www.rfc-editor.org/rfc/rfc7807.html#section-3.1 +// - https://www.rfc-editor.org/rfc/rfc8555.html#section-7.3.3 type ProblemDetails struct { Type string `json:"type,omitempty"` Detail string `json:"detail,omitempty"` @@ -26,7 +26,7 @@ type ProblemDetails struct { } // SubProblem a "subproblems". -// - https://tools.ietf.org/html/rfc8555#section-6.7.1 +// - https://www.rfc-editor.org/rfc/rfc8555.html#section-6.7.1 type SubProblem struct { Type string `json:"type,omitempty"` Detail string `json:"detail,omitempty"` diff --git a/vendor/github.com/go-acme/lego/v4/certcrypto/crypto.go b/vendor/github.com/go-acme/lego/v4/certcrypto/crypto.go index 44f6e4d4..519c7a45 100644 --- a/vendor/github.com/go-acme/lego/v4/certcrypto/crypto.go +++ b/vendor/github.com/go-acme/lego/v4/certcrypto/crypto.go @@ -261,7 +261,7 @@ func generateDerCert(privateKey *rsa.PrivateKey, expiration time.Time, domain st } if expiration.IsZero() { - expiration = time.Now().Add(365) + expiration = time.Now().AddDate(1, 0, 0) } template := x509.Certificate{ diff --git a/vendor/github.com/go-acme/lego/v4/certificate/certificates.go b/vendor/github.com/go-acme/lego/v4/certificate/certificates.go index 497fe3dd..d6ab4b41 100644 --- a/vendor/github.com/go-acme/lego/v4/certificate/certificates.go +++ b/vendor/github.com/go-acme/lego/v4/certificate/certificates.go @@ -241,7 +241,7 @@ func (c *Certifier) getForOrder(domains []string, order acme.ExtendedOrder, bund commonName := domains[0] // RFC8555 Section 7.4 "Applying for Certificate Issuance" - // https://tools.ietf.org/html/rfc8555#section-7.4 + // https://www.rfc-editor.org/rfc/rfc8555.html#section-7.4 // says: // Clients SHOULD NOT make any assumptions about the sort order of // "identifiers" or "authorizations" elements in the returned order @@ -365,6 +365,11 @@ func (c *Certifier) checkResponse(order acme.ExtendedOrder, certRes *Resource, b // Revoke takes a PEM encoded certificate or bundle and tries to revoke it at the CA. func (c *Certifier) Revoke(cert []byte) error { + return c.RevokeWithReason(cert, nil) +} + +// RevokeWithReason takes a PEM encoded certificate or bundle and tries to revoke it at the CA. +func (c *Certifier) RevokeWithReason(cert []byte, reason *uint) error { certificates, err := certcrypto.ParsePEMBundle(cert) if err != nil { return err @@ -377,6 +382,7 @@ func (c *Certifier) Revoke(cert []byte) error { revokeMsg := acme.RevokeCertMessage{ Certificate: base64.RawURLEncoding.EncodeToString(x509Cert.Raw), + Reason: reason, } return c.core.Certificates.Revoke(revokeMsg) @@ -577,11 +583,11 @@ func checkOrderStatus(order acme.ExtendedOrder) (bool, error) { } } -// https://tools.ietf.org/html/rfc8555#section-7.1.4 +// https://www.rfc-editor.org/rfc/rfc8555.html#section-7.1.4 // The domain name MUST be encoded in the form in which it would appear in a certificate. // That is, it MUST be encoded according to the rules in Section 7 of [RFC5280]. // -// https://tools.ietf.org/html/rfc5280#section-7 +// https://www.rfc-editor.org/rfc/rfc5280.html#section-7 func sanitizeDomain(domains []string) []string { var sanitizedDomains []string for _, domain := range domains { diff --git a/vendor/github.com/go-acme/lego/v4/challenge/challenges.go b/vendor/github.com/go-acme/lego/v4/challenge/challenges.go index 4f6dc6bf..39bf3bee 100644 --- a/vendor/github.com/go-acme/lego/v4/challenge/challenges.go +++ b/vendor/github.com/go-acme/lego/v4/challenge/challenges.go @@ -10,15 +10,15 @@ import ( type Type string const ( - // HTTP01 is the "http-01" ACME challenge https://tools.ietf.org/html/rfc8555#section-8.3 + // HTTP01 is the "http-01" ACME challenge https://www.rfc-editor.org/rfc/rfc8555.html#section-8.3 // Note: ChallengePath returns the URL path to fulfill this challenge. HTTP01 = Type("http-01") - // DNS01 is the "dns-01" ACME challenge https://tools.ietf.org/html/rfc8555#section-8.4 + // DNS01 is the "dns-01" ACME challenge https://www.rfc-editor.org/rfc/rfc8555.html#section-8.4 // Note: GetRecord returns a DNS record which will fulfill this challenge. DNS01 = Type("dns-01") - // TLSALPN01 is the "tls-alpn-01" ACME challenge https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-07 + // TLSALPN01 is the "tls-alpn-01" ACME challenge https://www.rfc-editor.org/rfc/rfc8737.html TLSALPN01 = Type("tls-alpn-01") ) diff --git a/vendor/github.com/go-acme/lego/v4/challenge/dns01/dns_challenge_manual.go b/vendor/github.com/go-acme/lego/v4/challenge/dns01/dns_challenge_manual.go index b2f5d41f..ced3ab58 100644 --- a/vendor/github.com/go-acme/lego/v4/challenge/dns01/dns_challenge_manual.go +++ b/vendor/github.com/go-acme/lego/v4/challenge/dns01/dns_challenge_manual.go @@ -8,7 +8,7 @@ import ( ) const ( - dnsTemplate = `%s %d IN TXT "%s"` + dnsTemplate = `%s %d IN TXT %q` ) // DNSProviderManual is an implementation of the ChallengeProvider interface. diff --git a/vendor/github.com/go-acme/lego/v4/challenge/http01/domain_matcher.go b/vendor/github.com/go-acme/lego/v4/challenge/http01/domain_matcher.go index 6ac6eab6..231f02d7 100644 --- a/vendor/github.com/go-acme/lego/v4/challenge/http01/domain_matcher.go +++ b/vendor/github.com/go-acme/lego/v4/challenge/http01/domain_matcher.go @@ -23,7 +23,7 @@ import ( // RFC7239 has standardized the different forwarding headers into a single header named Forwarded. // The header value has a different format, so you should use forwardedMatcher // when the http01.ProviderServer operates behind a RFC7239 compatible proxy. -// https://tools.ietf.org/html/rfc7239 +// https://www.rfc-editor.org/rfc/rfc7239.html // // Note: RFC7239 also reminds us, "that an HTTP list [...] may be split over multiple header fields" (section 7.1), // meaning that @@ -66,7 +66,7 @@ func (m arbitraryMatcher) matches(r *http.Request, domain string) bool { } // forwardedMatcher checks whether the Forwarded header contains a "host" element starting with a domain name. -// See https://tools.ietf.org/html/rfc7239 for details. +// See https://www.rfc-editor.org/rfc/rfc7239.html for details. type forwardedMatcher struct{} func (m *forwardedMatcher) name() string { diff --git a/vendor/github.com/go-acme/lego/v4/challenge/http01/http_challenge_server.go b/vendor/github.com/go-acme/lego/v4/challenge/http01/http_challenge_server.go index 84a33530..f69f5ac1 100644 --- a/vendor/github.com/go-acme/lego/v4/challenge/http01/http_challenge_server.go +++ b/vendor/github.com/go-acme/lego/v4/challenge/http01/http_challenge_server.go @@ -2,9 +2,11 @@ package http01 import ( "fmt" + "io/fs" "net" "net/http" "net/textproto" + "os" "strings" "github.com/go-acme/lego/v4/log" @@ -14,8 +16,11 @@ import ( // It may be instantiated without using the NewProviderServer function if // you want only to use the default values. type ProviderServer struct { - iface string - port string + address string + network string // must be valid argument to net.Listen + + socketMode fs.FileMode + matcher domainMatcher done chan bool listener net.Listener @@ -29,24 +34,34 @@ func NewProviderServer(iface, port string) *ProviderServer { port = "80" } - return &ProviderServer{iface: iface, port: port, matcher: &hostMatcher{}} + return &ProviderServer{network: "tcp", address: net.JoinHostPort(iface, port), matcher: &hostMatcher{}} +} + +func NewUnixProviderServer(socketPath string, mode fs.FileMode) *ProviderServer { + return &ProviderServer{network: "unix", address: socketPath, socketMode: mode, matcher: &hostMatcher{}} } // Present starts a web server and makes the token available at `ChallengePath(token)` for web requests. func (s *ProviderServer) Present(domain, token, keyAuth string) error { var err error - s.listener, err = net.Listen("tcp", s.GetAddress()) + s.listener, err = net.Listen(s.network, s.GetAddress()) if err != nil { return fmt.Errorf("could not start HTTP server for challenge: %w", err) } + if s.network == "unix" { + if err = os.Chmod(s.address, s.socketMode); err != nil { + return fmt.Errorf("chmod %s: %w", s.address, err) + } + } + s.done = make(chan bool) go s.serve(domain, token, keyAuth) return nil } func (s *ProviderServer) GetAddress() string { - return net.JoinHostPort(s.iface, s.port) + return s.address } // CleanUp closes the HTTP server and removes the token from `ChallengePath(token)`. @@ -69,7 +84,7 @@ func (s *ProviderServer) CleanUp(domain, token, keyAuth string) error { // // The exact behavior depends on the value of headerName: // - "" (the empty string) and "Host" will restore the default and only check the Host header -// - "Forwarded" will look for a Forwarded header, and inspect it according to https://tools.ietf.org/html/rfc7239 +// - "Forwarded" will look for a Forwarded header, and inspect it according to https://www.rfc-editor.org/rfc/rfc7239.html // - any other value will check the header value with the same name. func (s *ProviderServer) SetProxyHeader(headerName string) { switch h := textproto.CanonicalMIMEHeaderKey(headerName); h { @@ -85,7 +100,7 @@ func (s *ProviderServer) SetProxyHeader(headerName string) { func (s *ProviderServer) serve(domain, token, keyAuth string) { path := ChallengePath(token) - // The incoming request must will be validated to prevent DNS rebind attacks. + // The incoming request will be validated to prevent DNS rebind attacks. // We only respond with the keyAuth, when we're receiving a GET requests with // the "Host" header matching the domain (the latter is configurable though SetProxyHeader). mux := http.NewServeMux() @@ -99,7 +114,7 @@ func (s *ProviderServer) serve(domain, token, keyAuth string) { } log.Infof("[%s] Served key authentication", domain) } else { - log.Warnf("Received request for domain %s with method %s but the domain did not match any challenge. Please ensure your are passing the %s header properly.", r.Host, r.Method, s.matcher.name()) + log.Warnf("Received request for domain %s with method %s but the domain did not match any challenge. Please ensure you are passing the %s header properly.", r.Host, r.Method, s.matcher.name()) _, err := w.Write([]byte("TEST")) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) diff --git a/vendor/github.com/go-acme/lego/v4/challenge/tlsalpn01/tls_alpn_challenge.go b/vendor/github.com/go-acme/lego/v4/challenge/tlsalpn01/tls_alpn_challenge.go index 7eb02cf0..04ba7150 100644 --- a/vendor/github.com/go-acme/lego/v4/challenge/tlsalpn01/tls_alpn_challenge.go +++ b/vendor/github.com/go-acme/lego/v4/challenge/tlsalpn01/tls_alpn_challenge.go @@ -16,7 +16,7 @@ import ( ) // idPeAcmeIdentifierV1 is the SMI Security for PKIX Certification Extension OID referencing the ACME extension. -// Reference: https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-07#section-6.1 +// Reference: https://www.rfc-editor.org/rfc/rfc8737.html#section-6.1 var idPeAcmeIdentifierV1 = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 31} type ValidateFunc func(core *api.Core, domain string, chlng acme.Challenge) error @@ -83,7 +83,7 @@ func ChallengeBlocks(domain, keyAuth string) ([]byte, []byte, error) { // Add the keyAuth digest as the acmeValidation-v1 extension // (marked as critical such that it won't be used by non-ACME software). - // Reference: https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-07#section-3 + // Reference: https://www.rfc-editor.org/rfc/rfc8737.html#section-3 extensions := []pkix.Extension{ { Id: idPeAcmeIdentifierV1, diff --git a/vendor/github.com/go-acme/lego/v4/challenge/tlsalpn01/tls_alpn_challenge_server.go b/vendor/github.com/go-acme/lego/v4/challenge/tlsalpn01/tls_alpn_challenge_server.go index a49dd937..e0976a6b 100644 --- a/vendor/github.com/go-acme/lego/v4/challenge/tlsalpn01/tls_alpn_challenge_server.go +++ b/vendor/github.com/go-acme/lego/v4/challenge/tlsalpn01/tls_alpn_challenge_server.go @@ -61,7 +61,7 @@ func (s *ProviderServer) Present(domain, token, keyAuth string) error { // We must set that the `acme-tls/1` application level protocol is supported // so that the protocol negotiation can succeed. Reference: - // https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-07#section-6.2 + // https://www.rfc-editor.org/rfc/rfc8737.html#section-6.2 tlsConf.NextProtos = []string{ACMETLS1Protocol} // Create the listener with the created tls.Config. diff --git a/vendor/github.com/go-acme/lego/v4/lego/client_config.go b/vendor/github.com/go-acme/lego/v4/lego/client_config.go index ed8c4a57..e75739c1 100644 --- a/vendor/github.com/go-acme/lego/v4/lego/client_config.go +++ b/vendor/github.com/go-acme/lego/v4/lego/client_config.go @@ -7,6 +7,8 @@ import ( "net" "net/http" "os" + "strconv" + "strings" "time" "github.com/go-acme/lego/v4/certcrypto" @@ -16,10 +18,15 @@ import ( const ( // caCertificatesEnvVar is the environment variable name that can be used to // specify the path to PEM encoded CA Certificates that can be used to - // authenticate an ACME server with a HTTPS certificate not issued by a CA in + // authenticate an ACME server with an HTTPS certificate not issued by a CA in // the system-wide trusted root list. + // Multiple file paths can be added by using os.PathListSeparator as a separator. caCertificatesEnvVar = "LEGO_CA_CERTIFICATES" + // caSystemCertPool is the environment variable name that can be used to define + // if the certificates pool must use a copy of the system cert pool. + caSystemCertPool = "LEGO_CA_SYSTEM_CERT_POOL" + // caServerNameEnvVar is the environment variable name that can be used to // specify the CA server name that can be used to // authenticate an ACME server with a HTTPS certificate not issued by a CA in @@ -81,23 +88,44 @@ func createDefaultHTTPClient() *http.Client { } // initCertPool creates a *x509.CertPool populated with the PEM certificates -// found in the filepath specified in the caCertificatesEnvVar OS environment -// variable. If the caCertificatesEnvVar is not set then initCertPool will -// return nil. If there is an error creating a *x509.CertPool from the provided -// caCertificatesEnvVar value then initCertPool will panic. +// found in the filepath specified in the caCertificatesEnvVar OS environment variable. +// If the caCertificatesEnvVar is not set then initCertPool will return nil. +// If there is an error creating a *x509.CertPool from the provided caCertificatesEnvVar value then initCertPool will panic. +// If the caSystemCertPool is set to a "truthy value" (`1`, `t`, `T`, `TRUE`, `true`, `True`) then a copy of system cert pool will be used. +// caSystemCertPool requires caCertificatesEnvVar to be set. func initCertPool() *x509.CertPool { - if customCACertsPath := os.Getenv(caCertificatesEnvVar); customCACertsPath != "" { - customCAs, err := os.ReadFile(customCACertsPath) + customCACertsPath := os.Getenv(caCertificatesEnvVar) + if customCACertsPath == "" { + return nil + } + + certPool := getCertPool() + + for _, customPath := range strings.Split(customCACertsPath, string(os.PathListSeparator)) { + customCAs, err := os.ReadFile(customPath) if err != nil { panic(fmt.Sprintf("error reading %s=%q: %v", - caCertificatesEnvVar, customCACertsPath, err)) + caCertificatesEnvVar, customPath, err)) } - certPool := x509.NewCertPool() + if ok := certPool.AppendCertsFromPEM(customCAs); !ok { panic(fmt.Sprintf("error creating x509 cert pool from %s=%q: %v", - caCertificatesEnvVar, customCACertsPath, err)) + caCertificatesEnvVar, customPath, err)) } - return certPool } - return nil + + return certPool +} + +func getCertPool() *x509.CertPool { + useSystemCertPool, _ := strconv.ParseBool(os.Getenv(caSystemCertPool)) + if !useSystemCertPool { + return x509.NewCertPool() + } + + pool, err := x509.SystemCertPool() + if err == nil { + return pool + } + return x509.NewCertPool() } diff --git a/vendor/modules.txt b/vendor/modules.txt index 9d8aa3bd..d737c78f 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -94,7 +94,7 @@ github.com/gardener/external-dns-management/pkg/dns # github.com/ghodss/yaml v1.0.0 ## explicit github.com/ghodss/yaml -# github.com/go-acme/lego/v4 v4.5.3 +# github.com/go-acme/lego/v4 v4.6.0 ## explicit; go 1.16 github.com/go-acme/lego/v4/acme github.com/go-acme/lego/v4/acme/api