diff --git a/.golangci.toml b/.golangci.toml index 54f8430c..c0719964 100644 --- a/.golangci.toml +++ b/.golangci.toml @@ -38,7 +38,10 @@ "nestif", # too many false-positive "goerr113", # not relevant "noctx", - "nlreturn", + "nlreturn", # not relevant + "wrapcheck", + "tparallel", # not relevant + "exhaustivestruct", # too many false-positive ] [issues] @@ -63,13 +66,13 @@ text = "string `(bar|foo)` has (\\d+) occurrences, make it a constant" [[issues.exclude-rules]] path = "certcrypto/crypto.go" - text = "`(tlsFeatureExtensionOID|ocspMustStapleFeature)` is a global variable" + text = "(tlsFeatureExtensionOID|ocspMustStapleFeature) is a global variable" [[issues.exclude-rules]] path = "challenge/dns01/nameserver.go" - text = "`(defaultNameservers|recursiveNameservers|dnsTimeout|fqdnSoaCache|muFqdnSoaCache)` is a global variable" + text = "(defaultNameservers|recursiveNameservers|dnsTimeout|fqdnSoaCache|muFqdnSoaCache) is a global variable" [[issues.exclude-rules]] path = "challenge/dns01/nameserver_test.go" - text = "`findXByFqdnTestCases` is a global variable" + text = "findXByFqdnTestCases is a global variable" [[issues.exclude-rules]] path = "challenge/http01/domain_matcher.go" text = "string `Host` has \\d occurrences, make it a constant" @@ -81,28 +84,25 @@ text = "Function 'parseForwardedHeader' has too many statements" [[issues.exclude-rules]] path = "challenge/tlsalpn01/tls_alpn_challenge.go" - text = "`idPeAcmeIdentifierV1` is a global variable" + text = "idPeAcmeIdentifierV1 is a global variable" [[issues.exclude-rules]] path = "log/logger.go" - text = "`Logger` is a global variable" - [[issues.exclude-rules]] - path = "cmd/lego/main.go" - text = "`version` is a global variable" + text = "Logger is a global variable" [[issues.exclude-rules]] path = "e2e/(dnschallenge/)?[\\d\\w]+_test.go" - text = "`load` is a global variable" + text = "load is a global variable" [[issues.exclude-rules]] path = "providers/dns/([\\d\\w]+/)*[\\d\\w]+_test.go" - text = "`envTest` is a global variable" + text = "envTest is a global variable" [[issues.exclude-rules]] path = "providers/dns/namecheap/namecheap_test.go" - text = "`testCases` is a global variable" + text = "testCases is a global variable" [[issues.exclude-rules]] path = "providers/dns/acmedns/acmedns_test.go" - text = "`(errorClientErr|errorStorageErr|egTestAccount)` is a global variable" + text = "egTestAccount is a global variable" [[issues.exclude-rules]] path = "providers/http/memcached/memcached_test.go" - text = "`memcachedHosts` is a global variable" + text = "memcachedHosts is a global variable" [[issues.exclude-rules]] path = "providers/dns/sakuracloud/client_test.go" text = "cyclomatic complexity 13 of func `(TestDNSProvider_cleanupTXTRecord_concurrent|TestDNSProvider_addTXTRecord_concurrent)` is high" diff --git a/README.md b/README.md index e769827e..fca82732 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ Let's Encrypt client and ACME library written in Go. ## Features +- ACME v2 [RFC 8555](https://tools.ietf.org/html/rfc8555) - Register with CA - Obtain certificates, both from scratch or with an existing CSR - Renew certificates diff --git a/acme/api/account.go b/acme/api/account.go index 26ada38a..54ea8a74 100644 --- a/acme/api/account.go +++ b/acme/api/account.go @@ -38,6 +38,7 @@ func (a *AccountService) NewEAB(accMsg acme.Account, kid, hmacEncoded string) (a if err != nil { return acme.ExtendedAccount{}, fmt.Errorf("acme: error signing eab content: %w", err) } + accMsg.ExternalAccountBinding = eabJWS return a.New(accMsg) diff --git a/acme/api/api.go b/acme/api/api.go index 269a689a..06b47c4d 100644 --- a/acme/api/api.go +++ b/acme/api/api.go @@ -90,14 +90,14 @@ func (a *Core) retrievablePost(uri string, content []byte, response interface{}) var err error resp, err = a.signedPost(uri, content, response) if err != nil { - switch err.(type) { // Retry if the nonce was invalidated - case *acme.NonceError: - return err - default: - cancel() + var e *acme.NonceError + if errors.As(err, &e) { return err } + + cancel() + return err } return nil @@ -109,7 +109,7 @@ func (a *Core) retrievablePost(uri string, content []byte, response interface{}) err := backoff.RetryNotify(operation, backoff.WithContext(bo, ctx), notify) if err != nil { - return nil, err + return resp, err } return resp, nil diff --git a/challenge/tlsalpn01/tls_alpn_challenge_server.go b/challenge/tlsalpn01/tls_alpn_challenge_server.go index cc853472..a49dd937 100644 --- a/challenge/tlsalpn01/tls_alpn_challenge_server.go +++ b/challenge/tlsalpn01/tls_alpn_challenge_server.go @@ -2,6 +2,7 @@ package tlsalpn01 import ( "crypto/tls" + "errors" "fmt" "net" "net/http" @@ -87,7 +88,7 @@ func (s *ProviderServer) CleanUp(domain, token, keyAuth string) error { } // Server was created, close it. - if err := s.listener.Close(); err != nil && err != http.ErrServerClosed { + if err := s.listener.Close(); err != nil && errors.Is(err, http.ErrServerClosed) { return err } diff --git a/cmd/hook.go b/cmd/hook.go index 0aef6338..0b0ca403 100644 --- a/cmd/hook.go +++ b/cmd/hook.go @@ -29,7 +29,7 @@ func launchHook(hook string, meta map[string]string) error { fmt.Println(string(output)) } - if ctxCmd.Err() == context.DeadlineExceeded { + if errors.Is(ctxCmd.Err(), context.DeadlineExceeded) { return errors.New("hook timed out") } diff --git a/providers/dns/acmedns/acmedns.go b/providers/dns/acmedns/acmedns.go index f50ad966..7e04e7b2 100644 --- a/providers/dns/acmedns/acmedns.go +++ b/providers/dns/acmedns/acmedns.go @@ -109,10 +109,10 @@ func (d *DNSProvider) Present(domain, _, keyAuth string) error { // Check if credentials were previously saved for this domain. account, err := d.storage.Fetch(domain) // Errors other than goacmeDNS.ErrDomainNotFound are unexpected. - if err != nil && err != goacmedns.ErrDomainNotFound { + if err != nil && !errors.Is(err, goacmedns.ErrDomainNotFound) { return err } - if err == goacmedns.ErrDomainNotFound { + if errors.Is(err, goacmedns.ErrDomainNotFound) { // The account did not exist. Create a new one and return an error // indicating the required one-time manual CNAME setup. return d.register(domain, fqdn) diff --git a/providers/dns/azure/azure.go b/providers/dns/azure/azure.go index 70fe190e..c3d7245e 100644 --- a/providers/dns/azure/azure.go +++ b/providers/dns/azure/azure.go @@ -182,8 +182,8 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // Get existing record set rset, err := rsc.Get(ctx, d.config.ResourceGroup, zone, relative, dns.TXT) if err != nil { - detailedError, ok := err.(autorest.DetailedError) - if !ok || detailedError.StatusCode != http.StatusNotFound { + var detailed *autorest.DetailedError + if !errors.As(err, detailed) || detailed.StatusCode != http.StatusNotFound { return fmt.Errorf("azure: %w", err) } } @@ -276,7 +276,7 @@ func getAuthorizer(config *Config) (autorest.Authorizer, error) { spToken, err := credentialsConfig.ServicePrincipalToken() if err != nil { - return nil, fmt.Errorf("failed to get oauth token from client credentials: %v", err) + return nil, fmt.Errorf("failed to get oauth token from client credentials: %w", err) } spToken.SetSender(config.HTTPClient) diff --git a/providers/dns/cloudflare/cloudflare.go b/providers/dns/cloudflare/cloudflare.go index d8a33a24..6684a69b 100644 --- a/providers/dns/cloudflare/cloudflare.go +++ b/providers/dns/cloudflare/cloudflare.go @@ -77,6 +77,7 @@ func NewDNSProvider() (*DNSProvider, error) { []string{"CLOUDFLARE_ZONE_API_TOKEN", "CF_ZONE_API_TOKEN", "CLOUDFLARE_DNS_API_TOKEN", "CF_DNS_API_TOKEN"}, ) if errT != nil { + // nolint:errorlint return nil, fmt.Errorf("cloudflare: %v or %v", err, errT) } } diff --git a/providers/dns/edgedns/edgedns.go b/providers/dns/edgedns/edgedns.go index 3e0952cf..b592cf5f 100644 --- a/providers/dns/edgedns/edgedns.go +++ b/providers/dns/edgedns/edgedns.go @@ -223,6 +223,6 @@ func isNotFound(err error) bool { return false } - e, ok := err.(configdns.ConfigDNSError) - return ok && e.NotFound() + var e configdns.ConfigDNSError + return errors.As(err, &e) && e.NotFound() } diff --git a/providers/dns/gcloud/googlecloud.go b/providers/dns/gcloud/googlecloud.go index 6a9bc800..271675eb 100644 --- a/providers/dns/gcloud/googlecloud.go +++ b/providers/dns/gcloud/googlecloud.go @@ -241,10 +241,9 @@ func (d *DNSProvider) applyChanges(zone string, change *dns.Change) error { chg, err := d.client.Changes.Create(d.config.Project, zone, change).Do() if err != nil { - if v, ok := err.(*googleapi.Error); ok { - if v.Code == http.StatusNotFound { - return nil - } + var v *googleapi.Error + if errors.As(err, &v) && v.Code == http.StatusNotFound { + return nil } data, _ := json.Marshal(change) diff --git a/providers/dns/inwx/inwx.go b/providers/dns/inwx/inwx.go index b8afb375..7b8f03d6 100644 --- a/providers/dns/inwx/inwx.go +++ b/providers/dns/inwx/inwx.go @@ -126,15 +126,15 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { _, err = d.client.Nameservers.CreateRecord(request) if err != nil { - switch er := err.(type) { - case *goinwx.ErrorResponse: + var er *goinwx.ErrorResponse + if errors.As(err, &er) { if er.Message == "Object exists" { return nil } return fmt.Errorf("inwx: %w", err) - default: - return fmt.Errorf("inwx: %w", err) } + + return fmt.Errorf("inwx: %w", err) } return nil diff --git a/providers/dns/joker/provider_dmapi.go b/providers/dns/joker/provider_dmapi.go index 11934959..7a0ef877 100644 --- a/providers/dns/joker/provider_dmapi.go +++ b/providers/dns/joker/provider_dmapi.go @@ -26,6 +26,7 @@ func newDmapiProvider() (*dmapiProvider, error) { var errU error values, errU = env.Get(EnvUsername, EnvPassword) if errU != nil { + // nolint:errorlint return nil, fmt.Errorf("joker: %v or %v", errU, err) } } diff --git a/providers/dns/joker/provider_svc.go b/providers/dns/joker/provider_svc.go index d1566dc5..781f1e2c 100644 --- a/providers/dns/joker/provider_svc.go +++ b/providers/dns/joker/provider_svc.go @@ -21,7 +21,7 @@ type svcProvider struct { func newSvcProvider() (*svcProvider, error) { values, err := env.Get(EnvUsername, EnvPassword) if err != nil { - return nil, fmt.Errorf("joker: %v", err) + return nil, fmt.Errorf("joker: %w", err) } config := NewDefaultConfig() diff --git a/providers/dns/ns1/ns1.go b/providers/dns/ns1/ns1.go index ed3c9d49..42d99565 100644 --- a/providers/dns/ns1/ns1.go +++ b/providers/dns/ns1/ns1.go @@ -95,7 +95,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { record, _, err := d.client.Records.Get(zone.Zone, dns01.UnFqdn(fqdn), "TXT") // Create a new record - if err == rest.ErrRecordMissing || record == nil { + if errors.Is(err, rest.ErrRecordMissing) || record == nil { log.Infof("Create a new record for [zone: %s, fqdn: %s, domain: %s]", zone.Zone, fqdn, domain) record = dns.NewRecord(zone.Zone, dns01.UnFqdn(fqdn), "TXT") diff --git a/providers/dns/pdns/client.go b/providers/dns/pdns/client.go index b2258df2..924e7650 100644 --- a/providers/dns/pdns/client.go +++ b/providers/dns/pdns/client.go @@ -2,6 +2,7 @@ package pdns import ( "encoding/json" + "errors" "fmt" "io" "net/http" @@ -172,7 +173,7 @@ func (d *DNSProvider) sendRequest(method, uri string, body io.Reader) (json.RawM var msg json.RawMessage err = json.NewDecoder(resp.Body).Decode(&msg) if err != nil { - if err == io.EOF { + if errors.Is(err, io.EOF) { // empty body return nil, nil } diff --git a/providers/dns/sakuracloud/client.go b/providers/dns/sakuracloud/client.go index b9b6d4be..1f557c8f 100644 --- a/providers/dns/sakuracloud/client.go +++ b/providers/dns/sakuracloud/client.go @@ -1,6 +1,7 @@ package sakuracloud import ( + "errors" "fmt" "net/http" "strings" @@ -70,9 +71,11 @@ func (d *DNSProvider) getHostedZone(domain string) (*sacloud.DNS, error) { res, err := d.client.Reset().WithNameLike(zoneName).Find() if err != nil { - if notFound, ok := err.(api.Error); ok && notFound.ResponseCode() == http.StatusNotFound { + var notFound api.Error + if errors.As(err, ¬Found) && notFound.ResponseCode() == http.StatusNotFound { return nil, fmt.Errorf("zone %s not found on SakuraCloud DNS: %w", zoneName, err) } + return nil, fmt.Errorf("API call failed: %w", err) } diff --git a/providers/dns/yandex/yandex.go b/providers/dns/yandex/yandex.go index 781895d1..15536ccf 100644 --- a/providers/dns/yandex/yandex.go +++ b/providers/dns/yandex/yandex.go @@ -58,7 +58,7 @@ type DNSProvider struct { func NewDNSProvider() (*DNSProvider, error) { values, err := env.Get(EnvPddToken) if err != nil { - return nil, fmt.Errorf("yandex: %v", err) + return nil, fmt.Errorf("yandex: %w", err) } config := NewDefaultConfig() @@ -79,7 +79,7 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { client, err := internal.NewClient(config.PddToken) if err != nil { - return nil, fmt.Errorf("yandex: %v", err) + return nil, fmt.Errorf("yandex: %w", err) } if config.HTTPClient != nil { @@ -95,7 +95,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { rootDomain, subDomain, err := splitDomain(fqdn) if err != nil { - return fmt.Errorf("yandex: %v", err) + return fmt.Errorf("yandex: %w", err) } data := internal.Record{ @@ -108,7 +108,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { _, err = d.client.AddRecord(data) if err != nil { - return fmt.Errorf("yandex: %v", err) + return fmt.Errorf("yandex: %w", err) } return nil @@ -120,12 +120,12 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { rootDomain, subDomain, err := splitDomain(fqdn) if err != nil { - return fmt.Errorf("yandex: %v", err) + return fmt.Errorf("yandex: %w", err) } records, err := d.client.GetRecords(rootDomain) if err != nil { - return fmt.Errorf("yandex: %v", err) + return fmt.Errorf("yandex: %w", err) } var record *internal.Record @@ -148,7 +148,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { _, err = d.client.RemoveRecord(data) if err != nil { - return fmt.Errorf("yandex: %v", err) + return fmt.Errorf("yandex: %w", err) } return nil } diff --git a/registration/registar.go b/registration/registar.go index 6bc0e6b0..8f97403b 100644 --- a/registration/registar.go +++ b/registration/registar.go @@ -58,8 +58,8 @@ func (r *Registrar) Register(options RegisterOptions) (*Resource, error) { account, err := r.core.Accounts.New(accMsg) if err != nil { // FIXME seems impossible - errorDetails, ok := err.(acme.ProblemDetails) - if !ok || errorDetails.HTTPStatus != http.StatusConflict { + var errorDetails acme.ProblemDetails + if !errors.As(err, &errorDetails) || errorDetails.HTTPStatus != http.StatusConflict { return nil, err } } @@ -81,9 +81,9 @@ func (r *Registrar) RegisterWithExternalAccountBinding(options RegisterEABOption account, err := r.core.Accounts.NewEAB(accMsg, options.Kid, options.HmacEncoded) if err != nil { - errorDetails, ok := err.(acme.ProblemDetails) // FIXME seems impossible - if !ok || errorDetails.HTTPStatus != http.StatusConflict { + var errorDetails acme.ProblemDetails + if !errors.As(err, &errorDetails) || errorDetails.HTTPStatus != http.StatusConflict { return nil, err } } @@ -96,7 +96,7 @@ func (r *Registrar) RegisterWithExternalAccountBinding(options RegisterEABOption // This is similar to the Register function, // but acting on an existing registration link and resource. func (r *Registrar) QueryRegistration() (*Resource, error) { - if r == nil || r.user == nil { + if r == nil || r.user == nil || r.user.GetRegistration() == nil { return nil, errors.New("acme: cannot query the registration of a nil client or user") }