Skip to content

Commit 34997d3

Browse files
committed
Accommodate ZeroSSL CSR requirements; fix DNS prop check
1 parent 32f8c88 commit 34997d3

File tree

5 files changed

+52
-16
lines changed

5 files changed

+52
-16
lines changed

config.go

+35-5
Original file line numberDiff line numberDiff line change
@@ -562,7 +562,7 @@ func (cfg *Config) obtainCert(ctx context.Context, name string, interactive bool
562562
}
563563
}
564564

565-
csr, err := cfg.generateCSR(privKey, []string{name})
565+
csr, err := cfg.generateCSR(privKey, []string{name}, false)
566566
if err != nil {
567567
return err
568568
}
@@ -584,7 +584,19 @@ func (cfg *Config) obtainCert(ctx context.Context, name string, interactive bool
584584
}
585585
}
586586

587-
issuedCert, err = issuer.Issue(ctx, csr)
587+
// TODO: ZeroSSL's API currently requires CommonName to be set, and requires it be
588+
// distinct from SANs. If this was a cert it would violate the BRs, but their certs
589+
// are compliant, so their CSR requirements just needlessly add friction, complexity,
590+
// and inefficiency for clients. CommonName has been deprecated for 25+ years.
591+
useCSR := csr
592+
if _, ok := issuer.(*ZeroSSLIssuer); ok {
593+
useCSR, err = cfg.generateCSR(privKey, []string{name}, true)
594+
if err != nil {
595+
return err
596+
}
597+
}
598+
599+
issuedCert, err = issuer.Issue(ctx, useCSR)
588600
if err == nil {
589601
issuerUsed = issuer
590602
break
@@ -808,7 +820,7 @@ func (cfg *Config) renewCert(ctx context.Context, name string, force, interactiv
808820
}
809821
}
810822

811-
csr, err := cfg.generateCSR(privateKey, []string{name})
823+
csr, err := cfg.generateCSR(privateKey, []string{name}, false)
812824
if err != nil {
813825
return err
814826
}
@@ -818,6 +830,18 @@ func (cfg *Config) renewCert(ctx context.Context, name string, force, interactiv
818830
var issuerUsed Issuer
819831
var issuerKeys []string
820832
for _, issuer := range cfg.Issuers {
833+
// TODO: ZeroSSL's API currently requires CommonName to be set, and requires it be
834+
// distinct from SANs. If this was a cert it would violate the BRs, but their certs
835+
// are compliant, so their CSR requirements just needlessly add friction, complexity,
836+
// and inefficiency for clients. CommonName has been deprecated for 25+ years.
837+
useCSR := csr
838+
if _, ok := issuer.(*ZeroSSLIssuer); ok {
839+
useCSR, err = cfg.generateCSR(privateKey, []string{name}, true)
840+
if err != nil {
841+
return err
842+
}
843+
}
844+
821845
issuerKeys = append(issuerKeys, issuer.IssuerKey())
822846
if prechecker, ok := issuer.(PreChecker); ok {
823847
err = prechecker.PreCheck(ctx, []string{name}, interactive)
@@ -826,7 +850,7 @@ func (cfg *Config) renewCert(ctx context.Context, name string, force, interactiv
826850
}
827851
}
828852

829-
issuedCert, err = issuer.Issue(ctx, csr)
853+
issuedCert, err = issuer.Issue(ctx, useCSR)
830854
if err == nil {
831855
issuerUsed = issuer
832856
break
@@ -898,10 +922,16 @@ func (cfg *Config) renewCert(ctx context.Context, name string, force, interactiv
898922
return err
899923
}
900924

901-
func (cfg *Config) generateCSR(privateKey crypto.PrivateKey, sans []string) (*x509.CertificateRequest, error) {
925+
// generateCSR generates a CSR for the given SANs. If useCN is true, CommonName will get the first SAN (TODO: this is only a temporary hack for ZeroSSL API support).
926+
func (cfg *Config) generateCSR(privateKey crypto.PrivateKey, sans []string, useCN bool) (*x509.CertificateRequest, error) {
902927
csrTemplate := new(x509.CertificateRequest)
903928

904929
for _, name := range sans {
930+
// TODO: This is a temporary hack to support ZeroSSL API...
931+
if useCN && csrTemplate.Subject.CommonName == "" && len(name) <= 64 {
932+
csrTemplate.Subject.CommonName = name
933+
continue
934+
}
905935
if ip := net.ParseIP(name); ip != nil {
906936
csrTemplate.IPAddresses = append(csrTemplate.IPAddresses, ip)
907937
} else if strings.Contains(name, "@") {

crypto.go

+5
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,11 @@ func hashCertificateChain(certChain [][]byte) string {
280280

281281
func namesFromCSR(csr *x509.CertificateRequest) []string {
282282
var nameSet []string
283+
// TODO: CommonName should not be used (it has been deprecated for 25+ years,
284+
// but Sectigo CA still requires it to be filled out and not overlap SANs...)
285+
if csr.Subject.CommonName != "" {
286+
nameSet = append(nameSet, csr.Subject.CommonName)
287+
}
283288
nameSet = append(nameSet, csr.DNSNames...)
284289
nameSet = append(nameSet, csr.EmailAddresses...)
285290
for _, v := range csr.IPAddresses {

dnsutil.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ func checkDNSPropagation(fqdn string, recType uint16, expectedValue string, chec
222222
if recType != dns.TypeCNAME {
223223
r, err := dnsQuery(fqdn, recType, resolvers, true)
224224
if err != nil {
225-
return false, err
225+
return false, fmt.Errorf("CNAME dns query: %v", err)
226226
}
227227
if r.Rcode == dns.RcodeSuccess {
228228
fqdn = updateDomainWithCName(r, fqdn)
@@ -232,7 +232,7 @@ func checkDNSPropagation(fqdn string, recType uint16, expectedValue string, chec
232232
if checkAuthoritativeServers {
233233
authoritativeServers, err := lookupNameservers(fqdn, resolvers)
234234
if err != nil {
235-
return false, err
235+
return false, fmt.Errorf("looking up authoritative nameservers: %v", err)
236236
}
237237
populateNameserverPorts(authoritativeServers)
238238
resolvers = authoritativeServers
@@ -244,9 +244,9 @@ func checkDNSPropagation(fqdn string, recType uint16, expectedValue string, chec
244244
// checkAuthoritativeNss queries each of the given nameservers for the expected TXT record.
245245
func checkAuthoritativeNss(fqdn string, recType uint16, expectedValue string, nameservers []string) (bool, error) {
246246
for _, ns := range nameservers {
247-
r, err := dnsQuery(fqdn, recType, []string{net.JoinHostPort(ns, "53")}, true)
247+
r, err := dnsQuery(fqdn, recType, []string{ns}, true)
248248
if err != nil {
249-
return false, err
249+
return false, fmt.Errorf("querying authoritative nameservers: %v", err)
250250
}
251251

252252
if r.Rcode != dns.RcodeSuccess {
@@ -290,12 +290,12 @@ func lookupNameservers(fqdn string, resolvers []string) ([]string, error) {
290290

291291
zone, err := findZoneByFQDN(fqdn, resolvers)
292292
if err != nil {
293-
return nil, fmt.Errorf("could not determine the zone: %w", err)
293+
return nil, fmt.Errorf("could not determine the zone for '%s': %w", fqdn, err)
294294
}
295295

296296
r, err := dnsQuery(zone, dns.TypeNS, resolvers, true)
297297
if err != nil {
298-
return nil, err
298+
return nil, fmt.Errorf("querying NS resolver for zone '%s' recursively: %v", zone, err)
299299
}
300300

301301
for _, rr := range r.Answer {

solvers.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,8 @@ func (s *DNSManager) wait(ctx context.Context, zrec zoneRecord) error {
430430
recType = dns.TypeCNAME
431431
}
432432

433+
absName := libdns.AbsoluteName(zrec.record.Name, zrec.zone)
434+
433435
var err error
434436
start := time.Now()
435437
for time.Since(start) < timeout {
@@ -439,9 +441,9 @@ func (s *DNSManager) wait(ctx context.Context, zrec zoneRecord) error {
439441
return ctx.Err()
440442
}
441443
var ready bool
442-
ready, err = checkDNSPropagation(libdns.AbsoluteName(zrec.record.Name, zrec.zone), recType, zrec.record.Value, checkAuthoritativeServers, resolvers)
444+
ready, err = checkDNSPropagation(absName, recType, zrec.record.Value, checkAuthoritativeServers, resolvers)
443445
if err != nil {
444-
return fmt.Errorf("checking DNS propagation of %q: %w", zrec.record.Name, err)
446+
return fmt.Errorf("checking DNS propagation of %q (relative=%s zone=%s resolvers=%v): %w", absName, zrec.record.Name, zrec.zone, resolvers, err)
445447
}
446448
if ready {
447449
return nil

zerosslissuer.go

+2-3
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,13 @@ func NewZeroSSLIssuer(cfg *Config, template ZeroSSLIssuer) *ZeroSSLIssuer {
4242
}
4343

4444
// ZeroSSLIssuer can get certificates from ZeroSSL's API. (To use ZeroSSL's ACME
45-
// endpoint, use the ACMEIssuer instead.)
45+
// endpoint, use the ACMEIssuer instead.) Note that use of the API is restricted
46+
// by payment tier.
4647
type ZeroSSLIssuer struct {
4748
// The API key (or "access key") for using the ZeroSSL API.
4849
APIKey string
4950

5051
// How many days the certificate should be valid for.
51-
// Note that customizing certificate lifetime may be
52-
// a paid feature.
5352
ValidityDays int
5453

5554
// The host to bind to when opening a listener for

0 commit comments

Comments
 (0)