diff options
Diffstat (limited to 'vendor/github.com/xenolf/lego/acme')
10 files changed, 85 insertions, 66 deletions
diff --git a/vendor/github.com/xenolf/lego/acme/client.go b/vendor/github.com/xenolf/lego/acme/client.go index 5eae8d26a..9f837af36 100644 --- a/vendor/github.com/xenolf/lego/acme/client.go +++ b/vendor/github.com/xenolf/lego/acme/client.go @@ -97,7 +97,7 @@ func NewClient(caDirURL string, user User, keyType KeyType) (*Client, error) { return &Client{directory: dir, user: user, jws: jws, keyType: keyType, solvers: solvers}, nil } -// SetChallengeProvider specifies a custom provider that will make the solution available +// SetChallengeProvider specifies a custom provider p that can solve the given challenge type. func (c *Client) SetChallengeProvider(challenge Challenge, p ChallengeProvider) error { switch challenge { case HTTP01: @@ -115,6 +115,9 @@ func (c *Client) SetChallengeProvider(challenge Challenge, p ChallengeProvider) // SetHTTPAddress specifies a custom interface:port to be used for HTTP based challenges. // If this option is not used, the default port 80 and all interfaces will be used. // To only specify a port and no interface use the ":port" notation. +// +// NOTE: This REPLACES any custom HTTP provider previously set by calling +// c.SetChallengeProvider with the default HTTP challenge provider. func (c *Client) SetHTTPAddress(iface string) error { host, port, err := net.SplitHostPort(iface) if err != nil { @@ -131,6 +134,9 @@ func (c *Client) SetHTTPAddress(iface string) error { // SetTLSAddress specifies a custom interface:port to be used for TLS based challenges. // If this option is not used, the default port 443 and all interfaces will be used. // To only specify a port and no interface use the ":port" notation. +// +// NOTE: This REPLACES any custom TLS-SNI provider previously set by calling +// c.SetChallengeProvider with the default TLS-SNI challenge provider. func (c *Client) SetTLSAddress(iface string) error { host, port, err := net.SplitHostPort(iface) if err != nil { @@ -347,7 +353,7 @@ DNSNames: // your issued certificate as a bundle. // This function will never return a partial certificate. If one domain in the list fails, // the whole certificate will fail. -func (c *Client) ObtainCertificate(domains []string, bundle bool, privKey crypto.PrivateKey) (CertificateResource, map[string]error) { +func (c *Client) ObtainCertificate(domains []string, bundle bool, privKey crypto.PrivateKey, mustStaple bool) (CertificateResource, map[string]error) { if bundle { logf("[INFO][%s] acme: Obtaining bundled SAN certificate", strings.Join(domains, ", ")) } else { @@ -368,7 +374,7 @@ func (c *Client) ObtainCertificate(domains []string, bundle bool, privKey crypto logf("[INFO][%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", ")) - cert, err := c.requestCertificate(challenges, bundle, privKey) + cert, err := c.requestCertificate(challenges, bundle, privKey, mustStaple) if err != nil { for _, chln := range challenges { failures[chln.Domain] = err @@ -404,7 +410,7 @@ func (c *Client) RevokeCertificate(certificate []byte) error { // If bundle is true, the []byte contains both the issuer certificate and // your issued certificate as a bundle. // For private key reuse the PrivateKey property of the passed in CertificateResource should be non-nil. -func (c *Client) RenewCertificate(cert CertificateResource, bundle bool) (CertificateResource, error) { +func (c *Client) RenewCertificate(cert CertificateResource, bundle, mustStaple bool) (CertificateResource, error) { // Input certificate is PEM encoded. Decode it here as we may need the decoded // cert later on in the renewal process. The input may be a bundle or a single certificate. certificates, err := parsePEMBundle(cert.Certificate) @@ -421,50 +427,7 @@ func (c *Client) RenewCertificate(cert CertificateResource, bundle bool) (Certif timeLeft := x509Cert.NotAfter.Sub(time.Now().UTC()) logf("[INFO][%s] acme: Trying renewal with %d hours remaining", cert.Domain, int(timeLeft.Hours())) - // The first step of renewal is to check if we get a renewed cert - // directly from the cert URL. - resp, err := httpGet(cert.CertURL) - if err != nil { - return CertificateResource{}, err - } - defer resp.Body.Close() - serverCertBytes, err := ioutil.ReadAll(resp.Body) - if err != nil { - return CertificateResource{}, err - } - - serverCert, err := x509.ParseCertificate(serverCertBytes) - if err != nil { - return CertificateResource{}, err - } - - // If the server responds with a different certificate we are effectively renewed. - // TODO: Further test if we can actually use the new certificate (Our private key works) - if !x509Cert.Equal(serverCert) { - logf("[INFO][%s] acme: Server responded with renewed certificate", cert.Domain) - issuedCert := pemEncode(derCertificateBytes(serverCertBytes)) - // If bundle is true, we want to return a certificate bundle. - // To do this, we need the issuer certificate. - if bundle { - // The issuer certificate link is always supplied via an "up" link - // in the response headers of a new certificate. - links := parseLinks(resp.Header["Link"]) - issuerCert, err := c.getIssuerCertificate(links["up"]) - if err != nil { - // If we fail to acquire the issuer cert, return the issued certificate - do not fail. - logf("[ERROR][%s] acme: Could not bundle issuer certificate: %v", cert.Domain, err) - } else { - // Success - append the issuer cert to the issued cert. - issuerCert = pemEncode(derCertificateBytes(issuerCert)) - issuedCert = append(issuedCert, issuerCert...) - } - } - - cert.Certificate = issuedCert - return cert, nil - } - - // If the certificate is the same, then we need to request a new certificate. + // We always need to request a new certificate to renew. // Start by checking to see if the certificate was based off a CSR, and // use that if it's defined. if len(cert.CSR) > 0 { @@ -499,7 +462,7 @@ func (c *Client) RenewCertificate(cert CertificateResource, bundle bool) (Certif domains = append(domains, x509Cert.Subject.CommonName) } - newCert, failures := c.ObtainCertificate(domains, bundle, privKey) + newCert, failures := c.ObtainCertificate(domains, bundle, privKey, mustStaple) return newCert, failures[cert.Domain] } @@ -600,7 +563,7 @@ func (c *Client) getChallenges(domains []string) ([]authorizationResource, map[s return challenges, failures } -func (c *Client) requestCertificate(authz []authorizationResource, bundle bool, privKey crypto.PrivateKey) (CertificateResource, error) { +func (c *Client) requestCertificate(authz []authorizationResource, bundle bool, privKey crypto.PrivateKey, mustStaple bool) (CertificateResource, error) { if len(authz) == 0 { return CertificateResource{}, errors.New("Passed no authorizations to requestCertificate!") } @@ -621,7 +584,7 @@ func (c *Client) requestCertificate(authz []authorizationResource, bundle bool, } // TODO: should the CSR be customizable? - csr, err := generateCsr(privKey, commonName.Domain, san) + csr, err := generateCsr(privKey, commonName.Domain, san, mustStaple) if err != nil { return CertificateResource{}, err } diff --git a/vendor/github.com/xenolf/lego/acme/crypto.go b/vendor/github.com/xenolf/lego/acme/crypto.go index af97f5d1e..c63b23b99 100644 --- a/vendor/github.com/xenolf/lego/acme/crypto.go +++ b/vendor/github.com/xenolf/lego/acme/crypto.go @@ -20,6 +20,8 @@ import ( "strings" "time" + "encoding/asn1" + "golang.org/x/crypto/ocsp" ) @@ -47,6 +49,12 @@ const ( OCSPServerFailed = ocsp.ServerFailed ) +// Constants for OCSP must staple +var ( + tlsFeatureExtensionOID = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 24} + ocspMustStapleFeature = []byte{0x30, 0x03, 0x02, 0x01, 0x05} +) + // GetOCSPForCert takes a PEM encoded cert or cert bundle returning the raw OCSP response, // the parsed response, and an error, if any. The returned []byte can be passed directly // into the OCSPStaple property of a tls.Certificate. If the bundle only contains the @@ -206,7 +214,7 @@ func generatePrivateKey(keyType KeyType) (crypto.PrivateKey, error) { return nil, fmt.Errorf("Invalid KeyType: %s", keyType) } -func generateCsr(privateKey crypto.PrivateKey, domain string, san []string) ([]byte, error) { +func generateCsr(privateKey crypto.PrivateKey, domain string, san []string, mustStaple bool) ([]byte, error) { template := x509.CertificateRequest{ Subject: pkix.Name{ CommonName: domain, @@ -217,6 +225,13 @@ func generateCsr(privateKey crypto.PrivateKey, domain string, san []string) ([]b template.DNSNames = san } + if mustStaple { + template.Extensions = append(template.Extensions, pkix.Extension{ + Id: tlsFeatureExtensionOID, + Value: ocspMustStapleFeature, + }) + } + return x509.CreateCertificateRequest(rand.Reader, &template, privateKey) } diff --git a/vendor/github.com/xenolf/lego/acme/crypto_test.go b/vendor/github.com/xenolf/lego/acme/crypto_test.go index d2fc5088b..6f43835fb 100644 --- a/vendor/github.com/xenolf/lego/acme/crypto_test.go +++ b/vendor/github.com/xenolf/lego/acme/crypto_test.go @@ -24,7 +24,7 @@ func TestGenerateCSR(t *testing.T) { t.Fatal("Error generating private key:", err) } - csr, err := generateCsr(key, "fizz.buzz", nil) + csr, err := generateCsr(key, "fizz.buzz", nil, true) if err != nil { t.Error("Error generating CSR:", err) } diff --git a/vendor/github.com/xenolf/lego/acme/dns_challenge.go b/vendor/github.com/xenolf/lego/acme/dns_challenge.go index c5fd354a1..30f2170ff 100644 --- a/vendor/github.com/xenolf/lego/acme/dns_challenge.go +++ b/vendor/github.com/xenolf/lego/acme/dns_challenge.go @@ -23,14 +23,37 @@ var ( fqdnToZone = map[string]string{} ) -var RecursiveNameservers = []string{ +const defaultResolvConf = "/etc/resolv.conf" + +var defaultNameservers = []string{ "google-public-dns-a.google.com:53", "google-public-dns-b.google.com:53", } +var RecursiveNameservers = getNameservers(defaultResolvConf, defaultNameservers) + // DNSTimeout is used to override the default DNS timeout of 10 seconds. var DNSTimeout = 10 * time.Second +// getNameservers attempts to get systems nameservers before falling back to the defaults +func getNameservers(path string, defaults []string) []string { + config, err := dns.ClientConfigFromFile(path) + if err != nil || len(config.Servers) == 0 { + return defaults + } + + systemNameservers := []string{} + for _, server := range config.Servers { + // ensure all servers have a port number + if _, _, err := net.SplitHostPort(server); err != nil { + systemNameservers = append(systemNameservers, net.JoinHostPort(server, "53")) + } else { + systemNameservers = append(systemNameservers, server) + } + } + return systemNameservers +} + // DNS01Record returns a DNS record which will fulfill the `dns-01` challenge func DNS01Record(domain, keyAuth string) (fqdn string, value string, ttl int) { keyAuthShaBytes := sha256.Sum256([]byte(keyAuth)) @@ -75,7 +98,7 @@ func (s *dnsChallenge) Solve(chlng challenge, domain string) error { fqdn, value, _ := DNS01Record(domain, keyAuth) - logf("[INFO][%s] Checking DNS record propagation...", domain) + logf("[INFO][%s] Checking DNS record propagation using %+v", domain, RecursiveNameservers) var timeout, interval time.Duration switch provider := s.provider.(type) { diff --git a/vendor/github.com/xenolf/lego/acme/dns_challenge_test.go b/vendor/github.com/xenolf/lego/acme/dns_challenge_test.go index 6e448854b..597aaac17 100644 --- a/vendor/github.com/xenolf/lego/acme/dns_challenge_test.go +++ b/vendor/github.com/xenolf/lego/acme/dns_challenge_test.go @@ -85,6 +85,15 @@ var checkAuthoritativeNssTestsErr = []struct { }, } +var checkResolvConfServersTests = []struct { + fixture string + expected []string + defaults []string +}{ + {"testdata/resolv.conf.1", []string{"10.200.3.249:53", "10.200.3.250:5353", "[2001:4860:4860::8844]:53", "[10.0.0.1]:5353"}, []string{"127.0.0.1:53"}}, + {"testdata/resolv.conf.nonexistant", []string{"127.0.0.1:53"}, []string{"127.0.0.1:53"}}, +} + func TestDNSValidServerResponse(t *testing.T) { PreCheckDNS = func(fqdn, value string) (bool, error) { return true, nil @@ -183,3 +192,15 @@ func TestCheckAuthoritativeNssErr(t *testing.T) { } } } + +func TestResolveConfServers(t *testing.T) { + for _, tt := range checkResolvConfServersTests { + result := getNameservers(tt.fixture, tt.defaults) + + sort.Strings(result) + sort.Strings(tt.expected) + if !reflect.DeepEqual(result, tt.expected) { + t.Errorf("#%s: expected %q; got %q", tt.fixture, tt.expected, result) + } + } +} diff --git a/vendor/github.com/xenolf/lego/acme/http_challenge_server.go b/vendor/github.com/xenolf/lego/acme/http_challenge_server.go index 42541380c..64c6a8280 100644 --- a/vendor/github.com/xenolf/lego/acme/http_challenge_server.go +++ b/vendor/github.com/xenolf/lego/acme/http_challenge_server.go @@ -63,7 +63,7 @@ func (s *HTTPProviderServer) serve(domain, token, keyAuth string) { w.Write([]byte(keyAuth)) logf("[INFO][%s] Served key authentication", domain) } else { - logf("[INFO] Received request for domain %s with method %s", r.Host, r.Method) + logf("[WARN] Received request for domain %s with method %s but the domain did not match any challenge. Please ensure your are passing the HOST header properly.", r.Host, r.Method) w.Write([]byte("TEST")) } }) diff --git a/vendor/github.com/xenolf/lego/acme/http_challenge_test.go b/vendor/github.com/xenolf/lego/acme/http_challenge_test.go index fdd8f4d27..7400f56d4 100644 --- a/vendor/github.com/xenolf/lego/acme/http_challenge_test.go +++ b/vendor/github.com/xenolf/lego/acme/http_challenge_test.go @@ -51,7 +51,7 @@ func TestHTTPChallengeInvalidPort(t *testing.T) { if err := solver.Solve(clientChallenge, "localhost:123456"); err == nil { t.Errorf("Solve error: got %v, want error", err) - } else if want := "invalid port 123456"; !strings.HasSuffix(err.Error(), want) { + } else if want, want18 := "invalid port 123456", "123456: invalid port"; !strings.HasSuffix(err.Error(), want) && !strings.HasSuffix(err.Error(), want18) { t.Errorf("Solve error: got %q, want suffix %q", err.Error(), want) } } diff --git a/vendor/github.com/xenolf/lego/acme/messages.go b/vendor/github.com/xenolf/lego/acme/messages.go index 0efeae674..0f6514c3f 100644 --- a/vendor/github.com/xenolf/lego/acme/messages.go +++ b/vendor/github.com/xenolf/lego/acme/messages.go @@ -13,17 +13,10 @@ type directory struct { RevokeCertURL string `json:"revoke-cert"` } -type recoveryKeyMessage struct { - Length int `json:"length,omitempty"` - Client jose.JsonWebKey `json:"client,omitempty"` - Server jose.JsonWebKey `json:"client,omitempty"` -} - type registrationMessage struct { Resource string `json:"resource"` Contact []string `json:"contact"` Delete bool `json:"delete,omitempty"` - // RecoveryKey recoveryKeyMessage `json:"recoveryKey,omitempty"` } // Registration is returned by the ACME server after the registration @@ -36,7 +29,6 @@ type Registration struct { Agreement string `json:"agreement,omitempty"` Authorizations string `json:"authorizations,omitempty"` Certificates string `json:"certificates,omitempty"` - // RecoveryKey recoveryKeyMessage `json:"recoveryKey,omitempty"` } // RegistrationResource represents all important informations about a registration diff --git a/vendor/github.com/xenolf/lego/acme/testdata/resolv.conf.1 b/vendor/github.com/xenolf/lego/acme/testdata/resolv.conf.1 new file mode 100644 index 000000000..3098f99b5 --- /dev/null +++ b/vendor/github.com/xenolf/lego/acme/testdata/resolv.conf.1 @@ -0,0 +1,5 @@ +domain company.com +nameserver 10.200.3.249 +nameserver 10.200.3.250:5353 +nameserver 2001:4860:4860::8844 +nameserver [10.0.0.1]:5353 diff --git a/vendor/github.com/xenolf/lego/acme/tls_sni_challenge_test.go b/vendor/github.com/xenolf/lego/acme/tls_sni_challenge_test.go index 3aec74565..83b2833a9 100644 --- a/vendor/github.com/xenolf/lego/acme/tls_sni_challenge_test.go +++ b/vendor/github.com/xenolf/lego/acme/tls_sni_challenge_test.go @@ -59,7 +59,7 @@ func TestTLSSNIChallengeInvalidPort(t *testing.T) { if err := solver.Solve(clientChallenge, "localhost:123456"); err == nil { t.Errorf("Solve error: got %v, want error", err) - } else if want := "invalid port 123456"; !strings.HasSuffix(err.Error(), want) { + } else if want, want18 := "invalid port 123456", "123456: invalid port"; !strings.HasSuffix(err.Error(), want) && !strings.HasSuffix(err.Error(), want18) { t.Errorf("Solve error: got %q, want suffix %q", err.Error(), want) } } |