From 6d8f122a5160f6d9e4c51579f2429dfaa62c7271 Mon Sep 17 00:00:00 2001 From: Christopher Speller Date: Fri, 16 Feb 2018 06:47:51 -0800 Subject: Upgrading server dependancies (#8308) --- vendor/github.com/xenolf/lego/.gitcookies.enc | Bin 480 -> 0 bytes vendor/github.com/xenolf/lego/.gitignore | 4 - vendor/github.com/xenolf/lego/.travis.yml | 16 - vendor/github.com/xenolf/lego/CHANGELOG.md | 154 ---- vendor/github.com/xenolf/lego/CONTRIBUTING.md | 32 - vendor/github.com/xenolf/lego/Dockerfile | 15 - vendor/github.com/xenolf/lego/LICENSE | 21 - vendor/github.com/xenolf/lego/README.md | 273 ------ vendor/github.com/xenolf/lego/account.go | 109 --- vendor/github.com/xenolf/lego/acme/challenges.go | 16 - vendor/github.com/xenolf/lego/acme/client.go | 825 ------------------ vendor/github.com/xenolf/lego/acme/client_test.go | 269 ------ vendor/github.com/xenolf/lego/acme/crypto.go | 347 -------- vendor/github.com/xenolf/lego/acme/crypto_test.go | 93 -- .../github.com/xenolf/lego/acme/dns_challenge.go | 309 ------- .../xenolf/lego/acme/dns_challenge_manual.go | 53 -- .../xenolf/lego/acme/dns_challenge_test.go | 200 ----- vendor/github.com/xenolf/lego/acme/error.go | 94 --- vendor/github.com/xenolf/lego/acme/http.go | 160 ---- .../github.com/xenolf/lego/acme/http_challenge.go | 41 - .../xenolf/lego/acme/http_challenge_server.go | 79 -- .../xenolf/lego/acme/http_challenge_test.go | 57 -- vendor/github.com/xenolf/lego/acme/http_test.go | 100 --- vendor/github.com/xenolf/lego/acme/jws.go | 131 --- vendor/github.com/xenolf/lego/acme/messages.go | 115 --- .../github.com/xenolf/lego/acme/pop_challenge.go | 1 - vendor/github.com/xenolf/lego/acme/provider.go | 28 - .../xenolf/lego/acme/testdata/resolv.conf.1 | 5 - .../xenolf/lego/acme/tls_sni_challenge.go | 67 -- .../xenolf/lego/acme/tls_sni_challenge_server.go | 62 -- .../xenolf/lego/acme/tls_sni_challenge_test.go | 65 -- vendor/github.com/xenolf/lego/acme/utils.go | 29 - vendor/github.com/xenolf/lego/acme/utils_test.go | 26 - vendor/github.com/xenolf/lego/cli.go | 232 ----- vendor/github.com/xenolf/lego/cli_handlers.go | 423 ---------- vendor/github.com/xenolf/lego/configuration.go | 76 -- vendor/github.com/xenolf/lego/crypto.go | 56 -- .../lego/providers/dns/auroradns/auroradns.go | 141 ---- .../lego/providers/dns/auroradns/auroradns_test.go | 148 ---- .../xenolf/lego/providers/dns/azure/azure.go | 151 ---- .../xenolf/lego/providers/dns/azure/azure_test.go | 89 -- .../lego/providers/dns/cloudflare/cloudflare.go | 223 ----- .../providers/dns/cloudflare/cloudflare_test.go | 80 -- .../providers/dns/digitalocean/digitalocean.go | 166 ---- .../dns/digitalocean/digitalocean_test.go | 117 --- .../xenolf/lego/providers/dns/dns_providers.go | 86 -- .../lego/providers/dns/dns_providers_test.go | 50 -- .../xenolf/lego/providers/dns/dnsimple/dnsimple.go | 180 ---- .../lego/providers/dns/dnsimple/dnsimple_test.go | 140 --- .../lego/providers/dns/dnsmadeeasy/dnsmadeeasy.go | 248 ------ .../providers/dns/dnsmadeeasy/dnsmadeeasy_test.go | 37 - .../xenolf/lego/providers/dns/dnspod/dnspod.go | 146 ---- .../lego/providers/dns/dnspod/dnspod_test.go | 72 -- .../xenolf/lego/providers/dns/dyn/dyn.go | 274 ------ .../xenolf/lego/providers/dns/dyn/dyn_test.go | 53 -- .../xenolf/lego/providers/dns/exoscale/exoscale.go | 128 --- .../lego/providers/dns/exoscale/exoscale_test.go | 103 --- .../xenolf/lego/providers/dns/gandi/gandi.go | 472 ----------- .../xenolf/lego/providers/dns/gandi/gandi_test.go | 939 --------------------- .../xenolf/lego/providers/dns/godaddy/godaddy.go | 155 ---- .../lego/providers/dns/godaddy/godaddy_test.go | 60 -- .../lego/providers/dns/googlecloud/googlecloud.go | 205 ----- .../providers/dns/googlecloud/googlecloud_test.go | 99 --- .../xenolf/lego/providers/dns/linode/linode.go | 131 --- .../lego/providers/dns/linode/linode_test.go | 317 ------- .../lego/providers/dns/namecheap/namecheap.go | 416 --------- .../lego/providers/dns/namecheap/namecheap_test.go | 402 --------- .../xenolf/lego/providers/dns/ns1/ns1.go | 97 --- .../xenolf/lego/providers/dns/ns1/ns1_test.go | 67 -- .../xenolf/lego/providers/dns/otc/mock.go | 152 ---- .../xenolf/lego/providers/dns/otc/otc.go | 388 --------- .../xenolf/lego/providers/dns/otc/otc_test.go | 112 --- .../xenolf/lego/providers/dns/ovh/ovh.go | 159 ---- .../xenolf/lego/providers/dns/ovh/ovh_test.go | 103 --- .../xenolf/lego/providers/dns/pdns/README.md | 7 - .../xenolf/lego/providers/dns/pdns/pdns.go | 343 -------- .../xenolf/lego/providers/dns/pdns/pdns_test.go | 80 -- .../lego/providers/dns/rackspace/rackspace.go | 284 ------- .../lego/providers/dns/rackspace/rackspace_test.go | 220 ----- .../xenolf/lego/providers/dns/rfc2136/rfc2136.go | 152 ---- .../lego/providers/dns/rfc2136/rfc2136_test.go | 244 ------ .../lego/providers/dns/route53/fixtures_test.go | 39 - .../xenolf/lego/providers/dns/route53/route53.go | 185 ---- .../dns/route53/route53_integration_test.go | 70 -- .../lego/providers/dns/route53/route53_test.go | 105 --- .../lego/providers/dns/route53/testutil_test.go | 38 - .../xenolf/lego/providers/dns/vultr/vultr.go | 127 --- .../xenolf/lego/providers/dns/vultr/vultr_test.go | 65 -- .../xenolf/lego/providers/http/memcached/README.md | 15 - .../lego/providers/http/memcached/memcached.go | 60 -- .../providers/http/memcached/memcached_test.go | 111 --- .../xenolf/lego/providers/http/webroot/webroot.go | 58 -- .../lego/providers/http/webroot/webroot_test.go | 46 - 93 files changed, 13738 deletions(-) delete mode 100644 vendor/github.com/xenolf/lego/.gitcookies.enc delete mode 100644 vendor/github.com/xenolf/lego/.gitignore delete mode 100644 vendor/github.com/xenolf/lego/.travis.yml delete mode 100644 vendor/github.com/xenolf/lego/CHANGELOG.md delete mode 100644 vendor/github.com/xenolf/lego/CONTRIBUTING.md delete mode 100644 vendor/github.com/xenolf/lego/Dockerfile delete mode 100644 vendor/github.com/xenolf/lego/LICENSE delete mode 100644 vendor/github.com/xenolf/lego/README.md delete mode 100644 vendor/github.com/xenolf/lego/account.go delete mode 100644 vendor/github.com/xenolf/lego/acme/challenges.go delete mode 100644 vendor/github.com/xenolf/lego/acme/client.go delete mode 100644 vendor/github.com/xenolf/lego/acme/client_test.go delete mode 100644 vendor/github.com/xenolf/lego/acme/crypto.go delete mode 100644 vendor/github.com/xenolf/lego/acme/crypto_test.go delete mode 100644 vendor/github.com/xenolf/lego/acme/dns_challenge.go delete mode 100644 vendor/github.com/xenolf/lego/acme/dns_challenge_manual.go delete mode 100644 vendor/github.com/xenolf/lego/acme/dns_challenge_test.go delete mode 100644 vendor/github.com/xenolf/lego/acme/error.go delete mode 100644 vendor/github.com/xenolf/lego/acme/http.go delete mode 100644 vendor/github.com/xenolf/lego/acme/http_challenge.go delete mode 100644 vendor/github.com/xenolf/lego/acme/http_challenge_server.go delete mode 100644 vendor/github.com/xenolf/lego/acme/http_challenge_test.go delete mode 100644 vendor/github.com/xenolf/lego/acme/http_test.go delete mode 100644 vendor/github.com/xenolf/lego/acme/jws.go delete mode 100644 vendor/github.com/xenolf/lego/acme/messages.go delete mode 100644 vendor/github.com/xenolf/lego/acme/pop_challenge.go delete mode 100644 vendor/github.com/xenolf/lego/acme/provider.go delete mode 100644 vendor/github.com/xenolf/lego/acme/testdata/resolv.conf.1 delete mode 100644 vendor/github.com/xenolf/lego/acme/tls_sni_challenge.go delete mode 100644 vendor/github.com/xenolf/lego/acme/tls_sni_challenge_server.go delete mode 100644 vendor/github.com/xenolf/lego/acme/tls_sni_challenge_test.go delete mode 100644 vendor/github.com/xenolf/lego/acme/utils.go delete mode 100644 vendor/github.com/xenolf/lego/acme/utils_test.go delete mode 100644 vendor/github.com/xenolf/lego/cli.go delete mode 100644 vendor/github.com/xenolf/lego/cli_handlers.go delete mode 100644 vendor/github.com/xenolf/lego/configuration.go delete mode 100644 vendor/github.com/xenolf/lego/crypto.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/auroradns/auroradns.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/auroradns/auroradns_test.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/azure/azure.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/azure/azure_test.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/cloudflare/cloudflare.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/cloudflare/cloudflare_test.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/digitalocean/digitalocean.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/digitalocean/digitalocean_test.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/dns_providers.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/dns_providers_test.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/dnsimple/dnsimple.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/dnsimple/dnsimple_test.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/dnsmadeeasy/dnsmadeeasy.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/dnsmadeeasy/dnsmadeeasy_test.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/dnspod/dnspod.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/dnspod/dnspod_test.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/dyn/dyn.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/dyn/dyn_test.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/exoscale/exoscale.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/exoscale/exoscale_test.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/gandi/gandi.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/gandi/gandi_test.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/godaddy/godaddy.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/godaddy/godaddy_test.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/googlecloud/googlecloud.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/googlecloud/googlecloud_test.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/linode/linode.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/linode/linode_test.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/namecheap/namecheap.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/namecheap/namecheap_test.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/ns1/ns1.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/ns1/ns1_test.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/otc/mock.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/otc/otc.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/otc/otc_test.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/ovh/ovh.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/ovh/ovh_test.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/pdns/README.md delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/pdns/pdns.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/pdns/pdns_test.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/rackspace/rackspace.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/rackspace/rackspace_test.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/rfc2136/rfc2136.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/rfc2136/rfc2136_test.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/route53/fixtures_test.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/route53/route53.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/route53/route53_integration_test.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/route53/route53_test.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/route53/testutil_test.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/vultr/vultr.go delete mode 100644 vendor/github.com/xenolf/lego/providers/dns/vultr/vultr_test.go delete mode 100644 vendor/github.com/xenolf/lego/providers/http/memcached/README.md delete mode 100644 vendor/github.com/xenolf/lego/providers/http/memcached/memcached.go delete mode 100644 vendor/github.com/xenolf/lego/providers/http/memcached/memcached_test.go delete mode 100644 vendor/github.com/xenolf/lego/providers/http/webroot/webroot.go delete mode 100644 vendor/github.com/xenolf/lego/providers/http/webroot/webroot_test.go (limited to 'vendor/github.com/xenolf') diff --git a/vendor/github.com/xenolf/lego/.gitcookies.enc b/vendor/github.com/xenolf/lego/.gitcookies.enc deleted file mode 100644 index 09c303c94..000000000 Binary files a/vendor/github.com/xenolf/lego/.gitcookies.enc and /dev/null differ diff --git a/vendor/github.com/xenolf/lego/.gitignore b/vendor/github.com/xenolf/lego/.gitignore deleted file mode 100644 index 74d32f0ab..000000000 --- a/vendor/github.com/xenolf/lego/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -lego.exe -lego -.lego -.idea diff --git a/vendor/github.com/xenolf/lego/.travis.yml b/vendor/github.com/xenolf/lego/.travis.yml deleted file mode 100644 index d7a626e23..000000000 --- a/vendor/github.com/xenolf/lego/.travis.yml +++ /dev/null @@ -1,16 +0,0 @@ -language: go -go: -- 1.8.x -- 1.9.x -- tip -services: - - memcached -env: - - MEMCACHED_HOSTS=localhost:11211 -install: -- go get -t ./... -script: -- go vet ./... -- go test -v ./... -before_install: -- '[ "${TRAVIS_PULL_REQUEST}" = "false" ] && openssl aes-256-cbc -K $encrypted_26c593b079d9_key -iv $encrypted_26c593b079d9_iv -in .gitcookies.enc -out .gitcookies -d || true' diff --git a/vendor/github.com/xenolf/lego/CHANGELOG.md b/vendor/github.com/xenolf/lego/CHANGELOG.md deleted file mode 100644 index d71cc23dc..000000000 --- a/vendor/github.com/xenolf/lego/CHANGELOG.md +++ /dev/null @@ -1,154 +0,0 @@ -# Changelog - -## [0.4.1] - 2017-09-26 - -### Added: -- lib: A new DNS provider for OTC. -- lib: The `AWS_HOSTED_ZONE_ID` environment variable for the Route53 DNS provider to directly specify the zone. -- lib: The `RFC2136_TIMEOUT` enviroment variable to make the timeout for the RFC2136 provider configurable. -- lib: The `GCE_SERVICE_ACCOUNT_FILE` environment variable to specify a service account file for the Google Cloud DNS provider. - -### Fixed: -- lib: Fixed an authentication issue with the latest Azure SDK. - -## [0.4.0] - 2017-07-13 - -### Added: -- CLI: The `--http-timeout` switch. This allows for an override of the default client HTTP timeout. -- lib: The `HTTPClient` field. This allows for an override of the default HTTP timeout for library HTTP requests. -- CLI: The `--dns-timeout` switch. This allows for an override of the default DNS timeout for library DNS requests. -- lib: The `DNSTimeout` switch. This allows for an override of the default client DNS timeout. -- lib: The `QueryRegistration` function on `acme.Client`. This performs a POST on the client registration's URI and gets the updated registration info. -- lib: The `DeleteRegistration` function on `acme.Client`. This deletes the registration as currently configured in the client. -- lib: The `ObtainCertificateForCSR` function on `acme.Client`. The function allows to request a certificate for an already existing CSR. -- CLI: The `--csr` switch. Allows to use already existing CSRs for certificate requests on the command line. -- CLI: The `--pem` flag. This will change the certificate output so it outputs a .pem file concatanating the .key and .crt files together. -- CLI: The `--dns-resolvers` flag. Allows for users to override the default DNS servers used for recursive lookup. -- lib: Added a memcached provider for the HTTP challenge. -- CLI: The `--memcached-host` flag. This allows to use memcached for challenge storage. -- CLI: The `--must-staple` flag. This enables OCSP must staple in the generated CSR. -- lib: The library will now honor entries in your resolv.conf. -- lib: Added a field `IssuerCertificate` to the `CertificateResource` struct. -- lib: A new DNS provider for OVH. -- lib: A new DNS provider for DNSMadeEasy. -- lib: A new DNS provider for Linode. -- lib: A new DNS provider for AuroraDNS. -- lib: A new DNS provider for NS1. -- lib: A new DNS provider for Azure DNS. -- lib: A new DNS provider for Rackspace DNS. -- lib: A new DNS provider for Exoscale DNS. -- lib: A new DNS provider for DNSPod. - -### Changed: -- lib: Exported the `PreCheckDNS` field so library users can manage the DNS check in tests. -- lib: The library will now skip challenge solving if a valid Authz already exists. - -### Removed: -- lib: The library will no longer check for auto renewed certificates. This has been removed from the spec and is not supported in Boulder. - -### Fixed: -- lib: Fix a problem with the Route53 provider where it was possible the verification was published to a private zone. -- lib: Loading an account from file should fail if a integral part is nil -- lib: Fix a potential issue where the Dyn provider could resolve to an incorrect zone. -- lib: If a registration encounteres a conflict, the old registration is now recovered. -- CLI: The account.json file no longer has the executable flag set. -- lib: Made the client registration more robust in case of a 403 HTTP response. -- lib: Fixed an issue with zone lookups when they have a CNAME in another zone. -- lib: Fixed the lookup for the authoritative zone for Google Cloud. -- lib: Fixed a race condition in the nonce store. -- lib: The Google Cloud provider now removes old entries before trying to add new ones. -- lib: Fixed a condition where we could stall due to an early error condition. -- lib: Fixed an issue where Authz object could end up in an active state after an error condition. - -## [0.3.1] - 2016-04-19 - -### Added: -- lib: A new DNS provider for Vultr. - -### Fixed: -- lib: DNS Provider for DigitalOcean could not handle subdomains properly. -- lib: handleHTTPError should only try to JSON decode error messages with the right content type. -- lib: The propagation checker for the DNS challenge would not retry on send errors. - - -## [0.3.0] - 2016-03-19 - -### Added: -- CLI: The `--dns` switch. To include the DNS challenge for consideration. When using this switch, all other solvers are disabled. Supported are the following solvers: cloudflare, digitalocean, dnsimple, dyn, gandi, googlecloud, namecheap, route53, rfc2136 and manual. -- CLI: The `--accept-tos` switch. Indicates your acceptance of the Let's Encrypt terms of service without prompting you. -- CLI: The `--webroot` switch. The HTTP-01 challenge may now be completed by dropping a file into a webroot. When using this switch, all other solvers are disabled. -- CLI: The `--key-type` switch. This replaces the `--rsa-key-size` switch and supports the following key types: EC256, EC384, RSA2048, RSA4096 and RSA8192. -- CLI: The `--dnshelp` switch. This displays a more in-depth help topic for DNS solvers. -- CLI: The `--no-bundle` sub switch for the `run` and `renew` commands. When this switch is set, the CLI will not bundle the issuer certificate with your certificate. -- lib: A new type for challenge identifiers `Challenge` -- lib: A new interface for custom challenge providers `acme.ChallengeProvider` -- lib: A new interface for DNS-01 providers to allow for custom timeouts for the validation function `acme.ChallengeProviderTimeout` -- lib: SetChallengeProvider function. Pass a challenge identifier and a Provider to replace the default behaviour of a challenge. -- lib: The DNS-01 challenge has been implemented with modular solvers using the `ChallengeProvider` interface. Included solvers are: cloudflare, digitalocean, dnsimple, gandi, namecheap, route53, rfc2136 and manual. -- lib: The `acme.KeyType` type was added and is used for the configuration of crypto parameters for RSA and EC keys. Valid KeyTypes are: EC256, EC384, RSA2048, RSA4096 and RSA8192. - -### Changed -- lib: ExcludeChallenges now expects to be passed an array of `Challenge` types. -- lib: HTTP-01 now supports custom solvers using the `ChallengeProvider` interface. -- lib: TLS-SNI-01 now supports custom solvers using the `ChallengeProvider` interface. -- lib: The `GetPrivateKey` function in the `acme.User` interface is now expected to return a `crypto.PrivateKey` instead of an `rsa.PrivateKey` for EC compat. -- lib: The `acme.NewClient` function now expects an `acme.KeyType` instead of the keyBits parameter. - -### Removed -- CLI: The `rsa-key-size` switch was removed in favor of `key-type` to support EC keys. - -### Fixed -- lib: Fixed a race condition in HTTP-01 -- lib: Fixed an issue where status codes on ACME challenge responses could lead to no action being taken. -- lib: Fixed a regression when calling the Renew function with a SAN certificate. - -## [0.2.0] - 2016-01-09 - -### Added: -- CLI: The `--exclude` or `-x` switch. To exclude a challenge from being solved. -- CLI: The `--http` switch. To set the listen address and port of HTTP based challenges. Supports `host:port` and `:port` for any interface. -- CLI: The `--tls` switch. To set the listen address and port of TLS based challenges. Supports `host:port` and `:port` for any interface. -- CLI: The `--reuse-key` switch for the `renew` operation. This lets you reuse an existing private key for renewals. -- lib: ExcludeChallenges function. Pass an array of challenge identifiers to exclude them from solving. -- lib: SetHTTPAddress function. Pass a port to set the listen port for HTTP based challenges. -- lib: SetTLSAddress function. Pass a port to set the listen port of TLS based challenges. -- lib: acme.UserAgent variable. Use this to customize the user agent on all requests sent by lego. - -### Changed: -- lib: NewClient does no longer accept the optPort parameter -- lib: ObtainCertificate now returns a SAN certificate if you pass more then one domain. -- lib: GetOCSPForCert now returns the parsed OCSP response instead of just the status. -- lib: ObtainCertificate has a new parameter `privKey crypto.PrivateKey` which lets you reuse an existing private key for new certificates. -- lib: RenewCertificate now expects the PrivateKey property of the CertificateResource to be set only if you want to reuse the key. - -### Removed: -- CLI: The `--port` switch was removed. -- lib: RenewCertificate does no longer offer to also revoke your old certificate. - -### Fixed: -- CLI: Fix logic using the `--days` parameter for renew - -## [0.1.1] - 2015-12-18 - -### Added: -- CLI: Added a way to automate renewal through a cronjob using the --days parameter to renew - -### Changed: -- lib: Improved log output on challenge failures. - -### Fixed: -- CLI: The short parameter for domains would not get accepted -- CLI: The cli did not return proper exit codes on error library errors. -- lib: RenewCertificate did not properly renew SAN certificates. - -### Security -- lib: Fix possible DOS on GetOCSPForCert - -## [0.1.0] - 2015-12-03 -- Initial release - -[0.3.1]: https://github.com/xenolf/lego/compare/v0.3.0...v0.3.1 -[0.3.0]: https://github.com/xenolf/lego/compare/v0.2.0...v0.3.0 -[0.2.0]: https://github.com/xenolf/lego/compare/v0.1.1...v0.2.0 -[0.1.1]: https://github.com/xenolf/lego/compare/v0.1.0...v0.1.1 -[0.1.0]: https://github.com/xenolf/lego/tree/v0.1.0 diff --git a/vendor/github.com/xenolf/lego/CONTRIBUTING.md b/vendor/github.com/xenolf/lego/CONTRIBUTING.md deleted file mode 100644 index 9939a5ab3..000000000 --- a/vendor/github.com/xenolf/lego/CONTRIBUTING.md +++ /dev/null @@ -1,32 +0,0 @@ -# How to contribute to lego - -Contributions in the form of patches and proposals are essential to keep lego great and to make it even better. -To ensure a great and easy experience for everyone, please review the few guidelines in this document. - -## Bug reports - -- Use the issue search to see if the issue has already been reported. -- Also look for closed issues to see if your issue has already been fixed. -- If both of the above do not apply create a new issue and include as much information as possible. - -Bug reports should include all information a person could need to reproduce your problem without the need to -follow up for more information. If possible, provide detailed steps for us to reproduce it, the expected behaviour and the actual behaviour. - -## Feature proposals and requests - -Feature requests are welcome and should be discussed in an issue. -Please keep proposals focused on one thing at a time and be as detailed as possible. -It is up to you to make a strong point about your proposal and convince us of the merits and the added complexity of this feature. - -## Pull requests - -Patches, new features and improvements are a great way to help the project. -Please keep them focused on one thing and do not include unrelated commits. - -All pull requests which alter the behaviour of the program, add new behaviour or somehow alter code in a non-trivial way should **always** include tests. - -If you want to contribute a significant pull request (with a non-trivial workload for you) please **ask first**. We do not want you to spend -a lot of time on something the project's developers might not want to merge into the project. - -**IMPORTANT**: By submitting a patch, you agree to allow the project -owners to license your work under the terms of the [MIT License](LICENSE). diff --git a/vendor/github.com/xenolf/lego/Dockerfile b/vendor/github.com/xenolf/lego/Dockerfile deleted file mode 100644 index 511e403ce..000000000 --- a/vendor/github.com/xenolf/lego/Dockerfile +++ /dev/null @@ -1,15 +0,0 @@ -FROM alpine:3.6 - -ENV GOPATH /go -ENV LEGO_VERSION tags/v0.4.1 - -RUN apk update && apk add --no-cache --virtual run-dependencies ca-certificates && \ - apk add --no-cache --virtual build-dependencies go git musl-dev && \ - go get -u github.com/xenolf/lego && \ - cd ${GOPATH}/src/github.com/xenolf/lego && \ - git checkout ${LEGO_VERSION} && \ - go build -o /usr/bin/lego . && \ - apk del build-dependencies && \ - rm -rf ${GOPATH} - -ENTRYPOINT [ "/usr/bin/lego" ] diff --git a/vendor/github.com/xenolf/lego/LICENSE b/vendor/github.com/xenolf/lego/LICENSE deleted file mode 100644 index 270cba089..000000000 --- a/vendor/github.com/xenolf/lego/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015-2017 Sebastian Erhart - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/vendor/github.com/xenolf/lego/README.md b/vendor/github.com/xenolf/lego/README.md deleted file mode 100644 index 9ec7e1f38..000000000 --- a/vendor/github.com/xenolf/lego/README.md +++ /dev/null @@ -1,273 +0,0 @@ -# lego -Let's Encrypt client and ACME library written in Go - -[![GoDoc](https://godoc.org/github.com/xenolf/lego/acme?status.svg)](https://godoc.org/github.com/xenolf/lego/acme) -[![Build Status](https://travis-ci.org/xenolf/lego.svg?branch=master)](https://travis-ci.org/xenolf/lego) -[![Dev Chat](https://img.shields.io/badge/dev%20chat-gitter-blue.svg?label=dev+chat)](https://gitter.im/xenolf/lego) -[![Beerpay](https://beerpay.io/xenolf/lego/badge.svg)](https://beerpay.io/xenolf/lego) - -#### General -This is a work in progress. Please do *NOT* run this on a production server and please report any bugs you find! - -#### Installation -lego supports both binary installs and install from source. - -To get the binary just download the latest release for your OS/Arch from [the release page](https://github.com/xenolf/lego/releases) -and put the binary somewhere convenient. lego does not assume anything about the location you run it from. - -To install from source, just run -``` -go get -u github.com/xenolf/lego -``` - -To build lego inside a Docker container, just run -``` -docker build -t lego . -``` -##### From the package manager -- [ArchLinux (AUR)](https://aur.archlinux.org/packages/lego-git): -``` -yaourt -S lego-git -``` -#### Features - -- Register with CA -- Obtain certificates, both from scratch or with an existing CSR -- Renew certificates -- Revoke certificates -- Robust implementation of all ACME challenges - - HTTP (http-01) - - TLS with Server Name Indication (tls-sni-01) - - DNS (dns-01) -- SAN certificate support -- Comes with multiple optional [DNS providers](https://github.com/xenolf/lego/tree/master/providers/dns) -- [Custom challenge solvers](https://github.com/xenolf/lego/wiki/Writing-a-Challenge-Solver) -- Certificate bundling -- OCSP helper function - -Please keep in mind that CLI switches and APIs are still subject to change. - -When using the standard `--path` option, all certificates and account configurations are saved to a folder *.lego* in the current working directory. - -#### Sudo -The CLI does not require root permissions but needs to bind to port 80 and 443 for certain challenges. -To run the CLI without sudo, you have four options: - -- Use setcap 'cap_net_bind_service=+ep' /path/to/program -- Pass the `--http` or/and the `--tls` option and specify a custom port to bind to. In this case you have to forward port 80/443 to these custom ports (see [Port Usage](#port-usage)). -- Pass the `--webroot` option and specify the path to your webroot folder. In this case the challenge will be written in a file in `.well-known/acme-challenge/` inside your webroot. -- Pass the `--dns` option and specify a DNS provider. - -#### Port Usage -By default lego assumes it is able to bind to ports 80 and 443 to solve challenges. -If this is not possible in your environment, you can use the `--http` and `--tls` options to instruct -lego to listen on that interface:port for any incoming challenges. - -If you are using this option, make sure you proxy all of the following traffic to these ports. - -HTTP Port: -- All plaintext HTTP requests to port 80 which begin with a request path of `/.well-known/acme-challenge/` for the HTTP challenge. - -TLS Port: -- All TLS handshakes on port 443 for the TLS-SNI challenge. - -This traffic redirection is only needed as long as lego solves challenges. As soon as you have received your certificates you can deactivate the forwarding. - -#### Usage - -``` -NAME: - lego - Let's Encrypt client written in Go - -USAGE: - lego [global options] command [command options] [arguments...] - -VERSION: - 0.4.1 - -COMMANDS: - run Register an account, then create and install a certificate - revoke Revoke a certificate - renew Renew a certificate - dnshelp Shows additional help for the --dns global option - help, h Shows a list of commands or help for one command - -GLOBAL OPTIONS: - --domains value, -d value Add a domain to the process. Can be specified multiple times. - --csr value, -c value Certificate signing request filename, if an external CSR is to be used - --server value, -s value CA hostname (and optionally :port). The server certificate must be trusted in order to avoid further modifications to the client. (default: "https://acme-v01.api.letsencrypt.org/directory") - --email value, -m value Email used for registration and recovery contact. - --accept-tos, -a By setting this flag to true you indicate that you accept the current Let's Encrypt terms of service. - --key-type value, -k value Key type to use for private keys. Supported: rsa2048, rsa4096, rsa8192, ec256, ec384 (default: "rsa2048") - --path value Directory to use for storing the data (default: "/.lego") - --exclude value, -x value Explicitly disallow solvers by name from being used. Solvers: "http-01", "tls-sni-01". - --webroot value Set the webroot folder to use for HTTP based challenges to write directly in a file in .well-known/acme-challenge - --memcached-host value Set the memcached host(s) to use for HTTP based challenges. Challenges will be written to all specified hosts. - --http value Set the port and interface to use for HTTP based challenges to listen on. Supported: interface:port or :port - --tls value Set the port and interface to use for TLS based challenges to listen on. Supported: interface:port or :port - --dns value Solve a DNS challenge using the specified provider. Disables all other challenges. Run 'lego dnshelp' for help on usage. - --http-timeout value Set the HTTP timeout value to a specific value in seconds. The default is 10 seconds. (default: 0) - --dns-timeout value Set the DNS timeout value to a specific value in seconds. The default is 10 seconds. (default: 0) - --dns-resolvers value Set the resolvers to use for performing recursive DNS queries. Supported: host:port. The default is to use Google's DNS resolvers. - --pem Generate a .pem file by concatanating the .key and .crt files together. - --help, -h show help - --version, -v print the version -``` - -##### CLI Example - -Assumes the `lego` binary has permission to bind to ports 80 and 443. You can get a pre-built binary from the [releases](https://github.com/xenolf/lego/releases) page. -If your environment does not allow you to bind to these ports, please read [Port Usage](#port-usage). - -Obtain a certificate: - -```bash -$ lego --email="foo@bar.com" --domains="example.com" run -``` - -(Find your certificate in the `.lego` folder of current working directory.) - -To renew the certificate: - -```bash -$ lego --email="foo@bar.com" --domains="example.com" renew -``` - -To renew the certificate only if it's older than 30 days - -```bash -$ lego --email="foo@bar.com" --domains="example.com" renew --days 30 -``` - -Obtain a certificate using the DNS challenge and AWS Route 53: - -```bash -$ AWS_REGION=us-east-1 AWS_ACCESS_KEY_ID=my_id AWS_SECRET_ACCESS_KEY=my_key lego --email="foo@bar.com" --domains="example.com" --dns="route53" run -``` - -Note that `--dns=foo` implies `--exclude=http-01` and `--exclude=tls-sni-01`. lego will not attempt other challenges if you've told it to use DNS instead. - -Obtain a certificate given a certificate signing request (CSR) generated by something else: - -```bash -$ lego --email="foo@bar.com" --csr=/path/to/csr.pem run -``` - -(lego will infer the domains to be validated based on the contents of the CSR, so make sure the CSR's Common Name and optional SubjectAltNames are set correctly.) - -lego defaults to communicating with the production Let's Encrypt ACME server. If you'd like to test something without issuing real certificates, consider using the staging endpoint instead: - -```bash -$ lego --server=https://acme-staging.api.letsencrypt.org/directory … -``` - -#### DNS Challenge API Details - -##### AWS Route 53 - -The following AWS IAM policy document describes the permissions required for lego to complete the DNS challenge. -Replace `` with the Route 53 zone ID of the domain you are authorizing. - -```json -{ - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Action": [ - "route53:GetChange", - "route53:ListHostedZonesByName" - ], - "Resource": [ - "*" - ] - }, - { - "Effect": "Allow", - "Action": [ - "route53:ChangeResourceRecordSets" - ], - "Resource": [ - "arn:aws:route53:::hostedzone/" - ] - } - ] -} -``` - -#### ACME Library Usage - -A valid, but bare-bones example use of the acme package: - -```go -// You'll need a user or account type that implements acme.User -type MyUser struct { - Email string - Registration *acme.RegistrationResource - key crypto.PrivateKey -} -func (u MyUser) GetEmail() string { - return u.Email -} -func (u MyUser) GetRegistration() *acme.RegistrationResource { - return u.Registration -} -func (u MyUser) GetPrivateKey() crypto.PrivateKey { - return u.key -} - -// Create a user. New accounts need an email and private key to start. -const rsaKeySize = 2048 -privateKey, err := rsa.GenerateKey(rand.Reader, rsaKeySize) -if err != nil { - log.Fatal(err) -} -myUser := MyUser{ - Email: "you@yours.com", - key: privateKey, -} - -// A client facilitates communication with the CA server. This CA URL is -// configured for a local dev instance of Boulder running in Docker in a VM. -client, err := acme.NewClient("http://192.168.99.100:4000/directory", &myUser, acme.RSA2048) -if err != nil { - log.Fatal(err) -} - -// We specify an http port of 5002 and an tls port of 5001 on all interfaces -// because we aren't running as root and can't bind a listener to port 80 and 443 -// (used later when we attempt to pass challenges). Keep in mind that we still -// need to proxy challenge traffic to port 5002 and 5001. -client.SetHTTPAddress(":5002") -client.SetTLSAddress(":5001") - -// New users will need to register -reg, err := client.Register() -if err != nil { - log.Fatal(err) -} -myUser.Registration = reg - -// SAVE THE USER. - -// The client has a URL to the current Let's Encrypt Subscriber -// Agreement. The user will need to agree to it. -err = client.AgreeToTOS() -if err != nil { - log.Fatal(err) -} - -// The acme library takes care of completing the challenges to obtain the certificate(s). -// The domains must resolve to this machine or you have to use the DNS challenge. -bundle := false -certificates, failures := client.ObtainCertificate([]string{"mydomain.com"}, bundle, nil, false) -if len(failures) > 0 { - log.Fatal(failures) -} - -// Each certificate comes back with the cert bytes, the bytes of the client's -// private key, and a certificate URL. SAVE THESE TO DISK. -fmt.Printf("%#v\n", certificates) - -// ... all done. -``` diff --git a/vendor/github.com/xenolf/lego/account.go b/vendor/github.com/xenolf/lego/account.go deleted file mode 100644 index 34856e16f..000000000 --- a/vendor/github.com/xenolf/lego/account.go +++ /dev/null @@ -1,109 +0,0 @@ -package main - -import ( - "crypto" - "encoding/json" - "io/ioutil" - "os" - "path" - - "github.com/xenolf/lego/acme" -) - -// Account represents a users local saved credentials -type Account struct { - Email string `json:"email"` - key crypto.PrivateKey - Registration *acme.RegistrationResource `json:"registration"` - - conf *Configuration -} - -// NewAccount creates a new account for an email address -func NewAccount(email string, conf *Configuration) *Account { - accKeysPath := conf.AccountKeysPath(email) - // TODO: move to function in configuration? - accKeyPath := accKeysPath + string(os.PathSeparator) + email + ".key" - if err := checkFolder(accKeysPath); err != nil { - logger().Fatalf("Could not check/create directory for account %s: %v", email, err) - } - - var privKey crypto.PrivateKey - if _, err := os.Stat(accKeyPath); os.IsNotExist(err) { - - logger().Printf("No key found for account %s. Generating a curve P384 EC key.", email) - privKey, err = generatePrivateKey(accKeyPath) - if err != nil { - logger().Fatalf("Could not generate RSA private account key for account %s: %v", email, err) - } - - logger().Printf("Saved key to %s", accKeyPath) - } else { - privKey, err = loadPrivateKey(accKeyPath) - if err != nil { - logger().Fatalf("Could not load RSA private key from file %s: %v", accKeyPath, err) - } - } - - accountFile := path.Join(conf.AccountPath(email), "account.json") - if _, err := os.Stat(accountFile); os.IsNotExist(err) { - return &Account{Email: email, key: privKey, conf: conf} - } - - fileBytes, err := ioutil.ReadFile(accountFile) - if err != nil { - logger().Fatalf("Could not load file for account %s -> %v", email, err) - } - - var acc Account - err = json.Unmarshal(fileBytes, &acc) - if err != nil { - logger().Fatalf("Could not parse file for account %s -> %v", email, err) - } - - acc.key = privKey - acc.conf = conf - - if acc.Registration == nil { - logger().Fatalf("Could not load account for %s. Registration is nil.", email) - } - - if acc.conf == nil { - logger().Fatalf("Could not load account for %s. Configuration is nil.", email) - } - - return &acc -} - -/** Implementation of the acme.User interface **/ - -// GetEmail returns the email address for the account -func (a *Account) GetEmail() string { - return a.Email -} - -// GetPrivateKey returns the private RSA account key. -func (a *Account) GetPrivateKey() crypto.PrivateKey { - return a.key -} - -// GetRegistration returns the server registration -func (a *Account) GetRegistration() *acme.RegistrationResource { - return a.Registration -} - -/** End **/ - -// Save the account to disk -func (a *Account) Save() error { - jsonBytes, err := json.MarshalIndent(a, "", "\t") - if err != nil { - return err - } - - return ioutil.WriteFile( - path.Join(a.conf.AccountPath(a.Email), "account.json"), - jsonBytes, - 0600, - ) -} diff --git a/vendor/github.com/xenolf/lego/acme/challenges.go b/vendor/github.com/xenolf/lego/acme/challenges.go deleted file mode 100644 index 857900507..000000000 --- a/vendor/github.com/xenolf/lego/acme/challenges.go +++ /dev/null @@ -1,16 +0,0 @@ -package acme - -// Challenge is a string that identifies a particular type and version of ACME challenge. -type Challenge string - -const ( - // HTTP01 is the "http-01" ACME challenge https://github.com/ietf-wg-acme/acme/blob/master/draft-ietf-acme-acme.md#http - // Note: HTTP01ChallengePath returns the URL path to fulfill this challenge - HTTP01 = Challenge("http-01") - // TLSSNI01 is the "tls-sni-01" ACME challenge https://github.com/ietf-wg-acme/acme/blob/master/draft-ietf-acme-acme.md#tls-with-server-name-indication-tls-sni - // Note: TLSSNI01ChallengeCert returns a certificate to fulfill this challenge - TLSSNI01 = Challenge("tls-sni-01") - // DNS01 is the "dns-01" ACME challenge https://github.com/ietf-wg-acme/acme/blob/master/draft-ietf-acme-acme.md#dns - // Note: DNS01Record returns a DNS record which will fulfill this challenge - DNS01 = Challenge("dns-01") -) diff --git a/vendor/github.com/xenolf/lego/acme/client.go b/vendor/github.com/xenolf/lego/acme/client.go deleted file mode 100644 index bcb844371..000000000 --- a/vendor/github.com/xenolf/lego/acme/client.go +++ /dev/null @@ -1,825 +0,0 @@ -// Package acme implements the ACME protocol for Let's Encrypt and other conforming providers. -package acme - -import ( - "crypto" - "crypto/x509" - "encoding/base64" - "encoding/json" - "errors" - "fmt" - "io/ioutil" - "log" - "net" - "net/http" - "regexp" - "strconv" - "strings" - "time" -) - -var ( - // Logger is an optional custom logger. - Logger *log.Logger -) - -const ( - // maxBodySize is the maximum size of body that we will read. - maxBodySize = 1024 * 1024 - - // overallRequestLimit is the overall number of request per second limited on the - // “new-reg”, “new-authz” and “new-cert” endpoints. From the documentation the - // limitation is 20 requests per second, but using 20 as value doesn't work but 18 do - overallRequestLimit = 18 -) - -// logf writes a log entry. It uses Logger if not -// nil, otherwise it uses the default log.Logger. -func logf(format string, args ...interface{}) { - if Logger != nil { - Logger.Printf(format, args...) - } else { - log.Printf(format, args...) - } -} - -// User interface is to be implemented by users of this library. -// It is used by the client type to get user specific information. -type User interface { - GetEmail() string - GetRegistration() *RegistrationResource - GetPrivateKey() crypto.PrivateKey -} - -// Interface for all challenge solvers to implement. -type solver interface { - Solve(challenge challenge, domain string) error -} - -type validateFunc func(j *jws, domain, uri string, chlng challenge) error - -// Client is the user-friendy way to ACME -type Client struct { - directory directory - user User - jws *jws - keyType KeyType - solvers map[Challenge]solver -} - -// NewClient creates a new ACME client on behalf of the user. The client will depend on -// the ACME directory located at caDirURL for the rest of its actions. A private -// key of type keyType (see KeyType contants) will be generated when requesting a new -// certificate if one isn't provided. -func NewClient(caDirURL string, user User, keyType KeyType) (*Client, error) { - privKey := user.GetPrivateKey() - if privKey == nil { - return nil, errors.New("private key was nil") - } - - var dir directory - if _, err := getJSON(caDirURL, &dir); err != nil { - return nil, fmt.Errorf("get directory at '%s': %v", caDirURL, err) - } - - if dir.NewRegURL == "" { - return nil, errors.New("directory missing new registration URL") - } - if dir.NewAuthzURL == "" { - return nil, errors.New("directory missing new authz URL") - } - if dir.NewCertURL == "" { - return nil, errors.New("directory missing new certificate URL") - } - if dir.RevokeCertURL == "" { - return nil, errors.New("directory missing revoke certificate URL") - } - - jws := &jws{privKey: privKey, directoryURL: caDirURL} - - // REVIEW: best possibility? - // Add all available solvers with the right index as per ACME - // spec to this map. Otherwise they won`t be found. - solvers := make(map[Challenge]solver) - solvers[HTTP01] = &httpChallenge{jws: jws, validate: validate, provider: &HTTPProviderServer{}} - solvers[TLSSNI01] = &tlsSNIChallenge{jws: jws, validate: validate, provider: &TLSProviderServer{}} - - return &Client{directory: dir, user: user, jws: jws, keyType: keyType, solvers: solvers}, nil -} - -// 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: - c.solvers[challenge] = &httpChallenge{jws: c.jws, validate: validate, provider: p} - case TLSSNI01: - c.solvers[challenge] = &tlsSNIChallenge{jws: c.jws, validate: validate, provider: p} - case DNS01: - c.solvers[challenge] = &dnsChallenge{jws: c.jws, validate: validate, provider: p} - default: - return fmt.Errorf("Unknown challenge %v", challenge) - } - return nil -} - -// 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 { - return err - } - - if chlng, ok := c.solvers[HTTP01]; ok { - chlng.(*httpChallenge).provider = NewHTTPProviderServer(host, port) - } - - return nil -} - -// 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 { - return err - } - - if chlng, ok := c.solvers[TLSSNI01]; ok { - chlng.(*tlsSNIChallenge).provider = NewTLSProviderServer(host, port) - } - return nil -} - -// ExcludeChallenges explicitly removes challenges from the pool for solving. -func (c *Client) ExcludeChallenges(challenges []Challenge) { - // Loop through all challenges and delete the requested one if found. - for _, challenge := range challenges { - delete(c.solvers, challenge) - } -} - -// Register the current account to the ACME server. -func (c *Client) Register() (*RegistrationResource, error) { - if c == nil || c.user == nil { - return nil, errors.New("acme: cannot register a nil client or user") - } - logf("[INFO] acme: Registering account for %s", c.user.GetEmail()) - - regMsg := registrationMessage{ - Resource: "new-reg", - } - if c.user.GetEmail() != "" { - regMsg.Contact = []string{"mailto:" + c.user.GetEmail()} - } else { - regMsg.Contact = []string{} - } - - var serverReg Registration - var regURI string - hdr, err := postJSON(c.jws, c.directory.NewRegURL, regMsg, &serverReg) - if err != nil { - remoteErr, ok := err.(RemoteError) - if ok && remoteErr.StatusCode == 409 { - regURI = hdr.Get("Location") - regMsg = registrationMessage{ - Resource: "reg", - } - if hdr, err = postJSON(c.jws, regURI, regMsg, &serverReg); err != nil { - return nil, err - } - } else { - return nil, err - } - } - - reg := &RegistrationResource{Body: serverReg} - - links := parseLinks(hdr["Link"]) - - if regURI == "" { - regURI = hdr.Get("Location") - } - reg.URI = regURI - if links["terms-of-service"] != "" { - reg.TosURL = links["terms-of-service"] - } - - if links["next"] != "" { - reg.NewAuthzURL = links["next"] - } else { - return nil, errors.New("acme: The server did not return 'next' link to proceed") - } - - return reg, nil -} - -// DeleteRegistration deletes the client's user registration from the ACME -// server. -func (c *Client) DeleteRegistration() error { - if c == nil || c.user == nil { - return errors.New("acme: cannot unregister a nil client or user") - } - logf("[INFO] acme: Deleting account for %s", c.user.GetEmail()) - - regMsg := registrationMessage{ - Resource: "reg", - Delete: true, - } - - _, err := postJSON(c.jws, c.user.GetRegistration().URI, regMsg, nil) - if err != nil { - return err - } - - return nil -} - -// QueryRegistration runs a POST request on the client's registration and -// returns the result. -// -// This is similar to the Register function, but acting on an existing -// registration link and resource. -func (c *Client) QueryRegistration() (*RegistrationResource, error) { - if c == nil || c.user == nil { - return nil, errors.New("acme: cannot query the registration of a nil client or user") - } - // Log the URL here instead of the email as the email may not be set - logf("[INFO] acme: Querying account for %s", c.user.GetRegistration().URI) - - regMsg := registrationMessage{ - Resource: "reg", - } - - var serverReg Registration - hdr, err := postJSON(c.jws, c.user.GetRegistration().URI, regMsg, &serverReg) - if err != nil { - return nil, err - } - - reg := &RegistrationResource{Body: serverReg} - - links := parseLinks(hdr["Link"]) - // Location: header is not returned so this needs to be populated off of - // existing URI - reg.URI = c.user.GetRegistration().URI - if links["terms-of-service"] != "" { - reg.TosURL = links["terms-of-service"] - } - - if links["next"] != "" { - reg.NewAuthzURL = links["next"] - } else { - return nil, errors.New("acme: No new-authz link in response to registration query") - } - - return reg, nil -} - -// AgreeToTOS updates the Client registration and sends the agreement to -// the server. -func (c *Client) AgreeToTOS() error { - reg := c.user.GetRegistration() - - reg.Body.Agreement = c.user.GetRegistration().TosURL - reg.Body.Resource = "reg" - _, err := postJSON(c.jws, c.user.GetRegistration().URI, c.user.GetRegistration().Body, nil) - return err -} - -// ObtainCertificateForCSR tries to obtain a certificate matching the CSR passed into it. -// The domains are inferred from the CommonName and SubjectAltNames, if any. The private key -// for this CSR is not required. -// If bundle is true, the []byte contains both the issuer certificate and -// 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) ObtainCertificateForCSR(csr x509.CertificateRequest, bundle bool) (CertificateResource, map[string]error) { - // figure out what domains it concerns - // start with the common name - domains := []string{csr.Subject.CommonName} - - // loop over the SubjectAltName DNS names -DNSNames: - for _, sanName := range csr.DNSNames { - for _, existingName := range domains { - if existingName == sanName { - // duplicate; skip this name - continue DNSNames - } - } - - // name is unique - domains = append(domains, sanName) - } - - if bundle { - logf("[INFO][%s] acme: Obtaining bundled SAN certificate given a CSR", strings.Join(domains, ", ")) - } else { - logf("[INFO][%s] acme: Obtaining SAN certificate given a CSR", strings.Join(domains, ", ")) - } - - challenges, failures := c.getChallenges(domains) - // If any challenge fails - return. Do not generate partial SAN certificates. - if len(failures) > 0 { - for _, auth := range challenges { - c.disableAuthz(auth) - } - - return CertificateResource{}, failures - } - - errs := c.solveChallenges(challenges) - // If any challenge fails - return. Do not generate partial SAN certificates. - if len(errs) > 0 { - return CertificateResource{}, errs - } - - logf("[INFO][%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", ")) - - cert, err := c.requestCertificateForCsr(challenges, bundle, csr.Raw, nil) - if err != nil { - for _, chln := range challenges { - failures[chln.Domain] = err - } - } - - // Add the CSR to the certificate so that it can be used for renewals. - cert.CSR = pemEncode(&csr) - - return cert, failures -} - -// ObtainCertificate tries to obtain a single certificate using all domains passed into it. -// The first domain in domains is used for the CommonName field of the certificate, all other -// domains are added using the Subject Alternate Names extension. A new private key is generated -// for every invocation of this function. If you do not want that you can supply your own private key -// in the privKey parameter. If this parameter is non-nil it will be used instead of generating a new one. -// If bundle is true, the []byte contains both the issuer certificate and -// 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, mustStaple bool) (CertificateResource, map[string]error) { - if bundle { - logf("[INFO][%s] acme: Obtaining bundled SAN certificate", strings.Join(domains, ", ")) - } else { - logf("[INFO][%s] acme: Obtaining SAN certificate", strings.Join(domains, ", ")) - } - - challenges, failures := c.getChallenges(domains) - // If any challenge fails - return. Do not generate partial SAN certificates. - if len(failures) > 0 { - for _, auth := range challenges { - c.disableAuthz(auth) - } - - return CertificateResource{}, failures - } - - errs := c.solveChallenges(challenges) - // If any challenge fails - return. Do not generate partial SAN certificates. - if len(errs) > 0 { - return CertificateResource{}, errs - } - - logf("[INFO][%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", ")) - - cert, err := c.requestCertificate(challenges, bundle, privKey, mustStaple) - if err != nil { - for _, chln := range challenges { - failures[chln.Domain] = err - } - } - - return cert, failures -} - -// RevokeCertificate takes a PEM encoded certificate or bundle and tries to revoke it at the CA. -func (c *Client) RevokeCertificate(certificate []byte) error { - certificates, err := parsePEMBundle(certificate) - if err != nil { - return err - } - - x509Cert := certificates[0] - if x509Cert.IsCA { - return fmt.Errorf("Certificate bundle starts with a CA certificate") - } - - encodedCert := base64.URLEncoding.EncodeToString(x509Cert.Raw) - - _, err = postJSON(c.jws, c.directory.RevokeCertURL, revokeCertMessage{Resource: "revoke-cert", Certificate: encodedCert}, nil) - return err -} - -// RenewCertificate takes a CertificateResource and tries to renew the certificate. -// If the renewal process succeeds, the new certificate will ge returned in a new CertResource. -// Please be aware that this function will return a new certificate in ANY case that is not an error. -// If the server does not provide us with a new cert on a GET request to the CertURL -// this function will start a new-cert flow where a new certificate gets generated. -// 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, 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) - if err != nil { - return CertificateResource{}, err - } - - x509Cert := certificates[0] - if x509Cert.IsCA { - return CertificateResource{}, fmt.Errorf("[%s] Certificate bundle starts with a CA certificate", cert.Domain) - } - - // This is just meant to be informal for the user. - timeLeft := x509Cert.NotAfter.Sub(time.Now().UTC()) - logf("[INFO][%s] acme: Trying renewal with %d hours remaining", cert.Domain, int(timeLeft.Hours())) - - // 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 { - csr, err := pemDecodeTox509CSR(cert.CSR) - if err != nil { - return CertificateResource{}, err - } - newCert, failures := c.ObtainCertificateForCSR(*csr, bundle) - return newCert, failures[cert.Domain] - } - - var privKey crypto.PrivateKey - if cert.PrivateKey != nil { - privKey, err = parsePEMPrivateKey(cert.PrivateKey) - if err != nil { - return CertificateResource{}, err - } - } - - var domains []string - var failures map[string]error - // check for SAN certificate - if len(x509Cert.DNSNames) > 1 { - domains = append(domains, x509Cert.Subject.CommonName) - for _, sanDomain := range x509Cert.DNSNames { - if sanDomain == x509Cert.Subject.CommonName { - continue - } - domains = append(domains, sanDomain) - } - } else { - domains = append(domains, x509Cert.Subject.CommonName) - } - - newCert, failures := c.ObtainCertificate(domains, bundle, privKey, mustStaple) - return newCert, failures[cert.Domain] -} - -// Looks through the challenge combinations to find a solvable match. -// Then solves the challenges in series and returns. -func (c *Client) solveChallenges(challenges []authorizationResource) map[string]error { - // loop through the resources, basically through the domains. - failures := make(map[string]error) - for _, authz := range challenges { - if authz.Body.Status == "valid" { - // Boulder might recycle recent validated authz (see issue #267) - logf("[INFO][%s] acme: Authorization already valid; skipping challenge", authz.Domain) - continue - } - // no solvers - no solving - if solvers := c.chooseSolvers(authz.Body, authz.Domain); solvers != nil { - for i, solver := range solvers { - // TODO: do not immediately fail if one domain fails to validate. - err := solver.Solve(authz.Body.Challenges[i], authz.Domain) - if err != nil { - c.disableAuthz(authz) - failures[authz.Domain] = err - } - } - } else { - c.disableAuthz(authz) - failures[authz.Domain] = fmt.Errorf("[%s] acme: Could not determine solvers", authz.Domain) - } - } - - return failures -} - -// Checks all combinations from the server and returns an array of -// solvers which should get executed in series. -func (c *Client) chooseSolvers(auth authorization, domain string) map[int]solver { - for _, combination := range auth.Combinations { - solvers := make(map[int]solver) - for _, idx := range combination { - if solver, ok := c.solvers[auth.Challenges[idx].Type]; ok { - solvers[idx] = solver - } else { - logf("[INFO][%s] acme: Could not find solver for: %s", domain, auth.Challenges[idx].Type) - } - } - - // If we can solve the whole combination, return the solvers - if len(solvers) == len(combination) { - return solvers - } - } - return nil -} - -// Get the challenges needed to proof our identifier to the ACME server. -func (c *Client) getChallenges(domains []string) ([]authorizationResource, map[string]error) { - resc, errc := make(chan authorizationResource), make(chan domainError) - - delay := time.Second / overallRequestLimit - - for _, domain := range domains { - time.Sleep(delay) - - go func(domain string) { - authMsg := authorization{Resource: "new-authz", Identifier: identifier{Type: "dns", Value: domain}} - var authz authorization - hdr, err := postJSON(c.jws, c.user.GetRegistration().NewAuthzURL, authMsg, &authz) - if err != nil { - errc <- domainError{Domain: domain, Error: err} - return - } - - links := parseLinks(hdr["Link"]) - if links["next"] == "" { - logf("[ERROR][%s] acme: Server did not provide next link to proceed", domain) - errc <- domainError{Domain: domain, Error: errors.New("Server did not provide next link to proceed")} - return - } - - resc <- authorizationResource{Body: authz, NewCertURL: links["next"], AuthURL: hdr.Get("Location"), Domain: domain} - }(domain) - } - - responses := make(map[string]authorizationResource) - failures := make(map[string]error) - for i := 0; i < len(domains); i++ { - select { - case res := <-resc: - responses[res.Domain] = res - case err := <-errc: - failures[err.Domain] = err.Error - } - } - - challenges := make([]authorizationResource, 0, len(responses)) - for _, domain := range domains { - if challenge, ok := responses[domain]; ok { - challenges = append(challenges, challenge) - } - } - - logAuthz(challenges) - - close(resc) - close(errc) - - return challenges, failures -} - -func logAuthz(authz []authorizationResource) { - for _, auth := range authz { - logf("[INFO][%s] AuthURL: %s", auth.Domain, auth.AuthURL) - } -} - -// cleanAuthz loops through the passed in slice and disables any auths which are not "valid" -func (c *Client) disableAuthz(auth authorizationResource) error { - var disabledAuth authorization - _, err := postJSON(c.jws, auth.AuthURL, deactivateAuthMessage{Resource: "authz", Status: "deactivated"}, &disabledAuth) - return err -} - -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!") - } - - var err error - if privKey == nil { - privKey, err = generatePrivateKey(c.keyType) - if err != nil { - return CertificateResource{}, err - } - } - - // determine certificate name(s) based on the authorization resources - commonName := authz[0] - var san []string - for _, auth := range authz[1:] { - san = append(san, auth.Domain) - } - - // TODO: should the CSR be customizable? - csr, err := generateCsr(privKey, commonName.Domain, san, mustStaple) - if err != nil { - return CertificateResource{}, err - } - - return c.requestCertificateForCsr(authz, bundle, csr, pemEncode(privKey)) -} - -func (c *Client) requestCertificateForCsr(authz []authorizationResource, bundle bool, csr []byte, privateKeyPem []byte) (CertificateResource, error) { - commonName := authz[0] - - var authURLs []string - for _, auth := range authz[1:] { - authURLs = append(authURLs, auth.AuthURL) - } - - csrString := base64.URLEncoding.EncodeToString(csr) - jsonBytes, err := json.Marshal(csrMessage{Resource: "new-cert", Csr: csrString, Authorizations: authURLs}) - if err != nil { - return CertificateResource{}, err - } - - resp, err := c.jws.post(commonName.NewCertURL, jsonBytes) - if err != nil { - return CertificateResource{}, err - } - - certRes := CertificateResource{ - Domain: commonName.Domain, - CertURL: resp.Header.Get("Location"), - PrivateKey: privateKeyPem, - } - - maxChecks := 1000 - for i := 0; i < maxChecks; i++ { - done, err := c.checkCertResponse(resp, &certRes, bundle) - resp.Body.Close() - if err != nil { - return CertificateResource{}, err - } - if done { - break - } - if i == maxChecks-1 { - return CertificateResource{}, fmt.Errorf("polled for certificate %d times; giving up", i) - } - resp, err = httpGet(certRes.CertURL) - if err != nil { - return CertificateResource{}, err - } - } - - return certRes, nil -} - -// checkCertResponse checks resp to see if a certificate is contained in the -// response, and if so, loads it into certRes and returns true. If the cert -// is not yet ready, it returns false. This function honors the waiting period -// required by the Retry-After header of the response, if specified. This -// function may read from resp.Body but does NOT close it. The certRes input -// should already have the Domain (common name) field populated. If bundle is -// true, the certificate will be bundled with the issuer's cert. -func (c *Client) checkCertResponse(resp *http.Response, certRes *CertificateResource, bundle bool) (bool, error) { - switch resp.StatusCode { - case 201, 202: - cert, err := ioutil.ReadAll(limitReader(resp.Body, maxBodySize)) - if err != nil { - return false, err - } - - // The server returns a body with a length of zero if the - // certificate was not ready at the time this request completed. - // Otherwise the body is the certificate. - if len(cert) > 0 { - certRes.CertStableURL = resp.Header.Get("Content-Location") - certRes.AccountRef = c.user.GetRegistration().URI - - issuedCert := pemEncode(derCertificateBytes(cert)) - - // 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("[WARNING][%s] acme: Could not bundle issuer certificate: %v", certRes.Domain, err) - } else { - issuerCert = pemEncode(derCertificateBytes(issuerCert)) - - // If bundle is true, we want to return a certificate bundle. - // To do this, we append the issuer cert to the issued cert. - if bundle { - issuedCert = append(issuedCert, issuerCert...) - } - } - - certRes.Certificate = issuedCert - certRes.IssuerCertificate = issuerCert - logf("[INFO][%s] Server responded with a certificate.", certRes.Domain) - return true, nil - } - - // The certificate was granted but is not yet issued. - // Check retry-after and loop. - ra := resp.Header.Get("Retry-After") - retryAfter, err := strconv.Atoi(ra) - if err != nil { - return false, err - } - - logf("[INFO][%s] acme: Server responded with status 202; retrying after %ds", certRes.Domain, retryAfter) - time.Sleep(time.Duration(retryAfter) * time.Second) - - return false, nil - default: - return false, handleHTTPError(resp) - } -} - -// getIssuerCertificate requests the issuer certificate -func (c *Client) getIssuerCertificate(url string) ([]byte, error) { - logf("[INFO] acme: Requesting issuer cert from %s", url) - resp, err := httpGet(url) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - issuerBytes, err := ioutil.ReadAll(limitReader(resp.Body, maxBodySize)) - if err != nil { - return nil, err - } - - _, err = x509.ParseCertificate(issuerBytes) - if err != nil { - return nil, err - } - - return issuerBytes, err -} - -func parseLinks(links []string) map[string]string { - aBrkt := regexp.MustCompile("[<>]") - slver := regexp.MustCompile("(.+) *= *\"(.+)\"") - linkMap := make(map[string]string) - - for _, link := range links { - - link = aBrkt.ReplaceAllString(link, "") - parts := strings.Split(link, ";") - - matches := slver.FindStringSubmatch(parts[1]) - if len(matches) > 0 { - linkMap[matches[2]] = parts[0] - } - } - - return linkMap -} - -// validate makes the ACME server start validating a -// challenge response, only returning once it is done. -func validate(j *jws, domain, uri string, chlng challenge) error { - var challengeResponse challenge - - hdr, err := postJSON(j, uri, chlng, &challengeResponse) - if err != nil { - return err - } - - // After the path is sent, the ACME server will access our server. - // Repeatedly check the server for an updated status on our request. - for { - switch challengeResponse.Status { - case "valid": - logf("[INFO][%s] The server validated our request", domain) - return nil - case "pending": - break - case "invalid": - return handleChallengeError(challengeResponse) - default: - return errors.New("The server returned an unexpected state.") - } - - ra, err := strconv.Atoi(hdr.Get("Retry-After")) - if err != nil { - // The ACME server MUST return a Retry-After. - // If it doesn't, we'll just poll hard. - ra = 1 - } - time.Sleep(time.Duration(ra) * time.Second) - - hdr, err = getJSON(uri, &challengeResponse) - if err != nil { - return err - } - } -} diff --git a/vendor/github.com/xenolf/lego/acme/client_test.go b/vendor/github.com/xenolf/lego/acme/client_test.go deleted file mode 100644 index b18334c8a..000000000 --- a/vendor/github.com/xenolf/lego/acme/client_test.go +++ /dev/null @@ -1,269 +0,0 @@ -package acme - -import ( - "crypto" - "crypto/rand" - "crypto/rsa" - "encoding/json" - "net" - "net/http" - "net/http/httptest" - "strings" - "testing" - "time" -) - -func TestNewClient(t *testing.T) { - keyBits := 32 // small value keeps test fast - keyType := RSA2048 - key, err := rsa.GenerateKey(rand.Reader, keyBits) - if err != nil { - t.Fatal("Could not generate test key:", err) - } - user := mockUser{ - email: "test@test.com", - regres: new(RegistrationResource), - privatekey: key, - } - - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - data, _ := json.Marshal(directory{NewAuthzURL: "http://test", NewCertURL: "http://test", NewRegURL: "http://test", RevokeCertURL: "http://test"}) - w.Write(data) - })) - - client, err := NewClient(ts.URL, user, keyType) - if err != nil { - t.Fatalf("Could not create client: %v", err) - } - - if client.jws == nil { - t.Fatalf("Expected client.jws to not be nil") - } - if expected, actual := key, client.jws.privKey; actual != expected { - t.Errorf("Expected jws.privKey to be %p but was %p", expected, actual) - } - - if client.keyType != keyType { - t.Errorf("Expected keyType to be %s but was %s", keyType, client.keyType) - } - - if expected, actual := 2, len(client.solvers); actual != expected { - t.Fatalf("Expected %d solver(s), got %d", expected, actual) - } -} - -func TestClientOptPort(t *testing.T) { - keyBits := 32 // small value keeps test fast - key, err := rsa.GenerateKey(rand.Reader, keyBits) - if err != nil { - t.Fatal("Could not generate test key:", err) - } - user := mockUser{ - email: "test@test.com", - regres: new(RegistrationResource), - privatekey: key, - } - - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - data, _ := json.Marshal(directory{NewAuthzURL: "http://test", NewCertURL: "http://test", NewRegURL: "http://test", RevokeCertURL: "http://test"}) - w.Write(data) - })) - - optPort := "1234" - optHost := "" - client, err := NewClient(ts.URL, user, RSA2048) - if err != nil { - t.Fatalf("Could not create client: %v", err) - } - client.SetHTTPAddress(net.JoinHostPort(optHost, optPort)) - client.SetTLSAddress(net.JoinHostPort(optHost, optPort)) - - httpSolver, ok := client.solvers[HTTP01].(*httpChallenge) - if !ok { - t.Fatal("Expected http-01 solver to be httpChallenge type") - } - if httpSolver.jws != client.jws { - t.Error("Expected http-01 to have same jws as client") - } - if got := httpSolver.provider.(*HTTPProviderServer).port; got != optPort { - t.Errorf("Expected http-01 to have port %s but was %s", optPort, got) - } - if got := httpSolver.provider.(*HTTPProviderServer).iface; got != optHost { - t.Errorf("Expected http-01 to have iface %s but was %s", optHost, got) - } - - httpsSolver, ok := client.solvers[TLSSNI01].(*tlsSNIChallenge) - if !ok { - t.Fatal("Expected tls-sni-01 solver to be httpChallenge type") - } - if httpsSolver.jws != client.jws { - t.Error("Expected tls-sni-01 to have same jws as client") - } - if got := httpsSolver.provider.(*TLSProviderServer).port; got != optPort { - t.Errorf("Expected tls-sni-01 to have port %s but was %s", optPort, got) - } - if got := httpsSolver.provider.(*TLSProviderServer).iface; got != optHost { - t.Errorf("Expected tls-sni-01 to have port %s but was %s", optHost, got) - } - - // test setting different host - optHost = "127.0.0.1" - client.SetHTTPAddress(net.JoinHostPort(optHost, optPort)) - client.SetTLSAddress(net.JoinHostPort(optHost, optPort)) - - if got := httpSolver.provider.(*HTTPProviderServer).iface; got != optHost { - t.Errorf("Expected http-01 to have iface %s but was %s", optHost, got) - } - if got := httpsSolver.provider.(*TLSProviderServer).port; got != optPort { - t.Errorf("Expected tls-sni-01 to have port %s but was %s", optPort, got) - } -} - -func TestNotHoldingLockWhileMakingHTTPRequests(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - time.Sleep(250 * time.Millisecond) - w.Header().Add("Replay-Nonce", "12345") - w.Header().Add("Retry-After", "0") - writeJSONResponse(w, &challenge{Type: "http-01", Status: "Valid", URI: "http://example.com/", Token: "token"}) - })) - defer ts.Close() - - privKey, _ := rsa.GenerateKey(rand.Reader, 512) - j := &jws{privKey: privKey, directoryURL: ts.URL} - ch := make(chan bool) - resultCh := make(chan bool) - go func() { - j.Nonce() - ch <- true - }() - go func() { - j.Nonce() - ch <- true - }() - go func() { - <-ch - <-ch - resultCh <- true - }() - select { - case <-resultCh: - case <-time.After(400 * time.Millisecond): - t.Fatal("JWS is probably holding a lock while making HTTP request") - } -} - -func TestValidate(t *testing.T) { - var statuses []string - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // Minimal stub ACME server for validation. - w.Header().Add("Replay-Nonce", "12345") - w.Header().Add("Retry-After", "0") - switch r.Method { - case "HEAD": - case "POST": - st := statuses[0] - statuses = statuses[1:] - writeJSONResponse(w, &challenge{Type: "http-01", Status: st, URI: "http://example.com/", Token: "token"}) - - case "GET": - st := statuses[0] - statuses = statuses[1:] - writeJSONResponse(w, &challenge{Type: "http-01", Status: st, URI: "http://example.com/", Token: "token"}) - - default: - http.Error(w, r.Method, http.StatusMethodNotAllowed) - } - })) - defer ts.Close() - - privKey, _ := rsa.GenerateKey(rand.Reader, 512) - j := &jws{privKey: privKey, directoryURL: ts.URL} - - tsts := []struct { - name string - statuses []string - want string - }{ - {"POST-unexpected", []string{"weird"}, "unexpected"}, - {"POST-valid", []string{"valid"}, ""}, - {"POST-invalid", []string{"invalid"}, "Error Detail"}, - {"GET-unexpected", []string{"pending", "weird"}, "unexpected"}, - {"GET-valid", []string{"pending", "valid"}, ""}, - {"GET-invalid", []string{"pending", "invalid"}, "Error Detail"}, - } - - for _, tst := range tsts { - statuses = tst.statuses - if err := validate(j, "example.com", ts.URL, challenge{Type: "http-01", Token: "token"}); err == nil && tst.want != "" { - t.Errorf("[%s] validate: got error %v, want something with %q", tst.name, err, tst.want) - } else if err != nil && !strings.Contains(err.Error(), tst.want) { - t.Errorf("[%s] validate: got error %v, want something with %q", tst.name, err, tst.want) - } - } -} - -func TestGetChallenges(t *testing.T) { - var ts *httptest.Server - ts = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - switch r.Method { - case "GET", "HEAD": - w.Header().Add("Replay-Nonce", "12345") - w.Header().Add("Retry-After", "0") - writeJSONResponse(w, directory{NewAuthzURL: ts.URL, NewCertURL: ts.URL, NewRegURL: ts.URL, RevokeCertURL: ts.URL}) - case "POST": - writeJSONResponse(w, authorization{}) - } - })) - defer ts.Close() - - keyBits := 512 // small value keeps test fast - keyType := RSA2048 - key, err := rsa.GenerateKey(rand.Reader, keyBits) - if err != nil { - t.Fatal("Could not generate test key:", err) - } - user := mockUser{ - email: "test@test.com", - regres: &RegistrationResource{NewAuthzURL: ts.URL}, - privatekey: key, - } - - client, err := NewClient(ts.URL, user, keyType) - if err != nil { - t.Fatalf("Could not create client: %v", err) - } - - _, failures := client.getChallenges([]string{"example.com"}) - if failures["example.com"] == nil { - t.Fatal("Expecting \"Server did not provide next link to proceed\" error, got nil") - } -} - -// writeJSONResponse marshals the body as JSON and writes it to the response. -func writeJSONResponse(w http.ResponseWriter, body interface{}) { - bs, err := json.Marshal(body) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - w.Header().Set("Content-Type", "application/json") - if _, err := w.Write(bs); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - } -} - -// stubValidate is like validate, except it does nothing. -func stubValidate(j *jws, domain, uri string, chlng challenge) error { - return nil -} - -type mockUser struct { - email string - regres *RegistrationResource - privatekey *rsa.PrivateKey -} - -func (u mockUser) GetEmail() string { return u.email } -func (u mockUser) GetRegistration() *RegistrationResource { return u.regres } -func (u mockUser) GetPrivateKey() crypto.PrivateKey { return u.privatekey } diff --git a/vendor/github.com/xenolf/lego/acme/crypto.go b/vendor/github.com/xenolf/lego/acme/crypto.go deleted file mode 100644 index fa868a90d..000000000 --- a/vendor/github.com/xenolf/lego/acme/crypto.go +++ /dev/null @@ -1,347 +0,0 @@ -package acme - -import ( - "bytes" - "crypto" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "crypto/rsa" - "crypto/x509" - "crypto/x509/pkix" - "encoding/base64" - "encoding/pem" - "errors" - "fmt" - "io" - "io/ioutil" - "math/big" - "net/http" - "strings" - "time" - - "encoding/asn1" - - "golang.org/x/crypto/ocsp" -) - -// KeyType represents the key algo as well as the key size or curve to use. -type KeyType string -type derCertificateBytes []byte - -// Constants for all key types we support. -const ( - EC256 = KeyType("P256") - EC384 = KeyType("P384") - RSA2048 = KeyType("2048") - RSA4096 = KeyType("4096") - RSA8192 = KeyType("8192") -) - -const ( - // OCSPGood means that the certificate is valid. - OCSPGood = ocsp.Good - // OCSPRevoked means that the certificate has been deliberately revoked. - OCSPRevoked = ocsp.Revoked - // OCSPUnknown means that the OCSP responder doesn't know about the certificate. - OCSPUnknown = ocsp.Unknown - // OCSPServerFailed means that the OCSP responder failed to process the request. - 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 -// issued certificate, this function will try to get the issuer certificate from the -// IssuingCertificateURL in the certificate. If the []byte and/or ocsp.Response return -// values are nil, the OCSP status may be assumed OCSPUnknown. -func GetOCSPForCert(bundle []byte) ([]byte, *ocsp.Response, error) { - certificates, err := parsePEMBundle(bundle) - if err != nil { - return nil, nil, err - } - - // We expect the certificate slice to be ordered downwards the chain. - // SRV CRT -> CA. We need to pull the leaf and issuer certs out of it, - // which should always be the first two certificates. If there's no - // OCSP server listed in the leaf cert, there's nothing to do. And if - // we have only one certificate so far, we need to get the issuer cert. - issuedCert := certificates[0] - if len(issuedCert.OCSPServer) == 0 { - return nil, nil, errors.New("no OCSP server specified in cert") - } - if len(certificates) == 1 { - // TODO: build fallback. If this fails, check the remaining array entries. - if len(issuedCert.IssuingCertificateURL) == 0 { - return nil, nil, errors.New("no issuing certificate URL") - } - - resp, err := httpGet(issuedCert.IssuingCertificateURL[0]) - if err != nil { - return nil, nil, err - } - defer resp.Body.Close() - - issuerBytes, err := ioutil.ReadAll(limitReader(resp.Body, 1024*1024)) - if err != nil { - return nil, nil, err - } - - issuerCert, err := x509.ParseCertificate(issuerBytes) - if err != nil { - return nil, nil, err - } - - // Insert it into the slice on position 0 - // We want it ordered right SRV CRT -> CA - certificates = append(certificates, issuerCert) - } - issuerCert := certificates[1] - - // Finally kick off the OCSP request. - ocspReq, err := ocsp.CreateRequest(issuedCert, issuerCert, nil) - if err != nil { - return nil, nil, err - } - - reader := bytes.NewReader(ocspReq) - req, err := httpPost(issuedCert.OCSPServer[0], "application/ocsp-request", reader) - if err != nil { - return nil, nil, err - } - defer req.Body.Close() - - ocspResBytes, err := ioutil.ReadAll(limitReader(req.Body, 1024*1024)) - ocspRes, err := ocsp.ParseResponse(ocspResBytes, issuerCert) - if err != nil { - return nil, nil, err - } - - return ocspResBytes, ocspRes, nil -} - -func getKeyAuthorization(token string, key interface{}) (string, error) { - var publicKey crypto.PublicKey - switch k := key.(type) { - case *ecdsa.PrivateKey: - publicKey = k.Public() - case *rsa.PrivateKey: - publicKey = k.Public() - } - - // Generate the Key Authorization for the challenge - jwk := keyAsJWK(publicKey) - if jwk == nil { - return "", errors.New("Could not generate JWK from key.") - } - thumbBytes, err := jwk.Thumbprint(crypto.SHA256) - if err != nil { - return "", err - } - - // unpad the base64URL - keyThumb := base64.URLEncoding.EncodeToString(thumbBytes) - index := strings.Index(keyThumb, "=") - if index != -1 { - keyThumb = keyThumb[:index] - } - - return token + "." + keyThumb, nil -} - -// parsePEMBundle parses a certificate bundle from top to bottom and returns -// a slice of x509 certificates. This function will error if no certificates are found. -func parsePEMBundle(bundle []byte) ([]*x509.Certificate, error) { - var certificates []*x509.Certificate - var certDERBlock *pem.Block - - for { - certDERBlock, bundle = pem.Decode(bundle) - if certDERBlock == nil { - break - } - - if certDERBlock.Type == "CERTIFICATE" { - cert, err := x509.ParseCertificate(certDERBlock.Bytes) - if err != nil { - return nil, err - } - certificates = append(certificates, cert) - } - } - - if len(certificates) == 0 { - return nil, errors.New("No certificates were found while parsing the bundle.") - } - - return certificates, nil -} - -func parsePEMPrivateKey(key []byte) (crypto.PrivateKey, error) { - keyBlock, _ := pem.Decode(key) - - switch keyBlock.Type { - case "RSA PRIVATE KEY": - return x509.ParsePKCS1PrivateKey(keyBlock.Bytes) - case "EC PRIVATE KEY": - return x509.ParseECPrivateKey(keyBlock.Bytes) - default: - return nil, errors.New("Unknown PEM header value") - } -} - -func generatePrivateKey(keyType KeyType) (crypto.PrivateKey, error) { - - switch keyType { - case EC256: - return ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - case EC384: - return ecdsa.GenerateKey(elliptic.P384(), rand.Reader) - case RSA2048: - return rsa.GenerateKey(rand.Reader, 2048) - case RSA4096: - return rsa.GenerateKey(rand.Reader, 4096) - case RSA8192: - return rsa.GenerateKey(rand.Reader, 8192) - } - - return nil, fmt.Errorf("Invalid KeyType: %s", keyType) -} - -func generateCsr(privateKey crypto.PrivateKey, domain string, san []string, mustStaple bool) ([]byte, error) { - template := x509.CertificateRequest{ - Subject: pkix.Name{ - CommonName: domain, - }, - } - - if len(san) > 0 { - template.DNSNames = san - } - - if mustStaple { - template.ExtraExtensions = append(template.ExtraExtensions, pkix.Extension{ - Id: tlsFeatureExtensionOID, - Value: ocspMustStapleFeature, - }) - } - - return x509.CreateCertificateRequest(rand.Reader, &template, privateKey) -} - -func pemEncode(data interface{}) []byte { - var pemBlock *pem.Block - switch key := data.(type) { - case *ecdsa.PrivateKey: - keyBytes, _ := x509.MarshalECPrivateKey(key) - pemBlock = &pem.Block{Type: "EC PRIVATE KEY", Bytes: keyBytes} - case *rsa.PrivateKey: - pemBlock = &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)} - break - case *x509.CertificateRequest: - pemBlock = &pem.Block{Type: "CERTIFICATE REQUEST", Bytes: key.Raw} - break - case derCertificateBytes: - pemBlock = &pem.Block{Type: "CERTIFICATE", Bytes: []byte(data.(derCertificateBytes))} - } - - return pem.EncodeToMemory(pemBlock) -} - -func pemDecode(data []byte) (*pem.Block, error) { - pemBlock, _ := pem.Decode(data) - if pemBlock == nil { - return nil, fmt.Errorf("Pem decode did not yield a valid block. Is the certificate in the right format?") - } - - return pemBlock, nil -} - -func pemDecodeTox509(pem []byte) (*x509.Certificate, error) { - pemBlock, err := pemDecode(pem) - if pemBlock == nil { - return nil, err - } - - return x509.ParseCertificate(pemBlock.Bytes) -} - -func pemDecodeTox509CSR(pem []byte) (*x509.CertificateRequest, error) { - pemBlock, err := pemDecode(pem) - if pemBlock == nil { - return nil, err - } - - if pemBlock.Type != "CERTIFICATE REQUEST" { - return nil, fmt.Errorf("PEM block is not a certificate request") - } - - return x509.ParseCertificateRequest(pemBlock.Bytes) -} - -// GetPEMCertExpiration returns the "NotAfter" date of a PEM encoded certificate. -// The certificate has to be PEM encoded. Any other encodings like DER will fail. -func GetPEMCertExpiration(cert []byte) (time.Time, error) { - pemBlock, err := pemDecode(cert) - if pemBlock == nil { - return time.Time{}, err - } - - return getCertExpiration(pemBlock.Bytes) -} - -// getCertExpiration returns the "NotAfter" date of a DER encoded certificate. -func getCertExpiration(cert []byte) (time.Time, error) { - pCert, err := x509.ParseCertificate(cert) - if err != nil { - return time.Time{}, err - } - - return pCert.NotAfter, nil -} - -func generatePemCert(privKey *rsa.PrivateKey, domain string) ([]byte, error) { - derBytes, err := generateDerCert(privKey, time.Time{}, domain) - if err != nil { - return nil, err - } - - return pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes}), nil -} - -func generateDerCert(privKey *rsa.PrivateKey, expiration time.Time, domain string) ([]byte, error) { - serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) - serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) - if err != nil { - return nil, err - } - - if expiration.IsZero() { - expiration = time.Now().Add(365) - } - - template := x509.Certificate{ - SerialNumber: serialNumber, - Subject: pkix.Name{ - CommonName: "ACME Challenge TEMP", - }, - NotBefore: time.Now(), - NotAfter: expiration, - - KeyUsage: x509.KeyUsageKeyEncipherment, - BasicConstraintsValid: true, - DNSNames: []string{domain}, - } - - return x509.CreateCertificate(rand.Reader, &template, &template, &privKey.PublicKey, privKey) -} - -func limitReader(rd io.ReadCloser, numBytes int64) io.ReadCloser { - return http.MaxBytesReader(nil, rd, numBytes) -} diff --git a/vendor/github.com/xenolf/lego/acme/crypto_test.go b/vendor/github.com/xenolf/lego/acme/crypto_test.go deleted file mode 100644 index 6f43835fb..000000000 --- a/vendor/github.com/xenolf/lego/acme/crypto_test.go +++ /dev/null @@ -1,93 +0,0 @@ -package acme - -import ( - "bytes" - "crypto/rand" - "crypto/rsa" - "testing" - "time" -) - -func TestGeneratePrivateKey(t *testing.T) { - key, err := generatePrivateKey(RSA2048) - if err != nil { - t.Error("Error generating private key:", err) - } - if key == nil { - t.Error("Expected key to not be nil, but it was") - } -} - -func TestGenerateCSR(t *testing.T) { - key, err := rsa.GenerateKey(rand.Reader, 512) - if err != nil { - t.Fatal("Error generating private key:", err) - } - - csr, err := generateCsr(key, "fizz.buzz", nil, true) - if err != nil { - t.Error("Error generating CSR:", err) - } - if csr == nil || len(csr) == 0 { - t.Error("Expected CSR with data, but it was nil or length 0") - } -} - -func TestPEMEncode(t *testing.T) { - buf := bytes.NewBufferString("TestingRSAIsSoMuchFun") - - reader := MockRandReader{b: buf} - key, err := rsa.GenerateKey(reader, 32) - if err != nil { - t.Fatal("Error generating private key:", err) - } - - data := pemEncode(key) - - if data == nil { - t.Fatal("Expected result to not be nil, but it was") - } - if len(data) != 127 { - t.Errorf("Expected PEM encoding to be length 127, but it was %d", len(data)) - } -} - -func TestPEMCertExpiration(t *testing.T) { - privKey, err := generatePrivateKey(RSA2048) - if err != nil { - t.Fatal("Error generating private key:", err) - } - - expiration := time.Now().Add(365) - expiration = expiration.Round(time.Second) - certBytes, err := generateDerCert(privKey.(*rsa.PrivateKey), expiration, "test.com") - if err != nil { - t.Fatal("Error generating cert:", err) - } - - buf := bytes.NewBufferString("TestingRSAIsSoMuchFun") - - // Some random string should return an error. - if ctime, err := GetPEMCertExpiration(buf.Bytes()); err == nil { - t.Errorf("Expected getCertExpiration to return an error for garbage string but returned %v", ctime) - } - - // A DER encoded certificate should return an error. - if _, err := GetPEMCertExpiration(certBytes); err == nil { - t.Errorf("Expected getCertExpiration to return an error for DER certificates but returned none.") - } - - // A PEM encoded certificate should work ok. - pemCert := pemEncode(derCertificateBytes(certBytes)) - if ctime, err := GetPEMCertExpiration(pemCert); err != nil || !ctime.Equal(expiration.UTC()) { - t.Errorf("Expected getCertExpiration to return %v but returned %v. Error: %v", expiration, ctime, err) - } -} - -type MockRandReader struct { - b *bytes.Buffer -} - -func (r MockRandReader) Read(p []byte) (int, error) { - return r.b.Read(p) -} diff --git a/vendor/github.com/xenolf/lego/acme/dns_challenge.go b/vendor/github.com/xenolf/lego/acme/dns_challenge.go deleted file mode 100644 index d6844dcd4..000000000 --- a/vendor/github.com/xenolf/lego/acme/dns_challenge.go +++ /dev/null @@ -1,309 +0,0 @@ -package acme - -import ( - "crypto/sha256" - "encoding/base64" - "errors" - "fmt" - "log" - "net" - "strings" - "time" - - "github.com/miekg/dns" -) - -type preCheckDNSFunc func(fqdn, value string) (bool, error) - -var ( - // PreCheckDNS checks DNS propagation before notifying ACME that - // the DNS challenge is ready. - PreCheckDNS preCheckDNSFunc = checkDNSPropagation - fqdnToZone = map[string]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)) - // base64URL encoding without padding - keyAuthSha := base64.URLEncoding.EncodeToString(keyAuthShaBytes[:sha256.Size]) - value = strings.TrimRight(keyAuthSha, "=") - ttl = 120 - fqdn = fmt.Sprintf("_acme-challenge.%s.", domain) - return -} - -// dnsChallenge implements the dns-01 challenge according to ACME 7.5 -type dnsChallenge struct { - jws *jws - validate validateFunc - provider ChallengeProvider -} - -func (s *dnsChallenge) Solve(chlng challenge, domain string) error { - logf("[INFO][%s] acme: Trying to solve DNS-01", domain) - - if s.provider == nil { - return errors.New("No DNS Provider configured") - } - - // Generate the Key Authorization for the challenge - keyAuth, err := getKeyAuthorization(chlng.Token, s.jws.privKey) - if err != nil { - return err - } - - err = s.provider.Present(domain, chlng.Token, keyAuth) - if err != nil { - return fmt.Errorf("Error presenting token: %s", err) - } - defer func() { - err := s.provider.CleanUp(domain, chlng.Token, keyAuth) - if err != nil { - log.Printf("Error cleaning up %s: %v ", domain, err) - } - }() - - fqdn, value, _ := DNS01Record(domain, keyAuth) - - logf("[INFO][%s] Checking DNS record propagation using %+v", domain, RecursiveNameservers) - - var timeout, interval time.Duration - switch provider := s.provider.(type) { - case ChallengeProviderTimeout: - timeout, interval = provider.Timeout() - default: - timeout, interval = 60*time.Second, 2*time.Second - } - - err = WaitFor(timeout, interval, func() (bool, error) { - return PreCheckDNS(fqdn, value) - }) - if err != nil { - return err - } - - return s.validate(s.jws, domain, chlng.URI, challenge{Resource: "challenge", Type: chlng.Type, Token: chlng.Token, KeyAuthorization: keyAuth}) -} - -// checkDNSPropagation checks if the expected TXT record has been propagated to all authoritative nameservers. -func checkDNSPropagation(fqdn, value string) (bool, error) { - // Initial attempt to resolve at the recursive NS - r, err := dnsQuery(fqdn, dns.TypeTXT, RecursiveNameservers, true) - if err != nil { - return false, err - } - if r.Rcode == dns.RcodeSuccess { - // If we see a CNAME here then use the alias - for _, rr := range r.Answer { - if cn, ok := rr.(*dns.CNAME); ok { - if cn.Hdr.Name == fqdn { - fqdn = cn.Target - break - } - } - } - } - - authoritativeNss, err := lookupNameservers(fqdn) - if err != nil { - return false, err - } - - return checkAuthoritativeNss(fqdn, value, authoritativeNss) -} - -// checkAuthoritativeNss queries each of the given nameservers for the expected TXT record. -func checkAuthoritativeNss(fqdn, value string, nameservers []string) (bool, error) { - for _, ns := range nameservers { - r, err := dnsQuery(fqdn, dns.TypeTXT, []string{net.JoinHostPort(ns, "53")}, false) - if err != nil { - return false, err - } - - if r.Rcode != dns.RcodeSuccess { - return false, fmt.Errorf("NS %s returned %s for %s", ns, dns.RcodeToString[r.Rcode], fqdn) - } - - var found bool - for _, rr := range r.Answer { - if txt, ok := rr.(*dns.TXT); ok { - if strings.Join(txt.Txt, "") == value { - found = true - break - } - } - } - - if !found { - return false, fmt.Errorf("NS %s did not return the expected TXT record", ns) - } - } - - return true, nil -} - -// dnsQuery will query a nameserver, iterating through the supplied servers as it retries -// The nameserver should include a port, to facilitate testing where we talk to a mock dns server. -func dnsQuery(fqdn string, rtype uint16, nameservers []string, recursive bool) (in *dns.Msg, err error) { - m := new(dns.Msg) - m.SetQuestion(fqdn, rtype) - m.SetEdns0(4096, false) - - if !recursive { - m.RecursionDesired = false - } - - // Will retry the request based on the number of servers (n+1) - for i := 1; i <= len(nameservers)+1; i++ { - ns := nameservers[i%len(nameservers)] - udp := &dns.Client{Net: "udp", Timeout: DNSTimeout} - in, _, err = udp.Exchange(m, ns) - - if err == dns.ErrTruncated { - tcp := &dns.Client{Net: "tcp", Timeout: DNSTimeout} - // If the TCP request succeeds, the err will reset to nil - in, _, err = tcp.Exchange(m, ns) - } - - if err == nil { - break - } - } - return -} - -// lookupNameservers returns the authoritative nameservers for the given fqdn. -func lookupNameservers(fqdn string) ([]string, error) { - var authoritativeNss []string - - zone, err := FindZoneByFqdn(fqdn, RecursiveNameservers) - if err != nil { - return nil, fmt.Errorf("Could not determine the zone: %v", err) - } - - r, err := dnsQuery(zone, dns.TypeNS, RecursiveNameservers, true) - if err != nil { - return nil, err - } - - for _, rr := range r.Answer { - if ns, ok := rr.(*dns.NS); ok { - authoritativeNss = append(authoritativeNss, strings.ToLower(ns.Ns)) - } - } - - if len(authoritativeNss) > 0 { - return authoritativeNss, nil - } - return nil, fmt.Errorf("Could not determine authoritative nameservers") -} - -// FindZoneByFqdn determines the zone apex for the given fqdn by recursing up the -// domain labels until the nameserver returns a SOA record in the answer section. -func FindZoneByFqdn(fqdn string, nameservers []string) (string, error) { - // Do we have it cached? - if zone, ok := fqdnToZone[fqdn]; ok { - return zone, nil - } - - labelIndexes := dns.Split(fqdn) - for _, index := range labelIndexes { - domain := fqdn[index:] - - in, err := dnsQuery(domain, dns.TypeSOA, nameservers, true) - if err != nil { - return "", err - } - - // Any response code other than NOERROR and NXDOMAIN is treated as error - if in.Rcode != dns.RcodeNameError && in.Rcode != dns.RcodeSuccess { - return "", fmt.Errorf("Unexpected response code '%s' for %s", - dns.RcodeToString[in.Rcode], domain) - } - - // Check if we got a SOA RR in the answer section - if in.Rcode == dns.RcodeSuccess { - - // CNAME records cannot/should not exist at the root of a zone. - // So we skip a domain when a CNAME is found. - if dnsMsgContainsCNAME(in) { - continue - } - - for _, ans := range in.Answer { - if soa, ok := ans.(*dns.SOA); ok { - zone := soa.Hdr.Name - fqdnToZone[fqdn] = zone - return zone, nil - } - } - } - } - - return "", fmt.Errorf("Could not find the start of authority") -} - -// dnsMsgContainsCNAME checks for a CNAME answer in msg -func dnsMsgContainsCNAME(msg *dns.Msg) bool { - for _, ans := range msg.Answer { - if _, ok := ans.(*dns.CNAME); ok { - return true - } - } - return false -} - -// ClearFqdnCache clears the cache of fqdn to zone mappings. Primarily used in testing. -func ClearFqdnCache() { - fqdnToZone = map[string]string{} -} - -// ToFqdn converts the name into a fqdn appending a trailing dot. -func ToFqdn(name string) string { - n := len(name) - if n == 0 || name[n-1] == '.' { - return name - } - return name + "." -} - -// UnFqdn converts the fqdn into a name removing the trailing dot. -func UnFqdn(name string) string { - n := len(name) - if n != 0 && name[n-1] == '.' { - return name[:n-1] - } - return name -} diff --git a/vendor/github.com/xenolf/lego/acme/dns_challenge_manual.go b/vendor/github.com/xenolf/lego/acme/dns_challenge_manual.go deleted file mode 100644 index 240384e60..000000000 --- a/vendor/github.com/xenolf/lego/acme/dns_challenge_manual.go +++ /dev/null @@ -1,53 +0,0 @@ -package acme - -import ( - "bufio" - "fmt" - "os" -) - -const ( - dnsTemplate = "%s %d IN TXT \"%s\"" -) - -// DNSProviderManual is an implementation of the ChallengeProvider interface -type DNSProviderManual struct{} - -// NewDNSProviderManual returns a DNSProviderManual instance. -func NewDNSProviderManual() (*DNSProviderManual, error) { - return &DNSProviderManual{}, nil -} - -// Present prints instructions for manually creating the TXT record -func (*DNSProviderManual) Present(domain, token, keyAuth string) error { - fqdn, value, ttl := DNS01Record(domain, keyAuth) - dnsRecord := fmt.Sprintf(dnsTemplate, fqdn, ttl, value) - - authZone, err := FindZoneByFqdn(fqdn, RecursiveNameservers) - if err != nil { - return err - } - - logf("[INFO] acme: Please create the following TXT record in your %s zone:", authZone) - logf("[INFO] acme: %s", dnsRecord) - logf("[INFO] acme: Press 'Enter' when you are done") - - reader := bufio.NewReader(os.Stdin) - _, _ = reader.ReadString('\n') - return nil -} - -// CleanUp prints instructions for manually removing the TXT record -func (*DNSProviderManual) CleanUp(domain, token, keyAuth string) error { - fqdn, _, ttl := DNS01Record(domain, keyAuth) - dnsRecord := fmt.Sprintf(dnsTemplate, fqdn, ttl, "...") - - authZone, err := FindZoneByFqdn(fqdn, RecursiveNameservers) - if err != nil { - return err - } - - logf("[INFO] acme: You can now remove this TXT record from your %s zone:", authZone) - logf("[INFO] acme: %s", dnsRecord) - return nil -} diff --git a/vendor/github.com/xenolf/lego/acme/dns_challenge_test.go b/vendor/github.com/xenolf/lego/acme/dns_challenge_test.go deleted file mode 100644 index 117ac3038..000000000 --- a/vendor/github.com/xenolf/lego/acme/dns_challenge_test.go +++ /dev/null @@ -1,200 +0,0 @@ -package acme - -import ( - "bufio" - "crypto/rand" - "crypto/rsa" - "net/http" - "net/http/httptest" - "os" - "reflect" - "sort" - "strings" - "testing" - "time" -) - -var lookupNameserversTestsOK = []struct { - fqdn string - nss []string -}{ - {"books.google.com.ng.", - []string{"ns1.google.com.", "ns2.google.com.", "ns3.google.com.", "ns4.google.com."}, - }, - {"www.google.com.", - []string{"ns1.google.com.", "ns2.google.com.", "ns3.google.com.", "ns4.google.com."}, - }, - {"physics.georgetown.edu.", - []string{"ns1.georgetown.edu.", "ns2.georgetown.edu.", "ns3.georgetown.edu."}, - }, -} - -var lookupNameserversTestsErr = []struct { - fqdn string - error string -}{ - // invalid tld - {"_null.n0n0.", - "Could not determine the zone", - }, -} - -var findZoneByFqdnTests = []struct { - fqdn string - zone string -}{ - {"mail.google.com.", "google.com."}, // domain is a CNAME - {"foo.google.com.", "google.com."}, // domain is a non-existent subdomain - {"example.com.ac.", "ac."}, // domain is a eTLD - {"cross-zone-example.assets.sh.", "assets.sh."}, // domain is a cross-zone CNAME -} - -var checkAuthoritativeNssTests = []struct { - fqdn, value string - ns []string - ok bool -}{ - // TXT RR w/ expected value - {"8.8.8.8.asn.routeviews.org.", "151698.8.8.024", []string{"asnums.routeviews.org."}, - true, - }, - // No TXT RR - {"ns1.google.com.", "", []string{"ns2.google.com."}, - false, - }, -} - -var checkAuthoritativeNssTestsErr = []struct { - fqdn, value string - ns []string - error string -}{ - // TXT RR /w unexpected value - {"8.8.8.8.asn.routeviews.org.", "fe01=", []string{"asnums.routeviews.org."}, - "did not return the expected TXT record", - }, - // No TXT RR - {"ns1.google.com.", "fe01=", []string{"ns2.google.com."}, - "did not return the expected TXT record", - }, -} - -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 - } - privKey, _ := rsa.GenerateKey(rand.Reader, 512) - - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Add("Replay-Nonce", "12345") - w.Write([]byte("{\"type\":\"dns01\",\"status\":\"valid\",\"uri\":\"http://some.url\",\"token\":\"http8\"}")) - })) - - manualProvider, _ := NewDNSProviderManual() - jws := &jws{privKey: privKey, directoryURL: ts.URL} - solver := &dnsChallenge{jws: jws, validate: validate, provider: manualProvider} - clientChallenge := challenge{Type: "dns01", Status: "pending", URI: ts.URL, Token: "http8"} - - go func() { - time.Sleep(time.Second * 2) - f := bufio.NewWriter(os.Stdout) - defer f.Flush() - f.WriteString("\n") - }() - - if err := solver.Solve(clientChallenge, "example.com"); err != nil { - t.Errorf("VALID: Expected Solve to return no error but the error was -> %v", err) - } -} - -func TestPreCheckDNS(t *testing.T) { - ok, err := PreCheckDNS("acme-staging.api.letsencrypt.org", "fe01=") - if err != nil || !ok { - t.Errorf("preCheckDNS failed for acme-staging.api.letsencrypt.org") - } -} - -func TestLookupNameserversOK(t *testing.T) { - for _, tt := range lookupNameserversTestsOK { - nss, err := lookupNameservers(tt.fqdn) - if err != nil { - t.Fatalf("#%s: got %q; want nil", tt.fqdn, err) - } - - sort.Strings(nss) - sort.Strings(tt.nss) - - if !reflect.DeepEqual(nss, tt.nss) { - t.Errorf("#%s: got %v; want %v", tt.fqdn, nss, tt.nss) - } - } -} - -func TestLookupNameserversErr(t *testing.T) { - for _, tt := range lookupNameserversTestsErr { - _, err := lookupNameservers(tt.fqdn) - if err == nil { - t.Fatalf("#%s: expected %q (error); got ", tt.fqdn, tt.error) - } - - if !strings.Contains(err.Error(), tt.error) { - t.Errorf("#%s: expected %q (error); got %q", tt.fqdn, tt.error, err) - continue - } - } -} - -func TestFindZoneByFqdn(t *testing.T) { - for _, tt := range findZoneByFqdnTests { - res, err := FindZoneByFqdn(tt.fqdn, RecursiveNameservers) - if err != nil { - t.Errorf("FindZoneByFqdn failed for %s: %v", tt.fqdn, err) - } - if res != tt.zone { - t.Errorf("%s: got %s; want %s", tt.fqdn, res, tt.zone) - } - } -} - -func TestCheckAuthoritativeNss(t *testing.T) { - for _, tt := range checkAuthoritativeNssTests { - ok, _ := checkAuthoritativeNss(tt.fqdn, tt.value, tt.ns) - if ok != tt.ok { - t.Errorf("%s: got %t; want %t", tt.fqdn, ok, tt.ok) - } - } -} - -func TestCheckAuthoritativeNssErr(t *testing.T) { - for _, tt := range checkAuthoritativeNssTestsErr { - _, err := checkAuthoritativeNss(tt.fqdn, tt.value, tt.ns) - if err == nil { - t.Fatalf("#%s: expected %q (error); got ", tt.fqdn, tt.error) - } - if !strings.Contains(err.Error(), tt.error) { - t.Errorf("#%s: expected %q (error); got %q", tt.fqdn, tt.error, err) - continue - } - } -} - -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/error.go b/vendor/github.com/xenolf/lego/acme/error.go deleted file mode 100644 index e4bc934c2..000000000 --- a/vendor/github.com/xenolf/lego/acme/error.go +++ /dev/null @@ -1,94 +0,0 @@ -package acme - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - "strings" -) - -const ( - tosAgreementError = "Must agree to subscriber agreement before any further actions" - invalidNonceError = "JWS has invalid anti-replay nonce" -) - -// RemoteError is the base type for all errors specific to the ACME protocol. -type RemoteError struct { - StatusCode int `json:"status,omitempty"` - Type string `json:"type"` - Detail string `json:"detail"` -} - -func (e RemoteError) Error() string { - return fmt.Sprintf("acme: Error %d - %s - %s", e.StatusCode, e.Type, e.Detail) -} - -// TOSError represents the error which is returned if the user needs to -// accept the TOS. -// TODO: include the new TOS url if we can somehow obtain it. -type TOSError struct { - RemoteError -} - -// NonceError represents the error which is returned if the -// nonce sent by the client was not accepted by the server. -type NonceError struct { - RemoteError -} - -type domainError struct { - Domain string - Error error -} - -type challengeError struct { - RemoteError - records []validationRecord -} - -func (c challengeError) Error() string { - - var errStr string - for _, validation := range c.records { - errStr = errStr + fmt.Sprintf("\tValidation for %s:%s\n\tResolved to:\n\t\t%s\n\tUsed: %s\n\n", - validation.Hostname, validation.Port, strings.Join(validation.ResolvedAddresses, "\n\t\t"), validation.UsedAddress) - } - - return fmt.Sprintf("%s\nError Detail:\n%s", c.RemoteError.Error(), errStr) -} - -func handleHTTPError(resp *http.Response) error { - var errorDetail RemoteError - - contentType := resp.Header.Get("Content-Type") - if contentType == "application/json" || contentType == "application/problem+json" { - err := json.NewDecoder(resp.Body).Decode(&errorDetail) - if err != nil { - return err - } - } else { - detailBytes, err := ioutil.ReadAll(limitReader(resp.Body, maxBodySize)) - if err != nil { - return err - } - errorDetail.Detail = string(detailBytes) - } - - errorDetail.StatusCode = resp.StatusCode - - // Check for errors we handle specifically - if errorDetail.StatusCode == http.StatusForbidden && errorDetail.Detail == tosAgreementError { - return TOSError{errorDetail} - } - - if errorDetail.StatusCode == http.StatusBadRequest && strings.HasPrefix(errorDetail.Detail, invalidNonceError) { - return NonceError{errorDetail} - } - - return errorDetail -} - -func handleChallengeError(chlng challenge) error { - return challengeError{chlng.Error, chlng.ValidationRecords} -} diff --git a/vendor/github.com/xenolf/lego/acme/http.go b/vendor/github.com/xenolf/lego/acme/http.go deleted file mode 100644 index e469e0de2..000000000 --- a/vendor/github.com/xenolf/lego/acme/http.go +++ /dev/null @@ -1,160 +0,0 @@ -package acme - -import ( - "encoding/json" - "errors" - "fmt" - "io" - "net" - "net/http" - "runtime" - "strings" - "time" -) - -// UserAgent (if non-empty) will be tacked onto the User-Agent string in requests. -var UserAgent string - -// HTTPClient is an HTTP client with a reasonable timeout value. -var HTTPClient = http.Client{ - Transport: &http.Transport{ - Proxy: http.ProxyFromEnvironment, - Dial: (&net.Dialer{ - Timeout: 30 * time.Second, - KeepAlive: 30 * time.Second, - }).Dial, - TLSHandshakeTimeout: 15 * time.Second, - ResponseHeaderTimeout: 15 * time.Second, - ExpectContinueTimeout: 1 * time.Second, - }, -} - -const ( - // defaultGoUserAgent is the Go HTTP package user agent string. Too - // bad it isn't exported. If it changes, we should update it here, too. - defaultGoUserAgent = "Go-http-client/1.1" - - // ourUserAgent is the User-Agent of this underlying library package. - ourUserAgent = "xenolf-acme" -) - -// httpHead performs a HEAD request with a proper User-Agent string. -// The response body (resp.Body) is already closed when this function returns. -func httpHead(url string) (resp *http.Response, err error) { - req, err := http.NewRequest("HEAD", url, nil) - if err != nil { - return nil, fmt.Errorf("failed to head %q: %v", url, err) - } - - req.Header.Set("User-Agent", userAgent()) - - resp, err = HTTPClient.Do(req) - if err != nil { - return resp, fmt.Errorf("failed to do head %q: %v", url, err) - } - resp.Body.Close() - return resp, err -} - -// httpPost performs a POST request with a proper User-Agent string. -// Callers should close resp.Body when done reading from it. -func httpPost(url string, bodyType string, body io.Reader) (resp *http.Response, err error) { - req, err := http.NewRequest("POST", url, body) - if err != nil { - return nil, fmt.Errorf("failed to post %q: %v", url, err) - } - req.Header.Set("Content-Type", bodyType) - req.Header.Set("User-Agent", userAgent()) - - return HTTPClient.Do(req) -} - -// httpGet performs a GET request with a proper User-Agent string. -// Callers should close resp.Body when done reading from it. -func httpGet(url string) (resp *http.Response, err error) { - req, err := http.NewRequest("GET", url, nil) - if err != nil { - return nil, fmt.Errorf("failed to get %q: %v", url, err) - } - req.Header.Set("User-Agent", userAgent()) - - return HTTPClient.Do(req) -} - -// getJSON performs an HTTP GET request and parses the response body -// as JSON, into the provided respBody object. -func getJSON(uri string, respBody interface{}) (http.Header, error) { - resp, err := httpGet(uri) - if err != nil { - return nil, fmt.Errorf("failed to get json %q: %v", uri, err) - } - defer resp.Body.Close() - - if resp.StatusCode >= http.StatusBadRequest { - return resp.Header, handleHTTPError(resp) - } - - return resp.Header, json.NewDecoder(resp.Body).Decode(respBody) -} - -// postJSON performs an HTTP POST request and parses the response body -// as JSON, into the provided respBody object. -func postJSON(j *jws, uri string, reqBody, respBody interface{}) (http.Header, error) { - jsonBytes, err := json.Marshal(reqBody) - if err != nil { - return nil, errors.New("Failed to marshal network message...") - } - - resp, err := j.post(uri, jsonBytes) - if err != nil { - return nil, fmt.Errorf("Failed to post JWS message. -> %v", err) - } - - defer resp.Body.Close() - - if resp.StatusCode >= http.StatusBadRequest { - - err := handleHTTPError(resp) - - switch err.(type) { - - case NonceError: - - // Retry once if the nonce was invalidated - - retryResp, err := j.post(uri, jsonBytes) - if err != nil { - return nil, fmt.Errorf("Failed to post JWS message. -> %v", err) - } - - defer retryResp.Body.Close() - - if retryResp.StatusCode >= http.StatusBadRequest { - return retryResp.Header, handleHTTPError(retryResp) - } - - if respBody == nil { - return retryResp.Header, nil - } - - return retryResp.Header, json.NewDecoder(retryResp.Body).Decode(respBody) - - default: - return resp.Header, err - - } - - } - - if respBody == nil { - return resp.Header, nil - } - - return resp.Header, json.NewDecoder(resp.Body).Decode(respBody) -} - -// userAgent builds and returns the User-Agent string to use in requests. -func userAgent() string { - ua := fmt.Sprintf("%s (%s; %s) %s %s", defaultGoUserAgent, runtime.GOOS, runtime.GOARCH, ourUserAgent, UserAgent) - return strings.TrimSpace(ua) -} diff --git a/vendor/github.com/xenolf/lego/acme/http_challenge.go b/vendor/github.com/xenolf/lego/acme/http_challenge.go deleted file mode 100644 index 95cb1fd81..000000000 --- a/vendor/github.com/xenolf/lego/acme/http_challenge.go +++ /dev/null @@ -1,41 +0,0 @@ -package acme - -import ( - "fmt" - "log" -) - -type httpChallenge struct { - jws *jws - validate validateFunc - provider ChallengeProvider -} - -// HTTP01ChallengePath returns the URL path for the `http-01` challenge -func HTTP01ChallengePath(token string) string { - return "/.well-known/acme-challenge/" + token -} - -func (s *httpChallenge) Solve(chlng challenge, domain string) error { - - logf("[INFO][%s] acme: Trying to solve HTTP-01", domain) - - // Generate the Key Authorization for the challenge - keyAuth, err := getKeyAuthorization(chlng.Token, s.jws.privKey) - if err != nil { - return err - } - - err = s.provider.Present(domain, chlng.Token, keyAuth) - if err != nil { - return fmt.Errorf("[%s] error presenting token: %v", domain, err) - } - defer func() { - err := s.provider.CleanUp(domain, chlng.Token, keyAuth) - if err != nil { - log.Printf("[%s] error cleaning up: %v", domain, err) - } - }() - - return s.validate(s.jws, domain, chlng.URI, challenge{Resource: "challenge", Type: chlng.Type, Token: chlng.Token, KeyAuthorization: keyAuth}) -} diff --git a/vendor/github.com/xenolf/lego/acme/http_challenge_server.go b/vendor/github.com/xenolf/lego/acme/http_challenge_server.go deleted file mode 100644 index 64c6a8280..000000000 --- a/vendor/github.com/xenolf/lego/acme/http_challenge_server.go +++ /dev/null @@ -1,79 +0,0 @@ -package acme - -import ( - "fmt" - "net" - "net/http" - "strings" -) - -// HTTPProviderServer implements ChallengeProvider for `http-01` challenge -// It may be instantiated without using the NewHTTPProviderServer function if -// you want only to use the default values. -type HTTPProviderServer struct { - iface string - port string - done chan bool - listener net.Listener -} - -// NewHTTPProviderServer creates a new HTTPProviderServer on the selected interface and port. -// Setting iface and / or port to an empty string will make the server fall back to -// the "any" interface and port 80 respectively. -func NewHTTPProviderServer(iface, port string) *HTTPProviderServer { - return &HTTPProviderServer{iface: iface, port: port} -} - -// Present starts a web server and makes the token available at `HTTP01ChallengePath(token)` for web requests. -func (s *HTTPProviderServer) Present(domain, token, keyAuth string) error { - if s.port == "" { - s.port = "80" - } - - var err error - s.listener, err = net.Listen("tcp", net.JoinHostPort(s.iface, s.port)) - if err != nil { - return fmt.Errorf("Could not start HTTP server for challenge -> %v", err) - } - - s.done = make(chan bool) - go s.serve(domain, token, keyAuth) - return nil -} - -// CleanUp closes the HTTP server and removes the token from `HTTP01ChallengePath(token)` -func (s *HTTPProviderServer) CleanUp(domain, token, keyAuth string) error { - if s.listener == nil { - return nil - } - s.listener.Close() - <-s.done - return nil -} - -func (s *HTTPProviderServer) serve(domain, token, keyAuth string) { - path := HTTP01ChallengePath(token) - - // The handler validates the HOST header and request type. - // For validation it then writes the token the server returned with the challenge - mux := http.NewServeMux() - mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { - if strings.HasPrefix(r.Host, domain) && r.Method == "GET" { - w.Header().Add("Content-Type", "text/plain") - w.Write([]byte(keyAuth)) - logf("[INFO][%s] Served key authentication", domain) - } else { - 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")) - } - }) - - httpServer := &http.Server{ - Handler: mux, - } - // Once httpServer is shut down we don't want any lingering - // connections, so disable KeepAlives. - httpServer.SetKeepAlivesEnabled(false) - httpServer.Serve(s.listener) - s.done <- true -} diff --git a/vendor/github.com/xenolf/lego/acme/http_challenge_test.go b/vendor/github.com/xenolf/lego/acme/http_challenge_test.go deleted file mode 100644 index 7400f56d4..000000000 --- a/vendor/github.com/xenolf/lego/acme/http_challenge_test.go +++ /dev/null @@ -1,57 +0,0 @@ -package acme - -import ( - "crypto/rand" - "crypto/rsa" - "io/ioutil" - "strings" - "testing" -) - -func TestHTTPChallenge(t *testing.T) { - privKey, _ := rsa.GenerateKey(rand.Reader, 512) - j := &jws{privKey: privKey} - clientChallenge := challenge{Type: HTTP01, Token: "http1"} - mockValidate := func(_ *jws, _, _ string, chlng challenge) error { - uri := "http://localhost:23457/.well-known/acme-challenge/" + chlng.Token - resp, err := httpGet(uri) - if err != nil { - return err - } - defer resp.Body.Close() - - if want := "text/plain"; resp.Header.Get("Content-Type") != want { - t.Errorf("Get(%q) Content-Type: got %q, want %q", uri, resp.Header.Get("Content-Type"), want) - } - - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return err - } - bodyStr := string(body) - - if bodyStr != chlng.KeyAuthorization { - t.Errorf("Get(%q) Body: got %q, want %q", uri, bodyStr, chlng.KeyAuthorization) - } - - return nil - } - solver := &httpChallenge{jws: j, validate: mockValidate, provider: &HTTPProviderServer{port: "23457"}} - - if err := solver.Solve(clientChallenge, "localhost:23457"); err != nil { - t.Errorf("Solve error: got %v, want nil", err) - } -} - -func TestHTTPChallengeInvalidPort(t *testing.T) { - privKey, _ := rsa.GenerateKey(rand.Reader, 128) - j := &jws{privKey: privKey} - clientChallenge := challenge{Type: HTTP01, Token: "http2"} - solver := &httpChallenge{jws: j, validate: stubValidate, provider: &HTTPProviderServer{port: "123456"}} - - if err := solver.Solve(clientChallenge, "localhost:123456"); err == nil { - t.Errorf("Solve error: got %v, want error", err) - } 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/http_test.go b/vendor/github.com/xenolf/lego/acme/http_test.go deleted file mode 100644 index 33a48a331..000000000 --- a/vendor/github.com/xenolf/lego/acme/http_test.go +++ /dev/null @@ -1,100 +0,0 @@ -package acme - -import ( - "net/http" - "net/http/httptest" - "strings" - "testing" -) - -func TestHTTPHeadUserAgent(t *testing.T) { - var ua, method string - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - ua = r.Header.Get("User-Agent") - method = r.Method - })) - defer ts.Close() - - _, err := httpHead(ts.URL) - if err != nil { - t.Fatal(err) - } - - if method != "HEAD" { - t.Errorf("Expected method to be HEAD, got %s", method) - } - if !strings.Contains(ua, ourUserAgent) { - t.Errorf("Expected User-Agent to contain '%s', got: '%s'", ourUserAgent, ua) - } -} - -func TestHTTPGetUserAgent(t *testing.T) { - var ua, method string - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - ua = r.Header.Get("User-Agent") - method = r.Method - })) - defer ts.Close() - - res, err := httpGet(ts.URL) - if err != nil { - t.Fatal(err) - } - res.Body.Close() - - if method != "GET" { - t.Errorf("Expected method to be GET, got %s", method) - } - if !strings.Contains(ua, ourUserAgent) { - t.Errorf("Expected User-Agent to contain '%s', got: '%s'", ourUserAgent, ua) - } -} - -func TestHTTPPostUserAgent(t *testing.T) { - var ua, method string - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - ua = r.Header.Get("User-Agent") - method = r.Method - })) - defer ts.Close() - - res, err := httpPost(ts.URL, "text/plain", strings.NewReader("falalalala")) - if err != nil { - t.Fatal(err) - } - res.Body.Close() - - if method != "POST" { - t.Errorf("Expected method to be POST, got %s", method) - } - if !strings.Contains(ua, ourUserAgent) { - t.Errorf("Expected User-Agent to contain '%s', got: '%s'", ourUserAgent, ua) - } -} - -func TestUserAgent(t *testing.T) { - ua := userAgent() - - if !strings.Contains(ua, defaultGoUserAgent) { - t.Errorf("Expected UA to contain %s, got '%s'", defaultGoUserAgent, ua) - } - if !strings.Contains(ua, ourUserAgent) { - t.Errorf("Expected UA to contain %s, got '%s'", ourUserAgent, ua) - } - if strings.HasSuffix(ua, " ") { - t.Errorf("UA should not have trailing spaces; got '%s'", ua) - } - - // customize the UA by appending a value - UserAgent = "MyApp/1.2.3" - ua = userAgent() - if !strings.Contains(ua, defaultGoUserAgent) { - t.Errorf("Expected UA to contain %s, got '%s'", defaultGoUserAgent, ua) - } - if !strings.Contains(ua, ourUserAgent) { - t.Errorf("Expected UA to contain %s, got '%s'", ourUserAgent, ua) - } - if !strings.Contains(ua, UserAgent) { - t.Errorf("Expected custom UA to contain %s, got '%s'", UserAgent, ua) - } -} diff --git a/vendor/github.com/xenolf/lego/acme/jws.go b/vendor/github.com/xenolf/lego/acme/jws.go deleted file mode 100644 index a39434342..000000000 --- a/vendor/github.com/xenolf/lego/acme/jws.go +++ /dev/null @@ -1,131 +0,0 @@ -package acme - -import ( - "bytes" - "crypto" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rsa" - "fmt" - "net/http" - "sync" - - "gopkg.in/square/go-jose.v1" -) - -type jws struct { - directoryURL string - privKey crypto.PrivateKey - nonces nonceManager -} - -func keyAsJWK(key interface{}) *jose.JsonWebKey { - switch k := key.(type) { - case *ecdsa.PublicKey: - return &jose.JsonWebKey{Key: k, Algorithm: "EC"} - case *rsa.PublicKey: - return &jose.JsonWebKey{Key: k, Algorithm: "RSA"} - - default: - return nil - } -} - -// Posts a JWS signed message to the specified URL. -// It does NOT close the response body, so the caller must -// do that if no error was returned. -func (j *jws) post(url string, content []byte) (*http.Response, error) { - signedContent, err := j.signContent(content) - if err != nil { - return nil, fmt.Errorf("Failed to sign content -> %s", err.Error()) - } - - resp, err := httpPost(url, "application/jose+json", bytes.NewBuffer([]byte(signedContent.FullSerialize()))) - if err != nil { - return nil, fmt.Errorf("Failed to HTTP POST to %s -> %s", url, err.Error()) - } - - nonce, nonceErr := getNonceFromResponse(resp) - if nonceErr == nil { - j.nonces.Push(nonce) - } - - return resp, nil -} - -func (j *jws) signContent(content []byte) (*jose.JsonWebSignature, error) { - - var alg jose.SignatureAlgorithm - switch k := j.privKey.(type) { - case *rsa.PrivateKey: - alg = jose.RS256 - case *ecdsa.PrivateKey: - if k.Curve == elliptic.P256() { - alg = jose.ES256 - } else if k.Curve == elliptic.P384() { - alg = jose.ES384 - } - } - - signer, err := jose.NewSigner(alg, j.privKey) - if err != nil { - return nil, fmt.Errorf("Failed to create jose signer -> %s", err.Error()) - } - signer.SetNonceSource(j) - - signed, err := signer.Sign(content) - if err != nil { - return nil, fmt.Errorf("Failed to sign content -> %s", err.Error()) - } - return signed, nil -} - -func (j *jws) Nonce() (string, error) { - if nonce, ok := j.nonces.Pop(); ok { - return nonce, nil - } - - return getNonce(j.directoryURL) -} - -type nonceManager struct { - nonces []string - sync.Mutex -} - -func (n *nonceManager) Pop() (string, bool) { - n.Lock() - defer n.Unlock() - - if len(n.nonces) == 0 { - return "", false - } - - nonce := n.nonces[len(n.nonces)-1] - n.nonces = n.nonces[:len(n.nonces)-1] - return nonce, true -} - -func (n *nonceManager) Push(nonce string) { - n.Lock() - defer n.Unlock() - n.nonces = append(n.nonces, nonce) -} - -func getNonce(url string) (string, error) { - resp, err := httpHead(url) - if err != nil { - return "", fmt.Errorf("Failed to get nonce from HTTP HEAD -> %s", err.Error()) - } - - return getNonceFromResponse(resp) -} - -func getNonceFromResponse(resp *http.Response) (string, error) { - nonce := resp.Header.Get("Replay-Nonce") - if nonce == "" { - return "", fmt.Errorf("Server did not respond with a proper nonce header.") - } - - return nonce, nil -} diff --git a/vendor/github.com/xenolf/lego/acme/messages.go b/vendor/github.com/xenolf/lego/acme/messages.go deleted file mode 100644 index 79ccf154e..000000000 --- a/vendor/github.com/xenolf/lego/acme/messages.go +++ /dev/null @@ -1,115 +0,0 @@ -package acme - -import ( - "time" - - "gopkg.in/square/go-jose.v1" -) - -type directory struct { - NewAuthzURL string `json:"new-authz"` - NewCertURL string `json:"new-cert"` - NewRegURL string `json:"new-reg"` - RevokeCertURL string `json:"revoke-cert"` -} - -type registrationMessage struct { - Resource string `json:"resource"` - Contact []string `json:"contact"` - Delete bool `json:"delete,omitempty"` -} - -// Registration is returned by the ACME server after the registration -// The client implementation should save this registration somewhere. -type Registration struct { - Resource string `json:"resource,omitempty"` - ID int `json:"id"` - Key jose.JsonWebKey `json:"key"` - Contact []string `json:"contact"` - Agreement string `json:"agreement,omitempty"` - Authorizations string `json:"authorizations,omitempty"` - Certificates string `json:"certificates,omitempty"` -} - -// RegistrationResource represents all important informations about a registration -// of which the client needs to keep track itself. -type RegistrationResource struct { - Body Registration `json:"body,omitempty"` - URI string `json:"uri,omitempty"` - NewAuthzURL string `json:"new_authzr_uri,omitempty"` - TosURL string `json:"terms_of_service,omitempty"` -} - -type authorizationResource struct { - Body authorization - Domain string - NewCertURL string - AuthURL string -} - -type authorization struct { - Resource string `json:"resource,omitempty"` - Identifier identifier `json:"identifier"` - Status string `json:"status,omitempty"` - Expires time.Time `json:"expires,omitempty"` - Challenges []challenge `json:"challenges,omitempty"` - Combinations [][]int `json:"combinations,omitempty"` -} - -type identifier struct { - Type string `json:"type"` - Value string `json:"value"` -} - -type validationRecord struct { - URI string `json:"url,omitempty"` - Hostname string `json:"hostname,omitempty"` - Port string `json:"port,omitempty"` - ResolvedAddresses []string `json:"addressesResolved,omitempty"` - UsedAddress string `json:"addressUsed,omitempty"` -} - -type challenge struct { - Resource string `json:"resource,omitempty"` - Type Challenge `json:"type,omitempty"` - Status string `json:"status,omitempty"` - URI string `json:"uri,omitempty"` - Token string `json:"token,omitempty"` - KeyAuthorization string `json:"keyAuthorization,omitempty"` - TLS bool `json:"tls,omitempty"` - Iterations int `json:"n,omitempty"` - Error RemoteError `json:"error,omitempty"` - ValidationRecords []validationRecord `json:"validationRecord,omitempty"` -} - -type csrMessage struct { - Resource string `json:"resource,omitempty"` - Csr string `json:"csr"` - Authorizations []string `json:"authorizations"` -} - -type revokeCertMessage struct { - Resource string `json:"resource"` - Certificate string `json:"certificate"` -} - -type deactivateAuthMessage struct { - Resource string `json:"resource,omitempty"` - Status string `jsom:"status"` -} - -// CertificateResource represents a CA issued certificate. -// PrivateKey, Certificate and IssuerCertificate are all -// already PEM encoded and can be directly written to disk. -// Certificate may be a certificate bundle, depending on the -// options supplied to create it. -type CertificateResource struct { - Domain string `json:"domain"` - CertURL string `json:"certUrl"` - CertStableURL string `json:"certStableUrl"` - AccountRef string `json:"accountRef,omitempty"` - PrivateKey []byte `json:"-"` - Certificate []byte `json:"-"` - IssuerCertificate []byte `json:"-"` - CSR []byte `json:"-"` -} diff --git a/vendor/github.com/xenolf/lego/acme/pop_challenge.go b/vendor/github.com/xenolf/lego/acme/pop_challenge.go deleted file mode 100644 index 8d2a213b0..000000000 --- a/vendor/github.com/xenolf/lego/acme/pop_challenge.go +++ /dev/null @@ -1 +0,0 @@ -package acme diff --git a/vendor/github.com/xenolf/lego/acme/provider.go b/vendor/github.com/xenolf/lego/acme/provider.go deleted file mode 100644 index d177ff07a..000000000 --- a/vendor/github.com/xenolf/lego/acme/provider.go +++ /dev/null @@ -1,28 +0,0 @@ -package acme - -import "time" - -// ChallengeProvider enables implementing a custom challenge -// provider. Present presents the solution to a challenge available to -// be solved. CleanUp will be called by the challenge if Present ends -// in a non-error state. -type ChallengeProvider interface { - Present(domain, token, keyAuth string) error - CleanUp(domain, token, keyAuth string) error -} - -// ChallengeProviderTimeout allows for implementing a -// ChallengeProvider where an unusually long timeout is required when -// waiting for an ACME challenge to be satisfied, such as when -// checking for DNS record progagation. If an implementor of a -// ChallengeProvider provides a Timeout method, then the return values -// of the Timeout method will be used when appropriate by the acme -// package. The interval value is the time between checks. -// -// The default values used for timeout and interval are 60 seconds and -// 2 seconds respectively. These are used when no Timeout method is -// defined for the ChallengeProvider. -type ChallengeProviderTimeout interface { - ChallengeProvider - Timeout() (timeout, interval time.Duration) -} diff --git a/vendor/github.com/xenolf/lego/acme/testdata/resolv.conf.1 b/vendor/github.com/xenolf/lego/acme/testdata/resolv.conf.1 deleted file mode 100644 index 3098f99b5..000000000 --- a/vendor/github.com/xenolf/lego/acme/testdata/resolv.conf.1 +++ /dev/null @@ -1,5 +0,0 @@ -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.go b/vendor/github.com/xenolf/lego/acme/tls_sni_challenge.go deleted file mode 100644 index 34383cbfa..000000000 --- a/vendor/github.com/xenolf/lego/acme/tls_sni_challenge.go +++ /dev/null @@ -1,67 +0,0 @@ -package acme - -import ( - "crypto/rsa" - "crypto/sha256" - "crypto/tls" - "encoding/hex" - "fmt" - "log" -) - -type tlsSNIChallenge struct { - jws *jws - validate validateFunc - provider ChallengeProvider -} - -func (t *tlsSNIChallenge) Solve(chlng challenge, domain string) error { - // FIXME: https://github.com/ietf-wg-acme/acme/pull/22 - // Currently we implement this challenge to track boulder, not the current spec! - - logf("[INFO][%s] acme: Trying to solve TLS-SNI-01", domain) - - // Generate the Key Authorization for the challenge - keyAuth, err := getKeyAuthorization(chlng.Token, t.jws.privKey) - if err != nil { - return err - } - - err = t.provider.Present(domain, chlng.Token, keyAuth) - if err != nil { - return fmt.Errorf("[%s] error presenting token: %v", domain, err) - } - defer func() { - err := t.provider.CleanUp(domain, chlng.Token, keyAuth) - if err != nil { - log.Printf("[%s] error cleaning up: %v", domain, err) - } - }() - return t.validate(t.jws, domain, chlng.URI, challenge{Resource: "challenge", Type: chlng.Type, Token: chlng.Token, KeyAuthorization: keyAuth}) -} - -// TLSSNI01ChallengeCert returns a certificate and target domain for the `tls-sni-01` challenge -func TLSSNI01ChallengeCert(keyAuth string) (tls.Certificate, string, error) { - // generate a new RSA key for the certificates - tempPrivKey, err := generatePrivateKey(RSA2048) - if err != nil { - return tls.Certificate{}, "", err - } - rsaPrivKey := tempPrivKey.(*rsa.PrivateKey) - rsaPrivPEM := pemEncode(rsaPrivKey) - - zBytes := sha256.Sum256([]byte(keyAuth)) - z := hex.EncodeToString(zBytes[:sha256.Size]) - domain := fmt.Sprintf("%s.%s.acme.invalid", z[:32], z[32:]) - tempCertPEM, err := generatePemCert(rsaPrivKey, domain) - if err != nil { - return tls.Certificate{}, "", err - } - - certificate, err := tls.X509KeyPair(tempCertPEM, rsaPrivPEM) - if err != nil { - return tls.Certificate{}, "", err - } - - return certificate, domain, nil -} diff --git a/vendor/github.com/xenolf/lego/acme/tls_sni_challenge_server.go b/vendor/github.com/xenolf/lego/acme/tls_sni_challenge_server.go deleted file mode 100644 index df00fbb5a..000000000 --- a/vendor/github.com/xenolf/lego/acme/tls_sni_challenge_server.go +++ /dev/null @@ -1,62 +0,0 @@ -package acme - -import ( - "crypto/tls" - "fmt" - "net" - "net/http" -) - -// TLSProviderServer implements ChallengeProvider for `TLS-SNI-01` challenge -// It may be instantiated without using the NewTLSProviderServer function if -// you want only to use the default values. -type TLSProviderServer struct { - iface string - port string - done chan bool - listener net.Listener -} - -// NewTLSProviderServer creates a new TLSProviderServer on the selected interface and port. -// Setting iface and / or port to an empty string will make the server fall back to -// the "any" interface and port 443 respectively. -func NewTLSProviderServer(iface, port string) *TLSProviderServer { - return &TLSProviderServer{iface: iface, port: port} -} - -// Present makes the keyAuth available as a cert -func (s *TLSProviderServer) Present(domain, token, keyAuth string) error { - if s.port == "" { - s.port = "443" - } - - cert, _, err := TLSSNI01ChallengeCert(keyAuth) - if err != nil { - return err - } - - tlsConf := new(tls.Config) - tlsConf.Certificates = []tls.Certificate{cert} - - s.listener, err = tls.Listen("tcp", net.JoinHostPort(s.iface, s.port), tlsConf) - if err != nil { - return fmt.Errorf("Could not start HTTPS server for challenge -> %v", err) - } - - s.done = make(chan bool) - go func() { - http.Serve(s.listener, nil) - s.done <- true - }() - return nil -} - -// CleanUp closes the HTTP server. -func (s *TLSProviderServer) CleanUp(domain, token, keyAuth string) error { - if s.listener == nil { - return nil - } - s.listener.Close() - <-s.done - return nil -} 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 deleted file mode 100644 index 83b2833a9..000000000 --- a/vendor/github.com/xenolf/lego/acme/tls_sni_challenge_test.go +++ /dev/null @@ -1,65 +0,0 @@ -package acme - -import ( - "crypto/rand" - "crypto/rsa" - "crypto/sha256" - "crypto/tls" - "encoding/hex" - "fmt" - "strings" - "testing" -) - -func TestTLSSNIChallenge(t *testing.T) { - privKey, _ := rsa.GenerateKey(rand.Reader, 512) - j := &jws{privKey: privKey} - clientChallenge := challenge{Type: TLSSNI01, Token: "tlssni1"} - mockValidate := func(_ *jws, _, _ string, chlng challenge) error { - conn, err := tls.Dial("tcp", "localhost:23457", &tls.Config{ - InsecureSkipVerify: true, - }) - if err != nil { - t.Errorf("Expected to connect to challenge server without an error. %s", err.Error()) - } - - // Expect the server to only return one certificate - connState := conn.ConnectionState() - if count := len(connState.PeerCertificates); count != 1 { - t.Errorf("Expected the challenge server to return exactly one certificate but got %d", count) - } - - remoteCert := connState.PeerCertificates[0] - if count := len(remoteCert.DNSNames); count != 1 { - t.Errorf("Expected the challenge certificate to have exactly one DNSNames entry but had %d", count) - } - - zBytes := sha256.Sum256([]byte(chlng.KeyAuthorization)) - z := hex.EncodeToString(zBytes[:sha256.Size]) - domain := fmt.Sprintf("%s.%s.acme.invalid", z[:32], z[32:]) - - if remoteCert.DNSNames[0] != domain { - t.Errorf("Expected the challenge certificate DNSName to match %s but was %s", domain, remoteCert.DNSNames[0]) - } - - return nil - } - solver := &tlsSNIChallenge{jws: j, validate: mockValidate, provider: &TLSProviderServer{port: "23457"}} - - if err := solver.Solve(clientChallenge, "localhost:23457"); err != nil { - t.Errorf("Solve error: got %v, want nil", err) - } -} - -func TestTLSSNIChallengeInvalidPort(t *testing.T) { - privKey, _ := rsa.GenerateKey(rand.Reader, 128) - j := &jws{privKey: privKey} - clientChallenge := challenge{Type: TLSSNI01, Token: "tlssni2"} - solver := &tlsSNIChallenge{jws: j, validate: stubValidate, provider: &TLSProviderServer{port: "123456"}} - - if err := solver.Solve(clientChallenge, "localhost:123456"); err == nil { - t.Errorf("Solve error: got %v, want error", err) - } 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/utils.go b/vendor/github.com/xenolf/lego/acme/utils.go deleted file mode 100644 index 2fa0db304..000000000 --- a/vendor/github.com/xenolf/lego/acme/utils.go +++ /dev/null @@ -1,29 +0,0 @@ -package acme - -import ( - "fmt" - "time" -) - -// WaitFor polls the given function 'f', once every 'interval', up to 'timeout'. -func WaitFor(timeout, interval time.Duration, f func() (bool, error)) error { - var lastErr string - timeup := time.After(timeout) - for { - select { - case <-timeup: - return fmt.Errorf("Time limit exceeded. Last error: %s", lastErr) - default: - } - - stop, err := f() - if stop { - return nil - } - if err != nil { - lastErr = err.Error() - } - - time.Sleep(interval) - } -} diff --git a/vendor/github.com/xenolf/lego/acme/utils_test.go b/vendor/github.com/xenolf/lego/acme/utils_test.go deleted file mode 100644 index 158af4116..000000000 --- a/vendor/github.com/xenolf/lego/acme/utils_test.go +++ /dev/null @@ -1,26 +0,0 @@ -package acme - -import ( - "testing" - "time" -) - -func TestWaitForTimeout(t *testing.T) { - c := make(chan error) - go func() { - err := WaitFor(3*time.Second, 1*time.Second, func() (bool, error) { - return false, nil - }) - c <- err - }() - - timeout := time.After(4 * time.Second) - select { - case <-timeout: - t.Fatal("timeout exceeded") - case err := <-c: - if err == nil { - t.Errorf("expected timeout error; got %v", err) - } - } -} diff --git a/vendor/github.com/xenolf/lego/cli.go b/vendor/github.com/xenolf/lego/cli.go deleted file mode 100644 index 58567be9f..000000000 --- a/vendor/github.com/xenolf/lego/cli.go +++ /dev/null @@ -1,232 +0,0 @@ -// Let's Encrypt client to go! -// CLI application for generating Let's Encrypt certificates using the ACME package. -package main - -import ( - "fmt" - "log" - "os" - "path" - "strings" - "text/tabwriter" - - "github.com/urfave/cli" - "github.com/xenolf/lego/acme" -) - -// Logger is used to log errors; if nil, the default log.Logger is used. -var Logger *log.Logger - -// logger is an helper function to retrieve the available logger -func logger() *log.Logger { - if Logger == nil { - Logger = log.New(os.Stderr, "", log.LstdFlags) - } - return Logger -} - -var gittag string - -func main() { - app := cli.NewApp() - app.Name = "lego" - app.Usage = "Let's Encrypt client written in Go" - - version := "0.4.1" - if strings.HasPrefix(gittag, "v") { - version = gittag - } - - app.Version = version - - acme.UserAgent = "lego/" + app.Version - - defaultPath := "" - cwd, err := os.Getwd() - if err == nil { - defaultPath = path.Join(cwd, ".lego") - } - - app.Before = func(c *cli.Context) error { - if c.GlobalString("path") == "" { - logger().Fatal("Could not determine current working directory. Please pass --path.") - } - return nil - } - - app.Commands = []cli.Command{ - { - Name: "run", - Usage: "Register an account, then create and install a certificate", - Action: run, - Flags: []cli.Flag{ - cli.BoolFlag{ - Name: "no-bundle", - Usage: "Do not create a certificate bundle by adding the issuers certificate to the new certificate.", - }, - cli.BoolFlag{ - Name: "must-staple", - Usage: "Include the OCSP must staple TLS extension in the CSR and generated certificate. Only works if the CSR is generated by lego.", - }, - }, - }, - { - Name: "revoke", - Usage: "Revoke a certificate", - Action: revoke, - }, - { - Name: "renew", - Usage: "Renew a certificate", - Action: renew, - Flags: []cli.Flag{ - cli.IntFlag{ - Name: "days", - Value: 0, - Usage: "The number of days left on a certificate to renew it.", - }, - cli.BoolFlag{ - Name: "reuse-key", - Usage: "Used to indicate you want to reuse your current private key for the new certificate.", - }, - cli.BoolFlag{ - Name: "no-bundle", - Usage: "Do not create a certificate bundle by adding the issuers certificate to the new certificate.", - }, - cli.BoolFlag{ - Name: "must-staple", - Usage: "Include the OCSP must staple TLS extension in the CSR and generated certificate. Only works if the CSR is generated by lego.", - }, - }, - }, - { - Name: "dnshelp", - Usage: "Shows additional help for the --dns global option", - Action: dnshelp, - }, - } - - app.Flags = []cli.Flag{ - cli.StringSliceFlag{ - Name: "domains, d", - Usage: "Add a domain to the process. Can be specified multiple times.", - }, - cli.StringFlag{ - Name: "csr, c", - Usage: "Certificate signing request filename, if an external CSR is to be used", - }, - cli.StringFlag{ - Name: "server, s", - Value: "https://acme-v01.api.letsencrypt.org/directory", - Usage: "CA hostname (and optionally :port). The server certificate must be trusted in order to avoid further modifications to the client.", - }, - cli.StringFlag{ - Name: "email, m", - Usage: "Email used for registration and recovery contact.", - }, - cli.BoolFlag{ - Name: "accept-tos, a", - Usage: "By setting this flag to true you indicate that you accept the current Let's Encrypt terms of service.", - }, - cli.StringFlag{ - Name: "key-type, k", - Value: "rsa2048", - Usage: "Key type to use for private keys. Supported: rsa2048, rsa4096, rsa8192, ec256, ec384", - }, - cli.StringFlag{ - Name: "path", - Usage: "Directory to use for storing the data", - Value: defaultPath, - }, - cli.StringSliceFlag{ - Name: "exclude, x", - Usage: "Explicitly disallow solvers by name from being used. Solvers: \"http-01\", \"tls-sni-01\".", - }, - cli.StringFlag{ - Name: "webroot", - Usage: "Set the webroot folder to use for HTTP based challenges to write directly in a file in .well-known/acme-challenge", - }, - cli.StringSliceFlag{ - Name: "memcached-host", - Usage: "Set the memcached host(s) to use for HTTP based challenges. Challenges will be written to all specified hosts.", - }, - cli.StringFlag{ - Name: "http", - Usage: "Set the port and interface to use for HTTP based challenges to listen on. Supported: interface:port or :port", - }, - cli.StringFlag{ - Name: "tls", - Usage: "Set the port and interface to use for TLS based challenges to listen on. Supported: interface:port or :port", - }, - cli.StringFlag{ - Name: "dns", - Usage: "Solve a DNS challenge using the specified provider. Disables all other challenges. Run 'lego dnshelp' for help on usage.", - }, - cli.IntFlag{ - Name: "http-timeout", - Usage: "Set the HTTP timeout value to a specific value in seconds. The default is 10 seconds.", - }, - cli.IntFlag{ - Name: "dns-timeout", - Usage: "Set the DNS timeout value to a specific value in seconds. The default is 10 seconds.", - }, - cli.StringSliceFlag{ - Name: "dns-resolvers", - Usage: "Set the resolvers to use for performing recursive DNS queries. Supported: host:port. The default is to use the system resolvers, or Google's DNS resolvers if the system's cannot be determined.", - }, - cli.BoolFlag{ - Name: "pem", - Usage: "Generate a .pem file by concatanating the .key and .crt files together.", - }, - } - - err = app.Run(os.Args) - if err != nil { - log.Fatal(err) - } -} - -func dnshelp(c *cli.Context) error { - fmt.Printf( - `Credentials for DNS providers must be passed through environment variables. - -Here is an example bash command using the CloudFlare DNS provider: - - $ CLOUDFLARE_EMAIL=foo@bar.com \ - CLOUDFLARE_API_KEY=b9841238feb177a84330febba8a83208921177bffe733 \ - lego --dns cloudflare --domains www.example.com --email me@bar.com run - -`) - - w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0) - fmt.Fprintln(w, "Valid providers and their associated credential environment variables:") - fmt.Fprintln(w) - fmt.Fprintln(w, "\tazure:\tAZURE_CLIENT_ID, AZURE_CLIENT_SECRET, AZURE_SUBSCRIPTION_ID, AZURE_TENANT_ID, AZURE_RESOURCE_GROUP") - fmt.Fprintln(w, "\tauroradns:\tAURORA_USER_ID, AURORA_KEY, AURORA_ENDPOINT") - fmt.Fprintln(w, "\tcloudflare:\tCLOUDFLARE_EMAIL, CLOUDFLARE_API_KEY") - fmt.Fprintln(w, "\tdigitalocean:\tDO_AUTH_TOKEN") - fmt.Fprintln(w, "\tdnsimple:\tDNSIMPLE_EMAIL, DNSIMPLE_OAUTH_TOKEN") - fmt.Fprintln(w, "\tdnsmadeeasy:\tDNSMADEEASY_API_KEY, DNSMADEEASY_API_SECRET") - fmt.Fprintln(w, "\texoscale:\tEXOSCALE_API_KEY, EXOSCALE_API_SECRET, EXOSCALE_ENDPOINT") - fmt.Fprintln(w, "\tgandi:\tGANDI_API_KEY") - fmt.Fprintln(w, "\tgcloud:\tGCE_PROJECT, GCE_SERVICE_ACCOUNT_FILE") - fmt.Fprintln(w, "\tlinode:\tLINODE_API_KEY") - fmt.Fprintln(w, "\tmanual:\tnone") - fmt.Fprintln(w, "\tnamecheap:\tNAMECHEAP_API_USER, NAMECHEAP_API_KEY") - fmt.Fprintln(w, "\trackspace:\tRACKSPACE_USER, RACKSPACE_API_KEY") - fmt.Fprintln(w, "\trfc2136:\tRFC2136_TSIG_KEY, RFC2136_TSIG_SECRET,\n\t\tRFC2136_TSIG_ALGORITHM, RFC2136_NAMESERVER") - fmt.Fprintln(w, "\troute53:\tAWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION, AWS_HOSTED_ZONE_ID") - fmt.Fprintln(w, "\tdyn:\tDYN_CUSTOMER_NAME, DYN_USER_NAME, DYN_PASSWORD") - fmt.Fprintln(w, "\tvultr:\tVULTR_API_KEY") - fmt.Fprintln(w, "\tovh:\tOVH_ENDPOINT, OVH_APPLICATION_KEY, OVH_APPLICATION_SECRET, OVH_CONSUMER_KEY") - fmt.Fprintln(w, "\tpdns:\tPDNS_API_KEY, PDNS_API_URL") - fmt.Fprintln(w, "\tdnspod:\tDNSPOD_API_KEY") - fmt.Fprintln(w, "\totc:\tOTC_USER_NAME, OTC_PASSWORD, OTC_PROJECT_NAME, OTC_DOMAIN_NAME, OTC_IDENTITY_ENDPOINT") - w.Flush() - - fmt.Println(` -For a more detailed explanation of a DNS provider's credential variables, -please consult their online documentation.`) - - return nil -} diff --git a/vendor/github.com/xenolf/lego/cli_handlers.go b/vendor/github.com/xenolf/lego/cli_handlers.go deleted file mode 100644 index b8790c4b2..000000000 --- a/vendor/github.com/xenolf/lego/cli_handlers.go +++ /dev/null @@ -1,423 +0,0 @@ -package main - -import ( - "bufio" - "bytes" - "crypto/x509" - "encoding/json" - "encoding/pem" - "io/ioutil" - "net/http" - "os" - "path" - "strings" - "time" - - "github.com/urfave/cli" - "github.com/xenolf/lego/acme" - "github.com/xenolf/lego/providers/dns" - "github.com/xenolf/lego/providers/http/memcached" - "github.com/xenolf/lego/providers/http/webroot" -) - -func checkFolder(path string) error { - if _, err := os.Stat(path); os.IsNotExist(err) { - return os.MkdirAll(path, 0700) - } - return nil -} - -func setup(c *cli.Context) (*Configuration, *Account, *acme.Client) { - - if c.GlobalIsSet("http-timeout") { - acme.HTTPClient = http.Client{Timeout: time.Duration(c.GlobalInt("http-timeout")) * time.Second} - } - - if c.GlobalIsSet("dns-timeout") { - acme.DNSTimeout = time.Duration(c.GlobalInt("dns-timeout")) * time.Second - } - - if len(c.GlobalStringSlice("dns-resolvers")) > 0 { - resolvers := []string{} - for _, resolver := range c.GlobalStringSlice("dns-resolvers") { - if !strings.Contains(resolver, ":") { - resolver += ":53" - } - resolvers = append(resolvers, resolver) - } - acme.RecursiveNameservers = resolvers - } - - err := checkFolder(c.GlobalString("path")) - if err != nil { - logger().Fatalf("Could not check/create path: %s", err.Error()) - } - - conf := NewConfiguration(c) - if len(c.GlobalString("email")) == 0 { - logger().Fatal("You have to pass an account (email address) to the program using --email or -m") - } - - //TODO: move to account struct? Currently MUST pass email. - acc := NewAccount(c.GlobalString("email"), conf) - - keyType, err := conf.KeyType() - if err != nil { - logger().Fatal(err.Error()) - } - - client, err := acme.NewClient(c.GlobalString("server"), acc, keyType) - if err != nil { - logger().Fatalf("Could not create client: %s", err.Error()) - } - - if len(c.GlobalStringSlice("exclude")) > 0 { - client.ExcludeChallenges(conf.ExcludedSolvers()) - } - - if c.GlobalIsSet("webroot") { - provider, err := webroot.NewHTTPProvider(c.GlobalString("webroot")) - if err != nil { - logger().Fatal(err) - } - - client.SetChallengeProvider(acme.HTTP01, provider) - - // --webroot=foo indicates that the user specifically want to do a HTTP challenge - // infer that the user also wants to exclude all other challenges - client.ExcludeChallenges([]acme.Challenge{acme.DNS01, acme.TLSSNI01}) - } - if c.GlobalIsSet("memcached-host") { - provider, err := memcached.NewMemcachedProvider(c.GlobalStringSlice("memcached-host")) - if err != nil { - logger().Fatal(err) - } - - client.SetChallengeProvider(acme.HTTP01, provider) - - // --memcached-host=foo:11211 indicates that the user specifically want to do a HTTP challenge - // infer that the user also wants to exclude all other challenges - client.ExcludeChallenges([]acme.Challenge{acme.DNS01, acme.TLSSNI01}) - } - if c.GlobalIsSet("http") { - if strings.Index(c.GlobalString("http"), ":") == -1 { - logger().Fatalf("The --http switch only accepts interface:port or :port for its argument.") - } - client.SetHTTPAddress(c.GlobalString("http")) - } - - if c.GlobalIsSet("tls") { - if strings.Index(c.GlobalString("tls"), ":") == -1 { - logger().Fatalf("The --tls switch only accepts interface:port or :port for its argument.") - } - client.SetTLSAddress(c.GlobalString("tls")) - } - - if c.GlobalIsSet("dns") { - provider, err := dns.NewDNSChallengeProviderByName(c.GlobalString("dns")) - if err != nil { - logger().Fatal(err) - } - - client.SetChallengeProvider(acme.DNS01, provider) - - // --dns=foo indicates that the user specifically want to do a DNS challenge - // infer that the user also wants to exclude all other challenges - client.ExcludeChallenges([]acme.Challenge{acme.HTTP01, acme.TLSSNI01}) - } - - return conf, acc, client -} - -func saveCertRes(certRes acme.CertificateResource, conf *Configuration) { - // We store the certificate, private key and metadata in different files - // as web servers would not be able to work with a combined file. - certOut := path.Join(conf.CertPath(), certRes.Domain+".crt") - privOut := path.Join(conf.CertPath(), certRes.Domain+".key") - pemOut := path.Join(conf.CertPath(), certRes.Domain+".pem") - metaOut := path.Join(conf.CertPath(), certRes.Domain+".json") - issuerOut := path.Join(conf.CertPath(), certRes.Domain+".issuer.crt") - - err := ioutil.WriteFile(certOut, certRes.Certificate, 0600) - if err != nil { - logger().Fatalf("Unable to save Certificate for domain %s\n\t%s", certRes.Domain, err.Error()) - } - - if certRes.IssuerCertificate != nil { - err = ioutil.WriteFile(issuerOut, certRes.IssuerCertificate, 0600) - if err != nil { - logger().Fatalf("Unable to save IssuerCertificate for domain %s\n\t%s", certRes.Domain, err.Error()) - } - } - - if certRes.PrivateKey != nil { - // if we were given a CSR, we don't know the private key - err = ioutil.WriteFile(privOut, certRes.PrivateKey, 0600) - if err != nil { - logger().Fatalf("Unable to save PrivateKey for domain %s\n\t%s", certRes.Domain, err.Error()) - } - - if conf.context.GlobalBool("pem") { - err = ioutil.WriteFile(pemOut, bytes.Join([][]byte{certRes.Certificate, certRes.PrivateKey}, nil), 0600) - if err != nil { - logger().Fatalf("Unable to save Certificate and PrivateKey in .pem for domain %s\n\t%s", certRes.Domain, err.Error()) - } - } - - } else if conf.context.GlobalBool("pem") { - // we don't have the private key; can't write the .pem file - logger().Fatalf("Unable to save pem without private key for domain %s\n\t%s; are you using a CSR?", certRes.Domain, err.Error()) - } - - jsonBytes, err := json.MarshalIndent(certRes, "", "\t") - if err != nil { - logger().Fatalf("Unable to marshal CertResource for domain %s\n\t%s", certRes.Domain, err.Error()) - } - - err = ioutil.WriteFile(metaOut, jsonBytes, 0600) - if err != nil { - logger().Fatalf("Unable to save CertResource for domain %s\n\t%s", certRes.Domain, err.Error()) - } -} - -func handleTOS(c *cli.Context, client *acme.Client, acc *Account) { - // Check for a global accept override - if c.GlobalBool("accept-tos") { - err := client.AgreeToTOS() - if err != nil { - logger().Fatalf("Could not agree to TOS: %s", err.Error()) - } - - acc.Save() - return - } - - reader := bufio.NewReader(os.Stdin) - logger().Printf("Please review the TOS at %s", acc.Registration.TosURL) - - for { - logger().Println("Do you accept the TOS? Y/n") - text, err := reader.ReadString('\n') - if err != nil { - logger().Fatalf("Could not read from console: %s", err.Error()) - } - - text = strings.Trim(text, "\r\n") - - if text == "n" { - logger().Fatal("You did not accept the TOS. Unable to proceed.") - } - - if text == "Y" || text == "y" || text == "" { - err = client.AgreeToTOS() - if err != nil { - logger().Fatalf("Could not agree to TOS: %s", err.Error()) - } - acc.Save() - break - } - - logger().Println("Your input was invalid. Please answer with one of Y/y, n or by pressing enter.") - } -} - -func readCSRFile(filename string) (*x509.CertificateRequest, error) { - bytes, err := ioutil.ReadFile(filename) - if err != nil { - return nil, err - } - raw := bytes - - // see if we can find a PEM-encoded CSR - var p *pem.Block - rest := bytes - for { - // decode a PEM block - p, rest = pem.Decode(rest) - - // did we fail? - if p == nil { - break - } - - // did we get a CSR? - if p.Type == "CERTIFICATE REQUEST" { - raw = p.Bytes - } - } - - // no PEM-encoded CSR - // assume we were given a DER-encoded ASN.1 CSR - // (if this assumption is wrong, parsing these bytes will fail) - return x509.ParseCertificateRequest(raw) -} - -func run(c *cli.Context) error { - conf, acc, client := setup(c) - if acc.Registration == nil { - reg, err := client.Register() - if err != nil { - logger().Fatalf("Could not complete registration\n\t%s", err.Error()) - } - - acc.Registration = reg - acc.Save() - - logger().Print("!!!! HEADS UP !!!!") - logger().Printf(` - Your account credentials have been saved in your Let's Encrypt - configuration directory at "%s". - You should make a secure backup of this folder now. This - configuration directory will also contain certificates and - private keys obtained from Let's Encrypt so making regular - backups of this folder is ideal.`, conf.AccountPath(c.GlobalString("email"))) - - } - - // If the agreement URL is empty, the account still needs to accept the LE TOS. - if acc.Registration.Body.Agreement == "" { - handleTOS(c, client, acc) - } - - // we require either domains or csr, but not both - hasDomains := len(c.GlobalStringSlice("domains")) > 0 - hasCsr := len(c.GlobalString("csr")) > 0 - if hasDomains && hasCsr { - logger().Fatal("Please specify either --domains/-d or --csr/-c, but not both") - } - if !hasDomains && !hasCsr { - logger().Fatal("Please specify --domains/-d (or --csr/-c if you already have a CSR)") - } - - var cert acme.CertificateResource - var failures map[string]error - - if hasDomains { - // obtain a certificate, generating a new private key - cert, failures = client.ObtainCertificate(c.GlobalStringSlice("domains"), !c.Bool("no-bundle"), nil, c.Bool("must-staple")) - } else { - // read the CSR - csr, err := readCSRFile(c.GlobalString("csr")) - if err != nil { - // we couldn't read the CSR - failures = map[string]error{"csr": err} - } else { - // obtain a certificate for this CSR - cert, failures = client.ObtainCertificateForCSR(*csr, !c.Bool("no-bundle")) - } - } - - if len(failures) > 0 { - for k, v := range failures { - logger().Printf("[%s] Could not obtain certificates\n\t%s", k, v.Error()) - } - - // Make sure to return a non-zero exit code if ObtainSANCertificate - // returned at least one error. Due to us not returning partial - // certificate we can just exit here instead of at the end. - os.Exit(1) - } - - err := checkFolder(conf.CertPath()) - if err != nil { - logger().Fatalf("Could not check/create path: %s", err.Error()) - } - - saveCertRes(cert, conf) - - return nil -} - -func revoke(c *cli.Context) error { - conf, acc, client := setup(c) - if acc.Registration == nil { - logger().Fatalf("Account %s is not registered. Use 'run' to register a new account.\n", acc.Email) - } - - err := checkFolder(conf.CertPath()) - if err != nil { - logger().Fatalf("Could not check/create path: %s", err.Error()) - } - - for _, domain := range c.GlobalStringSlice("domains") { - logger().Printf("Trying to revoke certificate for domain %s", domain) - - certPath := path.Join(conf.CertPath(), domain+".crt") - certBytes, err := ioutil.ReadFile(certPath) - - err = client.RevokeCertificate(certBytes) - if err != nil { - logger().Fatalf("Error while revoking the certificate for domain %s\n\t%s", domain, err.Error()) - } else { - logger().Print("Certificate was revoked.") - } - } - - return nil -} - -func renew(c *cli.Context) error { - conf, acc, client := setup(c) - if acc.Registration == nil { - logger().Fatalf("Account %s is not registered. Use 'run' to register a new account.\n", acc.Email) - } - - if len(c.GlobalStringSlice("domains")) <= 0 { - logger().Fatal("Please specify at least one domain.") - } - - domain := c.GlobalStringSlice("domains")[0] - - // load the cert resource from files. - // We store the certificate, private key and metadata in different files - // as web servers would not be able to work with a combined file. - certPath := path.Join(conf.CertPath(), domain+".crt") - privPath := path.Join(conf.CertPath(), domain+".key") - metaPath := path.Join(conf.CertPath(), domain+".json") - - certBytes, err := ioutil.ReadFile(certPath) - if err != nil { - logger().Fatalf("Error while loading the certificate for domain %s\n\t%s", domain, err.Error()) - } - - if c.IsSet("days") { - expTime, err := acme.GetPEMCertExpiration(certBytes) - if err != nil { - logger().Printf("Could not get Certification expiration for domain %s", domain) - } - - if int(expTime.Sub(time.Now()).Hours()/24.0) > c.Int("days") { - return nil - } - } - - metaBytes, err := ioutil.ReadFile(metaPath) - if err != nil { - logger().Fatalf("Error while loading the meta data for domain %s\n\t%s", domain, err.Error()) - } - - var certRes acme.CertificateResource - err = json.Unmarshal(metaBytes, &certRes) - if err != nil { - logger().Fatalf("Error while marshalling the meta data for domain %s\n\t%s", domain, err.Error()) - } - - if c.Bool("reuse-key") { - keyBytes, err := ioutil.ReadFile(privPath) - if err != nil { - logger().Fatalf("Error while loading the private key for domain %s\n\t%s", domain, err.Error()) - } - certRes.PrivateKey = keyBytes - } - - certRes.Certificate = certBytes - - newCert, err := client.RenewCertificate(certRes, !c.Bool("no-bundle"), c.Bool("must-staple")) - if err != nil { - logger().Fatalf("%s", err.Error()) - } - - saveCertRes(newCert, conf) - - return nil -} diff --git a/vendor/github.com/xenolf/lego/configuration.go b/vendor/github.com/xenolf/lego/configuration.go deleted file mode 100644 index f92c1fe96..000000000 --- a/vendor/github.com/xenolf/lego/configuration.go +++ /dev/null @@ -1,76 +0,0 @@ -package main - -import ( - "fmt" - "net/url" - "os" - "path" - "strings" - - "github.com/urfave/cli" - "github.com/xenolf/lego/acme" -) - -// Configuration type from CLI and config files. -type Configuration struct { - context *cli.Context -} - -// NewConfiguration creates a new configuration from CLI data. -func NewConfiguration(c *cli.Context) *Configuration { - return &Configuration{context: c} -} - -// KeyType the type from which private keys should be generated -func (c *Configuration) KeyType() (acme.KeyType, error) { - switch strings.ToUpper(c.context.GlobalString("key-type")) { - case "RSA2048": - return acme.RSA2048, nil - case "RSA4096": - return acme.RSA4096, nil - case "RSA8192": - return acme.RSA8192, nil - case "EC256": - return acme.EC256, nil - case "EC384": - return acme.EC384, nil - } - - return "", fmt.Errorf("Unsupported KeyType: %s", c.context.GlobalString("key-type")) -} - -// ExcludedSolvers is a list of solvers that are to be excluded. -func (c *Configuration) ExcludedSolvers() (cc []acme.Challenge) { - for _, s := range c.context.GlobalStringSlice("exclude") { - cc = append(cc, acme.Challenge(s)) - } - return -} - -// ServerPath returns the OS dependent path to the data for a specific CA -func (c *Configuration) ServerPath() string { - srv, _ := url.Parse(c.context.GlobalString("server")) - srvStr := strings.Replace(srv.Host, ":", "_", -1) - return strings.Replace(srvStr, "/", string(os.PathSeparator), -1) -} - -// CertPath gets the path for certificates. -func (c *Configuration) CertPath() string { - return path.Join(c.context.GlobalString("path"), "certificates") -} - -// AccountsPath returns the OS dependent path to the -// local accounts for a specific CA -func (c *Configuration) AccountsPath() string { - return path.Join(c.context.GlobalString("path"), "accounts", c.ServerPath()) -} - -// AccountPath returns the OS dependent path to a particular account -func (c *Configuration) AccountPath(acc string) string { - return path.Join(c.AccountsPath(), acc) -} - -// AccountKeysPath returns the OS dependent path to the keys of a particular account -func (c *Configuration) AccountKeysPath(acc string) string { - return path.Join(c.AccountPath(acc), "keys") -} diff --git a/vendor/github.com/xenolf/lego/crypto.go b/vendor/github.com/xenolf/lego/crypto.go deleted file mode 100644 index 8b23e2fc1..000000000 --- a/vendor/github.com/xenolf/lego/crypto.go +++ /dev/null @@ -1,56 +0,0 @@ -package main - -import ( - "crypto" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "crypto/x509" - "encoding/pem" - "errors" - "io/ioutil" - "os" -) - -func generatePrivateKey(file string) (crypto.PrivateKey, error) { - - privateKey, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader) - if err != nil { - return nil, err - } - - keyBytes, err := x509.MarshalECPrivateKey(privateKey) - if err != nil { - return nil, err - } - - pemKey := pem.Block{Type: "EC PRIVATE KEY", Bytes: keyBytes} - - certOut, err := os.Create(file) - if err != nil { - return nil, err - } - - pem.Encode(certOut, &pemKey) - certOut.Close() - - return privateKey, nil -} - -func loadPrivateKey(file string) (crypto.PrivateKey, error) { - keyBytes, err := ioutil.ReadFile(file) - if err != nil { - return nil, err - } - - keyBlock, _ := pem.Decode(keyBytes) - - switch keyBlock.Type { - case "RSA PRIVATE KEY": - return x509.ParsePKCS1PrivateKey(keyBlock.Bytes) - case "EC PRIVATE KEY": - return x509.ParseECPrivateKey(keyBlock.Bytes) - } - - return nil, errors.New("Unknown private key type.") -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/auroradns/auroradns.go b/vendor/github.com/xenolf/lego/providers/dns/auroradns/auroradns.go deleted file mode 100644 index 55b48f9b4..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/auroradns/auroradns.go +++ /dev/null @@ -1,141 +0,0 @@ -package auroradns - -import ( - "fmt" - "github.com/edeckers/auroradnsclient" - "github.com/edeckers/auroradnsclient/records" - "github.com/edeckers/auroradnsclient/zones" - "github.com/xenolf/lego/acme" - "os" - "sync" -) - -// DNSProvider describes a provider for AuroraDNS -type DNSProvider struct { - recordIDs map[string]string - recordIDsMu sync.Mutex - client *auroradnsclient.AuroraDNSClient -} - -// NewDNSProvider returns a DNSProvider instance configured for AuroraDNS. -// Credentials must be passed in the environment variables: AURORA_USER_ID -// and AURORA_KEY. -func NewDNSProvider() (*DNSProvider, error) { - userID := os.Getenv("AURORA_USER_ID") - key := os.Getenv("AURORA_KEY") - - endpoint := os.Getenv("AURORA_ENDPOINT") - if endpoint == "" { - endpoint = "https://api.auroradns.eu" - } - - return NewDNSProviderCredentials(endpoint, userID, key) -} - -// NewDNSProviderCredentials uses the supplied credentials to return a -// DNSProvider instance configured for AuroraDNS. -func NewDNSProviderCredentials(baseURL string, userID string, key string) (*DNSProvider, error) { - client, err := auroradnsclient.NewAuroraDNSClient(baseURL, userID, key) - if err != nil { - return nil, err - } - - return &DNSProvider{ - client: client, - recordIDs: make(map[string]string), - }, nil -} - -func (provider *DNSProvider) getZoneInformationByName(name string) (zones.ZoneRecord, error) { - zs, err := provider.client.GetZones() - - if err != nil { - return zones.ZoneRecord{}, err - } - - for _, element := range zs { - if element.Name == name { - return element, nil - } - } - - return zones.ZoneRecord{}, fmt.Errorf("Could not find Zone record") -} - -// Present creates a record with a secret -func (provider *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) - - authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) - if err != nil { - return fmt.Errorf("Could not determine zone for domain: '%s'. %s", domain, err) - } - - // 1. Aurora will happily create the TXT record when it is provided a fqdn, - // but it will only appear in the control panel and will not be - // propagated to DNS servers. Extract and use subdomain instead. - // 2. A trailing dot in the fqdn will cause Aurora to add a trailing dot to - // the subdomain, resulting in _acme-challenge.. rather - // than _acme-challenge. - - subdomain := fqdn[0 : len(fqdn)-len(authZone)-1] - - authZone = acme.UnFqdn(authZone) - - zoneRecord, err := provider.getZoneInformationByName(authZone) - - reqData := - records.CreateRecordRequest{ - RecordType: "TXT", - Name: subdomain, - Content: value, - TTL: 300, - } - - respData, err := provider.client.CreateRecord(zoneRecord.ID, reqData) - if err != nil { - return fmt.Errorf("Could not create record: '%s'.", err) - } - - provider.recordIDsMu.Lock() - provider.recordIDs[fqdn] = respData.ID - provider.recordIDsMu.Unlock() - - return nil -} - -// CleanUp removes a given record that was generated by Present -func (provider *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - - provider.recordIDsMu.Lock() - recordID, ok := provider.recordIDs[fqdn] - provider.recordIDsMu.Unlock() - - if !ok { - return fmt.Errorf("Unknown recordID for '%s'", fqdn) - } - - authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) - if err != nil { - return fmt.Errorf("Could not determine zone for domain: '%s'. %s", domain, err) - } - - authZone = acme.UnFqdn(authZone) - - zoneRecord, err := provider.getZoneInformationByName(authZone) - if err != nil { - return err - } - - _, err = provider.client.RemoveRecord(zoneRecord.ID, recordID) - if err != nil { - return err - } - - provider.recordIDsMu.Lock() - delete(provider.recordIDs, fqdn) - provider.recordIDsMu.Unlock() - - return nil -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/auroradns/auroradns_test.go b/vendor/github.com/xenolf/lego/providers/dns/auroradns/auroradns_test.go deleted file mode 100644 index f4df7fa61..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/auroradns/auroradns_test.go +++ /dev/null @@ -1,148 +0,0 @@ -package auroradns - -import ( - "fmt" - "io/ioutil" - "net/http" - "net/http/httptest" - "testing" -) - -var fakeAuroraDNSUserId = "asdf1234" -var fakeAuroraDNSKey = "key" - -func TestAuroraDNSPresent(t *testing.T) { - var requestReceived bool - - mock := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.Method == "GET" && r.URL.Path == "/zones" { - w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, `[{ - "id": "c56a4180-65aa-42ec-a945-5fd21dec0538", - "name": "example.com" - }]`) - return - } - - requestReceived = true - - if got, want := r.Method, "POST"; got != want { - t.Errorf("Expected method to be '%s' but got '%s'", want, got) - } - - if got, want := r.URL.Path, "/zones/c56a4180-65aa-42ec-a945-5fd21dec0538/records"; got != want { - t.Errorf("Expected path to be '%s' but got '%s'", want, got) - } - - if got, want := r.Header.Get("Content-Type"), "application/json"; got != want { - t.Errorf("Expected Content-Type to be '%s' but got '%s'", want, got) - } - - reqBody, err := ioutil.ReadAll(r.Body) - if err != nil { - t.Fatalf("Error reading request body: %v", err) - } - - if got, want := string(reqBody), - `{"type":"TXT","name":"_acme-challenge","content":"w6uP8Tcg6K2QR905Rms8iXTlksL6OD1KOWBxTK7wxPI","ttl":300}`; got != want { - - t.Errorf("Expected body data to be: `%s` but got `%s`", want, got) - } - - w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, `{ - "id": "c56a4180-65aa-42ec-a945-5fd21dec0538", - "type": "TXT", - "name": "_acme-challenge", - "ttl": 300 - }`) - })) - - defer mock.Close() - - auroraProvider, err := NewDNSProviderCredentials(mock.URL, fakeAuroraDNSUserId, fakeAuroraDNSKey) - if auroraProvider == nil { - t.Fatal("Expected non-nil AuroraDNS provider, but was nil") - } - - if err != nil { - t.Fatalf("Expected no error creating provider, but got: %v", err) - } - - err = auroraProvider.Present("example.com", "", "foobar") - if err != nil { - t.Fatalf("Expected no error creating TXT record, but got: %v", err) - } - - if !requestReceived { - t.Error("Expected request to be received by mock backend, but it wasn't") - } -} - -func TestAuroraDNSCleanUp(t *testing.T) { - var requestReceived bool - - mock := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.Method == "GET" && r.URL.Path == "/zones" { - w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, `[{ - "id": "c56a4180-65aa-42ec-a945-5fd21dec0538", - "name": "example.com" - }]`) - return - } - - if r.Method == "POST" && r.URL.Path == "/zones/c56a4180-65aa-42ec-a945-5fd21dec0538/records" { - w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, `{ - "id": "ec56a4180-65aa-42ec-a945-5fd21dec0538", - "type": "TXT", - "name": "_acme-challenge", - "ttl": 300 - }`) - return - } - - requestReceived = true - - if got, want := r.Method, "DELETE"; got != want { - t.Errorf("Expected method to be '%s' but got '%s'", want, got) - } - - if got, want := r.URL.Path, - "/zones/c56a4180-65aa-42ec-a945-5fd21dec0538/records/ec56a4180-65aa-42ec-a945-5fd21dec0538"; got != want { - t.Errorf("Expected path to be '%s' but got '%s'", want, got) - } - - if got, want := r.Header.Get("Content-Type"), "application/json"; got != want { - t.Errorf("Expected Content-Type to be '%s' but got '%s'", want, got) - } - - w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, `{}`) - })) - defer mock.Close() - - auroraProvider, err := NewDNSProviderCredentials(mock.URL, fakeAuroraDNSUserId, fakeAuroraDNSKey) - if auroraProvider == nil { - t.Fatal("Expected non-nil AuroraDNS provider, but was nil") - } - - if err != nil { - t.Fatalf("Expected no error creating provider, but got: %v", err) - } - - err = auroraProvider.Present("example.com", "", "foobar") - if err != nil { - t.Fatalf("Expected no error creating TXT record, but got: %v", err) - } - - err = auroraProvider.CleanUp("example.com", "", "foobar") - if err != nil { - t.Fatalf("Expected no error removing TXT record, but got: %v", err) - } - - if !requestReceived { - t.Error("Expected request to be received by mock backend, but it wasn't") - } -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/azure/azure.go b/vendor/github.com/xenolf/lego/providers/dns/azure/azure.go deleted file mode 100644 index 6a30b318a..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/azure/azure.go +++ /dev/null @@ -1,151 +0,0 @@ -// Package azure implements a DNS provider for solving the DNS-01 -// challenge using azure DNS. -// Azure doesn't like trailing dots on domain names, most of the acme code does. -package azure - -import ( - "fmt" - "os" - "time" - - "github.com/Azure/azure-sdk-for-go/arm/dns" - - "strings" - - "github.com/Azure/go-autorest/autorest" - "github.com/Azure/go-autorest/autorest/adal" - "github.com/Azure/go-autorest/autorest/azure" - "github.com/Azure/go-autorest/autorest/to" - "github.com/xenolf/lego/acme" -) - -// DNSProvider is an implementation of the acme.ChallengeProvider interface -type DNSProvider struct { - clientId string - clientSecret string - subscriptionId string - tenantId string - resourceGroup string -} - -// NewDNSProvider returns a DNSProvider instance configured for azure. -// Credentials must be passed in the environment variables: AZURE_CLIENT_ID, -// AZURE_CLIENT_SECRET, AZURE_SUBSCRIPTION_ID, AZURE_TENANT_ID, AZURE_RESOURCE_GROUP -func NewDNSProvider() (*DNSProvider, error) { - clientId := os.Getenv("AZURE_CLIENT_ID") - clientSecret := os.Getenv("AZURE_CLIENT_SECRET") - subscriptionId := os.Getenv("AZURE_SUBSCRIPTION_ID") - tenantId := os.Getenv("AZURE_TENANT_ID") - resourceGroup := os.Getenv("AZURE_RESOURCE_GROUP") - return NewDNSProviderCredentials(clientId, clientSecret, subscriptionId, tenantId, resourceGroup) -} - -// NewDNSProviderCredentials uses the supplied credentials to return a -// DNSProvider instance configured for azure. -func NewDNSProviderCredentials(clientId, clientSecret, subscriptionId, tenantId, resourceGroup string) (*DNSProvider, error) { - if clientId == "" || clientSecret == "" || subscriptionId == "" || tenantId == "" || resourceGroup == "" { - return nil, fmt.Errorf("Azure configuration missing") - } - - return &DNSProvider{ - clientId: clientId, - clientSecret: clientSecret, - subscriptionId: subscriptionId, - tenantId: tenantId, - resourceGroup: resourceGroup, - }, nil -} - -// Timeout returns the timeout and interval to use when checking for DNS -// propagation. Adjusting here to cope with spikes in propagation times. -func (c *DNSProvider) Timeout() (timeout, interval time.Duration) { - return 120 * time.Second, 2 * time.Second -} - -// Present creates a TXT record to fulfil the dns-01 challenge -func (c *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) - zone, err := c.getHostedZoneID(fqdn) - if err != nil { - return err - } - - rsc := dns.NewRecordSetsClient(c.subscriptionId) - spt, err := c.newServicePrincipalTokenFromCredentials(azure.PublicCloud.ResourceManagerEndpoint) - rsc.Authorizer = autorest.NewBearerAuthorizer(spt) - - relative := toRelativeRecord(fqdn, acme.ToFqdn(zone)) - rec := dns.RecordSet{ - Name: &relative, - RecordSetProperties: &dns.RecordSetProperties{ - TTL: to.Int64Ptr(60), - TxtRecords: &[]dns.TxtRecord{dns.TxtRecord{Value: &[]string{value}}}, - }, - } - _, err = rsc.CreateOrUpdate(c.resourceGroup, zone, relative, dns.TXT, rec, "", "") - - if err != nil { - return err - } - - return nil -} - -// Returns the relative record to the domain -func toRelativeRecord(domain, zone string) string { - return acme.UnFqdn(strings.TrimSuffix(domain, zone)) -} - -// CleanUp removes the TXT record matching the specified parameters -func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - - zone, err := c.getHostedZoneID(fqdn) - if err != nil { - return err - } - - relative := toRelativeRecord(fqdn, acme.ToFqdn(zone)) - rsc := dns.NewRecordSetsClient(c.subscriptionId) - spt, err := c.newServicePrincipalTokenFromCredentials(azure.PublicCloud.ResourceManagerEndpoint) - rsc.Authorizer = autorest.NewBearerAuthorizer(spt) - _, err = rsc.Delete(c.resourceGroup, zone, relative, dns.TXT, "") - if err != nil { - return err - } - - return nil -} - -// Checks that azure has a zone for this domain name. -func (c *DNSProvider) getHostedZoneID(fqdn string) (string, error) { - authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) - if err != nil { - return "", err - } - - // Now we want to to Azure and get the zone. - spt, err := c.newServicePrincipalTokenFromCredentials(azure.PublicCloud.ResourceManagerEndpoint) - - dc := dns.NewZonesClient(c.subscriptionId) - dc.Authorizer = autorest.NewBearerAuthorizer(spt) - - zone, err := dc.Get(c.resourceGroup, acme.UnFqdn(authZone)) - - if err != nil { - return "", err - } - - // zone.Name shouldn't have a trailing dot(.) - return to.String(zone.Name), nil -} - -// NewServicePrincipalTokenFromCredentials creates a new ServicePrincipalToken using values of the -// passed credentials map. -func (c *DNSProvider) newServicePrincipalTokenFromCredentials(scope string) (*adal.ServicePrincipalToken, error) { - oauthConfig, err := adal.NewOAuthConfig(azure.PublicCloud.ActiveDirectoryEndpoint, c.tenantId) - if err != nil { - panic(err) - } - return adal.NewServicePrincipalToken(*oauthConfig, c.clientId, c.clientSecret, scope) -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/azure/azure_test.go b/vendor/github.com/xenolf/lego/providers/dns/azure/azure_test.go deleted file mode 100644 index db55f578a..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/azure/azure_test.go +++ /dev/null @@ -1,89 +0,0 @@ -package azure - -import ( - "os" - "testing" - "time" - - "github.com/stretchr/testify/assert" -) - -var ( - azureLiveTest bool - azureClientID string - azureClientSecret string - azureSubscriptionID string - azureTenantID string - azureResourceGroup string - azureDomain string -) - -func init() { - azureClientID = os.Getenv("AZURE_CLIENT_ID") - azureClientSecret = os.Getenv("AZURE_CLIENT_SECRET") - azureSubscriptionID = os.Getenv("AZURE_SUBSCRIPTION_ID") - azureTenantID = os.Getenv("AZURE_TENANT_ID") - azureResourceGroup = os.Getenv("AZURE_RESOURCE_GROUP") - azureDomain = os.Getenv("AZURE_DOMAIN") - if len(azureClientID) > 0 && len(azureClientSecret) > 0 { - azureLiveTest = true - } -} - -func restoreAzureEnv() { - os.Setenv("AZURE_CLIENT_ID", azureClientID) - os.Setenv("AZURE_SUBSCRIPTION_ID", azureSubscriptionID) -} - -func TestNewDNSProviderValid(t *testing.T) { - if !azureLiveTest { - t.Skip("skipping live test (requires credentials)") - } - os.Setenv("AZURE_CLIENT_ID", "") - _, err := NewDNSProviderCredentials(azureClientID, azureClientSecret, azureSubscriptionID, azureTenantID, azureResourceGroup) - assert.NoError(t, err) - restoreAzureEnv() -} - -func TestNewDNSProviderValidEnv(t *testing.T) { - if !azureLiveTest { - t.Skip("skipping live test (requires credentials)") - } - os.Setenv("AZURE_CLIENT_ID", "other") - _, err := NewDNSProvider() - assert.NoError(t, err) - restoreAzureEnv() -} - -func TestNewDNSProviderMissingCredErr(t *testing.T) { - os.Setenv("AZURE_SUBSCRIPTION_ID", "") - _, err := NewDNSProvider() - assert.EqualError(t, err, "Azure configuration missing") - restoreAzureEnv() -} - -func TestLiveAzurePresent(t *testing.T) { - if !azureLiveTest { - t.Skip("skipping live test") - } - - provider, err := NewDNSProviderCredentials(azureClientID, azureClientSecret, azureSubscriptionID, azureTenantID, azureResourceGroup) - assert.NoError(t, err) - - err = provider.Present(azureDomain, "", "123d==") - assert.NoError(t, err) -} - -func TestLiveAzureCleanUp(t *testing.T) { - if !azureLiveTest { - t.Skip("skipping live test") - } - - provider, err := NewDNSProviderCredentials(azureClientID, azureClientSecret, azureSubscriptionID, azureTenantID, azureResourceGroup) - time.Sleep(time.Second * 1) - - assert.NoError(t, err) - - err = provider.CleanUp(azureDomain, "", "123d==") - assert.NoError(t, err) -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/cloudflare/cloudflare.go b/vendor/github.com/xenolf/lego/providers/dns/cloudflare/cloudflare.go deleted file mode 100644 index 84952238d..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/cloudflare/cloudflare.go +++ /dev/null @@ -1,223 +0,0 @@ -// Package cloudflare implements a DNS provider for solving the DNS-01 -// challenge using cloudflare DNS. -package cloudflare - -import ( - "bytes" - "encoding/json" - "fmt" - "io" - "net/http" - "os" - "time" - - "github.com/xenolf/lego/acme" -) - -// CloudFlareAPIURL represents the API endpoint to call. -// TODO: Unexport? -const CloudFlareAPIURL = "https://api.cloudflare.com/client/v4" - -// DNSProvider is an implementation of the acme.ChallengeProvider interface -type DNSProvider struct { - authEmail string - authKey string -} - -// NewDNSProvider returns a DNSProvider instance configured for cloudflare. -// Credentials must be passed in the environment variables: CLOUDFLARE_EMAIL -// and CLOUDFLARE_API_KEY. -func NewDNSProvider() (*DNSProvider, error) { - email := os.Getenv("CLOUDFLARE_EMAIL") - key := os.Getenv("CLOUDFLARE_API_KEY") - return NewDNSProviderCredentials(email, key) -} - -// NewDNSProviderCredentials uses the supplied credentials to return a -// DNSProvider instance configured for cloudflare. -func NewDNSProviderCredentials(email, key string) (*DNSProvider, error) { - if email == "" || key == "" { - return nil, fmt.Errorf("CloudFlare credentials missing") - } - - return &DNSProvider{ - authEmail: email, - authKey: key, - }, nil -} - -// Timeout returns the timeout and interval to use when checking for DNS -// propagation. Adjusting here to cope with spikes in propagation times. -func (c *DNSProvider) Timeout() (timeout, interval time.Duration) { - return 120 * time.Second, 2 * time.Second -} - -// Present creates a TXT record to fulfil the dns-01 challenge -func (c *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) - zoneID, err := c.getHostedZoneID(fqdn) - if err != nil { - return err - } - - rec := cloudFlareRecord{ - Type: "TXT", - Name: acme.UnFqdn(fqdn), - Content: value, - TTL: 120, - } - - body, err := json.Marshal(rec) - if err != nil { - return err - } - - _, err = c.makeRequest("POST", fmt.Sprintf("/zones/%s/dns_records", zoneID), bytes.NewReader(body)) - if err != nil { - return err - } - - return nil -} - -// CleanUp removes the TXT record matching the specified parameters -func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - - record, err := c.findTxtRecord(fqdn) - if err != nil { - return err - } - - _, err = c.makeRequest("DELETE", fmt.Sprintf("/zones/%s/dns_records/%s", record.ZoneID, record.ID), nil) - if err != nil { - return err - } - - return nil -} - -func (c *DNSProvider) getHostedZoneID(fqdn string) (string, error) { - // HostedZone represents a CloudFlare DNS zone - type HostedZone struct { - ID string `json:"id"` - Name string `json:"name"` - } - - authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) - if err != nil { - return "", err - } - - result, err := c.makeRequest("GET", "/zones?name="+acme.UnFqdn(authZone), nil) - if err != nil { - return "", err - } - - var hostedZone []HostedZone - err = json.Unmarshal(result, &hostedZone) - if err != nil { - return "", err - } - - if len(hostedZone) != 1 { - return "", fmt.Errorf("Zone %s not found in CloudFlare for domain %s", authZone, fqdn) - } - - return hostedZone[0].ID, nil -} - -func (c *DNSProvider) findTxtRecord(fqdn string) (*cloudFlareRecord, error) { - zoneID, err := c.getHostedZoneID(fqdn) - if err != nil { - return nil, err - } - - result, err := c.makeRequest( - "GET", - fmt.Sprintf("/zones/%s/dns_records?per_page=1000&type=TXT&name=%s", zoneID, acme.UnFqdn(fqdn)), - nil, - ) - if err != nil { - return nil, err - } - - var records []cloudFlareRecord - err = json.Unmarshal(result, &records) - if err != nil { - return nil, err - } - - for _, rec := range records { - if rec.Name == acme.UnFqdn(fqdn) { - return &rec, nil - } - } - - return nil, fmt.Errorf("No existing record found for %s", fqdn) -} - -func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawMessage, error) { - // APIError contains error details for failed requests - type APIError struct { - Code int `json:"code,omitempty"` - Message string `json:"message,omitempty"` - ErrorChain []APIError `json:"error_chain,omitempty"` - } - - // APIResponse represents a response from CloudFlare API - type APIResponse struct { - Success bool `json:"success"` - Errors []*APIError `json:"errors"` - Result json.RawMessage `json:"result"` - } - - req, err := http.NewRequest(method, fmt.Sprintf("%s%s", CloudFlareAPIURL, uri), body) - if err != nil { - return nil, err - } - - req.Header.Set("X-Auth-Email", c.authEmail) - req.Header.Set("X-Auth-Key", c.authKey) - //req.Header.Set("User-Agent", userAgent()) - - client := http.Client{Timeout: 30 * time.Second} - resp, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("Error querying Cloudflare API -> %v", err) - } - - defer resp.Body.Close() - - var r APIResponse - err = json.NewDecoder(resp.Body).Decode(&r) - if err != nil { - return nil, err - } - - if !r.Success { - if len(r.Errors) > 0 { - errStr := "" - for _, apiErr := range r.Errors { - errStr += fmt.Sprintf("\t Error: %d: %s", apiErr.Code, apiErr.Message) - for _, chainErr := range apiErr.ErrorChain { - errStr += fmt.Sprintf("<- %d: %s", chainErr.Code, chainErr.Message) - } - } - return nil, fmt.Errorf("Cloudflare API Error \n%s", errStr) - } - return nil, fmt.Errorf("Cloudflare API error") - } - - return r.Result, nil -} - -// cloudFlareRecord represents a CloudFlare DNS record -type cloudFlareRecord struct { - Name string `json:"name"` - Type string `json:"type"` - Content string `json:"content"` - ID string `json:"id,omitempty"` - TTL int `json:"ttl,omitempty"` - ZoneID string `json:"zone_id,omitempty"` -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/cloudflare/cloudflare_test.go b/vendor/github.com/xenolf/lego/providers/dns/cloudflare/cloudflare_test.go deleted file mode 100644 index 19b5a40b9..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/cloudflare/cloudflare_test.go +++ /dev/null @@ -1,80 +0,0 @@ -package cloudflare - -import ( - "os" - "testing" - "time" - - "github.com/stretchr/testify/assert" -) - -var ( - cflareLiveTest bool - cflareEmail string - cflareAPIKey string - cflareDomain string -) - -func init() { - cflareEmail = os.Getenv("CLOUDFLARE_EMAIL") - cflareAPIKey = os.Getenv("CLOUDFLARE_API_KEY") - cflareDomain = os.Getenv("CLOUDFLARE_DOMAIN") - if len(cflareEmail) > 0 && len(cflareAPIKey) > 0 && len(cflareDomain) > 0 { - cflareLiveTest = true - } -} - -func restoreCloudFlareEnv() { - os.Setenv("CLOUDFLARE_EMAIL", cflareEmail) - os.Setenv("CLOUDFLARE_API_KEY", cflareAPIKey) -} - -func TestNewDNSProviderValid(t *testing.T) { - os.Setenv("CLOUDFLARE_EMAIL", "") - os.Setenv("CLOUDFLARE_API_KEY", "") - _, err := NewDNSProviderCredentials("123", "123") - assert.NoError(t, err) - restoreCloudFlareEnv() -} - -func TestNewDNSProviderValidEnv(t *testing.T) { - os.Setenv("CLOUDFLARE_EMAIL", "test@example.com") - os.Setenv("CLOUDFLARE_API_KEY", "123") - _, err := NewDNSProvider() - assert.NoError(t, err) - restoreCloudFlareEnv() -} - -func TestNewDNSProviderMissingCredErr(t *testing.T) { - os.Setenv("CLOUDFLARE_EMAIL", "") - os.Setenv("CLOUDFLARE_API_KEY", "") - _, err := NewDNSProvider() - assert.EqualError(t, err, "CloudFlare credentials missing") - restoreCloudFlareEnv() -} - -func TestCloudFlarePresent(t *testing.T) { - if !cflareLiveTest { - t.Skip("skipping live test") - } - - provider, err := NewDNSProviderCredentials(cflareEmail, cflareAPIKey) - assert.NoError(t, err) - - err = provider.Present(cflareDomain, "", "123d==") - assert.NoError(t, err) -} - -func TestCloudFlareCleanUp(t *testing.T) { - if !cflareLiveTest { - t.Skip("skipping live test") - } - - time.Sleep(time.Second * 2) - - provider, err := NewDNSProviderCredentials(cflareEmail, cflareAPIKey) - assert.NoError(t, err) - - err = provider.CleanUp(cflareDomain, "", "123d==") - assert.NoError(t, err) -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/digitalocean/digitalocean.go b/vendor/github.com/xenolf/lego/providers/dns/digitalocean/digitalocean.go deleted file mode 100644 index da261b39a..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/digitalocean/digitalocean.go +++ /dev/null @@ -1,166 +0,0 @@ -// Package digitalocean implements a DNS provider for solving the DNS-01 -// challenge using digitalocean DNS. -package digitalocean - -import ( - "bytes" - "encoding/json" - "fmt" - "net/http" - "os" - "sync" - "time" - - "github.com/xenolf/lego/acme" -) - -// DNSProvider is an implementation of the acme.ChallengeProvider interface -// that uses DigitalOcean's REST API to manage TXT records for a domain. -type DNSProvider struct { - apiAuthToken string - recordIDs map[string]int - recordIDsMu sync.Mutex -} - -// NewDNSProvider returns a DNSProvider instance configured for Digital -// Ocean. Credentials must be passed in the environment variable: -// DO_AUTH_TOKEN. -func NewDNSProvider() (*DNSProvider, error) { - apiAuthToken := os.Getenv("DO_AUTH_TOKEN") - return NewDNSProviderCredentials(apiAuthToken) -} - -// NewDNSProviderCredentials uses the supplied credentials to return a -// DNSProvider instance configured for Digital Ocean. -func NewDNSProviderCredentials(apiAuthToken string) (*DNSProvider, error) { - if apiAuthToken == "" { - return nil, fmt.Errorf("DigitalOcean credentials missing") - } - return &DNSProvider{ - apiAuthToken: apiAuthToken, - recordIDs: make(map[string]int), - }, nil -} - -// Present creates a TXT record using the specified parameters -func (d *DNSProvider) Present(domain, token, keyAuth string) error { - // txtRecordRequest represents the request body to DO's API to make a TXT record - type txtRecordRequest struct { - RecordType string `json:"type"` - Name string `json:"name"` - Data string `json:"data"` - } - - // txtRecordResponse represents a response from DO's API after making a TXT record - type txtRecordResponse struct { - DomainRecord struct { - ID int `json:"id"` - Type string `json:"type"` - Name string `json:"name"` - Data string `json:"data"` - } `json:"domain_record"` - } - - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) - - authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) - if err != nil { - return fmt.Errorf("Could not determine zone for domain: '%s'. %s", domain, err) - } - - authZone = acme.UnFqdn(authZone) - - reqURL := fmt.Sprintf("%s/v2/domains/%s/records", digitalOceanBaseURL, authZone) - reqData := txtRecordRequest{RecordType: "TXT", Name: fqdn, Data: value} - body, err := json.Marshal(reqData) - if err != nil { - return err - } - - req, err := http.NewRequest("POST", reqURL, bytes.NewReader(body)) - if err != nil { - return err - } - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", d.apiAuthToken)) - - client := http.Client{Timeout: 30 * time.Second} - resp, err := client.Do(req) - if err != nil { - return err - } - defer resp.Body.Close() - - if resp.StatusCode >= 400 { - var errInfo digitalOceanAPIError - json.NewDecoder(resp.Body).Decode(&errInfo) - return fmt.Errorf("HTTP %d: %s: %s", resp.StatusCode, errInfo.ID, errInfo.Message) - } - - // Everything looks good; but we'll need the ID later to delete the record - var respData txtRecordResponse - err = json.NewDecoder(resp.Body).Decode(&respData) - if err != nil { - return err - } - d.recordIDsMu.Lock() - d.recordIDs[fqdn] = respData.DomainRecord.ID - d.recordIDsMu.Unlock() - - return nil -} - -// CleanUp removes the TXT record matching the specified parameters -func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - - // get the record's unique ID from when we created it - d.recordIDsMu.Lock() - recordID, ok := d.recordIDs[fqdn] - d.recordIDsMu.Unlock() - if !ok { - return fmt.Errorf("unknown record ID for '%s'", fqdn) - } - - authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) - if err != nil { - return fmt.Errorf("Could not determine zone for domain: '%s'. %s", domain, err) - } - - authZone = acme.UnFqdn(authZone) - - reqURL := fmt.Sprintf("%s/v2/domains/%s/records/%d", digitalOceanBaseURL, authZone, recordID) - req, err := http.NewRequest("DELETE", reqURL, nil) - if err != nil { - return err - } - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", d.apiAuthToken)) - - client := http.Client{Timeout: 30 * time.Second} - resp, err := client.Do(req) - if err != nil { - return err - } - defer resp.Body.Close() - - if resp.StatusCode >= 400 { - var errInfo digitalOceanAPIError - json.NewDecoder(resp.Body).Decode(&errInfo) - return fmt.Errorf("HTTP %d: %s: %s", resp.StatusCode, errInfo.ID, errInfo.Message) - } - - // Delete record ID from map - d.recordIDsMu.Lock() - delete(d.recordIDs, fqdn) - d.recordIDsMu.Unlock() - - return nil -} - -type digitalOceanAPIError struct { - ID string `json:"id"` - Message string `json:"message"` -} - -var digitalOceanBaseURL = "https://api.digitalocean.com" diff --git a/vendor/github.com/xenolf/lego/providers/dns/digitalocean/digitalocean_test.go b/vendor/github.com/xenolf/lego/providers/dns/digitalocean/digitalocean_test.go deleted file mode 100644 index 7498508ba..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/digitalocean/digitalocean_test.go +++ /dev/null @@ -1,117 +0,0 @@ -package digitalocean - -import ( - "fmt" - "io/ioutil" - "net/http" - "net/http/httptest" - "testing" -) - -var fakeDigitalOceanAuth = "asdf1234" - -func TestDigitalOceanPresent(t *testing.T) { - var requestReceived bool - - mock := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - requestReceived = true - - if got, want := r.Method, "POST"; got != want { - t.Errorf("Expected method to be '%s' but got '%s'", want, got) - } - if got, want := r.URL.Path, "/v2/domains/example.com/records"; got != want { - t.Errorf("Expected path to be '%s' but got '%s'", want, got) - } - if got, want := r.Header.Get("Content-Type"), "application/json"; got != want { - t.Errorf("Expected Content-Type to be '%s' but got '%s'", want, got) - } - if got, want := r.Header.Get("Authorization"), "Bearer asdf1234"; got != want { - t.Errorf("Expected Authorization to be '%s' but got '%s'", want, got) - } - - reqBody, err := ioutil.ReadAll(r.Body) - if err != nil { - t.Fatalf("Error reading request body: %v", err) - } - if got, want := string(reqBody), `{"type":"TXT","name":"_acme-challenge.example.com.","data":"w6uP8Tcg6K2QR905Rms8iXTlksL6OD1KOWBxTK7wxPI"}`; got != want { - t.Errorf("Expected body data to be: `%s` but got `%s`", want, got) - } - - w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, `{ - "domain_record": { - "id": 1234567, - "type": "TXT", - "name": "_acme-challenge", - "data": "w6uP8Tcg6K2QR905Rms8iXTlksL6OD1KOWBxTK7wxPI", - "priority": null, - "port": null, - "weight": null - } - }`) - })) - defer mock.Close() - digitalOceanBaseURL = mock.URL - - doprov, err := NewDNSProviderCredentials(fakeDigitalOceanAuth) - if doprov == nil { - t.Fatal("Expected non-nil DigitalOcean provider, but was nil") - } - if err != nil { - t.Fatalf("Expected no error creating provider, but got: %v", err) - } - - err = doprov.Present("example.com", "", "foobar") - if err != nil { - t.Fatalf("Expected no error creating TXT record, but got: %v", err) - } - if !requestReceived { - t.Error("Expected request to be received by mock backend, but it wasn't") - } -} - -func TestDigitalOceanCleanUp(t *testing.T) { - var requestReceived bool - - mock := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - requestReceived = true - - if got, want := r.Method, "DELETE"; got != want { - t.Errorf("Expected method to be '%s' but got '%s'", want, got) - } - if got, want := r.URL.Path, "/v2/domains/example.com/records/1234567"; got != want { - t.Errorf("Expected path to be '%s' but got '%s'", want, got) - } - // NOTE: Even though the body is empty, DigitalOcean API docs still show setting this Content-Type... - if got, want := r.Header.Get("Content-Type"), "application/json"; got != want { - t.Errorf("Expected Content-Type to be '%s' but got '%s'", want, got) - } - if got, want := r.Header.Get("Authorization"), "Bearer asdf1234"; got != want { - t.Errorf("Expected Authorization to be '%s' but got '%s'", want, got) - } - - w.WriteHeader(http.StatusNoContent) - })) - defer mock.Close() - digitalOceanBaseURL = mock.URL - - doprov, err := NewDNSProviderCredentials(fakeDigitalOceanAuth) - if doprov == nil { - t.Fatal("Expected non-nil DigitalOcean provider, but was nil") - } - if err != nil { - t.Fatalf("Expected no error creating provider, but got: %v", err) - } - - doprov.recordIDsMu.Lock() - doprov.recordIDs["_acme-challenge.example.com."] = 1234567 - doprov.recordIDsMu.Unlock() - - err = doprov.CleanUp("example.com", "", "") - if err != nil { - t.Fatalf("Expected no error removing TXT record, but got: %v", err) - } - if !requestReceived { - t.Error("Expected request to be received by mock backend, but it wasn't") - } -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/dns_providers.go b/vendor/github.com/xenolf/lego/providers/dns/dns_providers.go deleted file mode 100644 index d7530f788..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/dns_providers.go +++ /dev/null @@ -1,86 +0,0 @@ -// Factory for DNS providers -package dns - -import ( - "fmt" - - "github.com/xenolf/lego/acme" - "github.com/xenolf/lego/providers/dns/auroradns" - "github.com/xenolf/lego/providers/dns/azure" - "github.com/xenolf/lego/providers/dns/cloudflare" - "github.com/xenolf/lego/providers/dns/digitalocean" - "github.com/xenolf/lego/providers/dns/dnsimple" - "github.com/xenolf/lego/providers/dns/dnsmadeeasy" - "github.com/xenolf/lego/providers/dns/dnspod" - "github.com/xenolf/lego/providers/dns/dyn" - "github.com/xenolf/lego/providers/dns/exoscale" - "github.com/xenolf/lego/providers/dns/gandi" - "github.com/xenolf/lego/providers/dns/godaddy" - "github.com/xenolf/lego/providers/dns/googlecloud" - "github.com/xenolf/lego/providers/dns/linode" - "github.com/xenolf/lego/providers/dns/namecheap" - "github.com/xenolf/lego/providers/dns/ns1" - "github.com/xenolf/lego/providers/dns/otc" - "github.com/xenolf/lego/providers/dns/ovh" - "github.com/xenolf/lego/providers/dns/pdns" - "github.com/xenolf/lego/providers/dns/rackspace" - "github.com/xenolf/lego/providers/dns/rfc2136" - "github.com/xenolf/lego/providers/dns/route53" - "github.com/xenolf/lego/providers/dns/vultr" -) - -func NewDNSChallengeProviderByName(name string) (acme.ChallengeProvider, error) { - var err error - var provider acme.ChallengeProvider - switch name { - case "azure": - provider, err = azure.NewDNSProvider() - case "auroradns": - provider, err = auroradns.NewDNSProvider() - case "cloudflare": - provider, err = cloudflare.NewDNSProvider() - case "digitalocean": - provider, err = digitalocean.NewDNSProvider() - case "dnsimple": - provider, err = dnsimple.NewDNSProvider() - case "dnsmadeeasy": - provider, err = dnsmadeeasy.NewDNSProvider() - case "dnspod": - provider, err = dnspod.NewDNSProvider() - case "dyn": - provider, err = dyn.NewDNSProvider() - case "exoscale": - provider, err = exoscale.NewDNSProvider() - case "gandi": - provider, err = gandi.NewDNSProvider() - case "gcloud": - provider, err = googlecloud.NewDNSProvider() - case "godaddy": - provider, err = godaddy.NewDNSProvider() - case "linode": - provider, err = linode.NewDNSProvider() - case "manual": - provider, err = acme.NewDNSProviderManual() - case "namecheap": - provider, err = namecheap.NewDNSProvider() - case "rackspace": - provider, err = rackspace.NewDNSProvider() - case "route53": - provider, err = route53.NewDNSProvider() - case "rfc2136": - provider, err = rfc2136.NewDNSProvider() - case "vultr": - provider, err = vultr.NewDNSProvider() - case "ovh": - provider, err = ovh.NewDNSProvider() - case "pdns": - provider, err = pdns.NewDNSProvider() - case "ns1": - provider, err = ns1.NewDNSProvider() - case "otc": - provider, err = otc.NewDNSProvider() - default: - err = fmt.Errorf("Unrecognised DNS provider: %s", name) - } - return provider, err -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/dns_providers_test.go b/vendor/github.com/xenolf/lego/providers/dns/dns_providers_test.go deleted file mode 100644 index 3f87ffd33..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/dns_providers_test.go +++ /dev/null @@ -1,50 +0,0 @@ -package dns - -import ( - "os" - "reflect" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/xenolf/lego/providers/dns/exoscale" -) - -var ( - apiKey string - apiSecret string -) - -func init() { - apiSecret = os.Getenv("EXOSCALE_API_SECRET") - apiKey = os.Getenv("EXOSCALE_API_KEY") -} - -func restoreExoscaleEnv() { - os.Setenv("EXOSCALE_API_KEY", apiKey) - os.Setenv("EXOSCALE_API_SECRET", apiSecret) -} - -func TestKnownDNSProviderSuccess(t *testing.T) { - os.Setenv("EXOSCALE_API_KEY", "abc") - os.Setenv("EXOSCALE_API_SECRET", "123") - provider, err := NewDNSChallengeProviderByName("exoscale") - assert.NoError(t, err) - assert.NotNil(t, provider) - if reflect.TypeOf(provider) != reflect.TypeOf(&exoscale.DNSProvider{}) { - t.Errorf("Not loaded correct DNS proviver: %v is not *exoscale.DNSProvider", reflect.TypeOf(provider)) - } - restoreExoscaleEnv() -} - -func TestKnownDNSProviderError(t *testing.T) { - os.Setenv("EXOSCALE_API_KEY", "") - os.Setenv("EXOSCALE_API_SECRET", "") - _, err := NewDNSChallengeProviderByName("exoscale") - assert.Error(t, err) - restoreExoscaleEnv() -} - -func TestUnknownDNSProvider(t *testing.T) { - _, err := NewDNSChallengeProviderByName("foobar") - assert.Error(t, err) -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/dnsimple/dnsimple.go b/vendor/github.com/xenolf/lego/providers/dns/dnsimple/dnsimple.go deleted file mode 100644 index e3fea79ec..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/dnsimple/dnsimple.go +++ /dev/null @@ -1,180 +0,0 @@ -// Package dnsimple implements a DNS provider for solving the DNS-01 challenge -// using dnsimple DNS. -package dnsimple - -import ( - "fmt" - "os" - "strconv" - "strings" - - "github.com/dnsimple/dnsimple-go/dnsimple" - "github.com/xenolf/lego/acme" -) - -// DNSProvider is an implementation of the acme.ChallengeProvider interface. -type DNSProvider struct { - client *dnsimple.Client -} - -// NewDNSProvider returns a DNSProvider instance configured for dnsimple. -// Credentials must be passed in the environment variables: DNSIMPLE_OAUTH_TOKEN. -// -// See: https://developer.dnsimple.com/v2/#authentication -func NewDNSProvider() (*DNSProvider, error) { - accessToken := os.Getenv("DNSIMPLE_OAUTH_TOKEN") - baseUrl := os.Getenv("DNSIMPLE_BASE_URL") - - return NewDNSProviderCredentials(accessToken, baseUrl) -} - -// NewDNSProviderCredentials uses the supplied credentials to return a -// DNSProvider instance configured for dnsimple. -func NewDNSProviderCredentials(accessToken, baseUrl string) (*DNSProvider, error) { - if accessToken == "" { - return nil, fmt.Errorf("DNSimple OAuth token is missing") - } - - client := dnsimple.NewClient(dnsimple.NewOauthTokenCredentials(accessToken)) - client.UserAgent = "lego" - - if baseUrl != "" { - client.BaseURL = baseUrl - } - - return &DNSProvider{client: client}, nil -} - -// Present creates a TXT record to fulfil the dns-01 challenge. -func (c *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) - - zoneName, err := c.getHostedZone(domain) - - if err != nil { - return err - } - - accountID, err := c.getAccountID() - if err != nil { - return err - } - - recordAttributes := c.newTxtRecord(zoneName, fqdn, value, ttl) - _, err = c.client.Zones.CreateRecord(accountID, zoneName, *recordAttributes) - if err != nil { - return fmt.Errorf("DNSimple API call failed: %v", err) - } - - return nil -} - -// CleanUp removes the TXT record matching the specified parameters. -func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - - records, err := c.findTxtRecords(domain, fqdn) - if err != nil { - return err - } - - accountID, err := c.getAccountID() - if err != nil { - return err - } - - for _, rec := range records { - _, err := c.client.Zones.DeleteRecord(accountID, rec.ZoneID, rec.ID) - if err != nil { - return err - } - } - - return nil -} - -func (c *DNSProvider) getHostedZone(domain string) (string, error) { - authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) - if err != nil { - return "", err - } - - accountID, err := c.getAccountID() - if err != nil { - return "", err - } - - zoneName := acme.UnFqdn(authZone) - - zones, err := c.client.Zones.ListZones(accountID, &dnsimple.ZoneListOptions{NameLike: zoneName}) - if err != nil { - return "", fmt.Errorf("DNSimple API call failed: %v", err) - } - - var hostedZone dnsimple.Zone - for _, zone := range zones.Data { - if zone.Name == zoneName { - hostedZone = zone - } - } - - if hostedZone.ID == 0 { - return "", fmt.Errorf("Zone %s not found in DNSimple for domain %s", authZone, domain) - - } - - return hostedZone.Name, nil -} - -func (c *DNSProvider) findTxtRecords(domain, fqdn string) ([]dnsimple.ZoneRecord, error) { - zoneName, err := c.getHostedZone(domain) - if err != nil { - return nil, err - } - - accountID, err := c.getAccountID() - if err != nil { - return nil, err - } - - recordName := c.extractRecordName(fqdn, zoneName) - - result, err := c.client.Zones.ListRecords(accountID, zoneName, &dnsimple.ZoneRecordListOptions{Name: recordName, Type: "TXT", ListOptions: dnsimple.ListOptions{}}) - if err != nil { - return []dnsimple.ZoneRecord{}, fmt.Errorf("DNSimple API call has failed: %v", err) - } - - return result.Data, nil -} - -func (c *DNSProvider) newTxtRecord(zoneName, fqdn, value string, ttl int) *dnsimple.ZoneRecord { - name := c.extractRecordName(fqdn, zoneName) - - return &dnsimple.ZoneRecord{ - Type: "TXT", - Name: name, - Content: value, - TTL: ttl, - } -} - -func (c *DNSProvider) extractRecordName(fqdn, domain string) string { - name := acme.UnFqdn(fqdn) - if idx := strings.Index(name, "."+domain); idx != -1 { - return name[:idx] - } - return name -} - -func (c *DNSProvider) getAccountID() (string, error) { - whoamiResponse, err := c.client.Identity.Whoami() - if err != nil { - return "", err - } - - if whoamiResponse.Data.Account == nil { - return "", fmt.Errorf("DNSimple user tokens are not supported, please use an account token.") - } - - return strconv.Itoa(whoamiResponse.Data.Account.ID), nil -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/dnsimple/dnsimple_test.go b/vendor/github.com/xenolf/lego/providers/dns/dnsimple/dnsimple_test.go deleted file mode 100644 index bd35790d7..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/dnsimple/dnsimple_test.go +++ /dev/null @@ -1,140 +0,0 @@ -package dnsimple - -import ( - "os" - "testing" - "time" - - "github.com/stretchr/testify/assert" -) - -var ( - dnsimpleLiveTest bool - dnsimpleOauthToken string - dnsimpleDomain string - dnsimpleBaseUrl string -) - -func init() { - dnsimpleOauthToken = os.Getenv("DNSIMPLE_OAUTH_TOKEN") - dnsimpleDomain = os.Getenv("DNSIMPLE_DOMAIN") - dnsimpleBaseUrl = "https://api.sandbox.dnsimple.com" - - if len(dnsimpleOauthToken) > 0 && len(dnsimpleDomain) > 0 { - baseUrl := os.Getenv("DNSIMPLE_BASE_URL") - - if baseUrl != "" { - dnsimpleBaseUrl = baseUrl - } - - dnsimpleLiveTest = true - } -} - -func restoreDNSimpleEnv() { - os.Setenv("DNSIMPLE_OAUTH_TOKEN", dnsimpleOauthToken) - os.Setenv("DNSIMPLE_BASE_URL", dnsimpleBaseUrl) -} - -// -// NewDNSProvider -// - -func TestNewDNSProviderValid(t *testing.T) { - defer restoreDNSimpleEnv() - - os.Setenv("DNSIMPLE_OAUTH_TOKEN", "123") - provider, err := NewDNSProvider() - - assert.NotNil(t, provider) - assert.Equal(t, "lego", provider.client.UserAgent) - assert.NoError(t, err) -} - -func TestNewDNSProviderValidWithBaseUrl(t *testing.T) { - defer restoreDNSimpleEnv() - - os.Setenv("DNSIMPLE_OAUTH_TOKEN", "123") - os.Setenv("DNSIMPLE_BASE_URL", "https://api.dnsimple.test") - provider, err := NewDNSProvider() - - assert.NotNil(t, provider) - assert.NoError(t, err) - - assert.Equal(t, provider.client.BaseURL, "https://api.dnsimple.test") -} - -func TestNewDNSProviderInvalidWithMissingOauthToken(t *testing.T) { - if dnsimpleLiveTest { - t.Skip("skipping test in live mode") - } - - defer restoreDNSimpleEnv() - - provider, err := NewDNSProvider() - - assert.Nil(t, provider) - assert.EqualError(t, err, "DNSimple OAuth token is missing") -} - -// -// NewDNSProviderCredentials -// - -func TestNewDNSProviderCredentialsValid(t *testing.T) { - provider, err := NewDNSProviderCredentials("123", "") - - assert.NotNil(t, provider) - assert.Equal(t, "lego", provider.client.UserAgent) - assert.NoError(t, err) -} - -func TestNewDNSProviderCredentialsValidWithBaseUrl(t *testing.T) { - provider, err := NewDNSProviderCredentials("123", "https://api.dnsimple.test") - - assert.NotNil(t, provider) - assert.NoError(t, err) - - assert.Equal(t, provider.client.BaseURL, "https://api.dnsimple.test") -} - -func TestNewDNSProviderCredentialsInvalidWithMissingOauthToken(t *testing.T) { - provider, err := NewDNSProviderCredentials("", "") - - assert.Nil(t, provider) - assert.EqualError(t, err, "DNSimple OAuth token is missing") -} - -// -// Present -// - -func TestLiveDNSimplePresent(t *testing.T) { - if !dnsimpleLiveTest { - t.Skip("skipping live test") - } - - provider, err := NewDNSProviderCredentials(dnsimpleOauthToken, dnsimpleBaseUrl) - assert.NoError(t, err) - - err = provider.Present(dnsimpleDomain, "", "123d==") - assert.NoError(t, err) -} - -// -// Cleanup -// - -func TestLiveDNSimpleCleanUp(t *testing.T) { - if !dnsimpleLiveTest { - t.Skip("skipping live test") - } - - time.Sleep(time.Second * 1) - - provider, err := NewDNSProviderCredentials(dnsimpleOauthToken, dnsimpleBaseUrl) - assert.NoError(t, err) - - err = provider.CleanUp(dnsimpleDomain, "", "123d==") - assert.NoError(t, err) -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/dnsmadeeasy/dnsmadeeasy.go b/vendor/github.com/xenolf/lego/providers/dns/dnsmadeeasy/dnsmadeeasy.go deleted file mode 100644 index c4363a4eb..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/dnsmadeeasy/dnsmadeeasy.go +++ /dev/null @@ -1,248 +0,0 @@ -package dnsmadeeasy - -import ( - "bytes" - "crypto/hmac" - "crypto/sha1" - "crypto/tls" - "encoding/hex" - "encoding/json" - "fmt" - "net/http" - "os" - "strconv" - "strings" - "time" - - "github.com/xenolf/lego/acme" -) - -// DNSProvider is an implementation of the acme.ChallengeProvider interface that uses -// DNSMadeEasy's DNS API to manage TXT records for a domain. -type DNSProvider struct { - baseURL string - apiKey string - apiSecret string -} - -// Domain holds the DNSMadeEasy API representation of a Domain -type Domain struct { - ID int `json:"id"` - Name string `json:"name"` -} - -// Record holds the DNSMadeEasy API representation of a Domain Record -type Record struct { - ID int `json:"id"` - Type string `json:"type"` - Name string `json:"name"` - Value string `json:"value"` - TTL int `json:"ttl"` - SourceID int `json:"sourceId"` -} - -// NewDNSProvider returns a DNSProvider instance configured for DNSMadeEasy DNS. -// Credentials must be passed in the environment variables: DNSMADEEASY_API_KEY -// and DNSMADEEASY_API_SECRET. -func NewDNSProvider() (*DNSProvider, error) { - dnsmadeeasyAPIKey := os.Getenv("DNSMADEEASY_API_KEY") - dnsmadeeasyAPISecret := os.Getenv("DNSMADEEASY_API_SECRET") - dnsmadeeasySandbox := os.Getenv("DNSMADEEASY_SANDBOX") - - var baseURL string - - sandbox, _ := strconv.ParseBool(dnsmadeeasySandbox) - if sandbox { - baseURL = "https://api.sandbox.dnsmadeeasy.com/V2.0" - } else { - baseURL = "https://api.dnsmadeeasy.com/V2.0" - } - - return NewDNSProviderCredentials(baseURL, dnsmadeeasyAPIKey, dnsmadeeasyAPISecret) -} - -// NewDNSProviderCredentials uses the supplied credentials to return a -// DNSProvider instance configured for DNSMadeEasy. -func NewDNSProviderCredentials(baseURL, apiKey, apiSecret string) (*DNSProvider, error) { - if baseURL == "" || apiKey == "" || apiSecret == "" { - return nil, fmt.Errorf("DNS Made Easy credentials missing") - } - - return &DNSProvider{ - baseURL: baseURL, - apiKey: apiKey, - apiSecret: apiSecret, - }, nil -} - -// Present creates a TXT record using the specified parameters -func (d *DNSProvider) Present(domainName, token, keyAuth string) error { - fqdn, value, ttl := acme.DNS01Record(domainName, keyAuth) - - authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) - if err != nil { - return err - } - - // fetch the domain details - domain, err := d.getDomain(authZone) - if err != nil { - return err - } - - // create the TXT record - name := strings.Replace(fqdn, "."+authZone, "", 1) - record := &Record{Type: "TXT", Name: name, Value: value, TTL: ttl} - - err = d.createRecord(domain, record) - if err != nil { - return err - } - - return nil -} - -// CleanUp removes the TXT records matching the specified parameters -func (d *DNSProvider) CleanUp(domainName, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domainName, keyAuth) - - authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) - if err != nil { - return err - } - - // fetch the domain details - domain, err := d.getDomain(authZone) - if err != nil { - return err - } - - // find matching records - name := strings.Replace(fqdn, "."+authZone, "", 1) - records, err := d.getRecords(domain, name, "TXT") - if err != nil { - return err - } - - // delete records - for _, record := range *records { - err = d.deleteRecord(record) - if err != nil { - return err - } - } - - return nil -} - -func (d *DNSProvider) getDomain(authZone string) (*Domain, error) { - domainName := authZone[0 : len(authZone)-1] - resource := fmt.Sprintf("%s%s", "/dns/managed/name?domainname=", domainName) - - resp, err := d.sendRequest("GET", resource, nil) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - domain := &Domain{} - err = json.NewDecoder(resp.Body).Decode(&domain) - if err != nil { - return nil, err - } - - return domain, nil -} - -func (d *DNSProvider) getRecords(domain *Domain, recordName, recordType string) (*[]Record, error) { - resource := fmt.Sprintf("%s/%d/%s%s%s%s", "/dns/managed", domain.ID, "records?recordName=", recordName, "&type=", recordType) - - resp, err := d.sendRequest("GET", resource, nil) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - type recordsResponse struct { - Records *[]Record `json:"data"` - } - - records := &recordsResponse{} - err = json.NewDecoder(resp.Body).Decode(&records) - if err != nil { - return nil, err - } - - return records.Records, nil -} - -func (d *DNSProvider) createRecord(domain *Domain, record *Record) error { - url := fmt.Sprintf("%s/%d/%s", "/dns/managed", domain.ID, "records") - - resp, err := d.sendRequest("POST", url, record) - if err != nil { - return err - } - defer resp.Body.Close() - - return nil -} - -func (d *DNSProvider) deleteRecord(record Record) error { - resource := fmt.Sprintf("%s/%d/%s/%d", "/dns/managed", record.SourceID, "records", record.ID) - - resp, err := d.sendRequest("DELETE", resource, nil) - if err != nil { - return err - } - defer resp.Body.Close() - - return nil -} - -func (d *DNSProvider) sendRequest(method, resource string, payload interface{}) (*http.Response, error) { - url := fmt.Sprintf("%s%s", d.baseURL, resource) - - body, err := json.Marshal(payload) - if err != nil { - return nil, err - } - - timestamp := time.Now().UTC().Format(time.RFC1123) - signature := computeHMAC(timestamp, d.apiSecret) - - req, err := http.NewRequest(method, url, bytes.NewReader(body)) - if err != nil { - return nil, err - } - req.Header.Set("x-dnsme-apiKey", d.apiKey) - req.Header.Set("x-dnsme-requestDate", timestamp) - req.Header.Set("x-dnsme-hmac", signature) - req.Header.Set("accept", "application/json") - req.Header.Set("content-type", "application/json") - - transport := &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - } - client := &http.Client{ - Transport: transport, - Timeout: time.Duration(10 * time.Second), - } - resp, err := client.Do(req) - if err != nil { - return nil, err - } - - if resp.StatusCode > 299 { - return nil, fmt.Errorf("DNSMadeEasy API request failed with HTTP status code %d", resp.StatusCode) - } - - return resp, nil -} - -func computeHMAC(message string, secret string) string { - key := []byte(secret) - h := hmac.New(sha1.New, key) - h.Write([]byte(message)) - return hex.EncodeToString(h.Sum(nil)) -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/dnsmadeeasy/dnsmadeeasy_test.go b/vendor/github.com/xenolf/lego/providers/dns/dnsmadeeasy/dnsmadeeasy_test.go deleted file mode 100644 index e860ecb69..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/dnsmadeeasy/dnsmadeeasy_test.go +++ /dev/null @@ -1,37 +0,0 @@ -package dnsmadeeasy - -import ( - "os" - "testing" - - "github.com/stretchr/testify/assert" -) - -var ( - testLive bool - testAPIKey string - testAPISecret string - testDomain string -) - -func init() { - testAPIKey = os.Getenv("DNSMADEEASY_API_KEY") - testAPISecret = os.Getenv("DNSMADEEASY_API_SECRET") - testDomain = os.Getenv("DNSMADEEASY_DOMAIN") - os.Setenv("DNSMADEEASY_SANDBOX", "true") - testLive = len(testAPIKey) > 0 && len(testAPISecret) > 0 -} - -func TestPresentAndCleanup(t *testing.T) { - if !testLive { - t.Skip("skipping live test") - } - - provider, err := NewDNSProvider() - - err = provider.Present(testDomain, "", "123d==") - assert.NoError(t, err) - - err = provider.CleanUp(testDomain, "", "123d==") - assert.NoError(t, err) -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/dnspod/dnspod.go b/vendor/github.com/xenolf/lego/providers/dns/dnspod/dnspod.go deleted file mode 100644 index 0ce08a8bb..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/dnspod/dnspod.go +++ /dev/null @@ -1,146 +0,0 @@ -// Package dnspod implements a DNS provider for solving the DNS-01 challenge -// using dnspod DNS. -package dnspod - -import ( - "fmt" - "os" - "strings" - - "github.com/decker502/dnspod-go" - "github.com/xenolf/lego/acme" -) - -// DNSProvider is an implementation of the acme.ChallengeProvider interface. -type DNSProvider struct { - client *dnspod.Client -} - -// NewDNSProvider returns a DNSProvider instance configured for dnspod. -// Credentials must be passed in the environment variables: DNSPOD_API_KEY. -func NewDNSProvider() (*DNSProvider, error) { - key := os.Getenv("DNSPOD_API_KEY") - return NewDNSProviderCredentials(key) -} - -// NewDNSProviderCredentials uses the supplied credentials to return a -// DNSProvider instance configured for dnspod. -func NewDNSProviderCredentials(key string) (*DNSProvider, error) { - if key == "" { - return nil, fmt.Errorf("dnspod credentials missing") - } - - params := dnspod.CommonParams{LoginToken: key, Format: "json"} - return &DNSProvider{ - client: dnspod.NewClient(params), - }, nil -} - -// Present creates a TXT record to fulfil the dns-01 challenge. -func (c *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) - zoneID, zoneName, err := c.getHostedZone(domain) - if err != nil { - return err - } - - recordAttributes := c.newTxtRecord(zoneName, fqdn, value, ttl) - _, _, err = c.client.Domains.CreateRecord(zoneID, *recordAttributes) - if err != nil { - return fmt.Errorf("dnspod API call failed: %v", err) - } - - return nil -} - -// CleanUp removes the TXT record matching the specified parameters. -func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - - records, err := c.findTxtRecords(domain, fqdn) - if err != nil { - return err - } - - zoneID, _, err := c.getHostedZone(domain) - if err != nil { - return err - } - - for _, rec := range records { - _, err := c.client.Domains.DeleteRecord(zoneID, rec.ID) - if err != nil { - return err - } - } - return nil -} - -func (c *DNSProvider) getHostedZone(domain string) (string, string, error) { - zones, _, err := c.client.Domains.List() - if err != nil { - return "", "", fmt.Errorf("dnspod API call failed: %v", err) - } - - authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) - if err != nil { - return "", "", err - } - - var hostedZone dnspod.Domain - for _, zone := range zones { - if zone.Name == acme.UnFqdn(authZone) { - hostedZone = zone - } - } - - if hostedZone.ID == 0 { - return "", "", fmt.Errorf("Zone %s not found in dnspod for domain %s", authZone, domain) - - } - - return fmt.Sprintf("%v", hostedZone.ID), hostedZone.Name, nil -} - -func (c *DNSProvider) newTxtRecord(zone, fqdn, value string, ttl int) *dnspod.Record { - name := c.extractRecordName(fqdn, zone) - - return &dnspod.Record{ - Type: "TXT", - Name: name, - Value: value, - Line: "默认", - TTL: "600", - } -} - -func (c *DNSProvider) findTxtRecords(domain, fqdn string) ([]dnspod.Record, error) { - zoneID, zoneName, err := c.getHostedZone(domain) - if err != nil { - return nil, err - } - - var records []dnspod.Record - result, _, err := c.client.Domains.ListRecords(zoneID, "") - if err != nil { - return records, fmt.Errorf("dnspod API call has failed: %v", err) - } - - recordName := c.extractRecordName(fqdn, zoneName) - - for _, record := range result { - if record.Name == recordName { - records = append(records, record) - } - } - - return records, nil -} - -func (c *DNSProvider) extractRecordName(fqdn, domain string) string { - name := acme.UnFqdn(fqdn) - if idx := strings.Index(name, "."+domain); idx != -1 { - return name[:idx] - } - return name -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/dnspod/dnspod_test.go b/vendor/github.com/xenolf/lego/providers/dns/dnspod/dnspod_test.go deleted file mode 100644 index 3311eb0a6..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/dnspod/dnspod_test.go +++ /dev/null @@ -1,72 +0,0 @@ -package dnspod - -import ( - "github.com/stretchr/testify/assert" - "os" - "testing" - "time" -) - -var ( - dnspodLiveTest bool - dnspodAPIKey string - dnspodDomain string -) - -func init() { - dnspodAPIKey = os.Getenv("DNSPOD_API_KEY") - dnspodDomain = os.Getenv("DNSPOD_DOMAIN") - if len(dnspodAPIKey) > 0 && len(dnspodDomain) > 0 { - dnspodLiveTest = true - } -} - -func restorednspodEnv() { - os.Setenv("DNSPOD_API_KEY", dnspodAPIKey) -} - -func TestNewDNSProviderValid(t *testing.T) { - os.Setenv("DNSPOD_API_KEY", "") - _, err := NewDNSProviderCredentials("123") - assert.NoError(t, err) - restorednspodEnv() -} -func TestNewDNSProviderValidEnv(t *testing.T) { - os.Setenv("DNSPOD_API_KEY", "123") - _, err := NewDNSProvider() - assert.NoError(t, err) - restorednspodEnv() -} - -func TestNewDNSProviderMissingCredErr(t *testing.T) { - os.Setenv("DNSPOD_API_KEY", "") - _, err := NewDNSProvider() - assert.EqualError(t, err, "dnspod credentials missing") - restorednspodEnv() -} - -func TestLivednspodPresent(t *testing.T) { - if !dnspodLiveTest { - t.Skip("skipping live test") - } - - provider, err := NewDNSProviderCredentials(dnspodAPIKey) - assert.NoError(t, err) - - err = provider.Present(dnspodDomain, "", "123d==") - assert.NoError(t, err) -} - -func TestLivednspodCleanUp(t *testing.T) { - if !dnspodLiveTest { - t.Skip("skipping live test") - } - - time.Sleep(time.Second * 1) - - provider, err := NewDNSProviderCredentials(dnspodAPIKey) - assert.NoError(t, err) - - err = provider.CleanUp(dnspodDomain, "", "123d==") - assert.NoError(t, err) -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/dyn/dyn.go b/vendor/github.com/xenolf/lego/providers/dns/dyn/dyn.go deleted file mode 100644 index 384bc850c..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/dyn/dyn.go +++ /dev/null @@ -1,274 +0,0 @@ -// Package dyn implements a DNS provider for solving the DNS-01 challenge -// using Dyn Managed DNS. -package dyn - -import ( - "bytes" - "encoding/json" - "fmt" - "net/http" - "os" - "strconv" - "time" - - "github.com/xenolf/lego/acme" -) - -var dynBaseURL = "https://api.dynect.net/REST" - -type dynResponse struct { - // One of 'success', 'failure', or 'incomplete' - Status string `json:"status"` - - // The structure containing the actual results of the request - Data json.RawMessage `json:"data"` - - // The ID of the job that was created in response to a request. - JobID int `json:"job_id"` - - // A list of zero or more messages - Messages json.RawMessage `json:"msgs"` -} - -// DNSProvider is an implementation of the acme.ChallengeProvider interface that uses -// Dyn's Managed DNS API to manage TXT records for a domain. -type DNSProvider struct { - customerName string - userName string - password string - token string -} - -// NewDNSProvider returns a DNSProvider instance configured for Dyn DNS. -// Credentials must be passed in the environment variables: DYN_CUSTOMER_NAME, -// DYN_USER_NAME and DYN_PASSWORD. -func NewDNSProvider() (*DNSProvider, error) { - customerName := os.Getenv("DYN_CUSTOMER_NAME") - userName := os.Getenv("DYN_USER_NAME") - password := os.Getenv("DYN_PASSWORD") - return NewDNSProviderCredentials(customerName, userName, password) -} - -// NewDNSProviderCredentials uses the supplied credentials to return a -// DNSProvider instance configured for Dyn DNS. -func NewDNSProviderCredentials(customerName, userName, password string) (*DNSProvider, error) { - if customerName == "" || userName == "" || password == "" { - return nil, fmt.Errorf("DynDNS credentials missing") - } - - return &DNSProvider{ - customerName: customerName, - userName: userName, - password: password, - }, nil -} - -func (d *DNSProvider) sendRequest(method, resource string, payload interface{}) (*dynResponse, error) { - url := fmt.Sprintf("%s/%s", dynBaseURL, resource) - - body, err := json.Marshal(payload) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(method, url, bytes.NewReader(body)) - if err != nil { - return nil, err - } - req.Header.Set("Content-Type", "application/json") - if len(d.token) > 0 { - req.Header.Set("Auth-Token", d.token) - } - - client := &http.Client{Timeout: time.Duration(10 * time.Second)} - resp, err := client.Do(req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - if resp.StatusCode >= 400 { - return nil, fmt.Errorf("Dyn API request failed with HTTP status code %d", resp.StatusCode) - } else if resp.StatusCode == 307 { - // TODO add support for HTTP 307 response and long running jobs - return nil, fmt.Errorf("Dyn API request returned HTTP 307. This is currently unsupported") - } - - var dynRes dynResponse - err = json.NewDecoder(resp.Body).Decode(&dynRes) - if err != nil { - return nil, err - } - - if dynRes.Status == "failure" { - // TODO add better error handling - return nil, fmt.Errorf("Dyn API request failed: %s", dynRes.Messages) - } - - return &dynRes, nil -} - -// Starts a new Dyn API Session. Authenticates using customerName, userName, -// password and receives a token to be used in for subsequent requests. -func (d *DNSProvider) login() error { - type creds struct { - Customer string `json:"customer_name"` - User string `json:"user_name"` - Pass string `json:"password"` - } - - type session struct { - Token string `json:"token"` - Version string `json:"version"` - } - - payload := &creds{Customer: d.customerName, User: d.userName, Pass: d.password} - dynRes, err := d.sendRequest("POST", "Session", payload) - if err != nil { - return err - } - - var s session - err = json.Unmarshal(dynRes.Data, &s) - if err != nil { - return err - } - - d.token = s.Token - - return nil -} - -// Destroys Dyn Session -func (d *DNSProvider) logout() error { - if len(d.token) == 0 { - // nothing to do - return nil - } - - url := fmt.Sprintf("%s/Session", dynBaseURL) - req, err := http.NewRequest("DELETE", url, nil) - if err != nil { - return err - } - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Auth-Token", d.token) - - client := &http.Client{Timeout: time.Duration(10 * time.Second)} - resp, err := client.Do(req) - if err != nil { - return err - } - resp.Body.Close() - - if resp.StatusCode != 200 { - return fmt.Errorf("Dyn API request failed to delete session with HTTP status code %d", resp.StatusCode) - } - - d.token = "" - - return nil -} - -// Present creates a TXT record using the specified parameters -func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) - - authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) - if err != nil { - return err - } - - err = d.login() - if err != nil { - return err - } - - data := map[string]interface{}{ - "rdata": map[string]string{ - "txtdata": value, - }, - "ttl": strconv.Itoa(ttl), - } - - resource := fmt.Sprintf("TXTRecord/%s/%s/", authZone, fqdn) - _, err = d.sendRequest("POST", resource, data) - if err != nil { - return err - } - - err = d.publish(authZone, "Added TXT record for ACME dns-01 challenge using lego client") - if err != nil { - return err - } - - err = d.logout() - if err != nil { - return err - } - - return nil -} - -func (d *DNSProvider) publish(zone, notes string) error { - type publish struct { - Publish bool `json:"publish"` - Notes string `json:"notes"` - } - - pub := &publish{Publish: true, Notes: notes} - resource := fmt.Sprintf("Zone/%s/", zone) - _, err := d.sendRequest("PUT", resource, pub) - if err != nil { - return err - } - - return nil -} - -// CleanUp removes the TXT record matching the specified parameters -func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - - authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) - if err != nil { - return err - } - - err = d.login() - if err != nil { - return err - } - - resource := fmt.Sprintf("TXTRecord/%s/%s/", authZone, fqdn) - url := fmt.Sprintf("%s/%s", dynBaseURL, resource) - req, err := http.NewRequest("DELETE", url, nil) - if err != nil { - return err - } - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Auth-Token", d.token) - - client := &http.Client{Timeout: time.Duration(10 * time.Second)} - resp, err := client.Do(req) - if err != nil { - return err - } - resp.Body.Close() - - if resp.StatusCode != 200 { - return fmt.Errorf("Dyn API request failed to delete TXT record HTTP status code %d", resp.StatusCode) - } - - err = d.publish(authZone, "Removed TXT record for ACME dns-01 challenge using lego client") - if err != nil { - return err - } - - err = d.logout() - if err != nil { - return err - } - - return nil -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/dyn/dyn_test.go b/vendor/github.com/xenolf/lego/providers/dns/dyn/dyn_test.go deleted file mode 100644 index 0d28d5d0e..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/dyn/dyn_test.go +++ /dev/null @@ -1,53 +0,0 @@ -package dyn - -import ( - "os" - "testing" - "time" - - "github.com/stretchr/testify/assert" -) - -var ( - dynLiveTest bool - dynCustomerName string - dynUserName string - dynPassword string - dynDomain string -) - -func init() { - dynCustomerName = os.Getenv("DYN_CUSTOMER_NAME") - dynUserName = os.Getenv("DYN_USER_NAME") - dynPassword = os.Getenv("DYN_PASSWORD") - dynDomain = os.Getenv("DYN_DOMAIN") - if len(dynCustomerName) > 0 && len(dynUserName) > 0 && len(dynPassword) > 0 && len(dynDomain) > 0 { - dynLiveTest = true - } -} - -func TestLiveDynPresent(t *testing.T) { - if !dynLiveTest { - t.Skip("skipping live test") - } - - provider, err := NewDNSProvider() - assert.NoError(t, err) - - err = provider.Present(dynDomain, "", "123d==") - assert.NoError(t, err) -} - -func TestLiveDynCleanUp(t *testing.T) { - if !dynLiveTest { - t.Skip("skipping live test") - } - - time.Sleep(time.Second * 1) - - provider, err := NewDNSProvider() - assert.NoError(t, err) - - err = provider.CleanUp(dynDomain, "", "123d==") - assert.NoError(t, err) -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/exoscale/exoscale.go b/vendor/github.com/xenolf/lego/providers/dns/exoscale/exoscale.go deleted file mode 100644 index 4b125e8df..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/exoscale/exoscale.go +++ /dev/null @@ -1,128 +0,0 @@ -// Package exoscale implements a DNS provider for solving the DNS-01 challenge -// using exoscale DNS. -package exoscale - -import ( - "errors" - "fmt" - "os" - - "github.com/exoscale/egoscale" - "github.com/xenolf/lego/acme" -) - -// DNSProvider is an implementation of the acme.ChallengeProvider interface. -type DNSProvider struct { - client *egoscale.Client -} - -// Credentials must be passed in the environment variables: -// EXOSCALE_API_KEY, EXOSCALE_API_SECRET, EXOSCALE_ENDPOINT. -func NewDNSProvider() (*DNSProvider, error) { - key := os.Getenv("EXOSCALE_API_KEY") - secret := os.Getenv("EXOSCALE_API_SECRET") - endpoint := os.Getenv("EXOSCALE_ENDPOINT") - return NewDNSProviderClient(key, secret, endpoint) -} - -// Uses the supplied parameters to return a DNSProvider instance -// configured for Exoscale. -func NewDNSProviderClient(key, secret, endpoint string) (*DNSProvider, error) { - if key == "" || secret == "" { - return nil, fmt.Errorf("Exoscale credentials missing") - } - if endpoint == "" { - endpoint = "https://api.exoscale.ch/dns" - } - - return &DNSProvider{ - client: egoscale.NewClient(endpoint, key, secret), - }, nil -} - -// Present creates a TXT record to fulfil the dns-01 challenge. -func (c *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) - zone, recordName, err := c.FindZoneAndRecordName(fqdn, domain) - if err != nil { - return err - } - - recordID, err := c.FindExistingRecordId(zone, recordName) - if err != nil { - return err - } - - record := egoscale.DNSRecord{ - Name: recordName, - TTL: ttl, - Content: value, - RecordType: "TXT", - } - - if recordID == 0 { - _, err := c.client.CreateRecord(zone, record) - if err != nil { - return errors.New("Error while creating DNS record: " + err.Error()) - } - } else { - record.ID = recordID - _, err := c.client.UpdateRecord(zone, record) - if err != nil { - return errors.New("Error while updating DNS record: " + err.Error()) - } - } - - return nil -} - -// CleanUp removes the record matching the specified parameters. -func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - zone, recordName, err := c.FindZoneAndRecordName(fqdn, domain) - if err != nil { - return err - } - - recordID, err := c.FindExistingRecordId(zone, recordName) - if err != nil { - return err - } - - if recordID != 0 { - err = c.client.DeleteRecord(zone, recordID) - if err != nil { - return errors.New("Error while deleting DNS record: " + err.Error()) - } - } - - return nil -} - -// Query Exoscale to find an existing record for this name. -// Returns nil if no record could be found -func (c *DNSProvider) FindExistingRecordId(zone, recordName string) (int64, error) { - records, err := c.client.GetRecords(zone) - if err != nil { - return -1, errors.New("Error while retrievening DNS records: " + err.Error()) - } - for _, record := range records { - if record.Name == recordName { - return record.ID, nil - } - } - return 0, nil -} - -// Extract DNS zone and DNS entry name -func (c *DNSProvider) FindZoneAndRecordName(fqdn, domain string) (string, string, error) { - zone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) - if err != nil { - return "", "", err - } - zone = acme.UnFqdn(zone) - name := acme.UnFqdn(fqdn) - name = name[:len(name)-len("."+zone)] - - return zone, name, nil -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/exoscale/exoscale_test.go b/vendor/github.com/xenolf/lego/providers/dns/exoscale/exoscale_test.go deleted file mode 100644 index 343dd56f8..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/exoscale/exoscale_test.go +++ /dev/null @@ -1,103 +0,0 @@ -package exoscale - -import ( - "os" - "testing" - "time" - - "github.com/stretchr/testify/assert" -) - -var ( - exoscaleLiveTest bool - exoscaleAPIKey string - exoscaleAPISecret string - exoscaleDomain string -) - -func init() { - exoscaleAPISecret = os.Getenv("EXOSCALE_API_SECRET") - exoscaleAPIKey = os.Getenv("EXOSCALE_API_KEY") - exoscaleDomain = os.Getenv("EXOSCALE_DOMAIN") - if len(exoscaleAPIKey) > 0 && len(exoscaleAPISecret) > 0 && len(exoscaleDomain) > 0 { - exoscaleLiveTest = true - } -} - -func restoreExoscaleEnv() { - os.Setenv("EXOSCALE_API_KEY", exoscaleAPIKey) - os.Setenv("EXOSCALE_API_SECRET", exoscaleAPISecret) -} - -func TestNewDNSProviderValid(t *testing.T) { - os.Setenv("EXOSCALE_API_KEY", "") - os.Setenv("EXOSCALE_API_SECRET", "") - _, err := NewDNSProviderClient("example@example.com", "123", "") - assert.NoError(t, err) - restoreExoscaleEnv() -} -func TestNewDNSProviderValidEnv(t *testing.T) { - os.Setenv("EXOSCALE_API_KEY", "example@example.com") - os.Setenv("EXOSCALE_API_SECRET", "123") - _, err := NewDNSProvider() - assert.NoError(t, err) - restoreExoscaleEnv() -} - -func TestNewDNSProviderMissingCredErr(t *testing.T) { - os.Setenv("EXOSCALE_API_KEY", "") - os.Setenv("EXOSCALE_API_SECRET", "") - _, err := NewDNSProvider() - assert.EqualError(t, err, "Exoscale credentials missing") - restoreExoscaleEnv() -} - -func TestExtractRootRecordName(t *testing.T) { - provider, err := NewDNSProviderClient("example@example.com", "123", "") - assert.NoError(t, err) - - zone, recordName, err := provider.FindZoneAndRecordName("_acme-challenge.bar.com.", "bar.com") - assert.NoError(t, err) - assert.Equal(t, "bar.com", zone) - assert.Equal(t, "_acme-challenge", recordName) -} - -func TestExtractSubRecordName(t *testing.T) { - provider, err := NewDNSProviderClient("example@example.com", "123", "") - assert.NoError(t, err) - - zone, recordName, err := provider.FindZoneAndRecordName("_acme-challenge.foo.bar.com.", "foo.bar.com") - assert.NoError(t, err) - assert.Equal(t, "bar.com", zone) - assert.Equal(t, "_acme-challenge.foo", recordName) -} - -func TestLiveExoscalePresent(t *testing.T) { - if !exoscaleLiveTest { - t.Skip("skipping live test") - } - - provider, err := NewDNSProviderClient(exoscaleAPIKey, exoscaleAPISecret, "") - assert.NoError(t, err) - - err = provider.Present(exoscaleDomain, "", "123d==") - assert.NoError(t, err) - - // Present Twice to handle create / update - err = provider.Present(exoscaleDomain, "", "123d==") - assert.NoError(t, err) -} - -func TestLiveExoscaleCleanUp(t *testing.T) { - if !exoscaleLiveTest { - t.Skip("skipping live test") - } - - time.Sleep(time.Second * 1) - - provider, err := NewDNSProviderClient(exoscaleAPIKey, exoscaleAPISecret, "") - assert.NoError(t, err) - - err = provider.CleanUp(exoscaleDomain, "", "123d==") - assert.NoError(t, err) -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/gandi/gandi.go b/vendor/github.com/xenolf/lego/providers/dns/gandi/gandi.go deleted file mode 100644 index 422b02a21..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/gandi/gandi.go +++ /dev/null @@ -1,472 +0,0 @@ -// Package gandi implements a DNS provider for solving the DNS-01 -// challenge using Gandi DNS. -package gandi - -import ( - "bytes" - "encoding/xml" - "fmt" - "io" - "io/ioutil" - "net/http" - "os" - "strings" - "sync" - "time" - - "github.com/xenolf/lego/acme" -) - -// Gandi API reference: http://doc.rpc.gandi.net/index.html -// Gandi API domain examples: http://doc.rpc.gandi.net/domain/faq.html - -var ( - // endpoint is the Gandi XML-RPC endpoint used by Present and - // CleanUp. It is overridden during tests. - endpoint = "https://rpc.gandi.net/xmlrpc/" - // findZoneByFqdn determines the DNS zone of an fqdn. It is overridden - // during tests. - findZoneByFqdn = acme.FindZoneByFqdn -) - -// inProgressInfo contains information about an in-progress challenge -type inProgressInfo struct { - zoneID int // zoneID of gandi zone to restore in CleanUp - newZoneID int // zoneID of temporary gandi zone containing TXT record - authZone string // the domain name registered at gandi with trailing "." -} - -// DNSProvider is an implementation of the -// acme.ChallengeProviderTimeout interface that uses Gandi's XML-RPC -// API to manage TXT records for a domain. -type DNSProvider struct { - apiKey string - inProgressFQDNs map[string]inProgressInfo - inProgressAuthZones map[string]struct{} - inProgressMu sync.Mutex -} - -// NewDNSProvider returns a DNSProvider instance configured for Gandi. -// Credentials must be passed in the environment variable: GANDI_API_KEY. -func NewDNSProvider() (*DNSProvider, error) { - apiKey := os.Getenv("GANDI_API_KEY") - return NewDNSProviderCredentials(apiKey) -} - -// NewDNSProviderCredentials uses the supplied credentials to return a -// DNSProvider instance configured for Gandi. -func NewDNSProviderCredentials(apiKey string) (*DNSProvider, error) { - if apiKey == "" { - return nil, fmt.Errorf("No Gandi API Key given") - } - return &DNSProvider{ - apiKey: apiKey, - inProgressFQDNs: make(map[string]inProgressInfo), - inProgressAuthZones: make(map[string]struct{}), - }, nil -} - -// Present creates a TXT record using the specified parameters. It -// does this by creating and activating a new temporary Gandi DNS -// zone. This new zone contains the TXT record. -func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) - if ttl < 300 { - ttl = 300 // 300 is gandi minimum value for ttl - } - // find authZone and Gandi zone_id for fqdn - authZone, err := findZoneByFqdn(fqdn, acme.RecursiveNameservers) - if err != nil { - return fmt.Errorf("Gandi DNS: findZoneByFqdn failure: %v", err) - } - zoneID, err := d.getZoneID(authZone) - if err != nil { - return err - } - // determine name of TXT record - if !strings.HasSuffix( - strings.ToLower(fqdn), strings.ToLower("."+authZone)) { - return fmt.Errorf( - "Gandi DNS: unexpected authZone %s for fqdn %s", authZone, fqdn) - } - name := fqdn[:len(fqdn)-len("."+authZone)] - // acquire lock and check there is not a challenge already in - // progress for this value of authZone - d.inProgressMu.Lock() - defer d.inProgressMu.Unlock() - if _, ok := d.inProgressAuthZones[authZone]; ok { - return fmt.Errorf( - "Gandi DNS: challenge already in progress for authZone %s", - authZone) - } - // perform API actions to create and activate new gandi zone - // containing the required TXT record - newZoneName := fmt.Sprintf( - "%s [ACME Challenge %s]", - acme.UnFqdn(authZone), time.Now().Format(time.RFC822Z)) - newZoneID, err := d.cloneZone(zoneID, newZoneName) - if err != nil { - return err - } - newZoneVersion, err := d.newZoneVersion(newZoneID) - if err != nil { - return err - } - err = d.addTXTRecord(newZoneID, newZoneVersion, name, value, ttl) - if err != nil { - return err - } - err = d.setZoneVersion(newZoneID, newZoneVersion) - if err != nil { - return err - } - err = d.setZone(authZone, newZoneID) - if err != nil { - return err - } - // save data necessary for CleanUp - d.inProgressFQDNs[fqdn] = inProgressInfo{ - zoneID: zoneID, - newZoneID: newZoneID, - authZone: authZone, - } - d.inProgressAuthZones[authZone] = struct{}{} - return nil -} - -// CleanUp removes the TXT record matching the specified -// parameters. It does this by restoring the old Gandi DNS zone and -// removing the temporary one created by Present. -func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - // acquire lock and retrieve zoneID, newZoneID and authZone - d.inProgressMu.Lock() - defer d.inProgressMu.Unlock() - if _, ok := d.inProgressFQDNs[fqdn]; !ok { - // if there is no cleanup information then just return - return nil - } - zoneID := d.inProgressFQDNs[fqdn].zoneID - newZoneID := d.inProgressFQDNs[fqdn].newZoneID - authZone := d.inProgressFQDNs[fqdn].authZone - delete(d.inProgressFQDNs, fqdn) - delete(d.inProgressAuthZones, authZone) - // perform API actions to restore old gandi zone for authZone - err := d.setZone(authZone, zoneID) - if err != nil { - return err - } - err = d.deleteZone(newZoneID) - if err != nil { - return err - } - return nil -} - -// Timeout returns the values (40*time.Minute, 60*time.Second) which -// are used by the acme package as timeout and check interval values -// when checking for DNS record propagation with Gandi. -func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { - return 40 * time.Minute, 60 * time.Second -} - -// types for XML-RPC method calls and parameters - -type param interface { - param() -} -type paramString struct { - XMLName xml.Name `xml:"param"` - Value string `xml:"value>string"` -} -type paramInt struct { - XMLName xml.Name `xml:"param"` - Value int `xml:"value>int"` -} - -type structMember interface { - structMember() -} -type structMemberString struct { - Name string `xml:"name"` - Value string `xml:"value>string"` -} -type structMemberInt struct { - Name string `xml:"name"` - Value int `xml:"value>int"` -} -type paramStruct struct { - XMLName xml.Name `xml:"param"` - StructMembers []structMember `xml:"value>struct>member"` -} - -func (p paramString) param() {} -func (p paramInt) param() {} -func (m structMemberString) structMember() {} -func (m structMemberInt) structMember() {} -func (p paramStruct) param() {} - -type methodCall struct { - XMLName xml.Name `xml:"methodCall"` - MethodName string `xml:"methodName"` - Params []param `xml:"params"` -} - -// types for XML-RPC responses - -type response interface { - faultCode() int - faultString() string -} - -type responseFault struct { - FaultCode int `xml:"fault>value>struct>member>value>int"` - FaultString string `xml:"fault>value>struct>member>value>string"` -} - -func (r responseFault) faultCode() int { return r.FaultCode } -func (r responseFault) faultString() string { return r.FaultString } - -type responseStruct struct { - responseFault - StructMembers []struct { - Name string `xml:"name"` - ValueInt int `xml:"value>int"` - } `xml:"params>param>value>struct>member"` -} - -type responseInt struct { - responseFault - Value int `xml:"params>param>value>int"` -} - -type responseBool struct { - responseFault - Value bool `xml:"params>param>value>boolean"` -} - -// POSTing/Marshalling/Unmarshalling - -type rpcError struct { - faultCode int - faultString string -} - -func (e rpcError) Error() string { - return fmt.Sprintf( - "Gandi DNS: RPC Error: (%d) %s", e.faultCode, e.faultString) -} - -func httpPost(url string, bodyType string, body io.Reader) ([]byte, error) { - client := http.Client{Timeout: 60 * time.Second} - resp, err := client.Post(url, bodyType, body) - if err != nil { - return nil, fmt.Errorf("Gandi DNS: HTTP Post Error: %v", err) - } - defer resp.Body.Close() - b, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("Gandi DNS: HTTP Post Error: %v", err) - } - return b, nil -} - -// rpcCall makes an XML-RPC call to Gandi's RPC endpoint by -// marshalling the data given in the call argument to XML and sending -// that via HTTP Post to Gandi. The response is then unmarshalled into -// the resp argument. -func rpcCall(call *methodCall, resp response) error { - // marshal - b, err := xml.MarshalIndent(call, "", " ") - if err != nil { - return fmt.Errorf("Gandi DNS: Marshal Error: %v", err) - } - // post - b = append([]byte(``+"\n"), b...) - respBody, err := httpPost(endpoint, "text/xml", bytes.NewReader(b)) - if err != nil { - return err - } - // unmarshal - err = xml.Unmarshal(respBody, resp) - if err != nil { - return fmt.Errorf("Gandi DNS: Unmarshal Error: %v", err) - } - if resp.faultCode() != 0 { - return rpcError{ - faultCode: resp.faultCode(), faultString: resp.faultString()} - } - return nil -} - -// functions to perform API actions - -func (d *DNSProvider) getZoneID(domain string) (int, error) { - resp := &responseStruct{} - err := rpcCall(&methodCall{ - MethodName: "domain.info", - Params: []param{ - paramString{Value: d.apiKey}, - paramString{Value: domain}, - }, - }, resp) - if err != nil { - return 0, err - } - var zoneID int - for _, member := range resp.StructMembers { - if member.Name == "zone_id" { - zoneID = member.ValueInt - } - } - if zoneID == 0 { - return 0, fmt.Errorf( - "Gandi DNS: Could not determine zone_id for %s", domain) - } - return zoneID, nil -} - -func (d *DNSProvider) cloneZone(zoneID int, name string) (int, error) { - resp := &responseStruct{} - err := rpcCall(&methodCall{ - MethodName: "domain.zone.clone", - Params: []param{ - paramString{Value: d.apiKey}, - paramInt{Value: zoneID}, - paramInt{Value: 0}, - paramStruct{ - StructMembers: []structMember{ - structMemberString{ - Name: "name", - Value: name, - }}, - }, - }, - }, resp) - if err != nil { - return 0, err - } - var newZoneID int - for _, member := range resp.StructMembers { - if member.Name == "id" { - newZoneID = member.ValueInt - } - } - if newZoneID == 0 { - return 0, fmt.Errorf("Gandi DNS: Could not determine cloned zone_id") - } - return newZoneID, nil -} - -func (d *DNSProvider) newZoneVersion(zoneID int) (int, error) { - resp := &responseInt{} - err := rpcCall(&methodCall{ - MethodName: "domain.zone.version.new", - Params: []param{ - paramString{Value: d.apiKey}, - paramInt{Value: zoneID}, - }, - }, resp) - if err != nil { - return 0, err - } - if resp.Value == 0 { - return 0, fmt.Errorf("Gandi DNS: Could not create new zone version") - } - return resp.Value, nil -} - -func (d *DNSProvider) addTXTRecord(zoneID int, version int, name string, value string, ttl int) error { - resp := &responseStruct{} - err := rpcCall(&methodCall{ - MethodName: "domain.zone.record.add", - Params: []param{ - paramString{Value: d.apiKey}, - paramInt{Value: zoneID}, - paramInt{Value: version}, - paramStruct{ - StructMembers: []structMember{ - structMemberString{ - Name: "type", - Value: "TXT", - }, structMemberString{ - Name: "name", - Value: name, - }, structMemberString{ - Name: "value", - Value: value, - }, structMemberInt{ - Name: "ttl", - Value: ttl, - }}, - }, - }, - }, resp) - if err != nil { - return err - } - return nil -} - -func (d *DNSProvider) setZoneVersion(zoneID int, version int) error { - resp := &responseBool{} - err := rpcCall(&methodCall{ - MethodName: "domain.zone.version.set", - Params: []param{ - paramString{Value: d.apiKey}, - paramInt{Value: zoneID}, - paramInt{Value: version}, - }, - }, resp) - if err != nil { - return err - } - if !resp.Value { - return fmt.Errorf("Gandi DNS: could not set zone version") - } - return nil -} - -func (d *DNSProvider) setZone(domain string, zoneID int) error { - resp := &responseStruct{} - err := rpcCall(&methodCall{ - MethodName: "domain.zone.set", - Params: []param{ - paramString{Value: d.apiKey}, - paramString{Value: domain}, - paramInt{Value: zoneID}, - }, - }, resp) - if err != nil { - return err - } - var respZoneID int - for _, member := range resp.StructMembers { - if member.Name == "zone_id" { - respZoneID = member.ValueInt - } - } - if respZoneID != zoneID { - return fmt.Errorf( - "Gandi DNS: Could not set new zone_id for %s", domain) - } - return nil -} - -func (d *DNSProvider) deleteZone(zoneID int) error { - resp := &responseBool{} - err := rpcCall(&methodCall{ - MethodName: "domain.zone.delete", - Params: []param{ - paramString{Value: d.apiKey}, - paramInt{Value: zoneID}, - }, - }, resp) - if err != nil { - return err - } - if !resp.Value { - return fmt.Errorf("Gandi DNS: could not delete zone_id") - } - return nil -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/gandi/gandi_test.go b/vendor/github.com/xenolf/lego/providers/dns/gandi/gandi_test.go deleted file mode 100644 index 451333ca1..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/gandi/gandi_test.go +++ /dev/null @@ -1,939 +0,0 @@ -package gandi - -import ( - "crypto" - "crypto/rand" - "crypto/rsa" - "io" - "io/ioutil" - "net/http" - "net/http/httptest" - "os" - "regexp" - "strings" - "testing" - - "github.com/xenolf/lego/acme" -) - -// stagingServer is the Let's Encrypt staging server used by the live test -const stagingServer = "https://acme-staging.api.letsencrypt.org/directory" - -// user implements acme.User and is used by the live test -type user struct { - Email string - Registration *acme.RegistrationResource - key crypto.PrivateKey -} - -func (u *user) GetEmail() string { - return u.Email -} -func (u *user) GetRegistration() *acme.RegistrationResource { - return u.Registration -} -func (u *user) GetPrivateKey() crypto.PrivateKey { - return u.key -} - -// TestDNSProvider runs Present and CleanUp against a fake Gandi RPC -// Server, whose responses are predetermined for particular requests. -func TestDNSProvider(t *testing.T) { - fakeAPIKey := "123412341234123412341234" - fakeKeyAuth := "XXXX" - provider, err := NewDNSProviderCredentials(fakeAPIKey) - if err != nil { - t.Fatal(err) - } - regexpDate, err := regexp.Compile(`\[ACME Challenge [^\]:]*:[^\]]*\]`) - if err != nil { - t.Fatal(err) - } - // start fake RPC server - fakeServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.Header.Get("Content-Type") != "text/xml" { - t.Fatalf("Content-Type: text/xml header not found") - } - req, err := ioutil.ReadAll(r.Body) - if err != nil { - t.Fatal(err) - } - req = regexpDate.ReplaceAllLiteral( - req, []byte(`[ACME Challenge 01 Jan 16 00:00 +0000]`)) - resp, ok := serverResponses[string(req)] - if !ok { - t.Fatalf("Server response for request not found") - } - _, err = io.Copy(w, strings.NewReader(resp)) - if err != nil { - t.Fatal(err) - } - })) - defer fakeServer.Close() - // define function to override findZoneByFqdn with - fakeFindZoneByFqdn := func(fqdn string, nameserver []string) (string, error) { - return "example.com.", nil - } - // override gandi endpoint and findZoneByFqdn function - savedEndpoint, savedFindZoneByFqdn := endpoint, findZoneByFqdn - defer func() { - endpoint, findZoneByFqdn = savedEndpoint, savedFindZoneByFqdn - }() - endpoint, findZoneByFqdn = fakeServer.URL+"/", fakeFindZoneByFqdn - // run Present - err = provider.Present("abc.def.example.com", "", fakeKeyAuth) - if err != nil { - t.Fatal(err) - } - // run CleanUp - err = provider.CleanUp("abc.def.example.com", "", fakeKeyAuth) - if err != nil { - t.Fatal(err) - } -} - -// TestDNSProviderLive performs a live test to obtain a certificate -// using the Let's Encrypt staging server. It runs provided that both -// the environment variables GANDI_API_KEY and GANDI_TEST_DOMAIN are -// set. Otherwise the test is skipped. -// -// To complete this test, go test must be run with the -timeout=40m -// flag, since the default timeout of 10m is insufficient. -func TestDNSProviderLive(t *testing.T) { - apiKey := os.Getenv("GANDI_API_KEY") - domain := os.Getenv("GANDI_TEST_DOMAIN") - if apiKey == "" || domain == "" { - t.Skip("skipping live test") - } - // create a user. - const rsaKeySize = 2048 - privateKey, err := rsa.GenerateKey(rand.Reader, rsaKeySize) - if err != nil { - t.Fatal(err) - } - myUser := user{ - Email: "test@example.com", - key: privateKey, - } - // create a client using staging server - client, err := acme.NewClient(stagingServer, &myUser, acme.RSA2048) - if err != nil { - t.Fatal(err) - } - provider, err := NewDNSProviderCredentials(apiKey) - if err != nil { - t.Fatal(err) - } - err = client.SetChallengeProvider(acme.DNS01, provider) - if err != nil { - t.Fatal(err) - } - client.ExcludeChallenges([]acme.Challenge{acme.HTTP01, acme.TLSSNI01}) - // register and agree tos - reg, err := client.Register() - if err != nil { - t.Fatal(err) - } - myUser.Registration = reg - err = client.AgreeToTOS() - if err != nil { - t.Fatal(err) - } - // complete the challenge - bundle := false - _, failures := client.ObtainCertificate([]string{domain}, bundle, nil, false) - if len(failures) > 0 { - t.Fatal(failures) - } -} - -// serverResponses is the XML-RPC Request->Response map used by the -// fake RPC server. It was generated by recording a real RPC session -// which resulted in the successful issue of a cert, and then -// anonymizing the RPC data. -var serverResponses = map[string]string{ - // Present Request->Response 1 (getZoneID) - ` - - domain.info - - - 123412341234123412341234 - - - - - example.com. - - -`: ` - - - - - -date_updated -20160216T16:14:23 - - -date_delete -20170331T16:04:06 - - -is_premium -0 - - -date_hold_begin -20170215T02:04:06 - - -date_registry_end -20170215T02:04:06 - - -authinfo_expiration_date -20161211T21:31:20 - - -contacts - - -owner - - -handle -LEGO-GANDI - - -id -111111 - - - - -admin - - -handle -LEGO-GANDI - - -id -111111 - - - - -bill - - -handle -LEGO-GANDI - - -id -111111 - - - - -tech - - -handle -LEGO-GANDI - - -id -111111 - - - - -reseller - - - - -nameservers - -a.dns.gandi.net -b.dns.gandi.net -c.dns.gandi.net - - - -date_restore_end -20170501T02:04:06 - - -id -2222222 - - -authinfo -ABCDABCDAB - - -status - -clientTransferProhibited -serverTransferProhibited - - - -tags - - - - -date_hold_end -20170401T02:04:06 - - -services - -gandidns -gandimail - - - -date_pending_delete_end -20170506T02:04:06 - - -zone_id -1234567 - - -date_renew_begin -20120101T00:00:00 - - -fqdn -example.com - - -autorenew - - -date_registry_creation -20150215T02:04:06 - - -tld -org - - -date_created -20150215T03:04:06 - - - - - -`, - // Present Request->Response 2 (cloneZone) - ` - - domain.zone.clone - - - 123412341234123412341234 - - - - - 1234567 - - - - - 0 - - - - - - - name - - example.com [ACME Challenge 01 Jan 16 00:00 +0000] - - - - - -`: ` - - - - - -name -example.com [ACME Challenge 01 Jan 16 00:00 +0000] - - -versions - -1 - - - -date_updated -20160216T16:24:29 - - -id -7654321 - - -owner -LEGO-GANDI - - -version -1 - - -domains -0 - - -public -0 - - - - - -`, - // Present Request->Response 3 (newZoneVersion) - ` - - domain.zone.version.new - - - 123412341234123412341234 - - - - - 7654321 - - -`: ` - - - -2 - - - -`, - // Present Request->Response 4 (addTXTRecord) - ` - - domain.zone.record.add - - - 123412341234123412341234 - - - - - 7654321 - - - - - 2 - - - - - - - type - - TXT - - - - name - - _acme-challenge.abc.def - - - - value - - ezRpBPY8wH8djMLYjX2uCKPwiKDkFZ1SFMJ6ZXGlHrQ - - - - ttl - - 300 - - - - - -`: ` - - - - - -name -_acme-challenge.abc.def - - -type -TXT - - -id -333333333 - - -value -"ezRpBPY8wH8djMLYjX2uCKPwiKDkFZ1SFMJ6ZXGlHrQ" - - -ttl -300 - - - - - -`, - // Present Request->Response 5 (setZoneVersion) - ` - - domain.zone.version.set - - - 123412341234123412341234 - - - - - 7654321 - - - - - 2 - - -`: ` - - - -1 - - - -`, - // Present Request->Response 6 (setZone) - ` - - domain.zone.set - - - 123412341234123412341234 - - - - - example.com. - - - - - 7654321 - - -`: ` - - - - - -date_updated -20160216T16:14:23 - - -date_delete -20170331T16:04:06 - - -is_premium -0 - - -date_hold_begin -20170215T02:04:06 - - -date_registry_end -20170215T02:04:06 - - -authinfo_expiration_date -20161211T21:31:20 - - -contacts - - -owner - - -handle -LEGO-GANDI - - -id -111111 - - - - -admin - - -handle -LEGO-GANDI - - -id -111111 - - - - -bill - - -handle -LEGO-GANDI - - -id -111111 - - - - -tech - - -handle -LEGO-GANDI - - -id -111111 - - - - -reseller - - - - -nameservers - -a.dns.gandi.net -b.dns.gandi.net -c.dns.gandi.net - - - -date_restore_end -20170501T02:04:06 - - -id -2222222 - - -authinfo -ABCDABCDAB - - -status - -clientTransferProhibited -serverTransferProhibited - - - -tags - - - - -date_hold_end -20170401T02:04:06 - - -services - -gandidns -gandimail - - - -date_pending_delete_end -20170506T02:04:06 - - -zone_id -7654321 - - -date_renew_begin -20120101T00:00:00 - - -fqdn -example.com - - -autorenew - - -date_registry_creation -20150215T02:04:06 - - -tld -org - - -date_created -20150215T03:04:06 - - - - - -`, - // CleanUp Request->Response 1 (setZone) - ` - - domain.zone.set - - - 123412341234123412341234 - - - - - example.com. - - - - - 1234567 - - -`: ` - - - - - -date_updated -20160216T16:24:38 - - -date_delete -20170331T16:04:06 - - -is_premium -0 - - -date_hold_begin -20170215T02:04:06 - - -date_registry_end -20170215T02:04:06 - - -authinfo_expiration_date -20161211T21:31:20 - - -contacts - - -owner - - -handle -LEGO-GANDI - - -id -111111 - - - - -admin - - -handle -LEGO-GANDI - - -id -111111 - - - - -bill - - -handle -LEGO-GANDI - - -id -111111 - - - - -tech - - -handle -LEGO-GANDI - - -id -111111 - - - - -reseller - - - - -nameservers - -a.dns.gandi.net -b.dns.gandi.net -c.dns.gandi.net - - - -date_restore_end -20170501T02:04:06 - - -id -2222222 - - -authinfo -ABCDABCDAB - - -status - -clientTransferProhibited -serverTransferProhibited - - - -tags - - - - -date_hold_end -20170401T02:04:06 - - -services - -gandidns -gandimail - - - -date_pending_delete_end -20170506T02:04:06 - - -zone_id -1234567 - - -date_renew_begin -20120101T00:00:00 - - -fqdn -example.com - - -autorenew - - -date_registry_creation -20150215T02:04:06 - - -tld -org - - -date_created -20150215T03:04:06 - - - - - -`, - // CleanUp Request->Response 2 (deleteZone) - ` - - domain.zone.delete - - - 123412341234123412341234 - - - - - 7654321 - - -`: ` - - - -1 - - - -`, -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/godaddy/godaddy.go b/vendor/github.com/xenolf/lego/providers/dns/godaddy/godaddy.go deleted file mode 100644 index 4112f6628..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/godaddy/godaddy.go +++ /dev/null @@ -1,155 +0,0 @@ -// Package godaddy implements a DNS provider for solving the DNS-01 challenge using godaddy DNS. -package godaddy - -import ( - "fmt" - "io" - "net/http" - "os" - "time" - - "bytes" - "encoding/json" - "github.com/xenolf/lego/acme" - "io/ioutil" - "strings" -) - -// GoDaddyAPIURL represents the API endpoint to call. -const apiURL = "https://api.godaddy.com" - -// DNSProvider is an implementation of the acme.ChallengeProvider interface -type DNSProvider struct { - apiKey string - apiSecret string -} - -// NewDNSProvider returns a DNSProvider instance configured for godaddy. -// Credentials must be passed in the environment variables: GODADDY_API_KEY -// and GODADDY_API_SECRET. -func NewDNSProvider() (*DNSProvider, error) { - apikey := os.Getenv("GODADDY_API_KEY") - secret := os.Getenv("GODADDY_API_SECRET") - return NewDNSProviderCredentials(apikey, secret) -} - -// NewDNSProviderCredentials uses the supplied credentials to return a -// DNSProvider instance configured for godaddy. -func NewDNSProviderCredentials(apiKey, apiSecret string) (*DNSProvider, error) { - if apiKey == "" || apiSecret == "" { - return nil, fmt.Errorf("GoDaddy credentials missing") - } - - return &DNSProvider{apiKey, apiSecret}, nil -} - -// Timeout returns the timeout and interval to use when checking for DNS -// propagation. Adjusting here to cope with spikes in propagation times. -func (c *DNSProvider) Timeout() (timeout, interval time.Duration) { - return 120 * time.Second, 2 * time.Second -} - -func (c *DNSProvider) extractRecordName(fqdn, domain string) string { - name := acme.UnFqdn(fqdn) - if idx := strings.Index(name, "."+domain); idx != -1 { - return name[:idx] - } - return name -} - -// Present creates a TXT record to fulfil the dns-01 challenge -func (c *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) - domainZone, err := c.getZone(fqdn) - if err != nil { - return err - } - - if ttl < 600 { - ttl = 600 - } - - recordName := c.extractRecordName(fqdn, domainZone) - rec := []DNSRecord{ - { - Type: "TXT", - Name: recordName, - Data: value, - Ttl: ttl, - }, - } - - return c.updateRecords(rec, domainZone, recordName) -} - -func (c *DNSProvider) updateRecords(records []DNSRecord, domainZone string, recordName string) error { - body, err := json.Marshal(records) - if err != nil { - return err - } - - var resp *http.Response - resp, err = c.makeRequest("PUT", fmt.Sprintf("/v1/domains/%s/records/TXT/%s", domainZone, recordName), bytes.NewReader(body)) - if err != nil { - return err - } - - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - bodyBytes, _ := ioutil.ReadAll(resp.Body) - return fmt.Errorf("Could not create record %v; Status: %v; Body: %s\n", string(body), resp.StatusCode, string(bodyBytes)) - } - return nil -} - -// CleanUp sets null value in the TXT DNS record as GoDaddy has no proper DELETE record method -func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - domainZone, err := c.getZone(fqdn) - if err != nil { - return err - } - - recordName := c.extractRecordName(fqdn, domainZone) - rec := []DNSRecord{ - { - Type: "TXT", - Name: recordName, - Data: "null", - }, - } - - return c.updateRecords(rec, domainZone, recordName) -} - -func (c *DNSProvider) getZone(fqdn string) (string, error) { - authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) - if err != nil { - return "", err - } - - return acme.UnFqdn(authZone), nil -} - -func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (*http.Response, error) { - req, err := http.NewRequest(method, fmt.Sprintf("%s%s", apiURL, uri), body) - if err != nil { - return nil, err - } - - req.Header.Set("Accept", "application/json") - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", fmt.Sprintf("sso-key %s:%s", c.apiKey, c.apiSecret)) - - client := http.Client{Timeout: 30 * time.Second} - return client.Do(req) -} - -type DNSRecord struct { - Type string `json:"type"` - Name string `json:"name"` - Data string `json:"data"` - Priority int `json:"priority,omitempty"` - Ttl int `json:"ttl,omitempty"` -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/godaddy/godaddy_test.go b/vendor/github.com/xenolf/lego/providers/dns/godaddy/godaddy_test.go deleted file mode 100644 index de84d827e..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/godaddy/godaddy_test.go +++ /dev/null @@ -1,60 +0,0 @@ -package godaddy - -import ( - "os" - "testing" - - "github.com/stretchr/testify/assert" -) - -var ( - godaddyAPIKey string - godaddyAPISecret string - godaddyDomain string - godaddyLiveTest bool -) - -func init() { - godaddyAPIKey = os.Getenv("GODADDY_API_KEY") - godaddyAPISecret = os.Getenv("GODADDY_API_SECRET") - godaddyDomain = os.Getenv("GODADDY_DOMAIN") - - if len(godaddyAPIKey) > 0 && len(godaddyAPISecret) > 0 && len(godaddyDomain) > 0 { - godaddyLiveTest = true - } -} - -func TestNewDNSProvider(t *testing.T) { - provider, err := NewDNSProvider() - - if !godaddyLiveTest { - assert.Error(t, err) - } else { - assert.NotNil(t, provider) - assert.NoError(t, err) - } -} - -func TestDNSProvider_Present(t *testing.T) { - if !godaddyLiveTest { - t.Skip("skipping live test") - } - - provider, err := NewDNSProvider() - assert.NoError(t, err) - - err = provider.Present(godaddyDomain, "", "123d==") - assert.NoError(t, err) -} - -func TestDNSProvider_CleanUp(t *testing.T) { - if !godaddyLiveTest { - t.Skip("skipping live test") - } - - provider, err := NewDNSProvider() - assert.NoError(t, err) - - err = provider.CleanUp(godaddyDomain, "", "123d==") - assert.NoError(t, err) -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/googlecloud/googlecloud.go b/vendor/github.com/xenolf/lego/providers/dns/googlecloud/googlecloud.go deleted file mode 100644 index ba753f6dc..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/googlecloud/googlecloud.go +++ /dev/null @@ -1,205 +0,0 @@ -// Package googlecloud implements a DNS provider for solving the DNS-01 -// challenge using Google Cloud DNS. -package googlecloud - -import ( - "fmt" - "io/ioutil" - "os" - "time" - - "github.com/xenolf/lego/acme" - - "golang.org/x/net/context" - "golang.org/x/oauth2" - "golang.org/x/oauth2/google" - - "google.golang.org/api/dns/v1" -) - -// DNSProvider is an implementation of the DNSProvider interface. -type DNSProvider struct { - project string - client *dns.Service -} - -// NewDNSProvider returns a DNSProvider instance configured for Google Cloud -// DNS. Project name must be passed in the environment variable: GCE_PROJECT. -// A Service Account file can be passed in the environment variable: -// GCE_SERVICE_ACCOUNT_FILE -func NewDNSProvider() (*DNSProvider, error) { - project := os.Getenv("GCE_PROJECT") - if saFile, ok := os.LookupEnv("GCE_SERVICE_ACCOUNT_FILE"); ok { - return NewDNSProviderServiceAccount(project, saFile) - } - return NewDNSProviderCredentials(project) -} - -// NewDNSProviderCredentials uses the supplied credentials to return a -// DNSProvider instance configured for Google Cloud DNS. -func NewDNSProviderCredentials(project string) (*DNSProvider, error) { - if project == "" { - return nil, fmt.Errorf("Google Cloud project name missing") - } - - client, err := google.DefaultClient(context.Background(), dns.NdevClouddnsReadwriteScope) - if err != nil { - return nil, fmt.Errorf("Unable to get Google Cloud client: %v", err) - } - svc, err := dns.New(client) - if err != nil { - return nil, fmt.Errorf("Unable to create Google Cloud DNS service: %v", err) - } - return &DNSProvider{ - project: project, - client: svc, - }, nil -} - -// NewDNSProviderServiceAccount uses the supplied service account JSON file to -// return a DNSProvider instance configured for Google Cloud DNS. -func NewDNSProviderServiceAccount(project string, saFile string) (*DNSProvider, error) { - if project == "" { - return nil, fmt.Errorf("Google Cloud project name missing") - } - if saFile == "" { - return nil, fmt.Errorf("Google Cloud Service Account file missing") - } - - dat, err := ioutil.ReadFile(saFile) - if err != nil { - return nil, fmt.Errorf("Unable to read Service Account file: %v", err) - } - conf, err := google.JWTConfigFromJSON(dat, dns.NdevClouddnsReadwriteScope) - if err != nil { - return nil, fmt.Errorf("Unable to acquire config: %v", err) - } - client := conf.Client(oauth2.NoContext) - - svc, err := dns.New(client) - if err != nil { - return nil, fmt.Errorf("Unable to create Google Cloud DNS service: %v", err) - } - return &DNSProvider{ - project: project, - client: svc, - }, nil -} - -// Present creates a TXT record to fulfil the dns-01 challenge. -func (c *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) - - zone, err := c.getHostedZone(domain) - if err != nil { - return err - } - - rec := &dns.ResourceRecordSet{ - Name: fqdn, - Rrdatas: []string{value}, - Ttl: int64(ttl), - Type: "TXT", - } - change := &dns.Change{ - Additions: []*dns.ResourceRecordSet{rec}, - } - - // Look for existing records. - list, err := c.client.ResourceRecordSets.List(c.project, zone).Name(fqdn).Type("TXT").Do() - if err != nil { - return err - } - if len(list.Rrsets) > 0 { - // Attempt to delete the existing records when adding our new one. - change.Deletions = list.Rrsets - } - - chg, err := c.client.Changes.Create(c.project, zone, change).Do() - if err != nil { - return err - } - - // wait for change to be acknowledged - for chg.Status == "pending" { - time.Sleep(time.Second) - - chg, err = c.client.Changes.Get(c.project, zone, chg.Id).Do() - if err != nil { - return err - } - } - - return nil -} - -// CleanUp removes the TXT record matching the specified parameters. -func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - - zone, err := c.getHostedZone(domain) - if err != nil { - return err - } - - records, err := c.findTxtRecords(zone, fqdn) - if err != nil { - return err - } - - for _, rec := range records { - change := &dns.Change{ - Deletions: []*dns.ResourceRecordSet{rec}, - } - _, err = c.client.Changes.Create(c.project, zone, change).Do() - if err != nil { - return err - } - } - return nil -} - -// Timeout customizes the timeout values used by the ACME package for checking -// DNS record validity. -func (c *DNSProvider) Timeout() (timeout, interval time.Duration) { - return 180 * time.Second, 5 * time.Second -} - -// getHostedZone returns the managed-zone -func (c *DNSProvider) getHostedZone(domain string) (string, error) { - authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) - if err != nil { - return "", err - } - - zones, err := c.client.ManagedZones. - List(c.project). - DnsName(authZone). - Do() - if err != nil { - return "", fmt.Errorf("GoogleCloud API call failed: %v", err) - } - - if len(zones.ManagedZones) == 0 { - return "", fmt.Errorf("No matching GoogleCloud domain found for domain %s", authZone) - } - - return zones.ManagedZones[0].Name, nil -} - -func (c *DNSProvider) findTxtRecords(zone, fqdn string) ([]*dns.ResourceRecordSet, error) { - - recs, err := c.client.ResourceRecordSets.List(c.project, zone).Do() - if err != nil { - return nil, err - } - - found := []*dns.ResourceRecordSet{} - for _, r := range recs.Rrsets { - if r.Type == "TXT" && r.Name == fqdn { - found = append(found, r) - } - } - - return found, nil -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/googlecloud/googlecloud_test.go b/vendor/github.com/xenolf/lego/providers/dns/googlecloud/googlecloud_test.go deleted file mode 100644 index 75a10d9a4..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/googlecloud/googlecloud_test.go +++ /dev/null @@ -1,99 +0,0 @@ -package googlecloud - -import ( - "os" - "testing" - "time" - - "golang.org/x/net/context" - "golang.org/x/oauth2/google" - "google.golang.org/api/dns/v1" - - "github.com/stretchr/testify/assert" -) - -var ( - gcloudLiveTest bool - gcloudProject string - gcloudDomain string -) - -func init() { - gcloudProject = os.Getenv("GCE_PROJECT") - gcloudDomain = os.Getenv("GCE_DOMAIN") - _, err := google.DefaultClient(context.Background(), dns.NdevClouddnsReadwriteScope) - if err == nil && len(gcloudProject) > 0 && len(gcloudDomain) > 0 { - gcloudLiveTest = true - } -} - -func restoreGCloudEnv() { - os.Setenv("GCE_PROJECT", gcloudProject) -} - -func TestNewDNSProviderValid(t *testing.T) { - if !gcloudLiveTest { - t.Skip("skipping live test (requires credentials)") - } - os.Setenv("GCE_PROJECT", "") - _, err := NewDNSProviderCredentials("my-project") - assert.NoError(t, err) - restoreGCloudEnv() -} - -func TestNewDNSProviderValidEnv(t *testing.T) { - if !gcloudLiveTest { - t.Skip("skipping live test (requires credentials)") - } - os.Setenv("GCE_PROJECT", "my-project") - _, err := NewDNSProvider() - assert.NoError(t, err) - restoreGCloudEnv() -} - -func TestNewDNSProviderMissingCredErr(t *testing.T) { - os.Setenv("GCE_PROJECT", "") - _, err := NewDNSProvider() - assert.EqualError(t, err, "Google Cloud project name missing") - restoreGCloudEnv() -} - -func TestLiveGoogleCloudPresent(t *testing.T) { - if !gcloudLiveTest { - t.Skip("skipping live test") - } - - provider, err := NewDNSProviderCredentials(gcloudProject) - assert.NoError(t, err) - - err = provider.Present(gcloudDomain, "", "123d==") - assert.NoError(t, err) -} - -func TestLiveGoogleCloudPresentMultiple(t *testing.T) { - if !gcloudLiveTest { - t.Skip("skipping live test") - } - - provider, err := NewDNSProviderCredentials(gcloudProject) - assert.NoError(t, err) - - // Check that we're able to create multiple entries - err = provider.Present(gcloudDomain, "1", "123d==") - err = provider.Present(gcloudDomain, "2", "123d==") - assert.NoError(t, err) -} - -func TestLiveGoogleCloudCleanUp(t *testing.T) { - if !gcloudLiveTest { - t.Skip("skipping live test") - } - - time.Sleep(time.Second * 1) - - provider, err := NewDNSProviderCredentials(gcloudProject) - assert.NoError(t, err) - - err = provider.CleanUp(gcloudDomain, "", "123d==") - assert.NoError(t, err) -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/linode/linode.go b/vendor/github.com/xenolf/lego/providers/dns/linode/linode.go deleted file mode 100644 index a91d2b489..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/linode/linode.go +++ /dev/null @@ -1,131 +0,0 @@ -// Package linode implements a DNS provider for solving the DNS-01 challenge -// using Linode DNS. -package linode - -import ( - "errors" - "os" - "strings" - "time" - - "github.com/timewasted/linode/dns" - "github.com/xenolf/lego/acme" -) - -const ( - dnsMinTTLSecs = 300 - dnsUpdateFreqMins = 15 - dnsUpdateFudgeSecs = 120 -) - -type hostedZoneInfo struct { - domainId int - resourceName string -} - -// DNSProvider implements the acme.ChallengeProvider interface. -type DNSProvider struct { - linode *dns.DNS -} - -// NewDNSProvider returns a DNSProvider instance configured for Linode. -// Credentials must be passed in the environment variable: LINODE_API_KEY. -func NewDNSProvider() (*DNSProvider, error) { - apiKey := os.Getenv("LINODE_API_KEY") - return NewDNSProviderCredentials(apiKey) -} - -// NewDNSProviderCredentials uses the supplied credentials to return a -// DNSProvider instance configured for Linode. -func NewDNSProviderCredentials(apiKey string) (*DNSProvider, error) { - if len(apiKey) == 0 { - return nil, errors.New("Linode credentials missing") - } - - return &DNSProvider{ - linode: dns.New(apiKey), - }, nil -} - -// Timeout returns the timeout and interval to use when checking for DNS -// propagation. Adjusting here to cope with spikes in propagation times. -func (p *DNSProvider) Timeout() (timeout, interval time.Duration) { - // Since Linode only updates their zone files every X minutes, we need - // to figure out how many minutes we have to wait until we hit the next - // interval of X. We then wait another couple of minutes, just to be - // safe. Hopefully at some point during all of this, the record will - // have propagated throughout Linode's network. - minsRemaining := dnsUpdateFreqMins - (time.Now().Minute() % dnsUpdateFreqMins) - - timeout = (time.Duration(minsRemaining) * time.Minute) + - (dnsMinTTLSecs * time.Second) + - (dnsUpdateFudgeSecs * time.Second) - interval = 15 * time.Second - return -} - -// Present creates a TXT record using the specified parameters. -func (p *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) - zone, err := p.getHostedZoneInfo(fqdn) - if err != nil { - return err - } - - if _, err = p.linode.CreateDomainResourceTXT(zone.domainId, acme.UnFqdn(fqdn), value, 60); err != nil { - return err - } - - return nil -} - -// CleanUp removes the TXT record matching the specified parameters. -func (p *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) - zone, err := p.getHostedZoneInfo(fqdn) - if err != nil { - return err - } - - // Get all TXT records for the specified domain. - resources, err := p.linode.GetResourcesByType(zone.domainId, "TXT") - if err != nil { - return err - } - - // Remove the specified resource, if it exists. - for _, resource := range resources { - if resource.Name == zone.resourceName && resource.Target == value { - resp, err := p.linode.DeleteDomainResource(resource.DomainID, resource.ResourceID) - if err != nil { - return err - } - if resp.ResourceID != resource.ResourceID { - return errors.New("Error deleting resource: resource IDs do not match!") - } - break - } - } - - return nil -} - -func (p *DNSProvider) getHostedZoneInfo(fqdn string) (*hostedZoneInfo, error) { - // Lookup the zone that handles the specified FQDN. - authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) - if err != nil { - return nil, err - } - resourceName := strings.TrimSuffix(fqdn, "."+authZone) - - // Query the authority zone. - domain, err := p.linode.GetDomain(acme.UnFqdn(authZone)) - if err != nil { - return nil, err - } - - return &hostedZoneInfo{ - domainId: domain.DomainID, - resourceName: resourceName, - }, nil -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/linode/linode_test.go b/vendor/github.com/xenolf/lego/providers/dns/linode/linode_test.go deleted file mode 100644 index d9713a275..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/linode/linode_test.go +++ /dev/null @@ -1,317 +0,0 @@ -package linode - -import ( - "encoding/json" - "fmt" - "net/http" - "net/http/httptest" - "os" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/timewasted/linode" - "github.com/timewasted/linode/dns" -) - -type ( - LinodeResponse struct { - Action string `json:"ACTION"` - Data interface{} `json:"DATA"` - Errors []linode.ResponseError `json:"ERRORARRAY"` - } - MockResponse struct { - Response interface{} - Errors []linode.ResponseError - } - MockResponseMap map[string]MockResponse -) - -var ( - apiKey string - isTestLive bool -) - -func init() { - apiKey = os.Getenv("LINODE_API_KEY") - isTestLive = len(apiKey) != 0 -} - -func restoreEnv() { - os.Setenv("LINODE_API_KEY", apiKey) -} - -func newMockServer(t *testing.T, responses MockResponseMap) *httptest.Server { - srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // Ensure that we support the requested action. - action := r.URL.Query().Get("api_action") - resp, ok := responses[action] - if !ok { - msg := fmt.Sprintf("Unsupported mock action: %s", action) - require.FailNow(t, msg) - } - - // Build the response that the server will return. - linodeResponse := LinodeResponse{ - Action: action, - Data: resp.Response, - Errors: resp.Errors, - } - rawResponse, err := json.Marshal(linodeResponse) - if err != nil { - msg := fmt.Sprintf("Failed to JSON encode response: %v", err) - require.FailNow(t, msg) - } - - // Send the response. - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - w.Write(rawResponse) - })) - - time.Sleep(100 * time.Millisecond) - return srv -} - -func TestNewDNSProviderWithEnv(t *testing.T) { - os.Setenv("LINODE_API_KEY", "testing") - defer restoreEnv() - _, err := NewDNSProvider() - assert.NoError(t, err) -} - -func TestNewDNSProviderWithoutEnv(t *testing.T) { - os.Setenv("LINODE_API_KEY", "") - defer restoreEnv() - _, err := NewDNSProvider() - assert.EqualError(t, err, "Linode credentials missing") -} - -func TestNewDNSProviderCredentialsWithKey(t *testing.T) { - _, err := NewDNSProviderCredentials("testing") - assert.NoError(t, err) -} - -func TestNewDNSProviderCredentialsWithoutKey(t *testing.T) { - _, err := NewDNSProviderCredentials("") - assert.EqualError(t, err, "Linode credentials missing") -} - -func TestDNSProvider_Present(t *testing.T) { - os.Setenv("LINODE_API_KEY", "testing") - defer restoreEnv() - p, err := NewDNSProvider() - assert.NoError(t, err) - - domain := "example.com" - keyAuth := "dGVzdGluZw==" - mockResponses := MockResponseMap{ - "domain.list": MockResponse{ - Response: []dns.Domain{ - dns.Domain{ - Domain: domain, - DomainID: 1234, - }, - }, - }, - "domain.resource.create": MockResponse{ - Response: dns.ResourceResponse{ - ResourceID: 1234, - }, - }, - } - mockSrv := newMockServer(t, mockResponses) - defer mockSrv.Close() - p.linode.ToLinode().SetEndpoint(mockSrv.URL) - - err = p.Present(domain, "", keyAuth) - assert.NoError(t, err) -} - -func TestDNSProvider_PresentNoDomain(t *testing.T) { - os.Setenv("LINODE_API_KEY", "testing") - defer restoreEnv() - p, err := NewDNSProvider() - assert.NoError(t, err) - - domain := "example.com" - keyAuth := "dGVzdGluZw==" - mockResponses := MockResponseMap{ - "domain.list": MockResponse{ - Response: []dns.Domain{ - dns.Domain{ - Domain: "foobar.com", - DomainID: 1234, - }, - }, - }, - } - mockSrv := newMockServer(t, mockResponses) - defer mockSrv.Close() - p.linode.ToLinode().SetEndpoint(mockSrv.URL) - - err = p.Present(domain, "", keyAuth) - assert.EqualError(t, err, "dns: requested domain not found") -} - -func TestDNSProvider_PresentCreateFailed(t *testing.T) { - os.Setenv("LINODE_API_KEY", "testing") - defer restoreEnv() - p, err := NewDNSProvider() - assert.NoError(t, err) - - domain := "example.com" - keyAuth := "dGVzdGluZw==" - mockResponses := MockResponseMap{ - "domain.list": MockResponse{ - Response: []dns.Domain{ - dns.Domain{ - Domain: domain, - DomainID: 1234, - }, - }, - }, - "domain.resource.create": MockResponse{ - Response: nil, - Errors: []linode.ResponseError{ - linode.ResponseError{ - Code: 1234, - Message: "Failed to create domain resource", - }, - }, - }, - } - mockSrv := newMockServer(t, mockResponses) - defer mockSrv.Close() - p.linode.ToLinode().SetEndpoint(mockSrv.URL) - - err = p.Present(domain, "", keyAuth) - assert.EqualError(t, err, "Failed to create domain resource") -} - -func TestDNSProvider_PresentLive(t *testing.T) { - if !isTestLive { - t.Skip("Skipping live test") - } -} - -func TestDNSProvider_CleanUp(t *testing.T) { - os.Setenv("LINODE_API_KEY", "testing") - defer restoreEnv() - p, err := NewDNSProvider() - assert.NoError(t, err) - - domain := "example.com" - keyAuth := "dGVzdGluZw==" - mockResponses := MockResponseMap{ - "domain.list": MockResponse{ - Response: []dns.Domain{ - dns.Domain{ - Domain: domain, - DomainID: 1234, - }, - }, - }, - "domain.resource.list": MockResponse{ - Response: []dns.Resource{ - dns.Resource{ - DomainID: 1234, - Name: "_acme-challenge", - ResourceID: 1234, - Target: "ElbOJKOkFWiZLQeoxf-wb3IpOsQCdvoM0y_wn0TEkxM", - Type: "TXT", - }, - }, - }, - "domain.resource.delete": MockResponse{ - Response: dns.ResourceResponse{ - ResourceID: 1234, - }, - }, - } - mockSrv := newMockServer(t, mockResponses) - defer mockSrv.Close() - p.linode.ToLinode().SetEndpoint(mockSrv.URL) - - err = p.CleanUp(domain, "", keyAuth) - assert.NoError(t, err) -} - -func TestDNSProvider_CleanUpNoDomain(t *testing.T) { - os.Setenv("LINODE_API_KEY", "testing") - defer restoreEnv() - p, err := NewDNSProvider() - assert.NoError(t, err) - - domain := "example.com" - keyAuth := "dGVzdGluZw==" - mockResponses := MockResponseMap{ - "domain.list": MockResponse{ - Response: []dns.Domain{ - dns.Domain{ - Domain: "foobar.com", - DomainID: 1234, - }, - }, - }, - } - mockSrv := newMockServer(t, mockResponses) - defer mockSrv.Close() - p.linode.ToLinode().SetEndpoint(mockSrv.URL) - - err = p.CleanUp(domain, "", keyAuth) - assert.EqualError(t, err, "dns: requested domain not found") -} - -func TestDNSProvider_CleanUpDeleteFailed(t *testing.T) { - os.Setenv("LINODE_API_KEY", "testing") - defer restoreEnv() - p, err := NewDNSProvider() - assert.NoError(t, err) - - domain := "example.com" - keyAuth := "dGVzdGluZw==" - mockResponses := MockResponseMap{ - "domain.list": MockResponse{ - Response: []dns.Domain{ - dns.Domain{ - Domain: domain, - DomainID: 1234, - }, - }, - }, - "domain.resource.list": MockResponse{ - Response: []dns.Resource{ - dns.Resource{ - DomainID: 1234, - Name: "_acme-challenge", - ResourceID: 1234, - Target: "ElbOJKOkFWiZLQeoxf-wb3IpOsQCdvoM0y_wn0TEkxM", - Type: "TXT", - }, - }, - }, - "domain.resource.delete": MockResponse{ - Response: nil, - Errors: []linode.ResponseError{ - linode.ResponseError{ - Code: 1234, - Message: "Failed to delete domain resource", - }, - }, - }, - } - mockSrv := newMockServer(t, mockResponses) - defer mockSrv.Close() - p.linode.ToLinode().SetEndpoint(mockSrv.URL) - - err = p.CleanUp(domain, "", keyAuth) - assert.EqualError(t, err, "Failed to delete domain resource") -} - -func TestDNSProvider_CleanUpLive(t *testing.T) { - if !isTestLive { - t.Skip("Skipping live test") - } -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/namecheap/namecheap.go b/vendor/github.com/xenolf/lego/providers/dns/namecheap/namecheap.go deleted file mode 100644 index d7eb40935..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/namecheap/namecheap.go +++ /dev/null @@ -1,416 +0,0 @@ -// Package namecheap implements a DNS provider for solving the DNS-01 -// challenge using namecheap DNS. -package namecheap - -import ( - "encoding/xml" - "fmt" - "io/ioutil" - "net/http" - "net/url" - "os" - "strings" - "time" - - "github.com/xenolf/lego/acme" -) - -// Notes about namecheap's tool API: -// 1. Using the API requires registration. Once registered, use your account -// name and API key to access the API. -// 2. There is no API to add or modify a single DNS record. Instead you must -// read the entire list of records, make modifications, and then write the -// entire updated list of records. (Yuck.) -// 3. Namecheap's DNS updates can be slow to propagate. I've seen them take -// as long as an hour. -// 4. Namecheap requires you to whitelist the IP address from which you call -// its APIs. It also requires all API calls to include the whitelisted IP -// address as a form or query string value. This code uses a namecheap -// service to query the client's IP address. - -var ( - debug = false - defaultBaseURL = "https://api.namecheap.com/xml.response" - getIPURL = "https://dynamicdns.park-your-domain.com/getip" - httpClient = http.Client{Timeout: 60 * time.Second} -) - -// DNSProvider is an implementation of the ChallengeProviderTimeout interface -// that uses Namecheap's tool API to manage TXT records for a domain. -type DNSProvider struct { - baseURL string - apiUser string - apiKey string - clientIP string -} - -// NewDNSProvider returns a DNSProvider instance configured for namecheap. -// Credentials must be passed in the environment variables: NAMECHEAP_API_USER -// and NAMECHEAP_API_KEY. -func NewDNSProvider() (*DNSProvider, error) { - apiUser := os.Getenv("NAMECHEAP_API_USER") - apiKey := os.Getenv("NAMECHEAP_API_KEY") - return NewDNSProviderCredentials(apiUser, apiKey) -} - -// NewDNSProviderCredentials uses the supplied credentials to return a -// DNSProvider instance configured for namecheap. -func NewDNSProviderCredentials(apiUser, apiKey string) (*DNSProvider, error) { - if apiUser == "" || apiKey == "" { - return nil, fmt.Errorf("Namecheap credentials missing") - } - - clientIP, err := getClientIP() - if err != nil { - return nil, err - } - - return &DNSProvider{ - baseURL: defaultBaseURL, - apiUser: apiUser, - apiKey: apiKey, - clientIP: clientIP, - }, nil -} - -// Timeout returns the timeout and interval to use when checking for DNS -// propagation. Namecheap can sometimes take a long time to complete an -// update, so wait up to 60 minutes for the update to propagate. -func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { - return 60 * time.Minute, 15 * time.Second -} - -// host describes a DNS record returned by the Namecheap DNS gethosts API. -// Namecheap uses the term "host" to refer to all DNS records that include -// a host field (A, AAAA, CNAME, NS, TXT, URL). -type host struct { - Type string `xml:",attr"` - Name string `xml:",attr"` - Address string `xml:",attr"` - MXPref string `xml:",attr"` - TTL string `xml:",attr"` -} - -// apierror describes an error record in a namecheap API response. -type apierror struct { - Number int `xml:",attr"` - Description string `xml:",innerxml"` -} - -// getClientIP returns the client's public IP address. It uses namecheap's -// IP discovery service to perform the lookup. -func getClientIP() (addr string, err error) { - resp, err := httpClient.Get(getIPURL) - if err != nil { - return "", err - } - defer resp.Body.Close() - - clientIP, err := ioutil.ReadAll(resp.Body) - if err != nil { - return "", err - } - - if debug { - fmt.Println("Client IP:", string(clientIP)) - } - return string(clientIP), nil -} - -// A challenge repesents all the data needed to specify a dns-01 challenge -// to lets-encrypt. -type challenge struct { - domain string - key string - keyFqdn string - keyValue string - tld string - sld string - host string -} - -// newChallenge builds a challenge record from a domain name, a challenge -// authentication key, and a map of available TLDs. -func newChallenge(domain, keyAuth string, tlds map[string]string) (*challenge, error) { - domain = acme.UnFqdn(domain) - parts := strings.Split(domain, ".") - - // Find the longest matching TLD. - longest := -1 - for i := len(parts); i > 0; i-- { - t := strings.Join(parts[i-1:], ".") - if _, found := tlds[t]; found { - longest = i - 1 - } - } - if longest < 1 { - return nil, fmt.Errorf("Invalid domain name '%s'", domain) - } - - tld := strings.Join(parts[longest:], ".") - sld := parts[longest-1] - - var host string - if longest >= 1 { - host = strings.Join(parts[:longest-1], ".") - } - - key, keyValue, _ := acme.DNS01Record(domain, keyAuth) - - return &challenge{ - domain: domain, - key: "_acme-challenge." + host, - keyFqdn: key, - keyValue: keyValue, - tld: tld, - sld: sld, - host: host, - }, nil -} - -// setGlobalParams adds the namecheap global parameters to the provided url -// Values record. -func (d *DNSProvider) setGlobalParams(v *url.Values, cmd string) { - v.Set("ApiUser", d.apiUser) - v.Set("ApiKey", d.apiKey) - v.Set("UserName", d.apiUser) - v.Set("ClientIp", d.clientIP) - v.Set("Command", cmd) -} - -// getTLDs requests the list of available TLDs from namecheap. -func (d *DNSProvider) getTLDs() (tlds map[string]string, err error) { - values := make(url.Values) - d.setGlobalParams(&values, "namecheap.domains.getTldList") - - reqURL, _ := url.Parse(d.baseURL) - reqURL.RawQuery = values.Encode() - - resp, err := httpClient.Get(reqURL.String()) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - if resp.StatusCode >= 400 { - return nil, fmt.Errorf("getHosts HTTP error %d", resp.StatusCode) - } - - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, err - } - - type GetTldsResponse struct { - XMLName xml.Name `xml:"ApiResponse"` - Errors []apierror `xml:"Errors>Error"` - Result []struct { - Name string `xml:",attr"` - } `xml:"CommandResponse>Tlds>Tld"` - } - - var gtr GetTldsResponse - if err := xml.Unmarshal(body, >r); err != nil { - return nil, err - } - if len(gtr.Errors) > 0 { - return nil, fmt.Errorf("Namecheap error: %s [%d]", - gtr.Errors[0].Description, gtr.Errors[0].Number) - } - - tlds = make(map[string]string) - for _, t := range gtr.Result { - tlds[t.Name] = t.Name - } - return tlds, nil -} - -// getHosts reads the full list of DNS host records using the Namecheap API. -func (d *DNSProvider) getHosts(ch *challenge) (hosts []host, err error) { - values := make(url.Values) - d.setGlobalParams(&values, "namecheap.domains.dns.getHosts") - values.Set("SLD", ch.sld) - values.Set("TLD", ch.tld) - - reqURL, _ := url.Parse(d.baseURL) - reqURL.RawQuery = values.Encode() - - resp, err := httpClient.Get(reqURL.String()) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - if resp.StatusCode >= 400 { - return nil, fmt.Errorf("getHosts HTTP error %d", resp.StatusCode) - } - - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, err - } - - type GetHostsResponse struct { - XMLName xml.Name `xml:"ApiResponse"` - Status string `xml:"Status,attr"` - Errors []apierror `xml:"Errors>Error"` - Hosts []host `xml:"CommandResponse>DomainDNSGetHostsResult>host"` - } - - var ghr GetHostsResponse - if err = xml.Unmarshal(body, &ghr); err != nil { - return nil, err - } - if len(ghr.Errors) > 0 { - return nil, fmt.Errorf("Namecheap error: %s [%d]", - ghr.Errors[0].Description, ghr.Errors[0].Number) - } - - return ghr.Hosts, nil -} - -// setHosts writes the full list of DNS host records using the Namecheap API. -func (d *DNSProvider) setHosts(ch *challenge, hosts []host) error { - values := make(url.Values) - d.setGlobalParams(&values, "namecheap.domains.dns.setHosts") - values.Set("SLD", ch.sld) - values.Set("TLD", ch.tld) - - for i, h := range hosts { - ind := fmt.Sprintf("%d", i+1) - values.Add("HostName"+ind, h.Name) - values.Add("RecordType"+ind, h.Type) - values.Add("Address"+ind, h.Address) - values.Add("MXPref"+ind, h.MXPref) - values.Add("TTL"+ind, h.TTL) - } - - resp, err := httpClient.PostForm(d.baseURL, values) - if err != nil { - return err - } - defer resp.Body.Close() - - if resp.StatusCode >= 400 { - return fmt.Errorf("setHosts HTTP error %d", resp.StatusCode) - } - - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return err - } - - type SetHostsResponse struct { - XMLName xml.Name `xml:"ApiResponse"` - Status string `xml:"Status,attr"` - Errors []apierror `xml:"Errors>Error"` - Result struct { - IsSuccess string `xml:",attr"` - } `xml:"CommandResponse>DomainDNSSetHostsResult"` - } - - var shr SetHostsResponse - if err := xml.Unmarshal(body, &shr); err != nil { - return err - } - if len(shr.Errors) > 0 { - return fmt.Errorf("Namecheap error: %s [%d]", - shr.Errors[0].Description, shr.Errors[0].Number) - } - if shr.Result.IsSuccess != "true" { - return fmt.Errorf("Namecheap setHosts failed.") - } - - return nil -} - -// addChallengeRecord adds a DNS challenge TXT record to a list of namecheap -// host records. -func (d *DNSProvider) addChallengeRecord(ch *challenge, hosts *[]host) { - host := host{ - Name: ch.key, - Type: "TXT", - Address: ch.keyValue, - MXPref: "10", - TTL: "120", - } - - // If there's already a TXT record with the same name, replace it. - for i, h := range *hosts { - if h.Name == ch.key && h.Type == "TXT" { - (*hosts)[i] = host - return - } - } - - // No record was replaced, so add a new one. - *hosts = append(*hosts, host) -} - -// removeChallengeRecord removes a DNS challenge TXT record from a list of -// namecheap host records. Return true if a record was removed. -func (d *DNSProvider) removeChallengeRecord(ch *challenge, hosts *[]host) bool { - // Find the challenge TXT record and remove it if found. - for i, h := range *hosts { - if h.Name == ch.key && h.Type == "TXT" { - *hosts = append((*hosts)[:i], (*hosts)[i+1:]...) - return true - } - } - - return false -} - -// Present installs a TXT record for the DNS challenge. -func (d *DNSProvider) Present(domain, token, keyAuth string) error { - tlds, err := d.getTLDs() - if err != nil { - return err - } - - ch, err := newChallenge(domain, keyAuth, tlds) - if err != nil { - return err - } - - hosts, err := d.getHosts(ch) - if err != nil { - return err - } - - d.addChallengeRecord(ch, &hosts) - - if debug { - for _, h := range hosts { - fmt.Printf( - "%-5.5s %-30.30s %-6s %-70.70s\n", - h.Type, h.Name, h.TTL, h.Address) - } - } - - return d.setHosts(ch, hosts) -} - -// CleanUp removes a TXT record used for a previous DNS challenge. -func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - tlds, err := d.getTLDs() - if err != nil { - return err - } - - ch, err := newChallenge(domain, keyAuth, tlds) - if err != nil { - return err - } - - hosts, err := d.getHosts(ch) - if err != nil { - return err - } - - if removed := d.removeChallengeRecord(ch, &hosts); !removed { - return nil - } - - return d.setHosts(ch, hosts) -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/namecheap/namecheap_test.go b/vendor/github.com/xenolf/lego/providers/dns/namecheap/namecheap_test.go deleted file mode 100644 index 0631d4a3e..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/namecheap/namecheap_test.go +++ /dev/null @@ -1,402 +0,0 @@ -package namecheap - -import ( - "fmt" - "net/http" - "net/http/httptest" - "net/url" - "testing" -) - -var ( - fakeUser = "foo" - fakeKey = "bar" - fakeClientIP = "10.0.0.1" - - tlds = map[string]string{ - "com.au": "com.au", - "com": "com", - "co.uk": "co.uk", - "uk": "uk", - "edu": "edu", - "co.com": "co.com", - "za.com": "za.com", - } -) - -func assertEq(t *testing.T, variable, got, want string) { - if got != want { - t.Errorf("Expected %s to be '%s' but got '%s'", variable, want, got) - } -} - -func assertHdr(tc *testcase, t *testing.T, values *url.Values) { - ch, _ := newChallenge(tc.domain, "", tlds) - - assertEq(t, "ApiUser", values.Get("ApiUser"), fakeUser) - assertEq(t, "ApiKey", values.Get("ApiKey"), fakeKey) - assertEq(t, "UserName", values.Get("UserName"), fakeUser) - assertEq(t, "ClientIp", values.Get("ClientIp"), fakeClientIP) - assertEq(t, "SLD", values.Get("SLD"), ch.sld) - assertEq(t, "TLD", values.Get("TLD"), ch.tld) -} - -func mockServer(tc *testcase, t *testing.T, w http.ResponseWriter, r *http.Request) { - switch r.Method { - - case "GET": - values := r.URL.Query() - cmd := values.Get("Command") - switch cmd { - case "namecheap.domains.dns.getHosts": - assertHdr(tc, t, &values) - w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, tc.getHostsResponse) - case "namecheap.domains.getTldList": - w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, responseGetTlds) - default: - t.Errorf("Unexpected GET command: %s", cmd) - } - - case "POST": - r.ParseForm() - values := r.Form - cmd := values.Get("Command") - switch cmd { - case "namecheap.domains.dns.setHosts": - assertHdr(tc, t, &values) - w.WriteHeader(http.StatusOK) - fmt.Fprint(w, tc.setHostsResponse) - default: - t.Errorf("Unexpected POST command: %s", cmd) - } - - default: - t.Errorf("Unexpected http method: %s", r.Method) - - } -} - -func testGetHosts(tc *testcase, t *testing.T) { - mock := httptest.NewServer(http.HandlerFunc( - func(w http.ResponseWriter, r *http.Request) { - mockServer(tc, t, w, r) - })) - defer mock.Close() - - prov := &DNSProvider{ - baseURL: mock.URL, - apiUser: fakeUser, - apiKey: fakeKey, - clientIP: fakeClientIP, - } - - ch, _ := newChallenge(tc.domain, "", tlds) - hosts, err := prov.getHosts(ch) - if tc.errString != "" { - if err == nil || err.Error() != tc.errString { - t.Errorf("Namecheap getHosts case %s expected error", tc.name) - } - } else { - if err != nil { - t.Errorf("Namecheap getHosts case %s failed\n%v", tc.name, err) - } - } - -next1: - for _, h := range hosts { - for _, th := range tc.hosts { - if h == th { - continue next1 - } - } - t.Errorf("getHosts case %s unexpected record [%s:%s:%s]", - tc.name, h.Type, h.Name, h.Address) - } - -next2: - for _, th := range tc.hosts { - for _, h := range hosts { - if h == th { - continue next2 - } - } - t.Errorf("getHosts case %s missing record [%s:%s:%s]", - tc.name, th.Type, th.Name, th.Address) - } -} - -func mockDNSProvider(url string) *DNSProvider { - return &DNSProvider{ - baseURL: url, - apiUser: fakeUser, - apiKey: fakeKey, - clientIP: fakeClientIP, - } -} - -func testSetHosts(tc *testcase, t *testing.T) { - mock := httptest.NewServer(http.HandlerFunc( - func(w http.ResponseWriter, r *http.Request) { - mockServer(tc, t, w, r) - })) - defer mock.Close() - - prov := mockDNSProvider(mock.URL) - ch, _ := newChallenge(tc.domain, "", tlds) - hosts, err := prov.getHosts(ch) - if tc.errString != "" { - if err == nil || err.Error() != tc.errString { - t.Errorf("Namecheap getHosts case %s expected error", tc.name) - } - } else { - if err != nil { - t.Errorf("Namecheap getHosts case %s failed\n%v", tc.name, err) - } - } - if err != nil { - return - } - - err = prov.setHosts(ch, hosts) - if err != nil { - t.Errorf("Namecheap setHosts case %s failed", tc.name) - } -} - -func testPresent(tc *testcase, t *testing.T) { - mock := httptest.NewServer(http.HandlerFunc( - func(w http.ResponseWriter, r *http.Request) { - mockServer(tc, t, w, r) - })) - defer mock.Close() - - prov := mockDNSProvider(mock.URL) - err := prov.Present(tc.domain, "", "dummyKey") - if tc.errString != "" { - if err == nil || err.Error() != tc.errString { - t.Errorf("Namecheap Present case %s expected error", tc.name) - } - } else { - if err != nil { - t.Errorf("Namecheap Present case %s failed\n%v", tc.name, err) - } - } -} - -func testCleanUp(tc *testcase, t *testing.T) { - mock := httptest.NewServer(http.HandlerFunc( - func(w http.ResponseWriter, r *http.Request) { - mockServer(tc, t, w, r) - })) - defer mock.Close() - - prov := mockDNSProvider(mock.URL) - err := prov.CleanUp(tc.domain, "", "dummyKey") - if tc.errString != "" { - if err == nil || err.Error() != tc.errString { - t.Errorf("Namecheap CleanUp case %s expected error", tc.name) - } - } else { - if err != nil { - t.Errorf("Namecheap CleanUp case %s failed\n%v", tc.name, err) - } - } -} - -func TestNamecheap(t *testing.T) { - for _, tc := range testcases { - testGetHosts(&tc, t) - testSetHosts(&tc, t) - testPresent(&tc, t) - testCleanUp(&tc, t) - } -} - -func TestNamecheapDomainSplit(t *testing.T) { - tests := []struct { - domain string - valid bool - tld string - sld string - host string - }{ - {"a.b.c.test.co.uk", true, "co.uk", "test", "a.b.c"}, - {"test.co.uk", true, "co.uk", "test", ""}, - {"test.com", true, "com", "test", ""}, - {"test.co.com", true, "co.com", "test", ""}, - {"www.test.com.au", true, "com.au", "test", "www"}, - {"www.za.com", true, "za.com", "www", ""}, - {"", false, "", "", ""}, - {"a", false, "", "", ""}, - {"com", false, "", "", ""}, - {"co.com", false, "", "", ""}, - {"co.uk", false, "", "", ""}, - {"test.au", false, "", "", ""}, - {"za.com", false, "", "", ""}, - {"www.za", false, "", "", ""}, - {"www.test.au", false, "", "", ""}, - {"www.test.unk", false, "", "", ""}, - } - - for _, test := range tests { - valid := true - ch, err := newChallenge(test.domain, "", tlds) - if err != nil { - valid = false - } - - if test.valid && !valid { - t.Errorf("Expected '%s' to split", test.domain) - } else if !test.valid && valid { - t.Errorf("Expected '%s' to produce error", test.domain) - } - - if test.valid && valid { - assertEq(t, "domain", ch.domain, test.domain) - assertEq(t, "tld", ch.tld, test.tld) - assertEq(t, "sld", ch.sld, test.sld) - assertEq(t, "host", ch.host, test.host) - } - } -} - -type testcase struct { - name string - domain string - hosts []host - errString string - getHostsResponse string - setHostsResponse string -} - -var testcases = []testcase{ - { - "Test:Success:1", - "test.example.com", - []host{ - {"A", "home", "10.0.0.1", "10", "1799"}, - {"A", "www", "10.0.0.2", "10", "1200"}, - {"AAAA", "a", "::0", "10", "1799"}, - {"CNAME", "*", "example.com.", "10", "1799"}, - {"MXE", "example.com", "10.0.0.5", "10", "1800"}, - {"URL", "xyz", "https://google.com", "10", "1799"}, - }, - "", - responseGetHostsSuccess1, - responseSetHostsSuccess1, - }, - { - "Test:Success:2", - "example.com", - []host{ - {"A", "@", "10.0.0.2", "10", "1200"}, - {"A", "www", "10.0.0.3", "10", "60"}, - }, - "", - responseGetHostsSuccess2, - responseSetHostsSuccess2, - }, - { - "Test:Error:BadApiKey:1", - "test.example.com", - nil, - "Namecheap error: API Key is invalid or API access has not been enabled [1011102]", - responseGetHostsErrorBadAPIKey1, - "", - }, -} - -var responseGetHostsSuccess1 = ` - - - - namecheap.domains.dns.getHosts - - - - - - - - - - - PHX01SBAPI01 - --5:00 - 3.338 -` - -var responseSetHostsSuccess1 = ` - - - - namecheap.domains.dns.setHosts - - - - - - PHX01SBAPI01 - --5:00 - 2.347 -` - -var responseGetHostsSuccess2 = ` - - - - namecheap.domains.dns.getHosts - - - - - - - PHX01SBAPI01 - --5:00 - 3.338 -` - -var responseSetHostsSuccess2 = ` - - - - namecheap.domains.dns.setHosts - - - - - - PHX01SBAPI01 - --5:00 - 2.347 -` - -var responseGetHostsErrorBadAPIKey1 = ` - - - API Key is invalid or API access has not been enabled - - - - PHX01SBAPI01 - --5:00 - 0 -` - -var responseGetTlds = ` - - - - namecheap.domains.getTldList - - - Most recognized top level domain - - - PHX01SBAPI01 - --5:00 - 0.004 -` diff --git a/vendor/github.com/xenolf/lego/providers/dns/ns1/ns1.go b/vendor/github.com/xenolf/lego/providers/dns/ns1/ns1.go deleted file mode 100644 index 105d73f89..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/ns1/ns1.go +++ /dev/null @@ -1,97 +0,0 @@ -// Package ns1 implements a DNS provider for solving the DNS-01 challenge -// using NS1 DNS. -package ns1 - -import ( - "fmt" - "net/http" - "os" - "time" - - "github.com/xenolf/lego/acme" - "gopkg.in/ns1/ns1-go.v2/rest" - "gopkg.in/ns1/ns1-go.v2/rest/model/dns" -) - -// DNSProvider is an implementation of the acme.ChallengeProvider interface. -type DNSProvider struct { - client *rest.Client -} - -// NewDNSProvider returns a DNSProvider instance configured for NS1. -// Credentials must be passed in the environment variables: NS1_API_KEY. -func NewDNSProvider() (*DNSProvider, error) { - key := os.Getenv("NS1_API_KEY") - if key == "" { - return nil, fmt.Errorf("NS1 credentials missing") - } - return NewDNSProviderCredentials(key) -} - -// NewDNSProviderCredentials uses the supplied credentials to return a -// DNSProvider instance configured for NS1. -func NewDNSProviderCredentials(key string) (*DNSProvider, error) { - if key == "" { - return nil, fmt.Errorf("NS1 credentials missing") - } - - httpClient := &http.Client{Timeout: time.Second * 10} - client := rest.NewClient(httpClient, rest.SetAPIKey(key)) - - return &DNSProvider{client}, nil -} - -// Present creates a TXT record to fulfil the dns-01 challenge. -func (c *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) - - zone, err := c.getHostedZone(domain) - if err != nil { - return err - } - - record := c.newTxtRecord(zone, fqdn, value, ttl) - _, err = c.client.Records.Create(record) - if err != nil && err != rest.ErrRecordExists { - return err - } - - return nil -} - -// CleanUp removes the TXT record matching the specified parameters. -func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - - zone, err := c.getHostedZone(domain) - if err != nil { - return err - } - - name := acme.UnFqdn(fqdn) - _, err = c.client.Records.Delete(zone.Zone, name, "TXT") - return err -} - -func (c *DNSProvider) getHostedZone(domain string) (*dns.Zone, error) { - zone, _, err := c.client.Zones.Get(domain) - if err != nil { - return nil, err - } - - return zone, nil -} - -func (c *DNSProvider) newTxtRecord(zone *dns.Zone, fqdn, value string, ttl int) *dns.Record { - name := acme.UnFqdn(fqdn) - - return &dns.Record{ - Type: "TXT", - Zone: zone.Zone, - Domain: name, - TTL: ttl, - Answers: []*dns.Answer{ - {Rdata: []string{value}}, - }, - } -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/ns1/ns1_test.go b/vendor/github.com/xenolf/lego/providers/dns/ns1/ns1_test.go deleted file mode 100644 index eb9150dde..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/ns1/ns1_test.go +++ /dev/null @@ -1,67 +0,0 @@ -package ns1 - -import ( - "os" - "testing" - "time" - - "github.com/stretchr/testify/assert" -) - -var ( - liveTest bool - apiKey string - domain string -) - -func init() { - apiKey = os.Getenv("NS1_API_KEY") - domain = os.Getenv("NS1_DOMAIN") - if len(apiKey) > 0 && len(domain) > 0 { - liveTest = true - } -} - -func restoreNS1Env() { - os.Setenv("NS1_API_KEY", apiKey) -} - -func TestNewDNSProviderValid(t *testing.T) { - os.Setenv("NS1_API_KEY", "") - _, err := NewDNSProviderCredentials("123") - assert.NoError(t, err) - restoreNS1Env() -} - -func TestNewDNSProviderMissingCredErr(t *testing.T) { - os.Setenv("NS1_API_KEY", "") - _, err := NewDNSProvider() - assert.EqualError(t, err, "NS1 credentials missing") - restoreNS1Env() -} - -func TestLivePresent(t *testing.T) { - if !liveTest { - t.Skip("skipping live test") - } - - provider, err := NewDNSProviderCredentials(apiKey) - assert.NoError(t, err) - - err = provider.Present(domain, "", "123d==") - assert.NoError(t, err) -} - -func TestLiveCleanUp(t *testing.T) { - if !liveTest { - t.Skip("skipping live test") - } - - time.Sleep(time.Second * 1) - - provider, err := NewDNSProviderCredentials(apiKey) - assert.NoError(t, err) - - err = provider.CleanUp(domain, "", "123d==") - assert.NoError(t, err) -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/otc/mock.go b/vendor/github.com/xenolf/lego/providers/dns/otc/mock.go deleted file mode 100644 index 0f2acb4b4..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/otc/mock.go +++ /dev/null @@ -1,152 +0,0 @@ -package otc - -import ( - "fmt" - "github.com/stretchr/testify/assert" - "io/ioutil" - "net/http" - "net/http/httptest" - "testing" -) - -var fakeOTCUserName = "test" -var fakeOTCPassword = "test" -var fakeOTCDomainName = "test" -var fakeOTCProjectName = "test" -var fakeOTCToken = "62244bc21da68d03ebac94e6636ff01f" - -type DNSMock struct { - t *testing.T - Server *httptest.Server - Mux *http.ServeMux -} - -func NewDNSMock(t *testing.T) *DNSMock { - return &DNSMock{ - t: t, - } -} - -// Setup creates the mock server -func (m *DNSMock) Setup() { - m.Mux = http.NewServeMux() - m.Server = httptest.NewServer(m.Mux) -} - -// ShutdownServer creates the mock server -func (m *DNSMock) ShutdownServer() { - m.Server.Close() -} - -func (m *DNSMock) HandleAuthSuccessfully() { - m.Mux.HandleFunc("/v3/auth/token", func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("X-Subject-Token", fakeOTCToken) - - fmt.Fprintf(w, `{ - "token": { - "catalog": [ - { - "type": "dns", - "id": "56cd81db1f8445d98652479afe07c5ba", - "name": "", - "endpoints": [ - { - "url": "%s", - "region": "eu-de", - "region_id": "eu-de", - "interface": "public", - "id": "0047a06690484d86afe04877074efddf" - } - ] - } - ] - }}`, m.Server.URL) - }) -} - -func (m *DNSMock) HandleListZonesSuccessfully() { - m.Mux.HandleFunc("/v2/zones", func(w http.ResponseWriter, r *http.Request) { - fmt.Fprintf(w, `{ - "zones":[{ - "id":"123123" - }]} - `) - - assert.Equal(m.t, r.Method, "GET") - assert.Equal(m.t, r.URL.Path, "/v2/zones") - assert.Equal(m.t, r.URL.RawQuery, "name=example.com.") - assert.Equal(m.t, r.Header.Get("Content-Type"), "application/json") - }) -} - -func (m *DNSMock) HandleListZonesEmpty() { - m.Mux.HandleFunc("/v2/zones", func(w http.ResponseWriter, r *http.Request) { - fmt.Fprintf(w, `{ - "zones":[ - ]} - `) - - assert.Equal(m.t, r.Method, "GET") - assert.Equal(m.t, r.URL.Path, "/v2/zones") - assert.Equal(m.t, r.URL.RawQuery, "name=example.com.") - assert.Equal(m.t, r.Header.Get("Content-Type"), "application/json") - }) -} - -func (m *DNSMock) HandleDeleteRecordsetsSuccessfully() { - m.Mux.HandleFunc("/v2/zones/123123/recordsets/321321", func(w http.ResponseWriter, r *http.Request) { - fmt.Fprintf(w, `{ - "zones":[{ - "id":"123123" - }]} - `) - - assert.Equal(m.t, r.Method, "DELETE") - assert.Equal(m.t, r.URL.Path, "/v2/zones/123123/recordsets/321321") - assert.Equal(m.t, r.Header.Get("Content-Type"), "application/json") - }) -} - -func (m *DNSMock) HandleListRecordsetsEmpty() { - m.Mux.HandleFunc("/v2/zones/123123/recordsets", func(w http.ResponseWriter, r *http.Request) { - fmt.Fprintf(w, `{ - "recordsets":[ - ]} - `) - - assert.Equal(m.t, r.URL.Path, "/v2/zones/123123/recordsets") - assert.Equal(m.t, r.URL.RawQuery, "type=TXT&name=_acme-challenge.example.com.") - }) -} -func (m *DNSMock) HandleListRecordsetsSuccessfully() { - m.Mux.HandleFunc("/v2/zones/123123/recordsets", func(w http.ResponseWriter, r *http.Request) { - if r.Method == "GET" { - fmt.Fprintf(w, `{ - "recordsets":[{ - "id":"321321" - }]} - `) - - assert.Equal(m.t, r.URL.Path, "/v2/zones/123123/recordsets") - assert.Equal(m.t, r.URL.RawQuery, "type=TXT&name=_acme-challenge.example.com.") - - } else if r.Method == "POST" { - body, err := ioutil.ReadAll(r.Body) - - assert.Nil(m.t, err) - exceptedString := "{\"name\":\"_acme-challenge.example.com.\",\"description\":\"Added TXT record for ACME dns-01 challenge using lego client\",\"type\":\"TXT\",\"ttl\":300,\"records\":[\"\\\"w6uP8Tcg6K2QR905Rms8iXTlksL6OD1KOWBxTK7wxPI\\\"\"]}" - assert.Equal(m.t, string(body), exceptedString) - - fmt.Fprintf(w, `{ - "recordsets":[{ - "id":"321321" - }]} - `) - - } else { - m.t.Errorf("Expected method to be 'GET' or 'POST' but got '%s'", r.Method) - } - - assert.Equal(m.t, r.Header.Get("Content-Type"), "application/json") - }) -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/otc/otc.go b/vendor/github.com/xenolf/lego/providers/dns/otc/otc.go deleted file mode 100644 index 86bcaa9b7..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/otc/otc.go +++ /dev/null @@ -1,388 +0,0 @@ -// Package otc implements a DNS provider for solving the DNS-01 challenge -// using Open Telekom Cloud Managed DNS. -package otc - -import ( - "bytes" - "encoding/json" - "fmt" - "io" - "io/ioutil" - "net/http" - "os" - "time" - - "github.com/xenolf/lego/acme" -) - -// DNSProvider is an implementation of the acme.ChallengeProvider interface that uses -// OTC's Managed DNS API to manage TXT records for a domain. -type DNSProvider struct { - identityEndpoint string - otcBaseURL string - domainName string - projectName string - userName string - password string - token string -} - -// NewDNSProvider returns a DNSProvider instance configured for OTC DNS. -// Credentials must be passed in the environment variables: OTC_USER_NAME, -// OTC_DOMAIN_NAME, OTC_PASSWORD OTC_PROJECT_NAME and OTC_IDENTITY_ENDPOINT. -func NewDNSProvider() (*DNSProvider, error) { - domainName := os.Getenv("OTC_DOMAIN_NAME") - userName := os.Getenv("OTC_USER_NAME") - password := os.Getenv("OTC_PASSWORD") - projectName := os.Getenv("OTC_PROJECT_NAME") - identityEndpoint := os.Getenv("OTC_IDENTITY_ENDPOINT") - return NewDNSProviderCredentials(domainName, userName, password, projectName, identityEndpoint) -} - -// NewDNSProviderCredentials uses the supplied credentials to return a -// DNSProvider instance configured for OTC DNS. -func NewDNSProviderCredentials(domainName, userName, password, projectName, identityEndpoint string) (*DNSProvider, error) { - if domainName == "" || userName == "" || password == "" || projectName == "" { - return nil, fmt.Errorf("OTC credentials missing") - } - - if identityEndpoint == "" { - identityEndpoint = "https://iam.eu-de.otc.t-systems.com:443/v3/auth/tokens" - } - - return &DNSProvider{ - identityEndpoint: identityEndpoint, - domainName: domainName, - userName: userName, - password: password, - projectName: projectName, - }, nil -} - -func (d *DNSProvider) SendRequest(method, resource string, payload interface{}) (io.Reader, error) { - url := fmt.Sprintf("%s/%s", d.otcBaseURL, resource) - - body, err := json.Marshal(payload) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(method, url, bytes.NewReader(body)) - if err != nil { - return nil, err - } - req.Header.Set("Content-Type", "application/json") - if len(d.token) > 0 { - req.Header.Set("X-Auth-Token", d.token) - } - - // Workaround for keep alive bug in otc api - tr := http.DefaultTransport.(*http.Transport) - tr.DisableKeepAlives = true - - client := &http.Client{ - Timeout: time.Duration(10 * time.Second), - Transport: tr, - } - resp, err := client.Do(req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - if resp.StatusCode >= 400 { - return nil, fmt.Errorf("OTC API request %s failed with HTTP status code %d", url, resp.StatusCode) - } - - body1, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, err - } - - return bytes.NewReader(body1), nil -} - -func (d *DNSProvider) loginRequest() error { - type nameResponse struct { - Name string `json:"name"` - } - - type userResponse struct { - Name string `json:"name"` - Password string `json:"password"` - Domain nameResponse `json:"domain"` - } - - type passwordResponse struct { - User userResponse `json:"user"` - } - type identityResponse struct { - Methods []string `json:"methods"` - Password passwordResponse `json:"password"` - } - - type scopeResponse struct { - Project nameResponse `json:"project"` - } - - type authResponse struct { - Identity identityResponse `json:"identity"` - Scope scopeResponse `json:"scope"` - } - - type loginResponse struct { - Auth authResponse `json:"auth"` - } - - userResp := userResponse{ - Name: d.userName, - Password: d.password, - Domain: nameResponse{ - Name: d.domainName, - }, - } - - loginResp := loginResponse{ - Auth: authResponse{ - Identity: identityResponse{ - Methods: []string{"password"}, - Password: passwordResponse{ - User: userResp, - }, - }, - Scope: scopeResponse{ - Project: nameResponse{ - Name: d.projectName, - }, - }, - }, - } - - body, err := json.Marshal(loginResp) - if err != nil { - return err - } - req, err := http.NewRequest("POST", d.identityEndpoint, bytes.NewReader(body)) - if err != nil { - return err - } - req.Header.Set("Content-Type", "application/json") - - client := &http.Client{Timeout: time.Duration(10 * time.Second)} - resp, err := client.Do(req) - if err != nil { - return err - } - defer resp.Body.Close() - - if resp.StatusCode >= 400 { - return fmt.Errorf("OTC API request failed with HTTP status code %d", resp.StatusCode) - } - - d.token = resp.Header.Get("X-Subject-Token") - - if d.token == "" { - return fmt.Errorf("unable to get auth token") - } - - type endpointResponse struct { - Token struct { - Catalog []struct { - Type string `json:"type"` - Endpoints []struct { - URL string `json:"url"` - } `json:"endpoints"` - } `json:"catalog"` - } `json:"token"` - } - var endpointResp endpointResponse - - err = json.NewDecoder(resp.Body).Decode(&endpointResp) - if err != nil { - return err - } - - for _, v := range endpointResp.Token.Catalog { - if v.Type == "dns" { - for _, endpoint := range v.Endpoints { - d.otcBaseURL = fmt.Sprintf("%s/v2", endpoint.URL) - continue - } - } - } - - if d.otcBaseURL == "" { - return fmt.Errorf("unable to get dns endpoint") - } - - return nil -} - -// Starts a new OTC API Session. Authenticates using userName, password -// and receives a token to be used in for subsequent requests. -func (d *DNSProvider) login() error { - err := d.loginRequest() - if err != nil { - return err - } - - return nil -} - -func (d *DNSProvider) getZoneID(zone string) (string, error) { - type zoneItem struct { - ID string `json:"id"` - } - - type zonesResponse struct { - Zones []zoneItem `json:"zones"` - } - - resource := fmt.Sprintf("zones?name=%s", zone) - resp, err := d.SendRequest("GET", resource, nil) - if err != nil { - return "", err - } - - var zonesRes zonesResponse - err = json.NewDecoder(resp).Decode(&zonesRes) - if err != nil { - return "", err - } - - if len(zonesRes.Zones) < 1 { - return "", fmt.Errorf("zone %s not found", zone) - } - - if len(zonesRes.Zones) > 1 { - return "", fmt.Errorf("to many zones found") - } - - if zonesRes.Zones[0].ID == "" { - return "", fmt.Errorf("id not found") - } - - return zonesRes.Zones[0].ID, nil -} - -func (d *DNSProvider) getRecordSetID(zoneID string, fqdn string) (string, error) { - type recordSet struct { - ID string `json:"id"` - } - - type recordSetsResponse struct { - RecordSets []recordSet `json:"recordsets"` - } - - resource := fmt.Sprintf("zones/%s/recordsets?type=TXT&name=%s", zoneID, fqdn) - resp, err := d.SendRequest("GET", resource, nil) - if err != nil { - return "", err - } - - var recordSetsRes recordSetsResponse - err = json.NewDecoder(resp).Decode(&recordSetsRes) - if err != nil { - return "", err - } - - if len(recordSetsRes.RecordSets) < 1 { - return "", fmt.Errorf("record not found") - } - - if len(recordSetsRes.RecordSets) > 1 { - return "", fmt.Errorf("to many records found") - } - - if recordSetsRes.RecordSets[0].ID == "" { - return "", fmt.Errorf("id not found") - } - - return recordSetsRes.RecordSets[0].ID, nil -} - -func (d *DNSProvider) deleteRecordSet(zoneID, recordID string) error { - resource := fmt.Sprintf("zones/%s/recordsets/%s", zoneID, recordID) - - _, err := d.SendRequest("DELETE", resource, nil) - if err != nil { - return err - } - return nil -} - -// Present creates a TXT record using the specified parameters -func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) - - if ttl < 300 { - ttl = 300 // 300 is otc minimum value for ttl - } - - authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) - if err != nil { - return err - } - - err = d.login() - if err != nil { - return err - } - - zoneID, err := d.getZoneID(authZone) - if err != nil { - return fmt.Errorf("unable to get zone: %s", err) - } - - resource := fmt.Sprintf("zones/%s/recordsets", zoneID) - - type recordset struct { - Name string `json:"name"` - Description string `json:"description"` - Type string `json:"type"` - Ttl int `json:"ttl"` - Records []string `json:"records"` - } - - r1 := &recordset{ - Name: fqdn, - Description: "Added TXT record for ACME dns-01 challenge using lego client", - Type: "TXT", - Ttl: 300, - Records: []string{fmt.Sprintf("\"%s\"", value)}, - } - _, err = d.SendRequest("POST", resource, r1) - - if err != nil { - return err - } - - return nil -} - -// CleanUp removes the TXT record matching the specified parameters -func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - - authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) - if err != nil { - return err - } - - err = d.login() - if err != nil { - return err - } - - zoneID, err := d.getZoneID(authZone) - - if err != nil { - return err - } - - recordID, err := d.getRecordSetID(zoneID, fqdn) - if err != nil { - return fmt.Errorf("unable go get record %s for zone %s: %s", fqdn, domain, err) - } - return d.deleteRecordSet(zoneID, recordID) -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/otc/otc_test.go b/vendor/github.com/xenolf/lego/providers/dns/otc/otc_test.go deleted file mode 100644 index 0c05334a9..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/otc/otc_test.go +++ /dev/null @@ -1,112 +0,0 @@ -package otc - -import ( - "fmt" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" - "os" - "testing" -) - -type OTCDNSTestSuite struct { - suite.Suite - Mock *DNSMock -} - -func (s *OTCDNSTestSuite) TearDownSuite() { - s.Mock.ShutdownServer() -} - -func (s *OTCDNSTestSuite) SetupTest() { - s.Mock = NewDNSMock(s.T()) - s.Mock.Setup() - s.Mock.HandleAuthSuccessfully() - -} - -func TestOTCDNSTestSuite(t *testing.T) { - suite.Run(t, new(OTCDNSTestSuite)) -} - -func (s *OTCDNSTestSuite) createDNSProvider() (*DNSProvider, error) { - url := fmt.Sprintf("%s/v3/auth/token", s.Mock.Server.URL) - return NewDNSProviderCredentials(fakeOTCUserName, fakeOTCPassword, fakeOTCDomainName, fakeOTCProjectName, url) -} - -func (s *OTCDNSTestSuite) TestOTCDNSLoginEnv() { - os.Setenv("OTC_DOMAIN_NAME", "unittest1") - os.Setenv("OTC_USER_NAME", "unittest2") - os.Setenv("OTC_PASSWORD", "unittest3") - os.Setenv("OTC_PROJECT_NAME", "unittest4") - os.Setenv("OTC_IDENTITY_ENDPOINT", "unittest5") - - provider, err := NewDNSProvider() - assert.Nil(s.T(), err) - assert.Equal(s.T(), provider.domainName, "unittest1") - assert.Equal(s.T(), provider.userName, "unittest2") - assert.Equal(s.T(), provider.password, "unittest3") - assert.Equal(s.T(), provider.projectName, "unittest4") - assert.Equal(s.T(), provider.identityEndpoint, "unittest5") - - os.Setenv("OTC_IDENTITY_ENDPOINT", "") - - provider, err = NewDNSProvider() - assert.Nil(s.T(), err) - assert.Equal(s.T(), provider.identityEndpoint, "https://iam.eu-de.otc.t-systems.com:443/v3/auth/tokens") - - os.Clearenv() -} - -func (s *OTCDNSTestSuite) TestOTCDNSLoginEnvEmpty() { - _, err := NewDNSProvider() - assert.Equal(s.T(), "OTC credentials missing", err.Error()) - - os.Clearenv() -} - -func (s *OTCDNSTestSuite) TestOTCDNSLogin() { - otcProvider, err := s.createDNSProvider() - - assert.Nil(s.T(), err) - err = otcProvider.loginRequest() - assert.Nil(s.T(), err) - assert.Equal(s.T(), otcProvider.otcBaseURL, fmt.Sprintf("%s/v2", s.Mock.Server.URL)) - assert.Equal(s.T(), fakeOTCToken, otcProvider.token) -} - -func (s *OTCDNSTestSuite) TestOTCDNSEmptyZone() { - s.Mock.HandleListZonesEmpty() - s.Mock.HandleListRecordsetsSuccessfully() - - otcProvider, _ := s.createDNSProvider() - err := otcProvider.Present("example.com", "", "foobar") - assert.NotNil(s.T(), err) -} - -func (s *OTCDNSTestSuite) TestOTCDNSEmptyRecordset() { - s.Mock.HandleListZonesSuccessfully() - s.Mock.HandleListRecordsetsEmpty() - - otcProvider, _ := s.createDNSProvider() - err := otcProvider.CleanUp("example.com", "", "foobar") - assert.NotNil(s.T(), err) -} - -func (s *OTCDNSTestSuite) TestOTCDNSPresent() { - s.Mock.HandleListZonesSuccessfully() - s.Mock.HandleListRecordsetsSuccessfully() - - otcProvider, _ := s.createDNSProvider() - err := otcProvider.Present("example.com", "", "foobar") - assert.Nil(s.T(), err) -} - -func (s *OTCDNSTestSuite) TestOTCDNSCleanup() { - s.Mock.HandleListZonesSuccessfully() - s.Mock.HandleListRecordsetsSuccessfully() - s.Mock.HandleDeleteRecordsetsSuccessfully() - - otcProvider, _ := s.createDNSProvider() - err := otcProvider.CleanUp("example.com", "", "foobar") - assert.Nil(s.T(), err) -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/ovh/ovh.go b/vendor/github.com/xenolf/lego/providers/dns/ovh/ovh.go deleted file mode 100644 index 290a8d7df..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/ovh/ovh.go +++ /dev/null @@ -1,159 +0,0 @@ -// Package OVH implements a DNS provider for solving the DNS-01 -// challenge using OVH DNS. -package ovh - -import ( - "fmt" - "os" - "strings" - "sync" - - "github.com/ovh/go-ovh/ovh" - "github.com/xenolf/lego/acme" -) - -// OVH API reference: https://eu.api.ovh.com/ -// Create a Token: https://eu.api.ovh.com/createToken/ - -// DNSProvider is an implementation of the acme.ChallengeProvider interface -// that uses OVH's REST API to manage TXT records for a domain. -type DNSProvider struct { - client *ovh.Client - recordIDs map[string]int - recordIDsMu sync.Mutex -} - -// NewDNSProvider returns a DNSProvider instance configured for OVH -// Credentials must be passed in the environment variable: -// OVH_ENDPOINT : it must be ovh-eu or ovh-ca -// OVH_APPLICATION_KEY -// OVH_APPLICATION_SECRET -// OVH_CONSUMER_KEY -func NewDNSProvider() (*DNSProvider, error) { - apiEndpoint := os.Getenv("OVH_ENDPOINT") - applicationKey := os.Getenv("OVH_APPLICATION_KEY") - applicationSecret := os.Getenv("OVH_APPLICATION_SECRET") - consumerKey := os.Getenv("OVH_CONSUMER_KEY") - return NewDNSProviderCredentials(apiEndpoint, applicationKey, applicationSecret, consumerKey) -} - -// NewDNSProviderCredentials uses the supplied credentials to return a -// DNSProvider instance configured for OVH. -func NewDNSProviderCredentials(apiEndpoint, applicationKey, applicationSecret, consumerKey string) (*DNSProvider, error) { - if apiEndpoint == "" || applicationKey == "" || applicationSecret == "" || consumerKey == "" { - return nil, fmt.Errorf("OVH credentials missing") - } - - ovhClient, _ := ovh.NewClient( - apiEndpoint, - applicationKey, - applicationSecret, - consumerKey, - ) - - return &DNSProvider{ - client: ovhClient, - recordIDs: make(map[string]int), - }, nil -} - -// Present creates a TXT record to fulfil the dns-01 challenge. -func (d *DNSProvider) Present(domain, token, keyAuth string) error { - - // txtRecordRequest represents the request body to DO's API to make a TXT record - type txtRecordRequest struct { - FieldType string `json:"fieldType"` - SubDomain string `json:"subDomain"` - Target string `json:"target"` - TTL int `json:"ttl"` - } - - // txtRecordResponse represents a response from DO's API after making a TXT record - type txtRecordResponse struct { - ID int `json:"id"` - FieldType string `json:"fieldType"` - SubDomain string `json:"subDomain"` - Target string `json:"target"` - TTL int `json:"ttl"` - Zone string `json:"zone"` - } - - fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) - - // Parse domain name - authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) - if err != nil { - return fmt.Errorf("Could not determine zone for domain: '%s'. %s", domain, err) - } - - authZone = acme.UnFqdn(authZone) - subDomain := d.extractRecordName(fqdn, authZone) - - reqURL := fmt.Sprintf("/domain/zone/%s/record", authZone) - reqData := txtRecordRequest{FieldType: "TXT", SubDomain: subDomain, Target: value, TTL: ttl} - var respData txtRecordResponse - - // Create TXT record - err = d.client.Post(reqURL, reqData, &respData) - if err != nil { - fmt.Printf("Error when call OVH api to add record : %q \n", err) - return err - } - - // Apply the change - reqURL = fmt.Sprintf("/domain/zone/%s/refresh", authZone) - err = d.client.Post(reqURL, nil, nil) - if err != nil { - fmt.Printf("Error when call OVH api to refresh zone : %q \n", err) - return err - } - - d.recordIDsMu.Lock() - d.recordIDs[fqdn] = respData.ID - d.recordIDsMu.Unlock() - - return nil -} - -// CleanUp removes the TXT record matching the specified parameters -func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - - // get the record's unique ID from when we created it - d.recordIDsMu.Lock() - recordID, ok := d.recordIDs[fqdn] - d.recordIDsMu.Unlock() - if !ok { - return fmt.Errorf("unknown record ID for '%s'", fqdn) - } - - authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) - if err != nil { - return fmt.Errorf("Could not determine zone for domain: '%s'. %s", domain, err) - } - - authZone = acme.UnFqdn(authZone) - - reqURL := fmt.Sprintf("/domain/zone/%s/record/%d", authZone, recordID) - - err = d.client.Delete(reqURL, nil) - if err != nil { - fmt.Printf("Error when call OVH api to delete challenge record : %q \n", err) - return err - } - - // Delete record ID from map - d.recordIDsMu.Lock() - delete(d.recordIDs, fqdn) - d.recordIDsMu.Unlock() - - return nil -} - -func (d *DNSProvider) extractRecordName(fqdn, domain string) string { - name := acme.UnFqdn(fqdn) - if idx := strings.Index(name, "."+domain); idx != -1 { - return name[:idx] - } - return name -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/ovh/ovh_test.go b/vendor/github.com/xenolf/lego/providers/dns/ovh/ovh_test.go deleted file mode 100644 index 47da60e57..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/ovh/ovh_test.go +++ /dev/null @@ -1,103 +0,0 @@ -package ovh - -import ( - "os" - "testing" - "time" - - "github.com/stretchr/testify/assert" -) - -var ( - liveTest bool - apiEndpoint string - applicationKey string - applicationSecret string - consumerKey string - domain string -) - -func init() { - apiEndpoint = os.Getenv("OVH_ENDPOINT") - applicationKey = os.Getenv("OVH_APPLICATION_KEY") - applicationSecret = os.Getenv("OVH_APPLICATION_SECRET") - consumerKey = os.Getenv("OVH_CONSUMER_KEY") - liveTest = len(apiEndpoint) > 0 && len(applicationKey) > 0 && len(applicationSecret) > 0 && len(consumerKey) > 0 -} - -func restoreEnv() { - os.Setenv("OVH_ENDPOINT", apiEndpoint) - os.Setenv("OVH_APPLICATION_KEY", applicationKey) - os.Setenv("OVH_APPLICATION_SECRET", applicationSecret) - os.Setenv("OVH_CONSUMER_KEY", consumerKey) -} - -func TestNewDNSProviderValidEnv(t *testing.T) { - os.Setenv("OVH_ENDPOINT", "ovh-eu") - os.Setenv("OVH_APPLICATION_KEY", "1234") - os.Setenv("OVH_APPLICATION_SECRET", "5678") - os.Setenv("OVH_CONSUMER_KEY", "abcde") - defer restoreEnv() - _, err := NewDNSProvider() - assert.NoError(t, err) -} - -func TestNewDNSProviderMissingCredErr(t *testing.T) { - os.Setenv("OVH_ENDPOINT", "") - os.Setenv("OVH_APPLICATION_KEY", "1234") - os.Setenv("OVH_APPLICATION_SECRET", "5678") - os.Setenv("OVH_CONSUMER_KEY", "abcde") - defer restoreEnv() - _, err := NewDNSProvider() - assert.EqualError(t, err, "OVH credentials missing") - - os.Setenv("OVH_ENDPOINT", "ovh-eu") - os.Setenv("OVH_APPLICATION_KEY", "") - os.Setenv("OVH_APPLICATION_SECRET", "5678") - os.Setenv("OVH_CONSUMER_KEY", "abcde") - defer restoreEnv() - _, err = NewDNSProvider() - assert.EqualError(t, err, "OVH credentials missing") - - os.Setenv("OVH_ENDPOINT", "ovh-eu") - os.Setenv("OVH_APPLICATION_KEY", "1234") - os.Setenv("OVH_APPLICATION_SECRET", "") - os.Setenv("OVH_CONSUMER_KEY", "abcde") - defer restoreEnv() - _, err = NewDNSProvider() - assert.EqualError(t, err, "OVH credentials missing") - - os.Setenv("OVH_ENDPOINT", "ovh-eu") - os.Setenv("OVH_APPLICATION_KEY", "1234") - os.Setenv("OVH_APPLICATION_SECRET", "5678") - os.Setenv("OVH_CONSUMER_KEY", "") - defer restoreEnv() - _, err = NewDNSProvider() - assert.EqualError(t, err, "OVH credentials missing") -} - -func TestLivePresent(t *testing.T) { - if !liveTest { - t.Skip("skipping live test") - } - - provider, err := NewDNSProvider() - assert.NoError(t, err) - - err = provider.Present(domain, "", "123d==") - assert.NoError(t, err) -} - -func TestLiveCleanUp(t *testing.T) { - if !liveTest { - t.Skip("skipping live test") - } - - time.Sleep(time.Second * 1) - - provider, err := NewDNSProvider() - assert.NoError(t, err) - - err = provider.CleanUp(domain, "", "123d==") - assert.NoError(t, err) -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/pdns/README.md b/vendor/github.com/xenolf/lego/providers/dns/pdns/README.md deleted file mode 100644 index 23abb7669..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/pdns/README.md +++ /dev/null @@ -1,7 +0,0 @@ -## PowerDNS provider - -Tested and confirmed to work with PowerDNS authoratative server 3.4.8 and 4.0.1. Refer to [PowerDNS documentation](https://doc.powerdns.com/md/httpapi/README/) instructions on how to enable the built-in API interface. - -PowerDNS Notes: -- PowerDNS API does not currently support SSL, therefore you should take care to ensure that traffic between lego and the PowerDNS API is over a trusted network, VPN etc. -- In order to have the SOA serial automatically increment each time the `_acme-challenge` record is added/modified via the API, set `SOA-API-EDIT` to `INCEPTION-INCREMENT` for the zone in the `domainmetadata` table diff --git a/vendor/github.com/xenolf/lego/providers/dns/pdns/pdns.go b/vendor/github.com/xenolf/lego/providers/dns/pdns/pdns.go deleted file mode 100644 index a4fd22b0c..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/pdns/pdns.go +++ /dev/null @@ -1,343 +0,0 @@ -// Package pdns implements a DNS provider for solving the DNS-01 -// challenge using PowerDNS nameserver. -package pdns - -import ( - "bytes" - "encoding/json" - "fmt" - "io" - "net/http" - "net/url" - "os" - "strconv" - "strings" - "time" - - "github.com/xenolf/lego/acme" -) - -// DNSProvider is an implementation of the acme.ChallengeProvider interface -type DNSProvider struct { - apiKey string - host *url.URL - apiVersion int -} - -// NewDNSProvider returns a DNSProvider instance configured for pdns. -// Credentials must be passed in the environment variable: -// PDNS_API_URL and PDNS_API_KEY. -func NewDNSProvider() (*DNSProvider, error) { - key := os.Getenv("PDNS_API_KEY") - hostUrl, err := url.Parse(os.Getenv("PDNS_API_URL")) - if err != nil { - return nil, err - } - - return NewDNSProviderCredentials(hostUrl, key) -} - -// NewDNSProviderCredentials uses the supplied credentials to return a -// DNSProvider instance configured for pdns. -func NewDNSProviderCredentials(host *url.URL, key string) (*DNSProvider, error) { - if key == "" { - return nil, fmt.Errorf("PDNS API key missing") - } - - if host == nil || host.Host == "" { - return nil, fmt.Errorf("PDNS API URL missing") - } - - provider := &DNSProvider{ - host: host, - apiKey: key, - } - provider.getAPIVersion() - - return provider, nil -} - -// Timeout returns the timeout and interval to use when checking for DNS -// propagation. Adjusting here to cope with spikes in propagation times. -func (c *DNSProvider) Timeout() (timeout, interval time.Duration) { - return 120 * time.Second, 2 * time.Second -} - -// Present creates a TXT record to fulfil the dns-01 challenge -func (c *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) - zone, err := c.getHostedZone(fqdn) - if err != nil { - return err - } - - name := fqdn - - // pre-v1 API wants non-fqdn - if c.apiVersion == 0 { - name = acme.UnFqdn(fqdn) - } - - rec := pdnsRecord{ - Content: "\"" + value + "\"", - Disabled: false, - - // pre-v1 API - Type: "TXT", - Name: name, - TTL: 120, - } - - rrsets := rrSets{ - RRSets: []rrSet{ - rrSet{ - Name: name, - ChangeType: "REPLACE", - Type: "TXT", - Kind: "Master", - TTL: 120, - Records: []pdnsRecord{rec}, - }, - }, - } - - body, err := json.Marshal(rrsets) - if err != nil { - return err - } - - _, err = c.makeRequest("PATCH", zone.URL, bytes.NewReader(body)) - if err != nil { - fmt.Println("here") - return err - } - - return nil -} - -// CleanUp removes the TXT record matching the specified parameters -func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - - zone, err := c.getHostedZone(fqdn) - if err != nil { - return err - } - - set, err := c.findTxtRecord(fqdn) - if err != nil { - return err - } - - rrsets := rrSets{ - RRSets: []rrSet{ - rrSet{ - Name: set.Name, - Type: set.Type, - ChangeType: "DELETE", - }, - }, - } - body, err := json.Marshal(rrsets) - if err != nil { - return err - } - - _, err = c.makeRequest("PATCH", zone.URL, bytes.NewReader(body)) - if err != nil { - return err - } - - return nil -} - -func (c *DNSProvider) getHostedZone(fqdn string) (*hostedZone, error) { - var zone hostedZone - authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) - if err != nil { - return nil, err - } - - url := "/servers/localhost/zones" - result, err := c.makeRequest("GET", url, nil) - if err != nil { - return nil, err - } - - zones := []hostedZone{} - err = json.Unmarshal(result, &zones) - if err != nil { - return nil, err - } - - url = "" - for _, zone := range zones { - if acme.UnFqdn(zone.Name) == acme.UnFqdn(authZone) { - url = zone.URL - } - } - - result, err = c.makeRequest("GET", url, nil) - if err != nil { - return nil, err - } - - err = json.Unmarshal(result, &zone) - if err != nil { - return nil, err - } - - // convert pre-v1 API result - if len(zone.Records) > 0 { - zone.RRSets = []rrSet{} - for _, record := range zone.Records { - set := rrSet{ - Name: record.Name, - Type: record.Type, - Records: []pdnsRecord{record}, - } - zone.RRSets = append(zone.RRSets, set) - } - } - - return &zone, nil -} - -func (c *DNSProvider) findTxtRecord(fqdn string) (*rrSet, error) { - zone, err := c.getHostedZone(fqdn) - if err != nil { - return nil, err - } - - _, err = c.makeRequest("GET", zone.URL, nil) - if err != nil { - return nil, err - } - - for _, set := range zone.RRSets { - if (set.Name == acme.UnFqdn(fqdn) || set.Name == fqdn) && set.Type == "TXT" { - return &set, nil - } - } - - return nil, fmt.Errorf("No existing record found for %s", fqdn) -} - -func (c *DNSProvider) getAPIVersion() { - type APIVersion struct { - URL string `json:"url"` - Version int `json:"version"` - } - - result, err := c.makeRequest("GET", "/api", nil) - if err != nil { - return - } - - var versions []APIVersion - err = json.Unmarshal(result, &versions) - if err != nil { - return - } - - latestVersion := 0 - for _, v := range versions { - if v.Version > latestVersion { - latestVersion = v.Version - } - } - c.apiVersion = latestVersion -} - -func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawMessage, error) { - type APIError struct { - Error string `json:"error"` - } - var path = "" - if c.host.Path != "/" { - path = c.host.Path - } - if c.apiVersion > 0 { - if !strings.HasPrefix(uri, "api/v") { - uri = "/api/v" + strconv.Itoa(c.apiVersion) + uri - } else { - uri = "/" + uri - } - } - url := c.host.Scheme + "://" + c.host.Host + path + uri - req, err := http.NewRequest(method, url, body) - if err != nil { - return nil, err - } - - req.Header.Set("X-API-Key", c.apiKey) - - client := http.Client{Timeout: 30 * time.Second} - resp, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("Error talking to PDNS API -> %v", err) - } - - defer resp.Body.Close() - - if resp.StatusCode != 422 && (resp.StatusCode < 200 || resp.StatusCode >= 300) { - return nil, fmt.Errorf("Unexpected HTTP status code %d when fetching '%s'", resp.StatusCode, url) - } - - var msg json.RawMessage - err = json.NewDecoder(resp.Body).Decode(&msg) - switch { - case err == io.EOF: - // empty body - return nil, nil - case err != nil: - // other error - return nil, err - } - - // check for PowerDNS error message - if len(msg) > 0 && msg[0] == '{' { - var apiError APIError - err = json.Unmarshal(msg, &apiError) - if err != nil { - return nil, err - } - if apiError.Error != "" { - return nil, fmt.Errorf("Error talking to PDNS API -> %v", apiError.Error) - } - } - return msg, nil -} - -type pdnsRecord struct { - Content string `json:"content"` - Disabled bool `json:"disabled"` - - // pre-v1 API - Name string `json:"name"` - Type string `json:"type"` - TTL int `json:"ttl,omitempty"` -} - -type hostedZone struct { - ID string `json:"id"` - Name string `json:"name"` - URL string `json:"url"` - RRSets []rrSet `json:"rrsets"` - - // pre-v1 API - Records []pdnsRecord `json:"records"` -} - -type rrSet struct { - Name string `json:"name"` - Type string `json:"type"` - Kind string `json:"kind"` - ChangeType string `json:"changetype"` - Records []pdnsRecord `json:"records"` - TTL int `json:"ttl,omitempty"` -} - -type rrSets struct { - RRSets []rrSet `json:"rrsets"` -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/pdns/pdns_test.go b/vendor/github.com/xenolf/lego/providers/dns/pdns/pdns_test.go deleted file mode 100644 index 70e7670ed..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/pdns/pdns_test.go +++ /dev/null @@ -1,80 +0,0 @@ -package pdns - -import ( - "net/url" - "os" - "testing" - - "github.com/stretchr/testify/assert" -) - -var ( - pdnsLiveTest bool - pdnsURL *url.URL - pdnsURLStr string - pdnsAPIKey string - pdnsDomain string -) - -func init() { - pdnsURLStr = os.Getenv("PDNS_API_URL") - pdnsURL, _ = url.Parse(pdnsURLStr) - pdnsAPIKey = os.Getenv("PDNS_API_KEY") - pdnsDomain = os.Getenv("PDNS_DOMAIN") - if len(pdnsURLStr) > 0 && len(pdnsAPIKey) > 0 && len(pdnsDomain) > 0 { - pdnsLiveTest = true - } -} - -func restorePdnsEnv() { - os.Setenv("PDNS_API_URL", pdnsURLStr) - os.Setenv("PDNS_API_KEY", pdnsAPIKey) -} - -func TestNewDNSProviderValid(t *testing.T) { - os.Setenv("PDNS_API_URL", "") - os.Setenv("PDNS_API_KEY", "") - tmpURL, _ := url.Parse("http://localhost:8081") - _, err := NewDNSProviderCredentials(tmpURL, "123") - assert.NoError(t, err) - restorePdnsEnv() -} - -func TestNewDNSProviderValidEnv(t *testing.T) { - os.Setenv("PDNS_API_URL", "http://localhost:8081") - os.Setenv("PDNS_API_KEY", "123") - _, err := NewDNSProvider() - assert.NoError(t, err) - restorePdnsEnv() -} - -func TestNewDNSProviderMissingHostErr(t *testing.T) { - os.Setenv("PDNS_API_URL", "") - os.Setenv("PDNS_API_KEY", "123") - _, err := NewDNSProvider() - assert.EqualError(t, err, "PDNS API URL missing") - restorePdnsEnv() -} - -func TestNewDNSProviderMissingKeyErr(t *testing.T) { - os.Setenv("PDNS_API_URL", pdnsURLStr) - os.Setenv("PDNS_API_KEY", "") - _, err := NewDNSProvider() - assert.EqualError(t, err, "PDNS API key missing") - restorePdnsEnv() -} - -func TestPdnsPresentAndCleanup(t *testing.T) { - if !pdnsLiveTest { - t.Skip("skipping live test") - } - - provider, err := NewDNSProviderCredentials(pdnsURL, pdnsAPIKey) - assert.NoError(t, err) - - err = provider.Present(pdnsDomain, "", "123d==") - assert.NoError(t, err) - - err = provider.CleanUp(pdnsDomain, "", "123d==") - assert.NoError(t, err) -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/rackspace/rackspace.go b/vendor/github.com/xenolf/lego/providers/dns/rackspace/rackspace.go deleted file mode 100644 index 13daa8c8a..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/rackspace/rackspace.go +++ /dev/null @@ -1,284 +0,0 @@ -// Package rackspace implements a DNS provider for solving the DNS-01 -// challenge using rackspace DNS. -package rackspace - -import ( - "bytes" - "encoding/json" - "fmt" - "io" - "net/http" - "os" - "time" - - "github.com/xenolf/lego/acme" -) - -// rackspaceAPIURL represents the Identity API endpoint to call -var rackspaceAPIURL = "https://identity.api.rackspacecloud.com/v2.0/tokens" - -// DNSProvider is an implementation of the acme.ChallengeProvider interface -// used to store the reusable token and DNS API endpoint -type DNSProvider struct { - token string - cloudDNSEndpoint string -} - -// NewDNSProvider returns a DNSProvider instance configured for Rackspace. -// Credentials must be passed in the environment variables: RACKSPACE_USER -// and RACKSPACE_API_KEY. -func NewDNSProvider() (*DNSProvider, error) { - user := os.Getenv("RACKSPACE_USER") - key := os.Getenv("RACKSPACE_API_KEY") - return NewDNSProviderCredentials(user, key) -} - -// NewDNSProviderCredentials uses the supplied credentials to return a -// DNSProvider instance configured for Rackspace. It authenticates against -// the API, also grabbing the DNS Endpoint. -func NewDNSProviderCredentials(user, key string) (*DNSProvider, error) { - if user == "" || key == "" { - return nil, fmt.Errorf("Rackspace credentials missing") - } - - type APIKeyCredentials struct { - Username string `json:"username"` - APIKey string `json:"apiKey"` - } - - type Auth struct { - APIKeyCredentials `json:"RAX-KSKEY:apiKeyCredentials"` - } - - type RackspaceAuthData struct { - Auth `json:"auth"` - } - - type RackspaceIdentity struct { - Access struct { - ServiceCatalog []struct { - Endpoints []struct { - PublicURL string `json:"publicURL"` - TenantID string `json:"tenantId"` - } `json:"endpoints"` - Name string `json:"name"` - } `json:"serviceCatalog"` - Token struct { - ID string `json:"id"` - } `json:"token"` - } `json:"access"` - } - - authData := RackspaceAuthData{ - Auth: Auth{ - APIKeyCredentials: APIKeyCredentials{ - Username: user, - APIKey: key, - }, - }, - } - - body, err := json.Marshal(authData) - if err != nil { - return nil, err - } - - req, err := http.NewRequest("POST", rackspaceAPIURL, bytes.NewReader(body)) - if err != nil { - return nil, err - } - req.Header.Set("Content-Type", "application/json") - - client := http.Client{Timeout: 30 * time.Second} - resp, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("Error querying Rackspace Identity API: %v", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("Rackspace Authentication failed. Response code: %d", resp.StatusCode) - } - - var rackspaceIdentity RackspaceIdentity - err = json.NewDecoder(resp.Body).Decode(&rackspaceIdentity) - if err != nil { - return nil, err - } - - // Iterate through the Service Catalog to get the DNS Endpoint - var dnsEndpoint string - for _, service := range rackspaceIdentity.Access.ServiceCatalog { - if service.Name == "cloudDNS" { - dnsEndpoint = service.Endpoints[0].PublicURL - break - } - } - if dnsEndpoint == "" { - return nil, fmt.Errorf("Failed to populate DNS endpoint, check Rackspace API for changes.") - } - - return &DNSProvider{ - token: rackspaceIdentity.Access.Token.ID, - cloudDNSEndpoint: dnsEndpoint, - }, nil -} - -// Present creates a TXT record to fulfil the dns-01 challenge -func (c *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) - zoneID, err := c.getHostedZoneID(fqdn) - if err != nil { - return err - } - - rec := RackspaceRecords{ - RackspaceRecord: []RackspaceRecord{{ - Name: acme.UnFqdn(fqdn), - Type: "TXT", - Data: value, - TTL: 300, - }}, - } - - body, err := json.Marshal(rec) - if err != nil { - return err - } - - _, err = c.makeRequest("POST", fmt.Sprintf("/domains/%d/records", zoneID), bytes.NewReader(body)) - if err != nil { - return err - } - - return nil -} - -// CleanUp removes the TXT record matching the specified parameters -func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - zoneID, err := c.getHostedZoneID(fqdn) - if err != nil { - return err - } - - record, err := c.findTxtRecord(fqdn, zoneID) - if err != nil { - return err - } - - _, err = c.makeRequest("DELETE", fmt.Sprintf("/domains/%d/records?id=%s", zoneID, record.ID), nil) - if err != nil { - return err - } - - return nil -} - -// getHostedZoneID performs a lookup to get the DNS zone which needs -// modifying for a given FQDN -func (c *DNSProvider) getHostedZoneID(fqdn string) (int, error) { - // HostedZones represents the response when querying Rackspace DNS zones - type ZoneSearchResponse struct { - TotalEntries int `json:"totalEntries"` - HostedZones []struct { - ID int `json:"id"` - Name string `json:"name"` - } `json:"domains"` - } - - authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) - if err != nil { - return 0, err - } - - result, err := c.makeRequest("GET", fmt.Sprintf("/domains?name=%s", acme.UnFqdn(authZone)), nil) - if err != nil { - return 0, err - } - - var zoneSearchResponse ZoneSearchResponse - err = json.Unmarshal(result, &zoneSearchResponse) - if err != nil { - return 0, err - } - - // If nothing was returned, or for whatever reason more than 1 was returned (the search uses exact match, so should not occur) - if zoneSearchResponse.TotalEntries != 1 { - return 0, fmt.Errorf("Found %d zones for %s in Rackspace for domain %s", zoneSearchResponse.TotalEntries, authZone, fqdn) - } - - return zoneSearchResponse.HostedZones[0].ID, nil -} - -// findTxtRecord searches a DNS zone for a TXT record with a specific name -func (c *DNSProvider) findTxtRecord(fqdn string, zoneID int) (*RackspaceRecord, error) { - result, err := c.makeRequest("GET", fmt.Sprintf("/domains/%d/records?type=TXT&name=%s", zoneID, acme.UnFqdn(fqdn)), nil) - if err != nil { - return nil, err - } - - var records RackspaceRecords - err = json.Unmarshal(result, &records) - if err != nil { - return nil, err - } - - recordsLength := len(records.RackspaceRecord) - switch recordsLength { - case 1: - break - case 0: - return nil, fmt.Errorf("No TXT record found for %s", fqdn) - default: - return nil, fmt.Errorf("More than 1 TXT record found for %s", fqdn) - } - - return &records.RackspaceRecord[0], nil -} - -// makeRequest is a wrapper function used for making DNS API requests -func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawMessage, error) { - url := c.cloudDNSEndpoint + uri - req, err := http.NewRequest(method, url, body) - if err != nil { - return nil, err - } - - req.Header.Set("X-Auth-Token", c.token) - req.Header.Set("Content-Type", "application/json") - - client := http.Client{Timeout: 30 * time.Second} - resp, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("Error querying DNS API: %v", err) - } - - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusAccepted { - return nil, fmt.Errorf("Request failed for %s %s. Response code: %d", method, url, resp.StatusCode) - } - - var r json.RawMessage - err = json.NewDecoder(resp.Body).Decode(&r) - if err != nil { - return nil, fmt.Errorf("JSON decode failed for %s %s. Response code: %d", method, url, resp.StatusCode) - } - - return r, nil -} - -// RackspaceRecords is the list of records sent/received from the DNS API -type RackspaceRecords struct { - RackspaceRecord []RackspaceRecord `json:"records"` -} - -// RackspaceRecord represents a Rackspace DNS record -type RackspaceRecord struct { - Name string `json:"name"` - Type string `json:"type"` - Data string `json:"data"` - TTL int `json:"ttl,omitempty"` - ID string `json:"id,omitempty"` -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/rackspace/rackspace_test.go b/vendor/github.com/xenolf/lego/providers/dns/rackspace/rackspace_test.go deleted file mode 100644 index 22c979cad..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/rackspace/rackspace_test.go +++ /dev/null @@ -1,220 +0,0 @@ -package rackspace - -import ( - "fmt" - "io/ioutil" - "net/http" - "net/http/httptest" - "os" - "strings" - "testing" - "time" - - "github.com/stretchr/testify/assert" -) - -var ( - rackspaceLiveTest bool - rackspaceUser string - rackspaceAPIKey string - rackspaceDomain string - testAPIURL string -) - -func init() { - rackspaceUser = os.Getenv("RACKSPACE_USER") - rackspaceAPIKey = os.Getenv("RACKSPACE_API_KEY") - rackspaceDomain = os.Getenv("RACKSPACE_DOMAIN") - if len(rackspaceUser) > 0 && len(rackspaceAPIKey) > 0 && len(rackspaceDomain) > 0 { - rackspaceLiveTest = true - } -} - -func testRackspaceEnv() { - rackspaceAPIURL = testAPIURL - os.Setenv("RACKSPACE_USER", "testUser") - os.Setenv("RACKSPACE_API_KEY", "testKey") -} - -func liveRackspaceEnv() { - rackspaceAPIURL = "https://identity.api.rackspacecloud.com/v2.0/tokens" - os.Setenv("RACKSPACE_USER", rackspaceUser) - os.Setenv("RACKSPACE_API_KEY", rackspaceAPIKey) -} - -func startTestServers() (identityAPI, dnsAPI *httptest.Server) { - dnsAPI = httptest.NewServer(dnsMux()) - dnsEndpoint := dnsAPI.URL + "/123456" - - identityAPI = httptest.NewServer(identityHandler(dnsEndpoint)) - testAPIURL = identityAPI.URL + "/" - return -} - -func closeTestServers(identityAPI, dnsAPI *httptest.Server) { - identityAPI.Close() - dnsAPI.Close() -} - -func identityHandler(dnsEndpoint string) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - reqBody, err := ioutil.ReadAll(r.Body) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - return - } - resp, found := jsonMap[string(reqBody)] - if !found { - w.WriteHeader(http.StatusBadRequest) - return - } - resp = strings.Replace(resp, "https://dns.api.rackspacecloud.com/v1.0/123456", dnsEndpoint, 1) - w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, resp) - }) -} - -func dnsMux() *http.ServeMux { - mux := http.NewServeMux() - - // Used by `getHostedZoneID()` finding `zoneID` "?name=example.com" - mux.HandleFunc("/123456/domains", func(w http.ResponseWriter, r *http.Request) { - if r.URL.Query().Get("name") == "example.com" { - w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, jsonMap["zoneDetails"]) - return - } - w.WriteHeader(http.StatusBadRequest) - return - }) - - mux.HandleFunc("/123456/domains/112233/records", func(w http.ResponseWriter, r *http.Request) { - switch r.Method { - // Used by `Present()` creating the TXT record - case http.MethodPost: - reqBody, err := ioutil.ReadAll(r.Body) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - return - } - resp, found := jsonMap[string(reqBody)] - if !found { - w.WriteHeader(http.StatusBadRequest) - return - } - w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, resp) - // Used by `findTxtRecord()` finding `record.ID` "?type=TXT&name=_acme-challenge.example.com" - case http.MethodGet: - if r.URL.Query().Get("type") == "TXT" && r.URL.Query().Get("name") == "_acme-challenge.example.com" { - w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, jsonMap["recordDetails"]) - return - } - w.WriteHeader(http.StatusBadRequest) - return - // Used by `CleanUp()` deleting the TXT record "?id=445566" - case http.MethodDelete: - if r.URL.Query().Get("id") == "TXT-654321" { - w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, jsonMap["recordDelete"]) - return - } - w.WriteHeader(http.StatusBadRequest) - } - }) - - mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusNotFound) - fmt.Printf("Not Found for Request: (%+v)\n\n", r) - }) - - return mux -} - -func TestNewDNSProviderMissingCredErr(t *testing.T) { - testRackspaceEnv() - _, err := NewDNSProviderCredentials("", "") - assert.EqualError(t, err, "Rackspace credentials missing") -} - -func TestOfflineRackspaceValid(t *testing.T) { - testRackspaceEnv() - provider, err := NewDNSProviderCredentials(os.Getenv("RACKSPACE_USER"), os.Getenv("RACKSPACE_API_KEY")) - - assert.NoError(t, err) - assert.Equal(t, provider.token, "testToken", "The token should match") -} - -func TestOfflineRackspacePresent(t *testing.T) { - testRackspaceEnv() - provider, err := NewDNSProvider() - - if assert.NoError(t, err) { - err = provider.Present("example.com", "token", "keyAuth") - assert.NoError(t, err) - } -} - -func TestOfflineRackspaceCleanUp(t *testing.T) { - testRackspaceEnv() - provider, err := NewDNSProvider() - - if assert.NoError(t, err) { - err = provider.CleanUp("example.com", "token", "keyAuth") - assert.NoError(t, err) - } -} - -func TestNewDNSProviderValidEnv(t *testing.T) { - if !rackspaceLiveTest { - t.Skip("skipping live test") - } - - liveRackspaceEnv() - provider, err := NewDNSProvider() - assert.NoError(t, err) - assert.Contains(t, provider.cloudDNSEndpoint, "https://dns.api.rackspacecloud.com/v1.0/", "The endpoint URL should contain the base") -} - -func TestRackspacePresent(t *testing.T) { - if !rackspaceLiveTest { - t.Skip("skipping live test") - } - - liveRackspaceEnv() - provider, err := NewDNSProvider() - assert.NoError(t, err) - - err = provider.Present(rackspaceDomain, "", "112233445566==") - assert.NoError(t, err) -} - -func TestRackspaceCleanUp(t *testing.T) { - if !rackspaceLiveTest { - t.Skip("skipping live test") - } - - time.Sleep(time.Second * 15) - - liveRackspaceEnv() - provider, err := NewDNSProvider() - assert.NoError(t, err) - - err = provider.CleanUp(rackspaceDomain, "", "112233445566==") - assert.NoError(t, err) -} - -func TestMain(m *testing.M) { - identityAPI, dnsAPI := startTestServers() - defer closeTestServers(identityAPI, dnsAPI) - os.Exit(m.Run()) -} - -var jsonMap = map[string]string{ - `{"auth":{"RAX-KSKEY:apiKeyCredentials":{"username":"testUser","apiKey":"testKey"}}}`: `{"access":{"token":{"id":"testToken","expires":"1970-01-01T00:00:00.000Z","tenant":{"id":"123456","name":"123456"},"RAX-AUTH:authenticatedBy":["APIKEY"]},"serviceCatalog":[{"type":"rax:dns","endpoints":[{"publicURL":"https://dns.api.rackspacecloud.com/v1.0/123456","tenantId":"123456"}],"name":"cloudDNS"}],"user":{"id":"fakeUseID","name":"testUser"}}}`, - "zoneDetails": `{"domains":[{"name":"example.com","id":112233,"emailAddress":"hostmaster@example.com","updated":"1970-01-01T00:00:00.000+0000","created":"1970-01-01T00:00:00.000+0000"}],"totalEntries":1}`, - `{"records":[{"name":"_acme-challenge.example.com","type":"TXT","data":"pW9ZKG0xz_PCriK-nCMOjADy9eJcgGWIzkkj2fN4uZM","ttl":300}]}`: `{"request":"{\"records\":[{\"name\":\"_acme-challenge.example.com\",\"type\":\"TXT\",\"data\":\"pW9ZKG0xz_PCriK-nCMOjADy9eJcgGWIzkkj2fN4uZM\",\"ttl\":300}]}","status":"RUNNING","verb":"POST","jobId":"00000000-0000-0000-0000-0000000000","callbackUrl":"https://dns.api.rackspacecloud.com/v1.0/123456/status/00000000-0000-0000-0000-0000000000","requestUrl":"https://dns.api.rackspacecloud.com/v1.0/123456/domains/112233/records"}`, - "recordDetails": `{"records":[{"name":"_acme-challenge.example.com","id":"TXT-654321","type":"TXT","data":"pW9ZKG0xz_PCriK-nCMOjADy9eJcgGWIzkkj2fN4uZM","ttl":300,"updated":"1970-01-01T00:00:00.000+0000","created":"1970-01-01T00:00:00.000+0000"}]}`, - "recordDelete": `{"status":"RUNNING","verb":"DELETE","jobId":"00000000-0000-0000-0000-0000000000","callbackUrl":"https://dns.api.rackspacecloud.com/v1.0/123456/status/00000000-0000-0000-0000-0000000000","requestUrl":"https://dns.api.rackspacecloud.com/v1.0/123456/domains/112233/recordsid=TXT-654321"}`, -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/rfc2136/rfc2136.go b/vendor/github.com/xenolf/lego/providers/dns/rfc2136/rfc2136.go deleted file mode 100644 index dde42ddf1..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/rfc2136/rfc2136.go +++ /dev/null @@ -1,152 +0,0 @@ -// Package rfc2136 implements a DNS provider for solving the DNS-01 challenge -// using the rfc2136 dynamic update. -package rfc2136 - -import ( - "fmt" - "net" - "os" - "strings" - "time" - - "github.com/miekg/dns" - "github.com/xenolf/lego/acme" -) - -// DNSProvider is an implementation of the acme.ChallengeProvider interface that -// uses dynamic DNS updates (RFC 2136) to create TXT records on a nameserver. -type DNSProvider struct { - nameserver string - tsigAlgorithm string - tsigKey string - tsigSecret string - timeout time.Duration -} - -// NewDNSProvider returns a DNSProvider instance configured for rfc2136 -// dynamic update. Configured with environment variables: -// RFC2136_NAMESERVER: Network address in the form "host" or "host:port". -// RFC2136_TSIG_ALGORITHM: Defaults to hmac-md5.sig-alg.reg.int. (HMAC-MD5). -// See https://github.com/miekg/dns/blob/master/tsig.go for supported values. -// RFC2136_TSIG_KEY: Name of the secret key as defined in DNS server configuration. -// RFC2136_TSIG_SECRET: Secret key payload. -// RFC2136_TIMEOUT: DNS propagation timeout in time.ParseDuration format. (60s) -// To disable TSIG authentication, leave the RFC2136_TSIG* variables unset. -func NewDNSProvider() (*DNSProvider, error) { - nameserver := os.Getenv("RFC2136_NAMESERVER") - tsigAlgorithm := os.Getenv("RFC2136_TSIG_ALGORITHM") - tsigKey := os.Getenv("RFC2136_TSIG_KEY") - tsigSecret := os.Getenv("RFC2136_TSIG_SECRET") - timeout := os.Getenv("RFC2136_TIMEOUT") - return NewDNSProviderCredentials(nameserver, tsigAlgorithm, tsigKey, tsigSecret, timeout) -} - -// NewDNSProviderCredentials uses the supplied credentials to return a -// DNSProvider instance configured for rfc2136 dynamic update. To disable TSIG -// authentication, leave the TSIG parameters as empty strings. -// nameserver must be a network address in the form "host" or "host:port". -func NewDNSProviderCredentials(nameserver, tsigAlgorithm, tsigKey, tsigSecret, timeout string) (*DNSProvider, error) { - if nameserver == "" { - return nil, fmt.Errorf("RFC2136 nameserver missing") - } - - // Append the default DNS port if none is specified. - if _, _, err := net.SplitHostPort(nameserver); err != nil { - if strings.Contains(err.Error(), "missing port") { - nameserver = net.JoinHostPort(nameserver, "53") - } else { - return nil, err - } - } - d := &DNSProvider{ - nameserver: nameserver, - } - if tsigAlgorithm == "" { - tsigAlgorithm = dns.HmacMD5 - } - d.tsigAlgorithm = tsigAlgorithm - if len(tsigKey) > 0 && len(tsigSecret) > 0 { - d.tsigKey = tsigKey - d.tsigSecret = tsigSecret - } - - if timeout == "" { - d.timeout = 60 * time.Second - } else { - t, err := time.ParseDuration(timeout) - if err != nil { - return nil, err - } else if t < 0 { - return nil, fmt.Errorf("Invalid/negative RFC2136_TIMEOUT: %v", timeout) - } else { - d.timeout = t - } - } - - return d, nil -} - -// Returns the timeout configured with RFC2136_TIMEOUT, or 60s. -func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { - return d.timeout, 2 * time.Second -} - -// Present creates a TXT record using the specified parameters -func (r *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) - return r.changeRecord("INSERT", fqdn, value, ttl) -} - -// CleanUp removes the TXT record matching the specified parameters -func (r *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) - return r.changeRecord("REMOVE", fqdn, value, ttl) -} - -func (r *DNSProvider) changeRecord(action, fqdn, value string, ttl int) error { - // Find the zone for the given fqdn - zone, err := acme.FindZoneByFqdn(fqdn, []string{r.nameserver}) - if err != nil { - return err - } - - // Create RR - rr := new(dns.TXT) - rr.Hdr = dns.RR_Header{Name: fqdn, Rrtype: dns.TypeTXT, Class: dns.ClassINET, Ttl: uint32(ttl)} - rr.Txt = []string{value} - rrs := []dns.RR{rr} - - // Create dynamic update packet - m := new(dns.Msg) - m.SetUpdate(zone) - switch action { - case "INSERT": - // Always remove old challenge left over from who knows what. - m.RemoveRRset(rrs) - m.Insert(rrs) - case "REMOVE": - m.Remove(rrs) - default: - return fmt.Errorf("Unexpected action: %s", action) - } - - // Setup client - c := new(dns.Client) - c.SingleInflight = true - // TSIG authentication / msg signing - if len(r.tsigKey) > 0 && len(r.tsigSecret) > 0 { - m.SetTsig(dns.Fqdn(r.tsigKey), r.tsigAlgorithm, 300, time.Now().Unix()) - c.TsigSecret = map[string]string{dns.Fqdn(r.tsigKey): r.tsigSecret} - } - - // Send the query - reply, _, err := c.Exchange(m, r.nameserver) - if err != nil { - return fmt.Errorf("DNS update failed: %v", err) - } - if reply != nil && reply.Rcode != dns.RcodeSuccess { - return fmt.Errorf("DNS update failed. Server replied: %s", dns.RcodeToString[reply.Rcode]) - } - - return nil -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/rfc2136/rfc2136_test.go b/vendor/github.com/xenolf/lego/providers/dns/rfc2136/rfc2136_test.go deleted file mode 100644 index f3ca65b31..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/rfc2136/rfc2136_test.go +++ /dev/null @@ -1,244 +0,0 @@ -package rfc2136 - -import ( - "bytes" - "fmt" - "net" - "strings" - "sync" - "testing" - "time" - - "github.com/miekg/dns" - "github.com/xenolf/lego/acme" -) - -var ( - rfc2136TestDomain = "123456789.www.example.com" - rfc2136TestKeyAuth = "123d==" - rfc2136TestValue = "Now36o-3BmlB623-0c1qCIUmgWVVmDJb88KGl24pqpo" - rfc2136TestFqdn = "_acme-challenge.123456789.www.example.com." - rfc2136TestZone = "example.com." - rfc2136TestTTL = 120 - rfc2136TestTsigKey = "example.com." - rfc2136TestTsigSecret = "IwBTJx9wrDp4Y1RyC3H0gA==" -) - -var reqChan = make(chan *dns.Msg, 10) - -func TestRFC2136CanaryLocalTestServer(t *testing.T) { - acme.ClearFqdnCache() - dns.HandleFunc("example.com.", serverHandlerHello) - defer dns.HandleRemove("example.com.") - - server, addrstr, err := runLocalDNSTestServer("127.0.0.1:0", false) - if err != nil { - t.Fatalf("Failed to start test server: %v", err) - } - defer server.Shutdown() - - c := new(dns.Client) - m := new(dns.Msg) - m.SetQuestion("example.com.", dns.TypeTXT) - r, _, err := c.Exchange(m, addrstr) - if err != nil || len(r.Extra) == 0 { - t.Fatalf("Failed to communicate with test server: %v", err) - } - txt := r.Extra[0].(*dns.TXT).Txt[0] - if txt != "Hello world" { - t.Error("Expected test server to return 'Hello world' but got: ", txt) - } -} - -func TestRFC2136ServerSuccess(t *testing.T) { - acme.ClearFqdnCache() - dns.HandleFunc(rfc2136TestZone, serverHandlerReturnSuccess) - defer dns.HandleRemove(rfc2136TestZone) - - server, addrstr, err := runLocalDNSTestServer("127.0.0.1:0", false) - if err != nil { - t.Fatalf("Failed to start test server: %v", err) - } - defer server.Shutdown() - - provider, err := NewDNSProviderCredentials(addrstr, "", "", "", "") - if err != nil { - t.Fatalf("Expected NewDNSProviderCredentials() to return no error but the error was -> %v", err) - } - if err := provider.Present(rfc2136TestDomain, "", rfc2136TestKeyAuth); err != nil { - t.Errorf("Expected Present() to return no error but the error was -> %v", err) - } -} - -func TestRFC2136ServerError(t *testing.T) { - acme.ClearFqdnCache() - dns.HandleFunc(rfc2136TestZone, serverHandlerReturnErr) - defer dns.HandleRemove(rfc2136TestZone) - - server, addrstr, err := runLocalDNSTestServer("127.0.0.1:0", false) - if err != nil { - t.Fatalf("Failed to start test server: %v", err) - } - defer server.Shutdown() - - provider, err := NewDNSProviderCredentials(addrstr, "", "", "", "") - if err != nil { - t.Fatalf("Expected NewDNSProviderCredentials() to return no error but the error was -> %v", err) - } - if err := provider.Present(rfc2136TestDomain, "", rfc2136TestKeyAuth); err == nil { - t.Errorf("Expected Present() to return an error but it did not.") - } else if !strings.Contains(err.Error(), "NOTZONE") { - t.Errorf("Expected Present() to return an error with the 'NOTZONE' rcode string but it did not.") - } -} - -func TestRFC2136TsigClient(t *testing.T) { - acme.ClearFqdnCache() - dns.HandleFunc(rfc2136TestZone, serverHandlerReturnSuccess) - defer dns.HandleRemove(rfc2136TestZone) - - server, addrstr, err := runLocalDNSTestServer("127.0.0.1:0", true) - if err != nil { - t.Fatalf("Failed to start test server: %v", err) - } - defer server.Shutdown() - - provider, err := NewDNSProviderCredentials(addrstr, "", rfc2136TestTsigKey, rfc2136TestTsigSecret, "") - if err != nil { - t.Fatalf("Expected NewDNSProviderCredentials() to return no error but the error was -> %v", err) - } - if err := provider.Present(rfc2136TestDomain, "", rfc2136TestKeyAuth); err != nil { - t.Errorf("Expected Present() to return no error but the error was -> %v", err) - } -} - -func TestRFC2136ValidUpdatePacket(t *testing.T) { - acme.ClearFqdnCache() - dns.HandleFunc(rfc2136TestZone, serverHandlerPassBackRequest) - defer dns.HandleRemove(rfc2136TestZone) - - server, addrstr, err := runLocalDNSTestServer("127.0.0.1:0", false) - if err != nil { - t.Fatalf("Failed to start test server: %v", err) - } - defer server.Shutdown() - - txtRR, _ := dns.NewRR(fmt.Sprintf("%s %d IN TXT %s", rfc2136TestFqdn, rfc2136TestTTL, rfc2136TestValue)) - rrs := []dns.RR{txtRR} - m := new(dns.Msg) - m.SetUpdate(rfc2136TestZone) - m.RemoveRRset(rrs) - m.Insert(rrs) - expectstr := m.String() - expect, err := m.Pack() - if err != nil { - t.Fatalf("Error packing expect msg: %v", err) - } - - provider, err := NewDNSProviderCredentials(addrstr, "", "", "", "") - if err != nil { - t.Fatalf("Expected NewDNSProviderCredentials() to return no error but the error was -> %v", err) - } - - if err := provider.Present(rfc2136TestDomain, "", "1234d=="); err != nil { - t.Errorf("Expected Present() to return no error but the error was -> %v", err) - } - - rcvMsg := <-reqChan - rcvMsg.Id = m.Id - actual, err := rcvMsg.Pack() - if err != nil { - t.Fatalf("Error packing actual msg: %v", err) - } - - if !bytes.Equal(actual, expect) { - tmp := new(dns.Msg) - if err := tmp.Unpack(actual); err != nil { - t.Fatalf("Error unpacking actual msg: %v", err) - } - t.Errorf("Expected msg:\n%s", expectstr) - t.Errorf("Actual msg:\n%v", tmp) - } -} - -func runLocalDNSTestServer(listenAddr string, tsig bool) (*dns.Server, string, error) { - pc, err := net.ListenPacket("udp", listenAddr) - if err != nil { - return nil, "", err - } - server := &dns.Server{PacketConn: pc, ReadTimeout: time.Hour, WriteTimeout: time.Hour} - if tsig { - server.TsigSecret = map[string]string{rfc2136TestTsigKey: rfc2136TestTsigSecret} - } - - waitLock := sync.Mutex{} - waitLock.Lock() - server.NotifyStartedFunc = waitLock.Unlock - - go func() { - server.ActivateAndServe() - pc.Close() - }() - - waitLock.Lock() - return server, pc.LocalAddr().String(), nil -} - -func serverHandlerHello(w dns.ResponseWriter, req *dns.Msg) { - m := new(dns.Msg) - m.SetReply(req) - m.Extra = make([]dns.RR, 1) - m.Extra[0] = &dns.TXT{ - Hdr: dns.RR_Header{Name: m.Question[0].Name, Rrtype: dns.TypeTXT, Class: dns.ClassINET, Ttl: 0}, - Txt: []string{"Hello world"}, - } - w.WriteMsg(m) -} - -func serverHandlerReturnSuccess(w dns.ResponseWriter, req *dns.Msg) { - m := new(dns.Msg) - m.SetReply(req) - if req.Opcode == dns.OpcodeQuery && req.Question[0].Qtype == dns.TypeSOA && req.Question[0].Qclass == dns.ClassINET { - // Return SOA to appease findZoneByFqdn() - soaRR, _ := dns.NewRR(fmt.Sprintf("%s %d IN SOA ns1.%s admin.%s 2016022801 28800 7200 2419200 1200", rfc2136TestZone, rfc2136TestTTL, rfc2136TestZone, rfc2136TestZone)) - m.Answer = []dns.RR{soaRR} - } - - if t := req.IsTsig(); t != nil { - if w.TsigStatus() == nil { - // Validated - m.SetTsig(rfc2136TestZone, dns.HmacMD5, 300, time.Now().Unix()) - } - } - - w.WriteMsg(m) -} - -func serverHandlerReturnErr(w dns.ResponseWriter, req *dns.Msg) { - m := new(dns.Msg) - m.SetRcode(req, dns.RcodeNotZone) - w.WriteMsg(m) -} - -func serverHandlerPassBackRequest(w dns.ResponseWriter, req *dns.Msg) { - m := new(dns.Msg) - m.SetReply(req) - if req.Opcode == dns.OpcodeQuery && req.Question[0].Qtype == dns.TypeSOA && req.Question[0].Qclass == dns.ClassINET { - // Return SOA to appease findZoneByFqdn() - soaRR, _ := dns.NewRR(fmt.Sprintf("%s %d IN SOA ns1.%s admin.%s 2016022801 28800 7200 2419200 1200", rfc2136TestZone, rfc2136TestTTL, rfc2136TestZone, rfc2136TestZone)) - m.Answer = []dns.RR{soaRR} - } - - if t := req.IsTsig(); t != nil { - if w.TsigStatus() == nil { - // Validated - m.SetTsig(rfc2136TestZone, dns.HmacMD5, 300, time.Now().Unix()) - } - } - - w.WriteMsg(m) - if req.Opcode != dns.OpcodeQuery || req.Question[0].Qtype != dns.TypeSOA || req.Question[0].Qclass != dns.ClassINET { - // Only talk back when it is not the SOA RR. - reqChan <- req - } -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/route53/fixtures_test.go b/vendor/github.com/xenolf/lego/providers/dns/route53/fixtures_test.go deleted file mode 100644 index a5cc9c878..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/route53/fixtures_test.go +++ /dev/null @@ -1,39 +0,0 @@ -package route53 - -var ChangeResourceRecordSetsResponse = ` - - - /change/123456 - PENDING - 2016-02-10T01:36:41.958Z - -` - -var ListHostedZonesByNameResponse = ` - - - - /hostedzone/ABCDEFG - example.com. - D2224C5B-684A-DB4A-BB9A-E09E3BAFEA7A - - Test comment - false - - 10 - - - true - example2.com - ZLT12321321124 - 1 -` - -var GetChangeResponse = ` - - - 123456 - INSYNC - 2016-02-10T01:36:41.958Z - -` diff --git a/vendor/github.com/xenolf/lego/providers/dns/route53/route53.go b/vendor/github.com/xenolf/lego/providers/dns/route53/route53.go deleted file mode 100644 index 934f0a2d4..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/route53/route53.go +++ /dev/null @@ -1,185 +0,0 @@ -// Package route53 implements a DNS provider for solving the DNS-01 challenge -// using AWS Route 53 DNS. -package route53 - -import ( - "fmt" - "math/rand" - "os" - "strings" - "time" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/client" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/route53" - "github.com/xenolf/lego/acme" -) - -const ( - maxRetries = 5 - route53TTL = 10 -) - -// DNSProvider implements the acme.ChallengeProvider interface -type DNSProvider struct { - client *route53.Route53 - hostedZoneID string -} - -// customRetryer implements the client.Retryer interface by composing the -// DefaultRetryer. It controls the logic for retrying recoverable request -// errors (e.g. when rate limits are exceeded). -type customRetryer struct { - client.DefaultRetryer -} - -// RetryRules overwrites the DefaultRetryer's method. -// It uses a basic exponential backoff algorithm that returns an initial -// delay of ~400ms with an upper limit of ~30 seconds which should prevent -// causing a high number of consecutive throttling errors. -// For reference: Route 53 enforces an account-wide(!) 5req/s query limit. -func (d customRetryer) RetryRules(r *request.Request) time.Duration { - retryCount := r.RetryCount - if retryCount > 7 { - retryCount = 7 - } - - delay := (1 << uint(retryCount)) * (rand.Intn(50) + 200) - return time.Duration(delay) * time.Millisecond -} - -// NewDNSProvider returns a DNSProvider instance configured for the AWS -// Route 53 service. -// -// AWS Credentials are automatically detected in the following locations -// and prioritized in the following order: -// 1. Environment variables: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, -// AWS_REGION, [AWS_SESSION_TOKEN] -// 2. Shared credentials file (defaults to ~/.aws/credentials) -// 3. Amazon EC2 IAM role -// -// If AWS_HOSTED_ZONE_ID is not set, Lego tries to determine the correct -// public hosted zone via the FQDN. -// -// See also: https://github.com/aws/aws-sdk-go/wiki/configuring-sdk -func NewDNSProvider() (*DNSProvider, error) { - hostedZoneID := os.Getenv("AWS_HOSTED_ZONE_ID") - - r := customRetryer{} - r.NumMaxRetries = maxRetries - config := request.WithRetryer(aws.NewConfig(), r) - client := route53.New(session.New(config)) - - return &DNSProvider{ - client: client, - hostedZoneID: hostedZoneID, - }, nil -} - -// Present creates a TXT record using the specified parameters -func (r *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) - value = `"` + value + `"` - return r.changeRecord("UPSERT", fqdn, value, route53TTL) -} - -// CleanUp removes the TXT record matching the specified parameters -func (r *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) - value = `"` + value + `"` - return r.changeRecord("DELETE", fqdn, value, route53TTL) -} - -func (r *DNSProvider) changeRecord(action, fqdn, value string, ttl int) error { - hostedZoneID, err := r.getHostedZoneID(fqdn) - if err != nil { - return fmt.Errorf("Failed to determine Route 53 hosted zone ID: %v", err) - } - - recordSet := newTXTRecordSet(fqdn, value, ttl) - reqParams := &route53.ChangeResourceRecordSetsInput{ - HostedZoneId: aws.String(hostedZoneID), - ChangeBatch: &route53.ChangeBatch{ - Comment: aws.String("Managed by Lego"), - Changes: []*route53.Change{ - { - Action: aws.String(action), - ResourceRecordSet: recordSet, - }, - }, - }, - } - - resp, err := r.client.ChangeResourceRecordSets(reqParams) - if err != nil { - return fmt.Errorf("Failed to change Route 53 record set: %v", err) - } - - statusID := resp.ChangeInfo.Id - - return acme.WaitFor(120*time.Second, 4*time.Second, func() (bool, error) { - reqParams := &route53.GetChangeInput{ - Id: statusID, - } - resp, err := r.client.GetChange(reqParams) - if err != nil { - return false, fmt.Errorf("Failed to query Route 53 change status: %v", err) - } - if *resp.ChangeInfo.Status == route53.ChangeStatusInsync { - return true, nil - } - return false, nil - }) -} - -func (r *DNSProvider) getHostedZoneID(fqdn string) (string, error) { - if r.hostedZoneID != "" { - return r.hostedZoneID, nil - } - - authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) - if err != nil { - return "", err - } - - // .DNSName should not have a trailing dot - reqParams := &route53.ListHostedZonesByNameInput{ - DNSName: aws.String(acme.UnFqdn(authZone)), - } - resp, err := r.client.ListHostedZonesByName(reqParams) - if err != nil { - return "", err - } - - var hostedZoneID string - for _, hostedZone := range resp.HostedZones { - // .Name has a trailing dot - if !*hostedZone.Config.PrivateZone && *hostedZone.Name == authZone { - hostedZoneID = *hostedZone.Id - break - } - } - - if len(hostedZoneID) == 0 { - return "", fmt.Errorf("Zone %s not found in Route 53 for domain %s", authZone, fqdn) - } - - if strings.HasPrefix(hostedZoneID, "/hostedzone/") { - hostedZoneID = strings.TrimPrefix(hostedZoneID, "/hostedzone/") - } - - return hostedZoneID, nil -} - -func newTXTRecordSet(fqdn, value string, ttl int) *route53.ResourceRecordSet { - return &route53.ResourceRecordSet{ - Name: aws.String(fqdn), - Type: aws.String("TXT"), - TTL: aws.Int64(int64(ttl)), - ResourceRecords: []*route53.ResourceRecord{ - {Value: aws.String(value)}, - }, - } -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/route53/route53_integration_test.go b/vendor/github.com/xenolf/lego/providers/dns/route53/route53_integration_test.go deleted file mode 100644 index 17ba4a08a..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/route53/route53_integration_test.go +++ /dev/null @@ -1,70 +0,0 @@ -package route53 - -import ( - "fmt" - "os" - "testing" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/route53" -) - -func TestRoute53TTL(t *testing.T) { - - m, err := testGetAndPreCheck() - if err != nil { - t.Skip(err.Error()) - } - - provider, err := NewDNSProvider() - if err != nil { - t.Fatalf("Fatal: %s", err.Error()) - } - - err = provider.Present(m["route53Domain"], "foo", "bar") - if err != nil { - t.Fatalf("Fatal: %s", err.Error()) - } - // we need a separate R53 client here as the one in the DNS provider is - // unexported. - fqdn := "_acme-challenge." + m["route53Domain"] + "." - svc := route53.New(session.New()) - zoneID, err := provider.getHostedZoneID(fqdn) - if err != nil { - provider.CleanUp(m["route53Domain"], "foo", "bar") - t.Fatalf("Fatal: %s", err.Error()) - } - params := &route53.ListResourceRecordSetsInput{ - HostedZoneId: aws.String(zoneID), - } - resp, err := svc.ListResourceRecordSets(params) - if err != nil { - provider.CleanUp(m["route53Domain"], "foo", "bar") - t.Fatalf("Fatal: %s", err.Error()) - } - - for _, v := range resp.ResourceRecordSets { - if *v.Name == fqdn && *v.Type == "TXT" && *v.TTL == 10 { - provider.CleanUp(m["route53Domain"], "foo", "bar") - return - } - } - provider.CleanUp(m["route53Domain"], "foo", "bar") - t.Fatalf("Could not find a TXT record for _acme-challenge.%s with a TTL of 10", m["route53Domain"]) -} - -func testGetAndPreCheck() (map[string]string, error) { - m := map[string]string{ - "route53Key": os.Getenv("AWS_ACCESS_KEY_ID"), - "route53Secret": os.Getenv("AWS_SECRET_ACCESS_KEY"), - "route53Region": os.Getenv("AWS_REGION"), - "route53Domain": os.Getenv("R53_DOMAIN"), - } - for _, v := range m { - if v == "" { - return nil, fmt.Errorf("AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION, and R53_DOMAIN are needed to run this test") - } - } - return m, nil -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/route53/route53_test.go b/vendor/github.com/xenolf/lego/providers/dns/route53/route53_test.go deleted file mode 100644 index de4e28f3d..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/route53/route53_test.go +++ /dev/null @@ -1,105 +0,0 @@ -package route53 - -import ( - "net/http/httptest" - "os" - "testing" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/credentials" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/route53" - "github.com/stretchr/testify/assert" -) - -var ( - route53Secret string - route53Key string - route53Region string - route53Zone string -) - -func init() { - route53Key = os.Getenv("AWS_ACCESS_KEY_ID") - route53Secret = os.Getenv("AWS_SECRET_ACCESS_KEY") - route53Region = os.Getenv("AWS_REGION") - route53Zone = os.Getenv("AWS_HOSTED_ZONE_ID") -} - -func restoreRoute53Env() { - os.Setenv("AWS_ACCESS_KEY_ID", route53Key) - os.Setenv("AWS_SECRET_ACCESS_KEY", route53Secret) - os.Setenv("AWS_REGION", route53Region) - os.Setenv("AWS_HOSTED_ZONE_ID", route53Zone) -} - -func makeRoute53Provider(ts *httptest.Server) *DNSProvider { - config := &aws.Config{ - Credentials: credentials.NewStaticCredentials("abc", "123", " "), - Endpoint: aws.String(ts.URL), - Region: aws.String("mock-region"), - MaxRetries: aws.Int(1), - } - - client := route53.New(session.New(config)) - return &DNSProvider{client: client} -} - -func TestCredentialsFromEnv(t *testing.T) { - os.Setenv("AWS_ACCESS_KEY_ID", "123") - os.Setenv("AWS_SECRET_ACCESS_KEY", "123") - os.Setenv("AWS_REGION", "us-east-1") - - config := &aws.Config{ - CredentialsChainVerboseErrors: aws.Bool(true), - } - - sess := session.New(config) - _, err := sess.Config.Credentials.Get() - assert.NoError(t, err, "Expected credentials to be set from environment") - - restoreRoute53Env() -} - -func TestRegionFromEnv(t *testing.T) { - os.Setenv("AWS_REGION", "us-east-1") - - sess := session.New(aws.NewConfig()) - assert.Equal(t, "us-east-1", *sess.Config.Region, "Expected Region to be set from environment") - - restoreRoute53Env() -} - -func TestHostedZoneIDFromEnv(t *testing.T) { - const testZoneID = "testzoneid" - - defer restoreRoute53Env() - os.Setenv("AWS_HOSTED_ZONE_ID", testZoneID) - - provider, err := NewDNSProvider() - assert.NoError(t, err, "Expected no error constructing DNSProvider") - - fqdn, err := provider.getHostedZoneID("whatever") - assert.NoError(t, err, "Expected FQDN to be resolved to environment variable value") - - assert.Equal(t, testZoneID, fqdn) -} - -func TestRoute53Present(t *testing.T) { - mockResponses := MockResponseMap{ - "/2013-04-01/hostedzonesbyname": MockResponse{StatusCode: 200, Body: ListHostedZonesByNameResponse}, - "/2013-04-01/hostedzone/ABCDEFG/rrset/": MockResponse{StatusCode: 200, Body: ChangeResourceRecordSetsResponse}, - "/2013-04-01/change/123456": MockResponse{StatusCode: 200, Body: GetChangeResponse}, - } - - ts := newMockServer(t, mockResponses) - defer ts.Close() - - provider := makeRoute53Provider(ts) - - domain := "example.com" - keyAuth := "123456d==" - - err := provider.Present(domain, "", keyAuth) - assert.NoError(t, err, "Expected Present to return no error") -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/route53/testutil_test.go b/vendor/github.com/xenolf/lego/providers/dns/route53/testutil_test.go deleted file mode 100644 index e448a6858..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/route53/testutil_test.go +++ /dev/null @@ -1,38 +0,0 @@ -package route53 - -import ( - "fmt" - "net/http" - "net/http/httptest" - "testing" - "time" - - "github.com/stretchr/testify/require" -) - -// MockResponse represents a predefined response used by a mock server -type MockResponse struct { - StatusCode int - Body string -} - -// MockResponseMap maps request paths to responses -type MockResponseMap map[string]MockResponse - -func newMockServer(t *testing.T, responses MockResponseMap) *httptest.Server { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - path := r.URL.Path - resp, ok := responses[path] - if !ok { - msg := fmt.Sprintf("Requested path not found in response map: %s", path) - require.FailNow(t, msg) - } - - w.Header().Set("Content-Type", "application/xml") - w.WriteHeader(resp.StatusCode) - w.Write([]byte(resp.Body)) - })) - - time.Sleep(100 * time.Millisecond) - return ts -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/vultr/vultr.go b/vendor/github.com/xenolf/lego/providers/dns/vultr/vultr.go deleted file mode 100644 index bc2067579..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/vultr/vultr.go +++ /dev/null @@ -1,127 +0,0 @@ -// Package vultr implements a DNS provider for solving the DNS-01 challenge using -// the vultr DNS. -// See https://www.vultr.com/api/#dns -package vultr - -import ( - "fmt" - "os" - "strings" - - vultr "github.com/JamesClonk/vultr/lib" - "github.com/xenolf/lego/acme" -) - -// DNSProvider is an implementation of the acme.ChallengeProvider interface. -type DNSProvider struct { - client *vultr.Client -} - -// NewDNSProvider returns a DNSProvider instance with a configured Vultr client. -// Authentication uses the VULTR_API_KEY environment variable. -func NewDNSProvider() (*DNSProvider, error) { - apiKey := os.Getenv("VULTR_API_KEY") - return NewDNSProviderCredentials(apiKey) -} - -// NewDNSProviderCredentials uses the supplied credentials to return a DNSProvider -// instance configured for Vultr. -func NewDNSProviderCredentials(apiKey string) (*DNSProvider, error) { - if apiKey == "" { - return nil, fmt.Errorf("Vultr credentials missing") - } - - c := &DNSProvider{ - client: vultr.NewClient(apiKey, nil), - } - - return c, nil -} - -// Present creates a TXT record to fulfil the DNS-01 challenge. -func (c *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) - - zoneDomain, err := c.getHostedZone(domain) - if err != nil { - return err - } - - name := c.extractRecordName(fqdn, zoneDomain) - - err = c.client.CreateDNSRecord(zoneDomain, name, "TXT", `"`+value+`"`, 0, ttl) - if err != nil { - return fmt.Errorf("Vultr API call failed: %v", err) - } - - return nil -} - -// CleanUp removes the TXT record matching the specified parameters. -func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - - zoneDomain, records, err := c.findTxtRecords(domain, fqdn) - if err != nil { - return err - } - - for _, rec := range records { - err := c.client.DeleteDNSRecord(zoneDomain, rec.RecordID) - if err != nil { - return err - } - } - return nil -} - -func (c *DNSProvider) getHostedZone(domain string) (string, error) { - domains, err := c.client.GetDNSDomains() - if err != nil { - return "", fmt.Errorf("Vultr API call failed: %v", err) - } - - var hostedDomain vultr.DNSDomain - for _, d := range domains { - if strings.HasSuffix(domain, d.Domain) { - if len(d.Domain) > len(hostedDomain.Domain) { - hostedDomain = d - } - } - } - if hostedDomain.Domain == "" { - return "", fmt.Errorf("No matching Vultr domain found for domain %s", domain) - } - - return hostedDomain.Domain, nil -} - -func (c *DNSProvider) findTxtRecords(domain, fqdn string) (string, []vultr.DNSRecord, error) { - zoneDomain, err := c.getHostedZone(domain) - if err != nil { - return "", nil, err - } - - var records []vultr.DNSRecord - result, err := c.client.GetDNSRecords(zoneDomain) - if err != nil { - return "", records, fmt.Errorf("Vultr API call has failed: %v", err) - } - - recordName := c.extractRecordName(fqdn, zoneDomain) - for _, record := range result { - if record.Type == "TXT" && record.Name == recordName { - records = append(records, record) - } - } - - return zoneDomain, records, nil -} - -func (c *DNSProvider) extractRecordName(fqdn, domain string) string { - name := acme.UnFqdn(fqdn) - if idx := strings.Index(name, "."+domain); idx != -1 { - return name[:idx] - } - return name -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/vultr/vultr_test.go b/vendor/github.com/xenolf/lego/providers/dns/vultr/vultr_test.go deleted file mode 100644 index 7c8cdaf1e..000000000 --- a/vendor/github.com/xenolf/lego/providers/dns/vultr/vultr_test.go +++ /dev/null @@ -1,65 +0,0 @@ -package vultr - -import ( - "os" - "testing" - "time" - - "github.com/stretchr/testify/assert" -) - -var ( - liveTest bool - apiKey string - domain string -) - -func init() { - apiKey = os.Getenv("VULTR_API_KEY") - domain = os.Getenv("VULTR_TEST_DOMAIN") - liveTest = len(apiKey) > 0 && len(domain) > 0 -} - -func restoreEnv() { - os.Setenv("VULTR_API_KEY", apiKey) -} - -func TestNewDNSProviderValidEnv(t *testing.T) { - os.Setenv("VULTR_API_KEY", "123") - defer restoreEnv() - _, err := NewDNSProvider() - assert.NoError(t, err) -} - -func TestNewDNSProviderMissingCredErr(t *testing.T) { - os.Setenv("VULTR_API_KEY", "") - defer restoreEnv() - _, err := NewDNSProvider() - assert.EqualError(t, err, "Vultr credentials missing") -} - -func TestLivePresent(t *testing.T) { - if !liveTest { - t.Skip("skipping live test") - } - - provider, err := NewDNSProvider() - assert.NoError(t, err) - - err = provider.Present(domain, "", "123d==") - assert.NoError(t, err) -} - -func TestLiveCleanUp(t *testing.T) { - if !liveTest { - t.Skip("skipping live test") - } - - time.Sleep(time.Second * 1) - - provider, err := NewDNSProvider() - assert.NoError(t, err) - - err = provider.CleanUp(domain, "", "123d==") - assert.NoError(t, err) -} diff --git a/vendor/github.com/xenolf/lego/providers/http/memcached/README.md b/vendor/github.com/xenolf/lego/providers/http/memcached/README.md deleted file mode 100644 index f14d216df..000000000 --- a/vendor/github.com/xenolf/lego/providers/http/memcached/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# Memcached http provider - -Publishes challenges into memcached where they can be retrieved by nginx. Allows -specifying multiple memcached servers and the responses will be published to all -of them, making it easier to verify when your domain is hosted on a cluster of -servers. - -Example nginx config: - -``` - location /.well-known/acme-challenge/ { - set $memcached_key "$uri"; - memcached_pass 127.0.0.1:11211; - } -``` diff --git a/vendor/github.com/xenolf/lego/providers/http/memcached/memcached.go b/vendor/github.com/xenolf/lego/providers/http/memcached/memcached.go deleted file mode 100644 index 9ac8b811d..000000000 --- a/vendor/github.com/xenolf/lego/providers/http/memcached/memcached.go +++ /dev/null @@ -1,60 +0,0 @@ -// Package memcached implements a HTTP provider for solving the HTTP-01 challenge using memcached -// in combination with a webserver. -package memcached - -import ( - "fmt" - "path" - - "github.com/rainycape/memcache" - "github.com/xenolf/lego/acme" -) - -// HTTPProvider implements ChallengeProvider for `http-01` challenge -type MemcachedProvider struct { - hosts []string -} - -// NewHTTPProvider returns a HTTPProvider instance with a configured webroot path -func NewMemcachedProvider(hosts []string) (*MemcachedProvider, error) { - if len(hosts) == 0 { - return nil, fmt.Errorf("No memcached hosts provided") - } - - c := &MemcachedProvider{ - hosts: hosts, - } - - return c, nil -} - -// Present makes the token available at `HTTP01ChallengePath(token)` by creating a file in the given webroot path -func (w *MemcachedProvider) Present(domain, token, keyAuth string) error { - var errs []error - - challengePath := path.Join("/", acme.HTTP01ChallengePath(token)) - for _, host := range w.hosts { - mc, err := memcache.New(host) - if err != nil { - errs = append(errs, err) - continue - } - mc.Add(&memcache.Item{ - Key: challengePath, - Value: []byte(keyAuth), - Expiration: 60, - }) - } - - if len(errs) == len(w.hosts) { - return fmt.Errorf("Unable to store key in any of the memcache hosts -> %v", errs) - } - - return nil -} - -// CleanUp removes the file created for the challenge -func (w *MemcachedProvider) CleanUp(domain, token, keyAuth string) error { - // Memcached will clean up itself, that's what expiration is for. - return nil -} diff --git a/vendor/github.com/xenolf/lego/providers/http/memcached/memcached_test.go b/vendor/github.com/xenolf/lego/providers/http/memcached/memcached_test.go deleted file mode 100644 index 287a33304..000000000 --- a/vendor/github.com/xenolf/lego/providers/http/memcached/memcached_test.go +++ /dev/null @@ -1,111 +0,0 @@ -package memcached - -import ( - "os" - "path" - "strings" - "testing" - - "github.com/rainycape/memcache" - "github.com/stretchr/testify/assert" - "github.com/xenolf/lego/acme" -) - -var ( - memcachedHosts []string -) - -const ( - domain = "lego.test" - token = "foo" - keyAuth = "bar" -) - -func init() { - memcachedHostsStr := os.Getenv("MEMCACHED_HOSTS") - if len(memcachedHostsStr) > 0 { - memcachedHosts = strings.Split(memcachedHostsStr, ",") - } -} - -func TestNewMemcachedProviderEmpty(t *testing.T) { - emptyHosts := make([]string, 0) - _, err := NewMemcachedProvider(emptyHosts) - assert.EqualError(t, err, "No memcached hosts provided") -} - -func TestNewMemcachedProviderValid(t *testing.T) { - if len(memcachedHosts) == 0 { - t.Skip("Skipping memcached tests") - } - _, err := NewMemcachedProvider(memcachedHosts) - assert.NoError(t, err) -} - -func TestMemcachedPresentSingleHost(t *testing.T) { - if len(memcachedHosts) == 0 { - t.Skip("Skipping memcached tests") - } - p, err := NewMemcachedProvider(memcachedHosts[0:1]) - assert.NoError(t, err) - - challengePath := path.Join("/", acme.HTTP01ChallengePath(token)) - - err = p.Present(domain, token, keyAuth) - assert.NoError(t, err) - mc, err := memcache.New(memcachedHosts[0]) - assert.NoError(t, err) - i, err := mc.Get(challengePath) - assert.NoError(t, err) - assert.Equal(t, i.Value, []byte(keyAuth)) -} - -func TestMemcachedPresentMultiHost(t *testing.T) { - if len(memcachedHosts) <= 1 { - t.Skip("Skipping memcached multi-host tests") - } - p, err := NewMemcachedProvider(memcachedHosts) - assert.NoError(t, err) - - challengePath := path.Join("/", acme.HTTP01ChallengePath(token)) - - err = p.Present(domain, token, keyAuth) - assert.NoError(t, err) - for _, host := range memcachedHosts { - mc, err := memcache.New(host) - assert.NoError(t, err) - i, err := mc.Get(challengePath) - assert.NoError(t, err) - assert.Equal(t, i.Value, []byte(keyAuth)) - } -} - -func TestMemcachedPresentPartialFailureMultiHost(t *testing.T) { - if len(memcachedHosts) == 0 { - t.Skip("Skipping memcached tests") - } - hosts := append(memcachedHosts, "5.5.5.5:11211") - p, err := NewMemcachedProvider(hosts) - assert.NoError(t, err) - - challengePath := path.Join("/", acme.HTTP01ChallengePath(token)) - - err = p.Present(domain, token, keyAuth) - assert.NoError(t, err) - for _, host := range memcachedHosts { - mc, err := memcache.New(host) - assert.NoError(t, err) - i, err := mc.Get(challengePath) - assert.NoError(t, err) - assert.Equal(t, i.Value, []byte(keyAuth)) - } -} - -func TestMemcachedCleanup(t *testing.T) { - if len(memcachedHosts) == 0 { - t.Skip("Skipping memcached tests") - } - p, err := NewMemcachedProvider(memcachedHosts) - assert.NoError(t, err) - assert.NoError(t, p.CleanUp(domain, token, keyAuth)) -} diff --git a/vendor/github.com/xenolf/lego/providers/http/webroot/webroot.go b/vendor/github.com/xenolf/lego/providers/http/webroot/webroot.go deleted file mode 100644 index 4bf211f39..000000000 --- a/vendor/github.com/xenolf/lego/providers/http/webroot/webroot.go +++ /dev/null @@ -1,58 +0,0 @@ -// Package webroot implements a HTTP provider for solving the HTTP-01 challenge using web server's root path. -package webroot - -import ( - "fmt" - "io/ioutil" - "os" - "path" - - "github.com/xenolf/lego/acme" -) - -// HTTPProvider implements ChallengeProvider for `http-01` challenge -type HTTPProvider struct { - path string -} - -// NewHTTPProvider returns a HTTPProvider instance with a configured webroot path -func NewHTTPProvider(path string) (*HTTPProvider, error) { - if _, err := os.Stat(path); os.IsNotExist(err) { - return nil, fmt.Errorf("Webroot path does not exist") - } - - c := &HTTPProvider{ - path: path, - } - - return c, nil -} - -// Present makes the token available at `HTTP01ChallengePath(token)` by creating a file in the given webroot path -func (w *HTTPProvider) Present(domain, token, keyAuth string) error { - var err error - - challengeFilePath := path.Join(w.path, acme.HTTP01ChallengePath(token)) - err = os.MkdirAll(path.Dir(challengeFilePath), 0755) - if err != nil { - return fmt.Errorf("Could not create required directories in webroot for HTTP challenge -> %v", err) - } - - err = ioutil.WriteFile(challengeFilePath, []byte(keyAuth), 0644) - if err != nil { - return fmt.Errorf("Could not write file in webroot for HTTP challenge -> %v", err) - } - - return nil -} - -// CleanUp removes the file created for the challenge -func (w *HTTPProvider) CleanUp(domain, token, keyAuth string) error { - var err error - err = os.Remove(path.Join(w.path, acme.HTTP01ChallengePath(token))) - if err != nil { - return fmt.Errorf("Could not remove file in webroot after HTTP challenge -> %v", err) - } - - return nil -} diff --git a/vendor/github.com/xenolf/lego/providers/http/webroot/webroot_test.go b/vendor/github.com/xenolf/lego/providers/http/webroot/webroot_test.go deleted file mode 100644 index 99c930ed3..000000000 --- a/vendor/github.com/xenolf/lego/providers/http/webroot/webroot_test.go +++ /dev/null @@ -1,46 +0,0 @@ -package webroot - -import ( - "io/ioutil" - "os" - "testing" -) - -func TestHTTPProvider(t *testing.T) { - webroot := "webroot" - domain := "domain" - token := "token" - keyAuth := "keyAuth" - challengeFilePath := webroot + "/.well-known/acme-challenge/" + token - - os.MkdirAll(webroot+"/.well-known/acme-challenge", 0777) - defer os.RemoveAll(webroot) - - provider, err := NewHTTPProvider(webroot) - if err != nil { - t.Errorf("Webroot provider error: got %v, want nil", err) - } - - err = provider.Present(domain, token, keyAuth) - if err != nil { - t.Errorf("Webroot provider present() error: got %v, want nil", err) - } - - if _, err := os.Stat(challengeFilePath); os.IsNotExist(err) { - t.Error("Challenge file was not created in webroot") - } - - data, err := ioutil.ReadFile(challengeFilePath) - if err != nil { - t.Errorf("Webroot provider ReadFile() error: got %v, want nil", err) - } - dataStr := string(data) - if dataStr != keyAuth { - t.Errorf("Challenge file content: got %q, want %q", dataStr, keyAuth) - } - - err = provider.CleanUp(domain, token, keyAuth) - if err != nil { - t.Errorf("Webroot provider CleanUp() error: got %v, want nil", err) - } -} -- cgit v1.2.3-1-g7c22