diff --git a/.dockerignore b/.dockerignore index ae055285..e27fb10a 100644 --- a/.dockerignore +++ b/.dockerignore @@ -5,3 +5,4 @@ lego.exe .vscode/ dist/ builds/ +docs/ diff --git a/.travis.yml b/.travis.yml index 69e3fbd6..5cc08977 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,12 +1,14 @@ language: go go: - - 1.10.x + - 1.12.x - 1.x services: - memcached +go_import_path: github.com/xenolf/lego + addons: hosts: # for e2e tests @@ -33,6 +35,10 @@ before_install: - curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b $GOPATH/bin v1.15.0 - golangci-lint --version + # Hugo - documentation + - wget -O /tmp/hugo.deb https://github.com/gohugoio/hugo/releases/download/v0.54.0/hugo_0.54.0_Linux-64bit.deb + - sudo dpkg -i /tmp/hugo.deb + install: - echo "TRAVIS_GO_VERSION=$TRAVIS_GO_VERSION" - dep status -v @@ -40,6 +46,13 @@ install: after_success: - make clean +before_deploy: + - > + if ! [ "$BEFORE_DEPLOY_RUN" ]; then + export BEFORE_DEPLOY_RUN=1; + make docs-build + fi + deploy: - provider: script skip_cleanup: true @@ -57,3 +70,11 @@ deploy: on: tags: true condition: $TRAVIS_GO_VERSION =~ ^1\.x$ + + - provider: pages + local_dir: docs/public + skip_cleanup: true + github_token: ${GITHUB_TOKEN} + on: + tags: true + condition: $TRAVIS_GO_VERSION =~ ^1\.x$ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8a4afbd5..0f2ef95f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -73,62 +73,3 @@ git push -u origin my-feature ## create a pull request on Github ## ``` - - -## DNS Providers: API references - -| DNS provider | Code | Documentation | Go client | -|---------------------------|----------------|--------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------| -| Acme DNS | `acmedns` | [documentation](https://github.com/joohoi/acme-dns#api) | [Go client](https://github.com/cpu/goacmedns) | -| Alibaba Cloud | `alidns` | [documentation](https://www.alibabacloud.com/help/doc-detail/42875.htm) | [Go client](https://github.com/aliyun/alibaba-cloud-sdk-go) | -| Aurora DNS | `auroradns` | [documentation](https://libcloud.readthedocs.io/en/latest/dns/drivers/auroradns.html#api-docs) | [Go client](https://github.com/nrdcg/auroradns) | -| Azure | `azure` | [documentation](https://docs.microsoft.com/en-us/go/azure/) | [Go client](https://github.com/Azure/azure-sdk-for-go) | -| Bluecat | `bluecat` | ? | - | -| Cloudflare | `cloudflare` | [documentation](https://api.cloudflare.com/) | [Go client](https://github.com/cloudflare/cloudflare-go) | -| ClouDNS | `cloudns` | [documentation](https://www.cloudns.net/wiki/article/42/) | - | -| CloudXNS | `cloudxns` | [documentation](https://www.cloudxns.net/Public/Doc/CloudXNS_api2.0_doc_zh-cn.zip) | - | -| ConoHa | `conoha` | [documentation](https://www.conoha.jp/docs/) | - | -| Openstack Designate | `designate` | [documentation](https://docs.openstack.org/designate/latest/) | [Go client](https://godoc.org/github.com/gophercloud/gophercloud/openstack/dns/v2) | -| Digital Ocean | `digitalocean` | [documentation](https://developers.digitalocean.com/documentation/v2/#domain-records) | - | -| DNSimple | `dnsimple` | [documentation](https://developer.dnsimple.com/v2/) | [Go client](https://github.com/dnsimple/dnsimple-go) | -| DNS Made Easy | `dnsmadeeasy` | [documentation](https://api-docs.dnsmadeeasy.com/) | - | -| DNSPod | `dnspod` | [documentation](https://www.dnspod.cn/docs/index.html) | [Go client](https://github.com/decker502/dnspod-go) | -| DreamHost | `dreamhost` | [documentation](https://help.dreamhost.com/hc/en-us/articles/217560167-API_overview) | - | -| Duck DNS | `duckdns` | [documentation](https://www.duckdns.org/spec.jsp) | - | -| Dyn | `dyn` | [documentation](https://help.dyn.com/rest/) | - | -| exec | `exec` | - | - | -| Exoscale | `exoscale` | [documentation](https://community.exoscale.com/documentation/dns/api/) | [Go client](https://github.com/exoscale/egoscale) | -| FastDNS | `fastdns` | [documentation](https://developer.akamai.com/api/web_performance/fast_dns_record_management/v1.html) | [Go client](https://github.com/akamai/AkamaiOPEN-edgegrid-golang) | -| Gandi | `gandi` | [documentation](http://doc.rpc.gandi.net/index.html) | - | -| Gandi v5 | `gandiv5` | [documentation](http://doc.livedns.gandi.net) | - | -| Google Cloud | `gcloud` | ? | [Go client](https://github.com/googleapis/google-api-go-client) | -| Glesys | `glesys` | [documentation](https://github.com/GleSYS/API/wiki/API-Documentation) | - | -| Go Daddy | `godaddy` | [documentation](https://developer.godaddy.com/doc/endpoint/domains) | - | -| hosting.de | `hostingde` | [documentation](https://www.hosting.de/api/#dns) | - | -| Internet Initiative Japan | `iij` | [documentation](http://manual.iij.jp/p2/pubapi/) | [Go client](https://github.com/iij/doapi) | -| INWX | `inwx` | [documentation](https://www.inwx.de/en/help/apidoc) | [Go client](https://github.com/nrdcg/goinwx) | -| Lightsail | `lightsail` | ? | [Go client](https://github.com/aws/aws-sdk-go/aws) | -| Linode (deprecated) | `linode` | [documentation](https://www.linode.com/api/dns) | [Go client](https://github.com/timewasted/linode) | -| Linodev4 | `linodev4` | [documentation](https://developers.linode.com/api/v4) | [Go client](https://github.com/linode/linodego) | -| Namecheap | `namecheap` | [documentation](https://www.namecheap.com/support/api/methods.aspx) | - | -| Name.com | `namedotcom` | [documentation](https://www.name.com/api-docs/DNS) | [Go client](https://github.com/namedotcom/go) | -| manual | `manual` | - | - | -| MyDNS.jp | `mydnsjp` | [documentation](https://www.mydns.jp/?MENU=030) | - | -| Netcup | `netcup` | [documentation](https://www.netcup-wiki.de/wiki/DNS_API) | - | -| NIFCloud | `nifcloud` | [documentation](https://mbaas.nifcloud.com/doc/current/rest/common/format.html) | - | -| NS1 | `ns1` | [documentation](https://ns1.com/api) | [Go client](https://github.com/ns1/ns1-go) | -| Open Telekom Cloud | `otc` | [documentation](https://docs.otc.t-systems.com/en-us/dns/index.html) | - | -| Oracle Cloud | `oraclecloud` | [documentation](https://docs.cloud.oracle.com/iaas/Content/DNS/Concepts/dnszonemanagement.htm) | [Go Client](https://github.com/oracle/oci-go-sdk) | -| OVH | `ovh` | [documentation](https://eu.api.ovh.com/) | [Go client](https://github.com/ovh/go-ovh) | -| PowerDNS | `pdns` | [documentation](https://doc.powerdns.com/md/httpapi/README/) | - | -| Rackspace | `rackspace` | [documentation](https://developer.rackspace.com/docs/cloud-dns/v1/) | - | -| RFC2136 | `rfc2136` | [documentation](https://tools.ietf.org/html/rfc2136) | - | -| Route 53 | `route53` | [documentation](https://docs.aws.amazon.com/Route53/latest/APIReference/API_Operations_Amazon_Route_53.html) | [Go client](https://github.com/aws/aws-sdk-go/aws) | -| Sakura Cloud | `sakuracloud` | [documentation](https://developer.sakura.ad.jp/cloud/api/1.1/) | [Go client](https://github.com/sacloud/libsacloud) | -| Selectel | `selectel` | [documentation](https://kb.selectel.com/23136054.html) | - | -| Stackpath | `stackpath` | [documentation](https://developer.stackpath.com/en/api/dns/#tag/Zone) | - | -| TransIP | `transip` | [documentation](https://api.transip.nl/docs/transip.nl/package-Transip.html) | [Go client](https://github.com/transip/gotransip) | -| VegaDNS | `vegadns` | [documentation](https://github.com/shupp/VegaDNS-API) | [Go client](https://github.com/OpenDNS/vegadns2client) | -| Vultr | `vultr` | [documentation](https://www.vultr.com/api/#dns) | [Go client](https://github.com/JamesClonk/vultr) | -| Vscale | `vscale` | [documentation](https://developers.vscale.io/documentation/api/v1/#api-Domains_Records) | - | -| Zone.ee | `zone` | [documentation](https://api.zone.eu/v2) | - | diff --git a/Dockerfile b/Dockerfile index cb942944..ed36091f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:alpine3.8 as builder +FROM golang:alpine3.9 as builder RUN apk --update upgrade \ && apk --no-cache --no-progress add make git \ @@ -8,7 +8,7 @@ WORKDIR /go/src/github.com/xenolf/lego COPY . . RUN make build -FROM alpine:3.8 +FROM alpine:3.9 RUN apk update && apk add --no-cache --virtual ca-certificates COPY --from=builder /go/src/github.com/xenolf/lego/dist/lego /usr/bin/lego ENTRYPOINT [ "/usr/bin/lego" ] diff --git a/Gopkg.lock b/Gopkg.lock index ba372228..3e3a84d7 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -38,6 +38,14 @@ revision = "39013ecb48eaf6ced3f4e3e1d95515140ce6b3cf" version = "v10.15.2" +[[projects]] + digest = "1:5d72bbcc9c8667b11c3dc3cbe681c5a6f71e5096744c0bf7726ab5c6425d5dc4" + name = "github.com/BurntSushi/toml" + packages = ["."] + pruneopts = "NUT" + revision = "3012a1dbe2e4bd1391d42b32f0577cb7bbc7f005" + version = "v0.3.1" + [[projects]] digest = "1:ed3fc9992df610d07c85c24e0b792268cc1ce226dd9bf8cb2e6ad9a377b35415" name = "github.com/JamesClonk/vultr" @@ -671,6 +679,7 @@ "github.com/Azure/go-autorest/autorest/azure", "github.com/Azure/go-autorest/autorest/azure/auth", "github.com/Azure/go-autorest/autorest/to", + "github.com/BurntSushi/toml", "github.com/JamesClonk/vultr/lib", "github.com/OpenDNS/vegadns2client", "github.com/akamai/AkamaiOPEN-edgegrid-golang/configdns-v1", diff --git a/Makefile b/Makefile index b6ba5c36..88e25a1b 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: clean checks test build image dependencies +.PHONY: clean checks test build image dependencies e2e fmt SRCS = $(shell git ls-files '*.go' | grep -v '^vendor/') @@ -10,7 +10,7 @@ TAG_NAME := $(shell git tag -l --contains HEAD) SHA := $(shell git rev-parse HEAD) VERSION := $(if $(TAG_NAME),$(TAG_NAME),$(SHA)) -default: clean checks test build +default: clean generate-dns checks test build clean: rm -rf dist/ builds/ cover.out @@ -39,6 +39,7 @@ fmt: gofmt -s -l -w $(SRCS) # Release helper +.PHONY: patch minor major detach patch: go run internal/release.go release -m patch @@ -51,3 +52,21 @@ major: detach: go run internal/release.go detach + +# Docs +.PHONY: docs-build docs-serve docs-themes + +docs-build: generate-dns + @make -C ./docs hugo-build + +docs-serve: generate-dns + @make -C ./docs hugo + +docs-themes: + @make -C ./docs hugo-themes + +# Generate DNS +.PHONY: generate-dns + +generate-dns: + go generate ./... diff --git a/README.md b/README.md index fe7a7646..9a2551d7 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # lego -Let's Encrypt client and ACME library written in Go +Let's Encrypt client and ACME library written in Go. [![GoDoc](https://godoc.org/github.com/xenolf/lego?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) @@ -8,37 +8,6 @@ Let's Encrypt client and ACME library written in Go [![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) -## Installation - -### Binaries - -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. - -### From Docker - -```bash -docker run xenolf/lego -h -``` - -### From package managers - -- [ArchLinux (AUR)](https://aur.archlinux.org/packages/lego): - -```bash -yay -S lego -``` - -**Note**: only the package manager for Arch Linux is officially supported by the lego team. - -### From sources - -To install from sources, just run: - -```bash -go get -u github.com/xenolf/lego/cmd/lego -``` - ## Features - Register with CA @@ -55,266 +24,38 @@ go get -u github.com/xenolf/lego/cmd/lego - Certificate bundling - OCSP helper function -Please keep in mind that CLI switches and APIs are still subject to change. +lego introduced support for ACME v2 in [v1.0.0](https://github.com/xenolf/lego/releases/tag/v1.0.0). If you still need to utilize ACME v1, you can do so by using the [v0.5.0](https://github.com/xenolf/lego/releases/tag/v0.5.0) version. -When using the standard `--path` option, all certificates and account configurations are saved to a folder `.lego` in the current working directory. +## Installation + +How to [install](https://xenolf.github.io/lego/installation/). ## Usage -```text -NAME: - lego - Let's Encrypt client written in Go +- as a [CLI](https://xenolf.github.io/lego/usage/cli) +- as a [library](https://xenolf.github.io/lego/usage/lib) -USAGE: - lego [global options] command [command options] [arguments...] +## Documentation -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 - list Display certificates and accounts information. - help, h Shows a list of commands or help for one command +Documentation is hosted live at https://xenolf.github.io/lego/. -GLOBAL OPTIONS: - --domains value, -d value Add a domain to the process. Can be specified multiple times. - --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-v02.api.letsencrypt.org/directory") - --accept-tos, -a By setting this flag to true you indicate that you accept the current Let's Encrypt terms of service. - --email value, -m value Email used for registration and recovery contact. - --csr value, -c value Certificate signing request filename, if an external CSR is to be used. - --eab Use External Account Binding for account registration. Requires --kid and --hmac. - --kid value Key identifier from External CA. Used for External Account Binding. - --hmac value MAC key from External CA. Should be in Base64 URL Encoding without padding format. Used for External Account Binding. - --key-type value, -k value Key type to use for private keys. Supported: rsa2048, rsa4096, rsa8192, ec256, ec384. (default: "rsa2048") - --filename value (deprecated) Filename of the generated certificate. - --path value Directory to use for storing the data. (default: "./.lego") - --http Use the HTTP challenge to solve challenges. Can be mixed with other types of challenges. - --http.port value Set the port and interface to use for HTTP based challenges to listen on.Supported: interface:port or :port. (default: ":80") - --http.webroot value Set the webroot folder to use for HTTP based challenges to write directly in a file in .well-known/acme-challenge. - --http.memcached-host value Set the memcached host(s) to use for HTTP based challenges. Challenges will be written to all specified hosts. - --tls Use the TLS challenge to solve challenges. Can be mixed with other types of challenges. - --tls.port value Set the port and interface to use for TLS based challenges to listen on. Supported: interface:port or :port. (default: ":443") - --dns value Solve a DNS challenge using the specified provider. Can be mixed with other types of challenges. Run 'lego dnshelp' for help on usage. - --dns.disable-cp By setting this flag to true, disables the need to wait the propagation of the TXT record to all authoritative name servers. - --dns.resolvers value 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. - --http-timeout value Set the HTTP timeout value to a specific value in seconds. (default: 0) - --dns-timeout value Set the DNS timeout value to a specific value in seconds. Used only when performing authoritative name servers queries. (default: 10) - --pem Generate a .pem file by concatenating the .key and .crt files together. - --cert.timeout value Set the certificate timeout value to a specific value in seconds. Only used when obtaining certificates. (default: 30) - --help, -h show help - --version, -v print the version -``` +## DNS providers -### Sudo +Detailed documentation is available [here](https://xenolf.github.io/lego/dns). -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.port` or/and the `--tls.port` 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 `--http.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.port` and `--tls.port` 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-ALPN 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. - -### 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" --http 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" --http renew -``` - -To renew the certificate only if it expires within 30 days - -```bash -lego --email="foo@bar.com" --domains="example.com" --http 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 -``` - -Obtain a certificate given a certificate signing request (CSR) generated by something else: - -```bash -lego --email="foo@bar.com" --http --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-v02.api.letsencrypt.org/directory … -``` - -## ACME Library Usage - -A valid, but bare-bones example use of the acme package: - -```go -package main - -import ( - "crypto" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "fmt" - "log" - - "github.com/xenolf/lego/certcrypto" - "github.com/xenolf/lego/certificate" - "github.com/xenolf/lego/challenge/http01" - "github.com/xenolf/lego/challenge/tlsalpn01" - "github.com/xenolf/lego/lego" - "github.com/xenolf/lego/registration" -) - -// You'll need a user or account type that implements acme.User -type MyUser struct { - Email string - Registration *registration.Resource - key crypto.PrivateKey -} - -func (u *MyUser) GetEmail() string { - return u.Email -} -func (u MyUser) GetRegistration() *registration.Resource { - return u.Registration -} -func (u *MyUser) GetPrivateKey() crypto.PrivateKey { - return u.key -} - -func main() { - - // Create a user. New accounts need an email and private key to start. - privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - if err != nil { - log.Fatal(err) - } - - myUser := MyUser{ - Email: "you@yours.com", - key: privateKey, - } - - config := lego.NewConfig(&myUser) - - // This CA URL is configured for a local dev instance of Boulder running in Docker in a VM. - config.CADirURL = "http://192.168.99.100:4000/directory" - config.Certificate.KeyType = certcrypto.RSA2048 - - // A client facilitates communication with the CA server. - client, err := lego.NewClient(config) - 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 you still - // need to proxy challenge traffic to port 5002 and 5001. - err = client.Challenge.SetHTTP01Provider(http01.NewProviderServer("", "5002")) - if err != nil { - log.Fatal(err) - } - err = client.Challenge.SetTLSALPN01Provider(tlsalpn01.NewProviderServer("", "5001")) - if err != nil { - log.Fatal(err) - } - - // New users will need to register - reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true}) - if err != nil { - log.Fatal(err) - } - myUser.Registration = reg - - request := certificate.ObtainRequest{ - Domains: []string{"mydomain.com"}, - Bundle: true, - } - certificates, err := client.Certificate.Obtain(request) - if err != nil { - log.Fatal(err) - } - - // 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. -} -``` - -## DNS Challenge API Details - -### AWS Route 53 - -The following AWS IAM policy document describes the permissions required for lego to complete the DNS challenge. - -```json -{ - "Version": "2012-10-17", - "Statement": [ - { - "Sid": "", - "Effect": "Allow", - "Action": [ - "route53:GetChange", - "route53:ChangeResourceRecordSets", - "route53:ListResourceRecordSets" - ], - "Resource": [ - "arn:aws:route53:::hostedzone/*", - "arn:aws:route53:::change/*" - ] - }, - { - "Sid": "", - "Effect": "Allow", - "Action": "route53:ListHostedZonesByName", - "Resource": "*" - } - ] -} -``` - -## ACME v1 - -lego introduced support for ACME v2 in [v1.0.0](https://github.com/xenolf/lego/releases/tag/v1.0.0), if you still need to utilize ACME v1, you can do so by using the [v0.5.0](https://github.com/xenolf/lego/releases/tag/v0.5.0) version. +| | | | | +|----------------------------------------------------------------|--------------------------------------------------------------------------------|-------------------------------------------------------------------|------------------------------------------------------------------| +| [Alibaba Cloud DNS](https://xenolf.github.io/lego/dns/alidns/) | [Amazon Lightsail](https://xenolf.github.io/lego/dns/lightsail/) | [Amazon Route 53](https://xenolf.github.io/lego/dns/route53/) | [Aurora DNS](https://xenolf.github.io/lego/dns/auroradns/) | +| [Azure](https://xenolf.github.io/lego/dns/azure/) | [Bluecat](https://xenolf.github.io/lego/dns/bluecat/) | [ClouDNS](https://xenolf.github.io/lego/dns/cloudns/) | [CloudXNS](https://xenolf.github.io/lego/dns/cloudxns/) | +| [Cloudflare](https://xenolf.github.io/lego/dns/cloudflare/) | [ConoHa](https://xenolf.github.io/lego/dns/conoha/) | [DNS Made Easy](https://xenolf.github.io/lego/dns/dnsmadeeasy/) | [DNSPod](https://xenolf.github.io/lego/dns/dnspod/) | +| [DNSimple](https://xenolf.github.io/lego/dns/dnsimple/) | [Designate DNSaaS for Openstack](https://xenolf.github.io/lego/dns/designate/) | [Digital Ocean](https://xenolf.github.io/lego/dns/digitalocean/) | [DreamHost](https://xenolf.github.io/lego/dns/dreamhost/) | +| [Duck DNS](https://xenolf.github.io/lego/dns/duckdns/) | [Dyn](https://xenolf.github.io/lego/dns/dyn/) | [Exoscale](https://xenolf.github.io/lego/dns/exoscale/) | [External program](https://xenolf.github.io/lego/dns/exec/) | +| [FastDNS](https://xenolf.github.io/lego/dns/fastdns/) | [Gandi](https://xenolf.github.io/lego/dns/gandi/) | [Gandi Live DNS (v5)](https://xenolf.github.io/lego/dns/gandiv5/) | [Glesys](https://xenolf.github.io/lego/dns/glesys/) | +| [Go Daddy](https://xenolf.github.io/lego/dns/godaddy/) | [Google Cloud](https://xenolf.github.io/lego/dns/gcloud/) | [HTTP request](https://xenolf.github.io/lego/dns/httpreq/) | [Hosting.de](https://xenolf.github.io/lego/dns/hostingde/) | +| [INWX](https://xenolf.github.io/lego/dns/inwx/) | [Internet Initiative Japan](https://xenolf.github.io/lego/dns/iij/) | [Joohoi's ACME-DNS](https://xenolf.github.io/lego/dns/acme-dns) | [Linode (deprecated)](https://xenolf.github.io/lego/dns/linode/) | +| [Linode (v4)](https://xenolf.github.io/lego/dns/linodev4/) | [Manual](https://xenolf.github.io/lego/dns/manual/) | [MyDNS.jp](https://xenolf.github.io/lego/dns/mydnsjp/) | [NIFCloud](https://xenolf.github.io/lego/dns/nifcloud/) | +| [NS1](https://xenolf.github.io/lego/dns/ns1/) | [Name.com](https://xenolf.github.io/lego/dns/namedotcom/) | [Namecheap](https://xenolf.github.io/lego/dns/namecheap/) | [Netcup](https://xenolf.github.io/lego/dns/netcup/) | +| [OVH](https://xenolf.github.io/lego/dns/ovh/) | [Open Telekom Cloud](https://xenolf.github.io/lego/dns/otc/) | [Oracle Cloud](https://xenolf.github.io/lego/dns/oraclecloud/) | [PowerDNS](https://xenolf.github.io/lego/dns/pdns/) | +| [RFC2136](https://xenolf.github.io/lego/dns/rfc2136/) | [Rackspace](https://xenolf.github.io/lego/dns/rackspace/) | [Sakura Cloud](https://xenolf.github.io/lego/dns/sakuracloud/) | [Selectel](https://xenolf.github.io/lego/dns/selectel/) | +| [Stackpath](https://xenolf.github.io/lego/dns/stackpath/) | [TransIP](https://xenolf.github.io/lego/dns/transip/) | [VegaDNS](https://xenolf.github.io/lego/dns/vegadns/) | [Vscale](https://xenolf.github.io/lego/dns/vscale/) | +| [Vultr](https://xenolf.github.io/lego/dns/vultr/) | [Zone.ee](https://xenolf.github.io/lego/dns/zoneee/) | | | diff --git a/cmd/cmd_dnshelp.go b/cmd/cmd_dnshelp.go index 99873435..8852833f 100644 --- a/cmd/cmd_dnshelp.go +++ b/cmd/cmd_dnshelp.go @@ -3,6 +3,7 @@ package cmd import ( "fmt" "os" + "strings" "text/tabwriter" "github.com/urfave/cli" @@ -11,141 +12,37 @@ import ( func createDNSHelp() cli.Command { return cli.Command{ Name: "dnshelp", - Usage: "Shows additional help for the --dns global option", + Usage: "Shows additional help for the '--dns' global option", Action: dnsHelp, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "code, c", + Usage: fmt.Sprintf("DNS code: %s", allDNSCodes()), + }, + }, } } -func dnsHelp(_ *cli.Context) error { - fmt.Printf( - `Credentials for DNS providers must be passed through environment variables. +func dnsHelp(ctx *cli.Context) error { + code := ctx.String("code") + if code == "" { + w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0) -Here is an example bash command using the CloudFlare DNS provider: + fmt.Fprintln(w, `Credentials for DNS providers must be passed through environment variables.`) + fmt.Fprintln(w) + fmt.Fprintln(w, `To display the documentation for a DNS providers:`) + fmt.Fprintln(w) + fmt.Fprintln(w, "\t$ lego dnshelp -c code") + fmt.Fprintln(w) + fmt.Fprintln(w, "All DNS codes:") + fmt.Fprintf(w, "\t%s\n", allDNSCodes()) + fmt.Fprintln(w) + fmt.Fprintln(w, "More information: https://xenolf.github.io/lego/dns") - $ CLOUDFLARE_EMAIL=foo@bar.com \ - CLOUDFLARE_API_KEY=b9841238feb177a84330febba8a83208921177bffe733 \ - lego --dns cloudflare --domains www.example.com --email me@bar.com run + return w.Flush() + } -`) - - 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, "\tacme-dns:\tACME_DNS_API_BASE, ACME_DNS_STORAGE_PATH") - fmt.Fprintln(w, "\talidns:\tALICLOUD_ACCESS_KEY, ALICLOUD_SECRET_KEY") - fmt.Fprintln(w, "\tauroradns:\tAURORA_USER_ID, AURORA_KEY, AURORA_ENDPOINT") - fmt.Fprintln(w, "\tazure:\tAZURE_CLIENT_ID, AZURE_CLIENT_SECRET, AZURE_SUBSCRIPTION_ID, AZURE_TENANT_ID, AZURE_RESOURCE_GROUP") - fmt.Fprintln(w, "\tbluecat:\tBLUECAT_SERVER_URL, BLUECAT_USER_NAME, BLUECAT_PASSWORD, BLUECAT_CONFIG_NAME, BLUECAT_DNS_VIEW") - fmt.Fprintln(w, "\tcloudflare:\tCLOUDFLARE_EMAIL, CLOUDFLARE_API_KEY") - fmt.Fprintln(w, "\tcloudns:\tCLOUDNS_AUTH_ID, CLOUDNS_AUTH_PASSWORD") - fmt.Fprintln(w, "\tcloudxns:\tCLOUDXNS_API_KEY, CLOUDXNS_SECRET_KEY") - fmt.Fprintln(w, "\tconoha:\tCONOHA_TENANT_ID, CONOHA_API_USERNAME, CONOHA_API_PASSWORD") - fmt.Fprintln(w, "\tdesignate:\tOS_AUTH_URL, OS_USERNAME, OS_PASSWORD, OS_TENANT_NAME, OS_REGION_NAME") - 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, "\tdnspod:\tDNSPOD_API_KEY") - fmt.Fprintln(w, "\tdreamhost:\tDREAMHOST_API_KEY") - fmt.Fprintln(w, "\tduckdns:\tDUCKDNS_TOKEN") - fmt.Fprintln(w, "\tdyn:\tDYN_CUSTOMER_NAME, DYN_USER_NAME, DYN_PASSWORD") - fmt.Fprintln(w, "\texec:\tEXEC_PATH, EXEC_MODE") - fmt.Fprintln(w, "\texoscale:\tEXOSCALE_API_KEY, EXOSCALE_API_SECRET, EXOSCALE_ENDPOINT") - fmt.Fprintln(w, "\tfastdns:\tAKAMAI_HOST, AKAMAI_CLIENT_TOKEN, AKAMAI_CLIENT_SECRET, AKAMAI_ACCESS_TOKEN") - fmt.Fprintln(w, "\tgandi:\tGANDI_API_KEY") - fmt.Fprintln(w, "\tgandiv5:\tGANDIV5_API_KEY") - fmt.Fprintln(w, "\tgcloud:\tGCE_PROJECT, 'Application Default Credentials', [GCE_SERVICE_ACCOUNT_FILE], [GCE_SERVICE_ACCOUNT]") - fmt.Fprintln(w, "\tglesys:\tGLESYS_API_USER, GLESYS_API_KEY") - fmt.Fprintln(w, "\tgodaddy:\tGODADDY_API_KEY, GODADDY_API_SECRET") - fmt.Fprintln(w, "\thostingde:\tHOSTINGDE_API_KEY, HOSTINGDE_ZONE_NAME") - fmt.Fprintln(w, "\thttpreq:\tHTTPREQ_ENDPOINT, HTTPREQ_MODE, HTTPREQ_USERNAME, HTTPREQ_PASSWORD") - fmt.Fprintln(w, "\tiij:\tIIJ_API_ACCESS_KEY, IIJ_API_SECRET_KEY, IIJ_DO_SERVICE_CODE") - fmt.Fprintln(w, "\tinwx:\tINWX_USERNAME, INWX_PASSWORD") - fmt.Fprintln(w, "\tlightsail:\tAWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, DNS_ZONE") - fmt.Fprintln(w, "\tlinode:\tLINODE_API_KEY") - fmt.Fprintln(w, "\tlinodev4:\tLINODE_TOKEN") - fmt.Fprintln(w, "\tmanual:\tnone") - fmt.Fprintln(w, "\tmydnsjp:\tMYDNSJP_MASTER_ID, MYDNSJP_PASSWORD") - fmt.Fprintln(w, "\tnamecheap:\tNAMECHEAP_API_USER, NAMECHEAP_API_KEY") - fmt.Fprintln(w, "\tnamedotcom:\tNAMECOM_USERNAME, NAMECOM_API_TOKEN") - fmt.Fprintln(w, "\tnetcup:\tNETCUP_CUSTOMER_NUMBER, NETCUP_API_KEY, NETCUP_API_PASSWORD") - fmt.Fprintln(w, "\tnifcloud:\tNIFCLOUD_ACCESS_KEY_ID, NIFCLOUD_SECRET_ACCESS_KEY") - fmt.Fprintln(w, "\tns1:\tNS1_API_KEY") - fmt.Fprintln(w, "\toraclecloud:\tOCI_PRIVKEY_FILE, OCI_PRIVKEY_PASS, OCI_TENANCY_OCID, OCI_USER_OCID, OCI_PUBKEY_FINGERPRINT, OCI_REGION, OCI_COMPARTMENT_OCID") - fmt.Fprintln(w, "\totc:\tOTC_USER_NAME, OTC_PASSWORD, OTC_PROJECT_NAME, OTC_DOMAIN_NAME, OTC_IDENTITY_ENDPOINT") - 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, "\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, "\tsakuracloud:\tSAKURACLOUD_ACCESS_TOKEN, SAKURACLOUD_ACCESS_TOKEN_SECRET") - fmt.Fprintln(w, "\tselectel:\tSELECTEL_API_TOKEN") - fmt.Fprintln(w, "\tstackpath:\tSTACKPATH_CLIENT_ID, STACKPATH_CLIENT_SECRET, STACKPATH_STACK_ID") - fmt.Fprintln(w, "\ttransip:\tTRANSIP_ACCOUNT_NAME, TRANSIP_PRIVATE_KEY_PATH") - fmt.Fprintln(w, "\tvegadns:\tSECRET_VEGADNS_KEY, SECRET_VEGADNS_SECRET, VEGADNS_URL") - fmt.Fprintln(w, "\tvscale:\tVSCALE_API_TOKEN") - fmt.Fprintln(w, "\tvultr:\tVULTR_API_KEY") - fmt.Fprintln(w, "\tzoneee:\tZONEEE_ENDPOINT, ZONEEE_API_USER, ZONEEE_API_KEY") - fmt.Fprintln(w) - fmt.Fprintln(w, "Additional configuration environment variables:") - fmt.Fprintln(w) - fmt.Fprintln(w, "\talidns:\tALICLOUD_POLLING_INTERVAL, ALICLOUD_PROPAGATION_TIMEOUT, ALICLOUD_TTL, ALICLOUD_HTTP_TIMEOUT") - fmt.Fprintln(w, "\tauroradns:\tAURORA_POLLING_INTERVAL, AURORA_PROPAGATION_TIMEOUT, AURORA_TTL") - fmt.Fprintln(w, "\tazure:\tAZURE_POLLING_INTERVAL, AZURE_PROPAGATION_TIMEOUT, AZURE_TTL, AZURE_METADATA_ENDPOINT") - fmt.Fprintln(w, "\tbluecat:\tBLUECAT_POLLING_INTERVAL, BLUECAT_PROPAGATION_TIMEOUT, BLUECAT_TTL, BLUECAT_HTTP_TIMEOUT") - fmt.Fprintln(w, "\tcloudflare:\tCLOUDFLARE_POLLING_INTERVAL, CLOUDFLARE_PROPAGATION_TIMEOUT, CLOUDFLARE_TTL, CLOUDFLARE_HTTP_TIMEOUT") - fmt.Fprintln(w, "\tcloudns:\tCLOUDNS_POLLING_INTERVAL, CLOUDNS_PROPAGATION_TIMEOUT, CLOUDNS_TTL, CLOUDNS_HTTP_TIMEOUT") - fmt.Fprintln(w, "\tcloudxns:\tCLOUDXNS_POLLING_INTERVAL, CLOUDXNS_PROPAGATION_TIMEOUT, CLOUDXNS_TTL, CLOUDXNS_HTTP_TIMEOUT") - fmt.Fprintln(w, "\tconoha:\tCONOHA_POLLING_INTERVAL, CONOHA_PROPAGATION_TIMEOUT, CONOHA_TTL, CONOHA_HTTP_TIMEOUT, CONOHA_REGION") - fmt.Fprintln(w, "\tdesignate:\tDESIGNATE_POLLING_INTERVAL, DESIGNATE_PROPAGATION_TIMEOUT, DESIGNATE_TTL") - fmt.Fprintln(w, "\tdigitalocean:\tDO_POLLING_INTERVAL, DO_PROPAGATION_TIMEOUT, DO_TTL, DO_HTTP_TIMEOUT") - fmt.Fprintln(w, "\tdnsimple:\tDNSIMPLE_TTL, DNSIMPLE_POLLING_INTERVAL, DNSIMPLE_PROPAGATION_TIMEOUT") - fmt.Fprintln(w, "\tdnsmadeeasy:\tDNSMADEEASY_POLLING_INTERVAL, DNSMADEEASY_PROPAGATION_TIMEOUT, DNSMADEEASY_TTL, DNSMADEEASY_HTTP_TIMEOUT") - fmt.Fprintln(w, "\tdnspod:\tDNSPOD_POLLING_INTERVAL, DNSPOD_PROPAGATION_TIMEOUT, DNSPOD_TTL, DNSPOD_HTTP_TIMEOUT") - fmt.Fprintln(w, "\tdreamhost:\tDREAMHOST_POLLING_INTERVAL, DREAMHOST_PROPAGATION_TIMEOUT, DREAMHOST_HTTP_TIMEOUT") - fmt.Fprintln(w, "\tduckdns:\tDUCKDNS_POLLING_INTERVAL, DUCKDNS_PROPAGATION_TIMEOUT, DUCKDNS_HTTP_TIMEOUT, DUCKDNS_SEQUENCE_INTERVAL") - fmt.Fprintln(w, "\tdyn:\tDYN_POLLING_INTERVAL, DYN_PROPAGATION_TIMEOUT, DYN_TTL, DYN_HTTP_TIMEOUT") - fmt.Fprintln(w, "\texec:\tEXEC_POLLING_INTERVAL, EXEC_PROPAGATION_TIMEOUT") - fmt.Fprintln(w, "\texoscale:\tEXOSCALE_POLLING_INTERVAL, EXOSCALE_PROPAGATION_TIMEOUT, EXOSCALE_TTL, EXOSCALE_HTTP_TIMEOUT") - fmt.Fprintln(w, "\tfastdns:\tAKAMAI_POLLING_INTERVAL, AKAMAI_PROPAGATION_TIMEOUT, AKAMAI_TTL") - fmt.Fprintln(w, "\tgandi:\tGANDI_POLLING_INTERVAL, GANDI_PROPAGATION_TIMEOUT, GANDI_TTL, GANDI_HTTP_TIMEOUT") - fmt.Fprintln(w, "\tgandiv5:\tGANDIV5_POLLING_INTERVAL, GANDIV5_PROPAGATION_TIMEOUT, GANDIV5_TTL, GANDIV5_HTTP_TIMEOUT") - fmt.Fprintln(w, "\tgcloud:\tGCE_POLLING_INTERVAL, GCE_PROPAGATION_TIMEOUT, GCE_TTL") - fmt.Fprintln(w, "\tglesys:\tGLESYS_POLLING_INTERVAL, GLESYS_PROPAGATION_TIMEOUT, GLESYS_TTL, GLESYS_HTTP_TIMEOUT") - fmt.Fprintln(w, "\tgodaddy:\tGODADDY_POLLING_INTERVAL, GODADDY_PROPAGATION_TIMEOUT, GODADDY_TTL, GODADDY_HTTP_TIMEOUT, GODADDY_SEQUENCE_INTERVAL") - fmt.Fprintln(w, "\thostingde:\tHOSTINGDE_POLLING_INTERVAL, HOSTINGDE_PROPAGATION_TIMEOUT, HOSTINGDE_TTL, HOSTINGDE_HTTP_TIMEOUT") - fmt.Fprintln(w, "\thttpreq:\tHTTPREQ_POLLING_INTERVAL, HTTPREQ_PROPAGATION_TIMEOUT, HTTPREQ_HTTP_TIMEOUT") - fmt.Fprintln(w, "\tiij:\tIIJ_POLLING_INTERVAL, IIJ_PROPAGATION_TIMEOUT, IIJ_TTL") - fmt.Fprintln(w, "\tinwx:\tINWX_POLLING_INTERVAL, INWX_PROPAGATION_TIMEOUT, INWX_TTL, INWX_SANDBOX") - fmt.Fprintln(w, "\tlightsail:\tLIGHTSAIL_POLLING_INTERVAL, LIGHTSAIL_PROPAGATION_TIMEOUT") - fmt.Fprintln(w, "\tlinode:\tLINODE_POLLING_INTERVAL, LINODE_TTL, LINODE_HTTP_TIMEOUT") - fmt.Fprintln(w, "\tlinodev4:\tLINODE_POLLING_INTERVAL, LINODE_TTL, LINODE_HTTP_TIMEOUT") - fmt.Fprintln(w, "\tmydnsjp:\tMYDNSJP_PROPAGATION_TIMEOUT, MYDNSJP_POLLING_INTERVAL, MYDNSJP_HTTP_TIMEOUT") - fmt.Fprintln(w, "\tnamecheap:\tNAMECHEAP_POLLING_INTERVAL, NAMECHEAP_PROPAGATION_TIMEOUT, NAMECHEAP_TTL, NAMECHEAP_HTTP_TIMEOUT") - fmt.Fprintln(w, "\tnamedotcom:\tNAMECOM_POLLING_INTERVAL, NAMECOM_PROPAGATION_TIMEOUT, NAMECOM_TTL, NAMECOM_HTTP_TIMEOUT") - fmt.Fprintln(w, "\tnetcup:\tNETCUP_POLLING_INTERVAL, NETCUP_PROPAGATION_TIMEOUT, NETCUP_TTL, NETCUP_HTTP_TIMEOUT") - fmt.Fprintln(w, "\tnifcloud:\tNIFCLOUD_POLLING_INTERVAL, NIFCLOUD_PROPAGATION_TIMEOUT, NIFCLOUD_TTL, NIFCLOUD_HTTP_TIMEOUT") - fmt.Fprintln(w, "\tns1:\tNS1_POLLING_INTERVAL, NS1_PROPAGATION_TIMEOUT, NS1_TTL, NS1_HTTP_TIMEOUT") - fmt.Fprintln(w, "\toraclecloud:\tOCI_TTL, OCI_PROPAGATION_TIMEOUT, OCI_POLLING_INTERVAL") - fmt.Fprintln(w, "\totc:\tOTC_POLLING_INTERVAL, OTC_PROPAGATION_TIMEOUT, OTC_TTL, OTC_HTTP_TIMEOUT") - fmt.Fprintln(w, "\tovh:\tOVH_POLLING_INTERVAL, OVH_PROPAGATION_TIMEOUT, OVH_TTL, OVH_HTTP_TIMEOUT") - fmt.Fprintln(w, "\tpdns:\tPDNS_POLLING_INTERVAL, PDNS_PROPAGATION_TIMEOUT, PDNS_TTL, PDNS_HTTP_TIMEOUT") - fmt.Fprintln(w, "\trackspace:\tRACKSPACE_POLLING_INTERVAL, RACKSPACE_PROPAGATION_TIMEOUT, RACKSPACE_TTL, RACKSPACE_HTTP_TIMEOUT") - fmt.Fprintln(w, "\trfc2136:\tRFC2136_POLLING_INTERVAL, RFC2136_PROPAGATION_TIMEOUT, RFC2136_TTL, RFC2136_SEQUENCE_INTERVAL, RFC2136_DNS_TIMEOUT") - fmt.Fprintln(w, "\troute53:\tAWS_POLLING_INTERVAL, AWS_PROPAGATION_TIMEOUT, AWS_TTL") - fmt.Fprintln(w, "\tsakuracloud:\tSAKURACLOUD_POLLING_INTERVAL, SAKURACLOUD_PROPAGATION_TIMEOUT, SAKURACLOUD_TTL") - fmt.Fprintln(w, "\tselectel:\tSELECTEL_BASE_URL, SELECTEL_TTL, SELECTEL_PROPAGATION_TIMEOUT, SELECTEL_POLLING_INTERVAL, SELECTEL_HTTP_TIMEOUT") - fmt.Fprintln(w, "\ttransip:\tTRANSIP_POLLING_INTERVAL, TRANSIP_PROPAGATION_TIMEOUT, TRANSIP_TTL") - fmt.Fprintln(w, "\tstackpath:\tSTACKPATH_POLLING_INTERVAL, STACKPATH_PROPAGATION_TIMEOUT, STACKPATH_TTL") - fmt.Fprintln(w, "\tvegadns:\tVEGADNS_POLLING_INTERVAL, VEGADNS_PROPAGATION_TIMEOUT, VEGADNS_TTL") - fmt.Fprintln(w, "\tvscale:\tVSCALE_BASE_URL, VSCALE_TTL, VSCALE_PROPAGATION_TIMEOUT, VSCALE_POLLING_INTERVAL, VSCALE_HTTP_TIMEOUT") - fmt.Fprintln(w, "\tvultr:\tVULTR_POLLING_INTERVAL, VULTR_PROPAGATION_TIMEOUT, VULTR_TTL, VULTR_HTTP_TIMEOUT") - fmt.Fprintln(w, "\tzoneee:\tZONEEE_POLLING_INTERVAL, ZONEEE_PROPAGATION_TIMEOUT, ZONEEE_HTTP_TIMEOUT") - - w.Flush() - - fmt.Println(` -For a more detailed explanation of a DNS provider's credential variables, -please consult their online documentation.`) + displayDNSHelp(strings.ToLower(code)) return nil } diff --git a/cmd/zz_gen_cmd_dnshelp.go b/cmd/zz_gen_cmd_dnshelp.go new file mode 100644 index 00000000..1ba4172b --- /dev/null +++ b/cmd/zz_gen_cmd_dnshelp.go @@ -0,0 +1,1144 @@ +package cmd + +// CODE GENERATED AUTOMATICALLY +// THIS FILE MUST NOT BE EDITED BY HAND + +import ( + "fmt" + "os" + "strings" + "text/tabwriter" + + "github.com/xenolf/lego/log" +) + +func allDNSCodes() string { + return strings.Join([]string{ + "acme-dns", + "alidns", + "auroradns", + "azure", + "bluecat", + "cloudflare", + "cloudns", + "cloudxns", + "conoha", + "designate", + "digitalocean", + "dnsimple", + "dnsmadeeasy", + "dnspod", + "dreamhost", + "duckdns", + "dyn", + "exec", + "exoscale", + "fastdns", + "gandi", + "gandiv5", + "gcloud", + "glesys", + "godaddy", + "hostingde", + "httpreq", + "iij", + "inwx", + "lightsail", + "linode", + "linodev4", + "mydnsjp", + "namecheap", + "namedotcom", + "netcup", + "nifcloud", + "ns1", + "oraclecloud", + "otc", + "ovh", + "pdns", + "rackspace", + "rfc2136", + "route53", + "sakuracloud", + "selectel", + "stackpath", + "transip", + "vegadns", + "vscale", + "vultr", + "zoneee", + }, ", ") +} + +func displayDNSHelp(name string) { + w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0) + switch name { + + case "acme-dns": + // generated from: providers/dns/acmedns/acmedns.toml + fmt.Fprintln(w, `Configuration for Joohoi's ACME-DNS.`) + fmt.Fprintln(w, `Code: 'acme-dns'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "ACME_DNS_API_BASE": The ACME-DNS API address`) + fmt.Fprintln(w, ` - "ACME_DNS_STORAGE_PATH": The ACME-DNS JSON account data file. A per-domain account will be registered/persisted to this file and used for TXT updates.`) + fmt.Fprintln(w) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/acme-dns`) + + case "alidns": + // generated from: providers/dns/alidns/alidns.toml + fmt.Fprintln(w, `Configuration for Alibaba Cloud DNS.`) + fmt.Fprintln(w, `Code: 'alidns'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "ALICLOUD_ACCESS_KEY": Access key ID`) + fmt.Fprintln(w, ` - "ALICLOUD_SECRET_KEY": Access Key secret`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "ALICLOUD_HTTP_TIMEOUT": API request timeout`) + fmt.Fprintln(w, ` - "ALICLOUD_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "ALICLOUD_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + fmt.Fprintln(w, ` - "ALICLOUD_TTL": The TTL of the TXT record used for the DNS challenge`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/alidns`) + + case "auroradns": + // generated from: providers/dns/auroradns/auroradns.toml + fmt.Fprintln(w, `Configuration for Aurora DNS.`) + fmt.Fprintln(w, `Code: 'auroradns'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "AURORA_ENDPOINT": API endpoint URL`) + fmt.Fprintln(w, ` - "AURORA_KEY": User API key`) + fmt.Fprintln(w, ` - "AURORA_USER_ID": User ID`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "AURORA_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "AURORA_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + fmt.Fprintln(w, ` - "AURORA_TTL": The TTL of the TXT record used for the DNS challenge`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/auroradns`) + + case "azure": + // generated from: providers/dns/azure/azure.toml + fmt.Fprintln(w, `Configuration for Azure.`) + fmt.Fprintln(w, `Code: 'azure'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "AZURE_CLIENT_ID": Client ID`) + fmt.Fprintln(w, ` - "AZURE_CLIENT_SECRET": Client secret`) + fmt.Fprintln(w, ` - "AZURE_RESOURCE_GROUP": Resource group`) + fmt.Fprintln(w, ` - "AZURE_SUBSCRIPTION_ID": Subscription ID`) + fmt.Fprintln(w, ` - "AZURE_TENANT_ID": Tenant ID`) + fmt.Fprintln(w, ` - "instance metadata service": If the credentials are **not** set via the environment, then it will attempt to get a bearer token via the [instance metadata service](https://docs.microsoft.com/en-us/azure/virtual-machines/windows/instance-metadata-service).`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "AZURE_METADATA_ENDPOINT": Metadata Service endpoint URL`) + fmt.Fprintln(w, ` - "AZURE_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "AZURE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + fmt.Fprintln(w, ` - "AZURE_TTL": The TTL of the TXT record used for the DNS challenge`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/azure`) + + case "bluecat": + // generated from: providers/dns/bluecat/bluecat.toml + fmt.Fprintln(w, `Configuration for Bluecat.`) + fmt.Fprintln(w, `Code: 'bluecat'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "BLUECAT_CONFIG_NAME": Configuration name`) + fmt.Fprintln(w, ` - "BLUECAT_DNS_VIEW": External DNS View Name`) + fmt.Fprintln(w, ` - "BLUECAT_PASSWORD": API password`) + fmt.Fprintln(w, ` - "BLUECAT_SERVER_URL": The server URL, should have scheme, hostname, and port (if required) of the authoritative Bluecat BAM serve`) + fmt.Fprintln(w, ` - "BLUECAT_USER_NAME": API username`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "BLUECAT_HTTP_TIMEOUT": API request timeout`) + fmt.Fprintln(w, ` - "BLUECAT_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "BLUECAT_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + fmt.Fprintln(w, ` - "BLUECAT_TTL": The TTL of the TXT record used for the DNS challenge`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/bluecat`) + + case "cloudflare": + // generated from: providers/dns/cloudflare/cloudflare.toml + fmt.Fprintln(w, `Configuration for Cloudflare.`) + fmt.Fprintln(w, `Code: 'cloudflare'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "CLOUDFLARE_API_KEY": API key`) + fmt.Fprintln(w, ` - "CLOUDFLARE_EMAIL": Account email`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "CLOUDFLARE_HTTP_TIMEOUT": API request timeout`) + fmt.Fprintln(w, ` - "CLOUDFLARE_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "CLOUDFLARE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + fmt.Fprintln(w, ` - "CLOUDFLARE_TTL": The TTL of the TXT record used for the DNS challenge`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/cloudflare`) + + case "cloudns": + // generated from: providers/dns/cloudns/cloudns.toml + fmt.Fprintln(w, `Configuration for ClouDNS.`) + fmt.Fprintln(w, `Code: 'cloudns'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "CLOUDNS_AUTH_ID": The API user ID`) + fmt.Fprintln(w, ` - "CLOUDNS_AUTH_PASSWORD": The password for API user ID`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "CLOUDNS_HTTP_TIMEOUT": API request timeout`) + fmt.Fprintln(w, ` - "CLOUDNS_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "CLOUDNS_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + fmt.Fprintln(w, ` - "CLOUDNS_TTL": The TTL of the TXT record used for the DNS challenge`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/cloudns`) + + case "cloudxns": + // generated from: providers/dns/cloudxns/cloudxns.toml + fmt.Fprintln(w, `Configuration for CloudXNS.`) + fmt.Fprintln(w, `Code: 'cloudxns'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "CLOUDXNS_API_KEY": The API key`) + fmt.Fprintln(w, ` - "CLOUDXNS_SECRET_KEY": THe API secret key`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "CLOUDXNS_HTTP_TIMEOUT": API request timeout`) + fmt.Fprintln(w, ` - "CLOUDXNS_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "CLOUDXNS_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + fmt.Fprintln(w, ` - "CLOUDXNS_TTL": The TTL of the TXT record used for the DNS challenge`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/cloudxns`) + + case "conoha": + // generated from: providers/dns/conoha/conoha.toml + fmt.Fprintln(w, `Configuration for ConoHa.`) + fmt.Fprintln(w, `Code: 'conoha'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "CONOHA_API_PASSWORD": The API password`) + fmt.Fprintln(w, ` - "CONOHA_API_USERNAME": The API username`) + fmt.Fprintln(w, ` - "CONOHA_TENANT_ID": Tenant ID`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "CONOHA_HTTP_TIMEOUT": API request timeout`) + fmt.Fprintln(w, ` - "CONOHA_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "CONOHA_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + fmt.Fprintln(w, ` - "CONOHA_REGION": The region`) + fmt.Fprintln(w, ` - "CONOHA_TTL": The TTL of the TXT record used for the DNS challenge`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/conoha`) + + case "designate": + // generated from: providers/dns/designate/designate.toml + fmt.Fprintln(w, `Configuration for Designate DNSaaS for Openstack.`) + fmt.Fprintln(w, `Code: 'designate'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "OS_AUTH_URL": Identity endpoint URL`) + fmt.Fprintln(w, ` - "OS_PASSWORD": Password`) + fmt.Fprintln(w, ` - "OS_REGION_NAME": Region name`) + fmt.Fprintln(w, ` - "OS_TENANT_NAME": Tenant name`) + fmt.Fprintln(w, ` - "OS_USERNAME": Username`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "DESIGNATE_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "DESIGNATE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + fmt.Fprintln(w, ` - "DESIGNATE_TTL": The TTL of the TXT record used for the DNS challenge`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/designate`) + + case "digitalocean": + // generated from: providers/dns/digitalocean/digitalocean.toml + fmt.Fprintln(w, `Configuration for Digital Ocean.`) + fmt.Fprintln(w, `Code: 'digitalocean'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "DO_AUTH_TOKEN": Authentication token`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "DO_HTTP_TIMEOUT": API request timeout`) + fmt.Fprintln(w, ` - "DO_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "DO_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + fmt.Fprintln(w, ` - "DO_TTL": The TTL of the TXT record used for the DNS challenge`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/digitalocean`) + + case "dnsimple": + // generated from: providers/dns/dnsimple/dnsimple.toml + fmt.Fprintln(w, `Configuration for DNSimple.`) + fmt.Fprintln(w, `Code: 'dnsimple'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "DNSIMPLE_BASE_URL": API endpoint URL`) + fmt.Fprintln(w, ` - "DNSIMPLE_OAUTH_TOKEN": OAuth token`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "DNSIMPLE_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "DNSIMPLE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + fmt.Fprintln(w, ` - "DNSIMPLE_TTL": The TTL of the TXT record used for the DNS challenge`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/dnsimple`) + + case "dnsmadeeasy": + // generated from: providers/dns/dnsmadeeasy/dnsmadeeasy.toml + fmt.Fprintln(w, `Configuration for DNS Made Easy.`) + fmt.Fprintln(w, `Code: 'dnsmadeeasy'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "DNSMADEEASY_API_KEY": The API key`) + fmt.Fprintln(w, ` - "DNSMADEEASY_API_SECRET": The API Secret key`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "DNSMADEEASY_HTTP_TIMEOUT": API request timeout`) + fmt.Fprintln(w, ` - "DNSMADEEASY_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "DNSMADEEASY_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + fmt.Fprintln(w, ` - "DNSMADEEASY_SANDBOX": Activate the sandbox (boolean)`) + fmt.Fprintln(w, ` - "DNSMADEEASY_TTL": The TTL of the TXT record used for the DNS challenge`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/dnsmadeeasy`) + + case "dnspod": + // generated from: providers/dns/dnspod/dnspod.toml + fmt.Fprintln(w, `Configuration for DNSPod.`) + fmt.Fprintln(w, `Code: 'dnspod'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "DNSPOD_API_KEY": The user token`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "DNSPOD_HTTP_TIMEOUT": API request timeout`) + fmt.Fprintln(w, ` - "DNSPOD_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "DNSPOD_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + fmt.Fprintln(w, ` - "DNSPOD_TTL": The TTL of the TXT record used for the DNS challenge`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/dnspod`) + + case "dreamhost": + // generated from: providers/dns/dreamhost/dreamhost.toml + fmt.Fprintln(w, `Configuration for DreamHost.`) + fmt.Fprintln(w, `Code: 'dreamhost'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "DREAMHOST_API_KEY": The API key`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "DREAMHOST_HTTP_TIMEOUT": API request timeout`) + fmt.Fprintln(w, ` - "DREAMHOST_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "DREAMHOST_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + fmt.Fprintln(w, ` - "DREAMHOST_TTL": The TTL of the TXT record used for the DNS challenge`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/dreamhost`) + + case "duckdns": + // generated from: providers/dns/duckdns/duckdns.toml + fmt.Fprintln(w, `Configuration for Duck DNS.`) + fmt.Fprintln(w, `Code: 'duckdns'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "DUCKDNS_TOKEN": Account token`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "DUCKDNS_HTTP_TIMEOUT": API request timeout`) + fmt.Fprintln(w, ` - "DUCKDNS_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "DUCKDNS_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + fmt.Fprintln(w, ` - "DUCKDNS_SEQUENCE_INTERVAL": Interval between iteration`) + fmt.Fprintln(w, ` - "DUCKDNS_TTL": The TTL of the TXT record used for the DNS challenge`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/duckdns`) + + case "dyn": + // generated from: providers/dns/dyn/dyn.toml + fmt.Fprintln(w, `Configuration for Dyn.`) + fmt.Fprintln(w, `Code: 'dyn'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "DYN_CUSTOMER_NAME": Customer name`) + fmt.Fprintln(w, ` - "DYN_PASSWORD": Paswword`) + fmt.Fprintln(w, ` - "DYN_USER_NAME": User name`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "DYN_HTTP_TIMEOUT": API request timeout`) + fmt.Fprintln(w, ` - "DYN_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "DYN_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + fmt.Fprintln(w, ` - "DYN_TTL": The TTL of the TXT record used for the DNS challenge`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/dyn`) + + case "exec": + // generated from: providers/dns/exec/exec.toml + fmt.Fprintln(w, `Configuration for External program.`) + fmt.Fprintln(w, `Code: 'exec'`) + fmt.Fprintln(w) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/exec`) + + case "exoscale": + // generated from: providers/dns/exoscale/exoscale.toml + fmt.Fprintln(w, `Configuration for Exoscale.`) + fmt.Fprintln(w, `Code: 'exoscale'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "EXOSCALE_API_KEY": API key`) + fmt.Fprintln(w, ` - "EXOSCALE_API_SECRET": API secret`) + fmt.Fprintln(w, ` - "EXOSCALE_ENDPOINT": API endpoint URL`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "EXOSCALE_HTTP_TIMEOUT": API request timeout`) + fmt.Fprintln(w, ` - "EXOSCALE_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "EXOSCALE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + fmt.Fprintln(w, ` - "EXOSCALE_TTL": The TTL of the TXT record used for the DNS challenge`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/exoscale`) + + case "fastdns": + // generated from: providers/dns/fastdns/fastdns.toml + fmt.Fprintln(w, `Configuration for FastDNS.`) + fmt.Fprintln(w, `Code: 'fastdns'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "AKAMAI_ACCESS_TOKEN": Access token`) + fmt.Fprintln(w, ` - "AKAMAI_CLIENT_SECRET": Client secret`) + fmt.Fprintln(w, ` - "AKAMAI_CLIENT_TOKEN": Client token`) + fmt.Fprintln(w, ` - "AKAMAI_HOST": API host`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "AKAMAI_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "AKAMAI_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + fmt.Fprintln(w, ` - "AKAMAI_TTL": The TTL of the TXT record used for the DNS challenge`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/fastdns`) + + case "gandi": + // generated from: providers/dns/gandi/gandi.toml + fmt.Fprintln(w, `Configuration for Gandi.`) + fmt.Fprintln(w, `Code: 'gandi'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "GANDI_API_KEY": API key`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "GANDI_HTTP_TIMEOUT": API request timeout`) + fmt.Fprintln(w, ` - "GANDI_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "GANDI_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + fmt.Fprintln(w, ` - "GANDI_TTL": The TTL of the TXT record used for the DNS challenge`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/gandi`) + + case "gandiv5": + // generated from: providers/dns/gandiv5/gandiv5.toml + fmt.Fprintln(w, `Configuration for Gandi Live DNS (v5).`) + fmt.Fprintln(w, `Code: 'gandiv5'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "GANDIV5_API_KEY": API key`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "GANDIV5_HTTP_TIMEOUT": API request timeout`) + fmt.Fprintln(w, ` - "GANDIV5_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "GANDIV5_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + fmt.Fprintln(w, ` - "GANDIV5_TTL": The TTL of the TXT record used for the DNS challenge`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/gandiv5`) + + case "gcloud": + // generated from: providers/dns/gcloud/gcloud.toml + fmt.Fprintln(w, `Configuration for Google Cloud.`) + fmt.Fprintln(w, `Code: 'gcloud'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "Application Default Credentials": [Documentation](https://cloud.google.com/docs/authentication/production#providing_credentials_to_your_application)`) + fmt.Fprintln(w, ` - "GCE_PROJECT": Project name`) + fmt.Fprintln(w, ` - "GCE_SERVICE_ACCOUNT": Account`) + fmt.Fprintln(w, ` - "GCE_SERVICE_ACCOUNT_FILE": Account file path`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "GCE_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "GCE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + fmt.Fprintln(w, ` - "GCE_TTL": The TTL of the TXT record used for the DNS challenge`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/gcloud`) + + case "glesys": + // generated from: providers/dns/glesys/glesys.toml + fmt.Fprintln(w, `Configuration for Glesys.`) + fmt.Fprintln(w, `Code: 'glesys'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "GLESYS_API_KEY": API key`) + fmt.Fprintln(w, ` - "GLESYS_API_USER": API user`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "GLESYS_HTTP_TIMEOUT": API request timeout`) + fmt.Fprintln(w, ` - "GLESYS_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "GLESYS_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + fmt.Fprintln(w, ` - "GLESYS_TTL": The TTL of the TXT record used for the DNS challenge`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/glesys`) + + case "godaddy": + // generated from: providers/dns/godaddy/godaddy.toml + fmt.Fprintln(w, `Configuration for Go Daddy.`) + fmt.Fprintln(w, `Code: 'godaddy'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "GODADDY_API_KEY": **TODO**`) + fmt.Fprintln(w, ` - "GODADDY_API_SECRET": **TODO**`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "GODADDY_HTTP_TIMEOUT": API request timeout`) + fmt.Fprintln(w, ` - "GODADDY_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "GODADDY_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + fmt.Fprintln(w, ` - "GODADDY_SEQUENCE_INTERVAL": Interval between iteration`) + fmt.Fprintln(w, ` - "GODADDY_TTL": The TTL of the TXT record used for the DNS challenge`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/godaddy`) + + case "hostingde": + // generated from: providers/dns/hostingde/hostingde.toml + fmt.Fprintln(w, `Configuration for Hosting.de.`) + fmt.Fprintln(w, `Code: 'hostingde'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "HOSTINGDE_API_KEY": **TODO**`) + fmt.Fprintln(w, ` - "HOSTINGDE_ZONE_NAME": **TODO**`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "HOSTINGDE_HTTP_TIMEOUT": API request timeout`) + fmt.Fprintln(w, ` - "HOSTINGDE_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "HOSTINGDE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + fmt.Fprintln(w, ` - "HOSTINGDE_TTL": The TTL of the TXT record used for the DNS challenge`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/hostingde`) + + case "httpreq": + // generated from: providers/dns/httpreq/httpreq.toml + fmt.Fprintln(w, `Configuration for HTTP request.`) + fmt.Fprintln(w, `Code: 'httpreq'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "HTTPREQ_ENDPOINT": The URL of the server`) + fmt.Fprintln(w, ` - "HTTPREQ_MODE": 'RAW', none`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "HTTPREQ_HTTP_TIMEOUT": API request timeout`) + fmt.Fprintln(w, ` - "HTTPREQ_PASSWORD": **TODO**`) + fmt.Fprintln(w, ` - "HTTPREQ_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "HTTPREQ_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + fmt.Fprintln(w, ` - "HTTPREQ_USERNAME": **TODO**`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/httpreq`) + + case "iij": + // generated from: providers/dns/iij/iij.toml + fmt.Fprintln(w, `Configuration for Internet Initiative Japan.`) + fmt.Fprintln(w, `Code: 'iij'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "IIJ_API_ACCESS_KEY": API access key`) + fmt.Fprintln(w, ` - "IIJ_API_SECRET_KEY": API secret key`) + fmt.Fprintln(w, ` - "IIJ_DO_SERVICE_CODE": DO service code`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "IIJ_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "IIJ_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + fmt.Fprintln(w, ` - "IIJ_TTL": The TTL of the TXT record used for the DNS challenge`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/iij`) + + case "inwx": + // generated from: providers/dns/inwx/inwx.toml + fmt.Fprintln(w, `Configuration for INWX.`) + fmt.Fprintln(w, `Code: 'inwx'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "INWX_PASSWORD": Password`) + fmt.Fprintln(w, ` - "INWX_USERNAME": Username`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "INWX_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "INWX_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + fmt.Fprintln(w, ` - "INWX_SANDBOX": Activate the sandbox (boolean)`) + fmt.Fprintln(w, ` - "INWX_TTL": The TTL of the TXT record used for the DNS challenge`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/inwx`) + + case "lightsail": + // generated from: providers/dns/lightsail/lightsail.toml + fmt.Fprintln(w, `Configuration for Amazon Lightsail.`) + fmt.Fprintln(w, `Code: 'lightsail'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "AWS_ACCESS_KEY_ID": Access key ID`) + fmt.Fprintln(w, ` - "AWS_SECRET_ACCESS_KEY": Secret access key`) + fmt.Fprintln(w, ` - "DNS_ZONE": DNS zone`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "LIGHTSAIL_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "LIGHTSAIL_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/lightsail`) + + case "linode": + // generated from: providers/dns/linode/linode.toml + fmt.Fprintln(w, `Configuration for Linode (deprecated).`) + fmt.Fprintln(w, `Code: 'linode'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "LINODE_API_KEY": API key`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "LINODE_HTTP_TIMEOUT": API request timeout`) + fmt.Fprintln(w, ` - "LINODE_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "LINODE_TTL": The TTL of the TXT record used for the DNS challenge`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/linode`) + + case "linodev4": + // generated from: providers/dns/linodev4/linodev4.toml + fmt.Fprintln(w, `Configuration for Linode (v4).`) + fmt.Fprintln(w, `Code: 'linodev4'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "LINODE_TOKEN": API token`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "LINODE_HTTP_TIMEOUT": API request timeout`) + fmt.Fprintln(w, ` - "LINODE_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "LINODE_TTL": The TTL of the TXT record used for the DNS challenge`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/linodev4`) + + case "mydnsjp": + // generated from: providers/dns/mydnsjp/mydnsjp.toml + fmt.Fprintln(w, `Configuration for MyDNS.jp.`) + fmt.Fprintln(w, `Code: 'mydnsjp'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "MYDNSJP_MASTER_ID": Master ID`) + fmt.Fprintln(w, ` - "MYDNSJP_PASSWORD": Password`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "MYDNSJP_HTTP_TIMEOUT": API request timeout`) + fmt.Fprintln(w, ` - "MYDNSJP_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "MYDNSJP_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + fmt.Fprintln(w, ` - "MYDNSJP_TTL": The TTL of the TXT record used for the DNS challenge`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/mydnsjp`) + + case "namecheap": + // generated from: providers/dns/namecheap/namecheap.toml + fmt.Fprintln(w, `Configuration for Namecheap.`) + fmt.Fprintln(w, `Code: 'namecheap'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "NAMECHEAP_API_KEY": API key`) + fmt.Fprintln(w, ` - "NAMECHEAP_API_USER": API user`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "NAMECHEAP_HTTP_TIMEOUT": API request timeout`) + fmt.Fprintln(w, ` - "NAMECHEAP_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "NAMECHEAP_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + fmt.Fprintln(w, ` - "NAMECHEAP_TTL": The TTL of the TXT record used for the DNS challenge`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/namecheap`) + + case "namedotcom": + // generated from: providers/dns/namedotcom/namedotcom.toml + fmt.Fprintln(w, `Configuration for Name.com.`) + fmt.Fprintln(w, `Code: 'namedotcom'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "NAMECOM_API_TOKEN": API token`) + fmt.Fprintln(w, ` - "NAMECOM_USERNAME": Username`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "NAMECOM_HTTP_TIMEOUT": API request timeout`) + fmt.Fprintln(w, ` - "NAMECOM_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "NAMECOM_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + fmt.Fprintln(w, ` - "NAMECOM_TTL": The TTL of the TXT record used for the DNS challenge`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/namedotcom`) + + case "netcup": + // generated from: providers/dns/netcup/netcup.toml + fmt.Fprintln(w, `Configuration for Netcup.`) + fmt.Fprintln(w, `Code: 'netcup'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "NETCUP_API_KEY": API key`) + fmt.Fprintln(w, ` - "NETCUP_API_PASSWORD": API password`) + fmt.Fprintln(w, ` - "NETCUP_CUSTOMER_NUMBER": Customer number`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "NETCUP_HTTP_TIMEOUT": API request timeout`) + fmt.Fprintln(w, ` - "NETCUP_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "NETCUP_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + fmt.Fprintln(w, ` - "NETCUP_TTL": The TTL of the TXT record used for the DNS challenge`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/netcup`) + + case "nifcloud": + // generated from: providers/dns/nifcloud/nifcloud.toml + fmt.Fprintln(w, `Configuration for NIFCloud.`) + fmt.Fprintln(w, `Code: 'nifcloud'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "NIFCLOUD_ACCESS_KEY_ID": Access key`) + fmt.Fprintln(w, ` - "NIFCLOUD_SECRET_ACCESS_KEY": Secret access key`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "NIFCLOUD_HTTP_TIMEOUT": API request timeout`) + fmt.Fprintln(w, ` - "NIFCLOUD_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "NIFCLOUD_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + fmt.Fprintln(w, ` - "NIFCLOUD_TTL": The TTL of the TXT record used for the DNS challenge`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/nifcloud`) + + case "ns1": + // generated from: providers/dns/ns1/ns1.toml + fmt.Fprintln(w, `Configuration for NS1.`) + fmt.Fprintln(w, `Code: 'ns1'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "NS1_API_KEY": API key`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "NS1_HTTP_TIMEOUT": API request timeout`) + fmt.Fprintln(w, ` - "NS1_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "NS1_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + fmt.Fprintln(w, ` - "NS1_TTL": The TTL of the TXT record used for the DNS challenge`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/ns1`) + + case "oraclecloud": + // generated from: providers/dns/oraclecloud/oraclecloud.toml + fmt.Fprintln(w, `Configuration for Oracle Cloud.`) + fmt.Fprintln(w, `Code: 'oraclecloud'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "OCI_COMPARTMENT_OCID": Compartment OCID`) + fmt.Fprintln(w, ` - "OCI_PRIVKEY_FILE": Private key file`) + fmt.Fprintln(w, ` - "OCI_PRIVKEY_PASS": Private key password`) + fmt.Fprintln(w, ` - "OCI_PUBKEY_FINGERPRINT": Public key fingerprint`) + fmt.Fprintln(w, ` - "OCI_REGION": Region`) + fmt.Fprintln(w, ` - "OCI_TENANCY_OCID": Tenanct OCID`) + fmt.Fprintln(w, ` - "OCI_USER_OCID": User OCID`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "OCI_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "OCI_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + fmt.Fprintln(w, ` - "OCI_TTL": The TTL of the TXT record used for the DNS challenge`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/oraclecloud`) + + case "otc": + // generated from: providers/dns/otc/otc.toml + fmt.Fprintln(w, `Configuration for Open Telekom Cloud.`) + fmt.Fprintln(w, `Code: 'otc'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "OTC_DOMAIN_NAME": Domain name`) + fmt.Fprintln(w, ` - "OTC_IDENTITY_ENDPOINT": Identity endpoint URL`) + fmt.Fprintln(w, ` - "OTC_PASSWORD": Password`) + fmt.Fprintln(w, ` - "OTC_PROJECT_NAME": Project name`) + fmt.Fprintln(w, ` - "OTC_USER_NAME": User name`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "OTC_HTTP_TIMEOUT": API request timeout`) + fmt.Fprintln(w, ` - "OTC_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "OTC_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + fmt.Fprintln(w, ` - "OTC_TTL": The TTL of the TXT record used for the DNS challenge`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/otc`) + + case "ovh": + // generated from: providers/dns/ovh/ovh.toml + fmt.Fprintln(w, `Configuration for OVH.`) + fmt.Fprintln(w, `Code: 'ovh'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "OVH_APPLICATION_KEY": Application key`) + fmt.Fprintln(w, ` - "OVH_APPLICATION_SECRET": Application secret`) + fmt.Fprintln(w, ` - "OVH_CONSUMER_KEY": Consumer key`) + fmt.Fprintln(w, ` - "OVH_ENDPOINT": Endpoint URL (ovh-eu or ovh-ca)`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "OVH_HTTP_TIMEOUT": API request timeout`) + fmt.Fprintln(w, ` - "OVH_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "OVH_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + fmt.Fprintln(w, ` - "OVH_TTL": The TTL of the TXT record used for the DNS challenge`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/ovh`) + + case "pdns": + // generated from: providers/dns/pdns/pdns.toml + fmt.Fprintln(w, `Configuration for PowerDNS.`) + fmt.Fprintln(w, `Code: 'pdns'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "PDNS_API_KEY": API key`) + fmt.Fprintln(w, ` - "PDNS_API_URL": API url`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "PDNS_HTTP_TIMEOUT": API request timeout`) + fmt.Fprintln(w, ` - "PDNS_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "PDNS_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + fmt.Fprintln(w, ` - "PDNS_TTL": The TTL of the TXT record used for the DNS challenge`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/pdns`) + + case "rackspace": + // generated from: providers/dns/rackspace/rackspace.toml + fmt.Fprintln(w, `Configuration for Rackspace.`) + fmt.Fprintln(w, `Code: 'rackspace'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "RACKSPACE_API_KEY": API key`) + fmt.Fprintln(w, ` - "RACKSPACE_USER": API user`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "RACKSPACE_HTTP_TIMEOUT": API request timeout`) + fmt.Fprintln(w, ` - "RACKSPACE_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "RACKSPACE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + fmt.Fprintln(w, ` - "RACKSPACE_TTL": The TTL of the TXT record used for the DNS challenge`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/rackspace`) + + case "rfc2136": + // generated from: providers/dns/rfc2136/rfc2136.toml + fmt.Fprintln(w, `Configuration for RFC2136.`) + fmt.Fprintln(w, `Code: 'rfc2136'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "RFC2136_NAMESERVER": Network address in the form "host" or "host:port"`) + fmt.Fprintln(w, ` - "RFC2136_TSIG_ALGORITHM": TSIG algorythm. See [miekg/dns#tsig.go](https://github.com/miekg/dns/blob/master/tsig.go) for supported values. To disable TSIG authentication, leave the 'RFC2136_TSIG*' variables unset.`) + fmt.Fprintln(w, ` - "RFC2136_TSIG_KEY": Name of the secret key as defined in DNS server configuration. To disable TSIG authentication, leave the 'RFC2136_TSIG*' variables unset.`) + fmt.Fprintln(w, ` - "RFC2136_TSIG_SECRET": Secret key payload. To disable TSIG authentication, leave the' RFC2136_TSIG*' variables unset.`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "RFC2136_DNS_TIMEOUT": API request timeout`) + fmt.Fprintln(w, ` - "RFC2136_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "RFC2136_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + fmt.Fprintln(w, ` - "RFC2136_SEQUENCE_INTERVAL": Interval between iteration`) + fmt.Fprintln(w, ` - "RFC2136_TTL": The TTL of the TXT record used for the DNS challenge`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/rfc2136`) + + case "route53": + // generated from: providers/dns/route53/route53.toml + fmt.Fprintln(w, `Configuration for Amazon Route 53.`) + fmt.Fprintln(w, `Code: 'route53'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "AWS_ACCESS_KEY_ID": `) + fmt.Fprintln(w, ` - "AWS_HOSTED_ZONE_ID": `) + fmt.Fprintln(w, ` - "AWS_REGION": `) + fmt.Fprintln(w, ` - "AWS_SECRET_ACCESS_KEY": `) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "AWS_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "AWS_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + fmt.Fprintln(w, ` - "AWS_TTL": The TTL of the TXT record used for the DNS challenge`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/route53`) + + case "sakuracloud": + // generated from: providers/dns/sakuracloud/sakuracloud.toml + fmt.Fprintln(w, `Configuration for Sakura Cloud.`) + fmt.Fprintln(w, `Code: 'sakuracloud'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "SAKURACLOUD_ACCESS_TOKEN": Access token`) + fmt.Fprintln(w, ` - "SAKURACLOUD_ACCESS_TOKEN_SECRET": Access token secret`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "SAKURACLOUD_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "SAKURACLOUD_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + fmt.Fprintln(w, ` - "SAKURACLOUD_TTL": The TTL of the TXT record used for the DNS challenge`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/sakuracloud`) + + case "selectel": + // generated from: providers/dns/selectel/selectel.toml + fmt.Fprintln(w, `Configuration for Selectel.`) + fmt.Fprintln(w, `Code: 'selectel'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "SELECTEL_API_TOKEN": API token`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "SELECTEL_BASE_URL": API endpoint URL`) + fmt.Fprintln(w, ` - "SELECTEL_HTTP_TIMEOUT": API request timeout`) + fmt.Fprintln(w, ` - "SELECTEL_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "SELECTEL_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + fmt.Fprintln(w, ` - "SELECTEL_TTL": The TTL of the TXT record used for the DNS challenge`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/selectel`) + + case "stackpath": + // generated from: providers/dns/stackpath/stackpath.toml + fmt.Fprintln(w, `Configuration for Stackpath.`) + fmt.Fprintln(w, `Code: 'stackpath'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "STACKPATH_CLIENT_ID": Client ID`) + fmt.Fprintln(w, ` - "STACKPATH_CLIENT_SECRET": Client secret`) + fmt.Fprintln(w, ` - "STACKPATH_STACK_ID": Stack ID`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "STACKPATH_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "STACKPATH_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + fmt.Fprintln(w, ` - "STACKPATH_TTL": The TTL of the TXT record used for the DNS challenge`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/stackpath`) + + case "transip": + // generated from: providers/dns/transip/transip.toml + fmt.Fprintln(w, `Configuration for TransIP.`) + fmt.Fprintln(w, `Code: 'transip'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "TRANSIP_ACCOUNT_NAME": Account name`) + fmt.Fprintln(w, ` - "TRANSIP_PRIVATE_KEY_PATH": Private key path`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "TRANSIP_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "TRANSIP_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + fmt.Fprintln(w, ` - "TRANSIP_TTL": The TTL of the TXT record used for the DNS challenge`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/transip`) + + case "vegadns": + // generated from: providers/dns/vegadns/vegadns.toml + fmt.Fprintln(w, `Configuration for VegaDNS.`) + fmt.Fprintln(w, `Code: 'vegadns'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "SECRET_VEGADNS_KEY": API key`) + fmt.Fprintln(w, ` - "SECRET_VEGADNS_SECRET": API secret`) + fmt.Fprintln(w, ` - "VEGADNS_URL": API endpoint URL`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "VEGADNS_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "VEGADNS_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + fmt.Fprintln(w, ` - "VEGADNS_TTL": The TTL of the TXT record used for the DNS challenge`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/vegadns`) + + case "vscale": + // generated from: providers/dns/vscale/vscale.toml + fmt.Fprintln(w, `Configuration for Vscale.`) + fmt.Fprintln(w, `Code: 'vscale'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "VSCALE_API_TOKEN": API token`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "VSCALE_BASE_URL": API enddpoint URL`) + fmt.Fprintln(w, ` - "VSCALE_HTTP_TIMEOUT": API request timeout`) + fmt.Fprintln(w, ` - "VSCALE_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "VSCALE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + fmt.Fprintln(w, ` - "VSCALE_TTL": The TTL of the TXT record used for the DNS challenge`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/vscale`) + + case "vultr": + // generated from: providers/dns/vultr/vultr.toml + fmt.Fprintln(w, `Configuration for Vultr.`) + fmt.Fprintln(w, `Code: 'vultr'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "VULTR_API_KEY": API key`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "VULTR_HTTP_TIMEOUT": API request timeout`) + fmt.Fprintln(w, ` - "VULTR_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "VULTR_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + fmt.Fprintln(w, ` - "VULTR_TTL": The TTL of the TXT record used for the DNS challenge`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/vultr`) + + case "zoneee": + // generated from: providers/dns/zoneee/zoneee.toml + fmt.Fprintln(w, `Configuration for Zone.ee.`) + fmt.Fprintln(w, `Code: 'zoneee'`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Credentials:`) + fmt.Fprintln(w, ` - "ZONEEE_API_KEY": API key`) + fmt.Fprintln(w, ` - "ZONEEE_API_USER": API user`) + fmt.Fprintln(w) + + fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "ZONEEE_ENDPOINT": API endpoint URL`) + fmt.Fprintln(w, ` - "ZONEEE_HTTP_TIMEOUT": API request timeout`) + fmt.Fprintln(w, ` - "ZONEEE_POLLING_INTERVAL": Time between DNS propagation check`) + fmt.Fprintln(w, ` - "ZONEEE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + fmt.Fprintln(w, ` - "ZONEEE_TTL": The TTL of the TXT record used for the DNS challenge`) + + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/zoneee`) + + case "manual": + fmt.Fprintln(w, `Solving the DNS-01 challenge using CLI prompt.`) + default: + log.Fatalf("%q is not yet supported.", name) + } + w.Flush() +} diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 00000000..7b822970 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,2 @@ +themes/ +public/ diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 00000000..09d3dad2 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +.PHONY: default clean hugo hugo-build + +default: hugo + +clean: + rm -rf public/ + + +hugo-build: clean hugo-themes + hugo --enableGitInfo --source . + +hugo: + hugo server --disableFastRender --enableGitInfo --watch --source . + # hugo server -D + +hugo-themes: + rm -rf themes + mkdir themes + git clone --depth=1 https://github.com/matcornic/hugo-theme-learn.git themes/hugo-theme-learn + rm -rf themes/hugo-theme-learn/.git diff --git a/docs/archetypes/default.md b/docs/archetypes/default.md new file mode 100644 index 00000000..00e77bd7 --- /dev/null +++ b/docs/archetypes/default.md @@ -0,0 +1,6 @@ +--- +title: "{{ replace .Name "-" " " | title }}" +date: {{ .Date }} +draft: true +--- + diff --git a/docs/config.toml b/docs/config.toml new file mode 100644 index 00000000..028dcf1c --- /dev/null +++ b/docs/config.toml @@ -0,0 +1,67 @@ +baseURL = "https://xenolf.github.io/lego/" +languageCode = "en-us" +title = "Lego" + +theme = "hugo-theme-learn" + +# Code higlighting settings +pygmentsCodefences = true +pygmentsCodeFencesGuesSsyntax = false +pygmentsOptions = "" +pygmentsStyle = "monokai" +# The monokai stylesheet is included in the base template. +pygmentsUseClasses = true + +[permalinks] + dns = "/dns/:slug/" + +[params] + # Prefix URL to edit current page. Will display an "Edit this page" button on top right hand corner of every page. + # Useful to give opportunity to people to create merge request for your doc. + # See the config.toml file from this documentation site to have an example. +# editURL = "" + # Author of the site, will be used in meta information + author = "Lego Team" + # Description of the site, will be used in meta information +# description = "" + # Shows a checkmark for visited pages on the menu + showVisitedLinks = true + # Disable search function. It will hide search bar +# disableSearch = false + # Javascript and CSS cache are automatically busted when new version of site is generated. + # Set this to true to disable this behavior (some proxies don't handle well this optimization) +# disableAssetsBusting = false + # Set this to true to disable copy-to-clipboard button for inline code. +# disableInlineCopyToClipBoard = true + # A title for shortcuts in menu is set by default. Set this to true to disable it. +# disableShortcutsTitle = false + # When using mulitlingual website, disable the switch language button. +# disableLanguageSwitchingButton = false + # Hide breadcrumbs in the header and only show the current page title +# disableBreadcrumb = true + # Hide Next and Previous page buttons normally displayed full height beside content +# disableNextPrev = true + # Order sections in menu by "weight" or "title". Default to "weight" +# ordersectionsby = "weight" + # Change default color scheme with a variant one. Can be "red", "blue", "green". + themeVariant = "blue" + +[Languages] +[Languages.en] + title = "Let’s Encrypt client and ACME library written in Go." + weight = 1 + languageName = "English" + +[[Languages.en.menu.shortcuts]] + name = " Github repo" + identifier = "ds" + url = "https://github.com/xenof/lego" + weight = 10 + +[[Languages.en.menu.shortcuts]] + name = " Issues" + url = "https://github.com/xenolf/lego/issues" + weight = 11 + +[outputs] + home = [ "HTML", "RSS", "JSON"] diff --git a/docs/content/_index.md b/docs/content/_index.md new file mode 100644 index 00000000..a3634dab --- /dev/null +++ b/docs/content/_index.md @@ -0,0 +1,30 @@ +--- +title: "Welcome" +date: 2019-03-03T16:39:46+01:00 +draft: false +chapter: true +--- + +# Lego + +Let's Encrypt client and ACME library written in Go. + +## 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) + - DNS (dns-01) + - TLS (tls-alpn-01) +- SAN certificate support +- Comes with multiple optional [DNS providers](dns) +- [Custom challenge solvers](usage/library/writing-a-challenge-solver/) +- Certificate bundling +- OCSP helper function + + +lego introduced support for ACME v2 in [v1.0.0](https://github.com/xenolf/lego/releases/tag/v1.0.0). +If you still need to utilize ACME v1, you can do so by using the [v0.5.0](https://github.com/xenolf/lego/releases/tag/v0.5.0) version. diff --git a/docs/content/dns/_index.md b/docs/content/dns/_index.md new file mode 100644 index 00000000..d4a80a51 --- /dev/null +++ b/docs/content/dns/_index.md @@ -0,0 +1,18 @@ +--- +title: "DNS Providers" +date: 2019-03-03T16:39:46+01:00 +draft: false +weight: 3 +--- + +Credentials for DNS providers must be passed through environment variables. + +Here is an example bash command using the CloudFlare DNS provider: + +```bash +CLOUDFLARE_EMAIL=foo@bar.com \ +CLOUDFLARE_API_KEY=b9841238feb177a84330febba8a83208921177bffe733 \ +lego --dns cloudflare --domains www.example.com --email me@bar.com run +``` + +{{%children style="h2" description="true" %}} \ No newline at end of file diff --git a/docs/content/dns/manual.md b/docs/content/dns/manual.md new file mode 100644 index 00000000..8a9c125e --- /dev/null +++ b/docs/content/dns/manual.md @@ -0,0 +1,59 @@ +--- +title: "Manual" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: manual +--- + +Solving the DNS-01 challenge using CLI prompt. + + + +## Example + +```txt +Do you accept the TOS? Y/n + +[INFO] acme: Registering account for test@test.com +!!!! HEADS UP !!!! + + Your account credentials have been saved in your Let's Encrypt + configuration directory at "~/.lego/accounts". + 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. + +[INFO] [test.com] acme: Obtaining bundled SAN certificate +[INFO] [test.com] AuthURL: https://acme-v02.api.letsencrypt.org/acme/authz/lornkZmVYjsh5wLHpxdQcZDPekGf_TYUM-MTJk3-yrA +[INFO] [test.com] acme: Could not find solver for: tls-alpn-01 +[INFO] [test.com] acme: Could not find solver for: http-01 +[INFO] [test.com] acme: use dns-01 solver +[INFO] [test.com] acme: Preparing to solve DNS-01 +lego: Please create the following TXT record in your test.com. zone: +_acme-challenge.test.com. 120 IN TXT "VP-dby1RBuUOnDZg1n9sF-cwicLsognMzJb0Vx8ttAI" +lego: Press 'Enter' when you are done + +Do you accept the TOS? Y/n + +[INFO] acme: Registering account for test@test.com +!!!! HEADS UP !!!! + + Your account credentials have been saved in your Let's Encrypt + configuration directory at "~/.lego/accounts". + 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. + +[INFO] [test.com] acme: Obtaining bundled SAN certificate +[INFO] [test.com] AuthURL: https://acme-v02.api.letsencrypt.org/acme/authz/lornkZmVYjsh5wLHpxdQcZDPekGf_TYUM-MTJk3-yrA +[INFO] [test.com] acme: Could not find solver for: tls-alpn-01 +[INFO] [test.com] acme: Could not find solver for: http-01 +[INFO] [test.com] acme: use dns-01 solver +[INFO] [test.com] acme: Preparing to solve DNS-01 +lego: Please create the following TXT record in your test.com. zone: +_acme-challenge.test.com. 120 IN TXT "VP-dby1RBuUOnDZg1n9sF-cwicLsognMzJb0Vx8ttAI" +lego: Press 'Enter' when you are done + +``` \ No newline at end of file diff --git a/docs/content/dns/zz_gen_acme-dns.md b/docs/content/dns/zz_gen_acme-dns.md new file mode 100644 index 00000000..ed671b45 --- /dev/null +++ b/docs/content/dns/zz_gen_acme-dns.md @@ -0,0 +1,46 @@ +--- +title: "Joohoi's ACME-DNS" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: acme-dns +--- + + + + + + +Configuration for [Joohoi's ACME-DNS](https://github.com/joohoi/acme-dns). + + + + +- Code: `acme-dns` + +{{% notice note %}} +_Please contribute by adding a CLI example._ +{{% /notice %}} + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `ACME_DNS_API_BASE` | The ACME-DNS API address | +| `ACME_DNS_STORAGE_PATH` | The ACME-DNS JSON account data file. A per-domain account will be registered/persisted to this file and used for TXT updates. | + + + + + + +## More information + +- [API documentation](https://github.com/joohoi/acme-dns#api) +- [Go client](https://github.com/cpu/goacmedns) + + + + diff --git a/docs/content/dns/zz_gen_alidns.md b/docs/content/dns/zz_gen_alidns.md new file mode 100644 index 00000000..0b1ae6fa --- /dev/null +++ b/docs/content/dns/zz_gen_alidns.md @@ -0,0 +1,54 @@ +--- +title: "Alibaba Cloud DNS" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: alidns +--- + + + + + + +Configuration for [Alibaba Cloud DNS](https://www.alibabacloud.com/product/dns). + + + + +- Code: `alidns` + +{{% notice note %}} +_Please contribute by adding a CLI example._ +{{% /notice %}} + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `ALICLOUD_ACCESS_KEY` | Access key ID | +| `ALICLOUD_SECRET_KEY` | Access Key secret | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `ALICLOUD_HTTP_TIMEOUT` | API request timeout | +| `ALICLOUD_POLLING_INTERVAL` | Time between DNS propagation check | +| `ALICLOUD_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `ALICLOUD_TTL` | The TTL of the TXT record used for the DNS challenge | + + + + +## More information + +- [API documentation](https://www.alibabacloud.com/help/doc-detail/42875.htm) +- [Go client](https://github.com/aliyun/alibaba-cloud-sdk-go) + + + + diff --git a/docs/content/dns/zz_gen_auroradns.md b/docs/content/dns/zz_gen_auroradns.md new file mode 100644 index 00000000..223819bb --- /dev/null +++ b/docs/content/dns/zz_gen_auroradns.md @@ -0,0 +1,54 @@ +--- +title: "Aurora DNS" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: auroradns +--- + + + + + + +Configuration for [Aurora DNS](https://www.pcextreme.com/aurora/dns). + + + + +- Code: `auroradns` + +{{% notice note %}} +_Please contribute by adding a CLI example._ +{{% /notice %}} + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `AURORA_ENDPOINT` | API endpoint URL | +| `AURORA_KEY` | User API key | +| `AURORA_USER_ID` | User ID | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `AURORA_POLLING_INTERVAL` | Time between DNS propagation check | +| `AURORA_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `AURORA_TTL` | The TTL of the TXT record used for the DNS challenge | + + + + +## More information + +- [API documentation](https://libcloud.readthedocs.io/en/latest/dns/drivers/auroradns.html#api-docs) +- [Go client](https://github.com/nrdcg/auroradns) + + + + diff --git a/docs/content/dns/zz_gen_azure.md b/docs/content/dns/zz_gen_azure.md new file mode 100644 index 00000000..4186dc87 --- /dev/null +++ b/docs/content/dns/zz_gen_azure.md @@ -0,0 +1,58 @@ +--- +title: "Azure" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: azure +--- + + + + + + +Configuration for [Azure](https://azure.microsoft.com/services/dns/). + + + + +- Code: `azure` + +{{% notice note %}} +_Please contribute by adding a CLI example._ +{{% /notice %}} + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `AZURE_CLIENT_ID` | Client ID | +| `AZURE_CLIENT_SECRET` | Client secret | +| `AZURE_RESOURCE_GROUP` | Resource group | +| `AZURE_SUBSCRIPTION_ID` | Subscription ID | +| `AZURE_TENANT_ID` | Tenant ID | +| `instance metadata service` | If the credentials are **not** set via the environment, then it will attempt to get a bearer token via the [instance metadata service](https://docs.microsoft.com/en-us/azure/virtual-machines/windows/instance-metadata-service). | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `AZURE_METADATA_ENDPOINT` | Metadata Service endpoint URL | +| `AZURE_POLLING_INTERVAL` | Time between DNS propagation check | +| `AZURE_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `AZURE_TTL` | The TTL of the TXT record used for the DNS challenge | + + + + +## More information + +- [API documentation](https://docs.microsoft.com/en-us/go/azure/) +- [Go client](https://github.com/Azure/azure-sdk-for-go) + + + + diff --git a/docs/content/dns/zz_gen_bluecat.md b/docs/content/dns/zz_gen_bluecat.md new file mode 100644 index 00000000..64d7082d --- /dev/null +++ b/docs/content/dns/zz_gen_bluecat.md @@ -0,0 +1,53 @@ +--- +title: "Bluecat" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: bluecat +--- + + + + + + +Configuration for [Bluecat](https://www.bluecatnetworks.com). + + + + +- Code: `bluecat` + +{{% notice note %}} +_Please contribute by adding a CLI example._ +{{% /notice %}} + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `BLUECAT_CONFIG_NAME` | Configuration name | +| `BLUECAT_DNS_VIEW` | External DNS View Name | +| `BLUECAT_PASSWORD` | API password | +| `BLUECAT_SERVER_URL` | The server URL, should have scheme, hostname, and port (if required) of the authoritative Bluecat BAM serve | +| `BLUECAT_USER_NAME` | API username | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `BLUECAT_HTTP_TIMEOUT` | API request timeout | +| `BLUECAT_POLLING_INTERVAL` | Time between DNS propagation check | +| `BLUECAT_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `BLUECAT_TTL` | The TTL of the TXT record used for the DNS challenge | + + + + + + + + diff --git a/docs/content/dns/zz_gen_cloudflare.md b/docs/content/dns/zz_gen_cloudflare.md new file mode 100644 index 00000000..34868c8a --- /dev/null +++ b/docs/content/dns/zz_gen_cloudflare.md @@ -0,0 +1,58 @@ +--- +title: "Cloudflare" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: cloudflare +--- + + + + + + +Configuration for [Cloudflare](https://www.cloudflare.com/dns/). + + + + +- Code: `cloudflare` + +Here is an example bash command using the Cloudflare provider: + +```bash +CLOUDFLARE_EMAIL=foo@bar.com \ +CLOUDFLARE_API_KEY=b9841238feb177a84330febba8a83208921177bffe733 \ +lego --dns cloudflare --domains my.domain.com --email my@email.com run +``` + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `CLOUDFLARE_API_KEY` | API key | +| `CLOUDFLARE_EMAIL` | Account email | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `CLOUDFLARE_HTTP_TIMEOUT` | API request timeout | +| `CLOUDFLARE_POLLING_INTERVAL` | Time between DNS propagation check | +| `CLOUDFLARE_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `CLOUDFLARE_TTL` | The TTL of the TXT record used for the DNS challenge | + + + + +## More information + +- [API documentation](https://api.cloudflare.com/) +- [Go client](https://github.com/cloudflare/cloudflare-go) + + + + diff --git a/docs/content/dns/zz_gen_cloudns.md b/docs/content/dns/zz_gen_cloudns.md new file mode 100644 index 00000000..6b5f14a9 --- /dev/null +++ b/docs/content/dns/zz_gen_cloudns.md @@ -0,0 +1,53 @@ +--- +title: "ClouDNS" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: cloudns +--- + + + + + + +Configuration for [ClouDNS](https://www.cloudns.net). + + + + +- Code: `cloudns` + +{{% notice note %}} +_Please contribute by adding a CLI example._ +{{% /notice %}} + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `CLOUDNS_AUTH_ID` | The API user ID | +| `CLOUDNS_AUTH_PASSWORD` | The password for API user ID | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `CLOUDNS_HTTP_TIMEOUT` | API request timeout | +| `CLOUDNS_POLLING_INTERVAL` | Time between DNS propagation check | +| `CLOUDNS_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `CLOUDNS_TTL` | The TTL of the TXT record used for the DNS challenge | + + + + +## More information + +- [API documentation](https://www.cloudns.net/wiki/article/42/) + + + + diff --git a/docs/content/dns/zz_gen_cloudxns.md b/docs/content/dns/zz_gen_cloudxns.md new file mode 100644 index 00000000..d89a5c5e --- /dev/null +++ b/docs/content/dns/zz_gen_cloudxns.md @@ -0,0 +1,53 @@ +--- +title: "CloudXNS" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: cloudxns +--- + + + + + + +Configuration for [CloudXNS](https://www.cloudxns.net/). + + + + +- Code: `cloudxns` + +{{% notice note %}} +_Please contribute by adding a CLI example._ +{{% /notice %}} + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `CLOUDXNS_API_KEY` | The API key | +| `CLOUDXNS_SECRET_KEY` | THe API secret key | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `CLOUDXNS_HTTP_TIMEOUT` | API request timeout | +| `CLOUDXNS_POLLING_INTERVAL` | Time between DNS propagation check | +| `CLOUDXNS_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `CLOUDXNS_TTL` | The TTL of the TXT record used for the DNS challenge | + + + + +## More information + +- [API documentation](https://www.cloudxns.net/Public/Doc/CloudXNS_api2.0_doc_zh-cn.zip) + + + + diff --git a/docs/content/dns/zz_gen_conoha.md b/docs/content/dns/zz_gen_conoha.md new file mode 100644 index 00000000..c37e245f --- /dev/null +++ b/docs/content/dns/zz_gen_conoha.md @@ -0,0 +1,55 @@ +--- +title: "ConoHa" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: conoha +--- + + + + + + +Configuration for [ConoHa](https://www.conoha.jp/). + + + + +- Code: `conoha` + +{{% notice note %}} +_Please contribute by adding a CLI example._ +{{% /notice %}} + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `CONOHA_API_PASSWORD` | The API password | +| `CONOHA_API_USERNAME` | The API username | +| `CONOHA_TENANT_ID` | Tenant ID | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `CONOHA_HTTP_TIMEOUT` | API request timeout | +| `CONOHA_POLLING_INTERVAL` | Time between DNS propagation check | +| `CONOHA_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `CONOHA_REGION` | The region | +| `CONOHA_TTL` | The TTL of the TXT record used for the DNS challenge | + + + + +## More information + +- [API documentation](https://www.conoha.jp/docs/) + + + + diff --git a/docs/content/dns/zz_gen_designate.md b/docs/content/dns/zz_gen_designate.md new file mode 100644 index 00000000..090be577 --- /dev/null +++ b/docs/content/dns/zz_gen_designate.md @@ -0,0 +1,56 @@ +--- +title: "Designate DNSaaS for Openstack" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: designate +--- + + + + + + +Configuration for [Designate DNSaaS for Openstack](https://docs.openstack.org/designate/latest/). + + + + +- Code: `designate` + +{{% notice note %}} +_Please contribute by adding a CLI example._ +{{% /notice %}} + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `OS_AUTH_URL` | Identity endpoint URL | +| `OS_PASSWORD` | Password | +| `OS_REGION_NAME` | Region name | +| `OS_TENANT_NAME` | Tenant name | +| `OS_USERNAME` | Username | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `DESIGNATE_POLLING_INTERVAL` | Time between DNS propagation check | +| `DESIGNATE_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `DESIGNATE_TTL` | The TTL of the TXT record used for the DNS challenge | + + + + +## More information + +- [API documentation](https://docs.openstack.org/designate/latest/) +- [Go client](https://godoc.org/github.com/gophercloud/gophercloud/openstack/dns/v2) + + + + diff --git a/docs/content/dns/zz_gen_digitalocean.md b/docs/content/dns/zz_gen_digitalocean.md new file mode 100644 index 00000000..09b31f7a --- /dev/null +++ b/docs/content/dns/zz_gen_digitalocean.md @@ -0,0 +1,52 @@ +--- +title: "Digital Ocean" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: digitalocean +--- + + + + + + +Configuration for [Digital Ocean](https://www.digitalocean.com/docs/networking/dns/). + + + + +- Code: `digitalocean` + +{{% notice note %}} +_Please contribute by adding a CLI example._ +{{% /notice %}} + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `DO_AUTH_TOKEN` | Authentication token | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `DO_HTTP_TIMEOUT` | API request timeout | +| `DO_POLLING_INTERVAL` | Time between DNS propagation check | +| `DO_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `DO_TTL` | The TTL of the TXT record used for the DNS challenge | + + + + +## More information + +- [API documentation](https://developers.digitalocean.com/documentation/v2/#domain-records) + + + + diff --git a/docs/content/dns/zz_gen_dnsimple.md b/docs/content/dns/zz_gen_dnsimple.md new file mode 100644 index 00000000..0689948b --- /dev/null +++ b/docs/content/dns/zz_gen_dnsimple.md @@ -0,0 +1,53 @@ +--- +title: "DNSimple" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: dnsimple +--- + + + + + + +Configuration for [DNSimple](https://dnsimple.com/). + + + + +- Code: `dnsimple` + +{{% notice note %}} +_Please contribute by adding a CLI example._ +{{% /notice %}} + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `DNSIMPLE_BASE_URL` | API endpoint URL | +| `DNSIMPLE_OAUTH_TOKEN` | OAuth token | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `DNSIMPLE_POLLING_INTERVAL` | Time between DNS propagation check | +| `DNSIMPLE_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `DNSIMPLE_TTL` | The TTL of the TXT record used for the DNS challenge | + + + + +## More information + +- [API documentation](https://developer.dnsimple.com/v2/) +- [Go client](https://github.com/dnsimple/dnsimple-go) + + + + diff --git a/docs/content/dns/zz_gen_dnsmadeeasy.md b/docs/content/dns/zz_gen_dnsmadeeasy.md new file mode 100644 index 00000000..5841f210 --- /dev/null +++ b/docs/content/dns/zz_gen_dnsmadeeasy.md @@ -0,0 +1,54 @@ +--- +title: "DNS Made Easy" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: dnsmadeeasy +--- + + + + + + +Configuration for [DNS Made Easy](https://dnsmadeeasy.com/). + + + + +- Code: `dnsmadeeasy` + +{{% notice note %}} +_Please contribute by adding a CLI example._ +{{% /notice %}} + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `DNSMADEEASY_API_KEY` | The API key | +| `DNSMADEEASY_API_SECRET` | The API Secret key | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `DNSMADEEASY_HTTP_TIMEOUT` | API request timeout | +| `DNSMADEEASY_POLLING_INTERVAL` | Time between DNS propagation check | +| `DNSMADEEASY_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `DNSMADEEASY_SANDBOX` | Activate the sandbox (boolean) | +| `DNSMADEEASY_TTL` | The TTL of the TXT record used for the DNS challenge | + + + + +## More information + +- [API documentation](https://api-docs.dnsmadeeasy.com/) + + + + diff --git a/docs/content/dns/zz_gen_dnspod.md b/docs/content/dns/zz_gen_dnspod.md new file mode 100644 index 00000000..76b0f81c --- /dev/null +++ b/docs/content/dns/zz_gen_dnspod.md @@ -0,0 +1,53 @@ +--- +title: "DNSPod" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: dnspod +--- + + + + + + +Configuration for [DNSPod](http://www.dnspod.com/). + + + + +- Code: `dnspod` + +{{% notice note %}} +_Please contribute by adding a CLI example._ +{{% /notice %}} + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `DNSPOD_API_KEY` | The user token | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `DNSPOD_HTTP_TIMEOUT` | API request timeout | +| `DNSPOD_POLLING_INTERVAL` | Time between DNS propagation check | +| `DNSPOD_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `DNSPOD_TTL` | The TTL of the TXT record used for the DNS challenge | + + + + +## More information + +- [API documentation](https://www.dnspod.com/docs/index.html) +- [Go client](https://github.com/decker502/dnspod-go) + + + + diff --git a/docs/content/dns/zz_gen_dreamhost.md b/docs/content/dns/zz_gen_dreamhost.md new file mode 100644 index 00000000..01f90f5f --- /dev/null +++ b/docs/content/dns/zz_gen_dreamhost.md @@ -0,0 +1,52 @@ +--- +title: "DreamHost" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: dreamhost +--- + + + + + + +Configuration for [DreamHost](https://www.dreamhost.com). + + + + +- Code: `dreamhost` + +{{% notice note %}} +_Please contribute by adding a CLI example._ +{{% /notice %}} + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `DREAMHOST_API_KEY` | The API key | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `DREAMHOST_HTTP_TIMEOUT` | API request timeout | +| `DREAMHOST_POLLING_INTERVAL` | Time between DNS propagation check | +| `DREAMHOST_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `DREAMHOST_TTL` | The TTL of the TXT record used for the DNS challenge | + + + + +## More information + +- [API documentation](https://help.dreamhost.com/hc/en-us/articles/217560167-API_overview) + + + + diff --git a/docs/content/dns/zz_gen_duckdns.md b/docs/content/dns/zz_gen_duckdns.md new file mode 100644 index 00000000..3c891588 --- /dev/null +++ b/docs/content/dns/zz_gen_duckdns.md @@ -0,0 +1,53 @@ +--- +title: "Duck DNS" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: duckdns +--- + + + + + + +Configuration for [Duck DNS](https://www.duckdns.org/). + + + + +- Code: `duckdns` + +{{% notice note %}} +_Please contribute by adding a CLI example._ +{{% /notice %}} + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `DUCKDNS_TOKEN` | Account token | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `DUCKDNS_HTTP_TIMEOUT` | API request timeout | +| `DUCKDNS_POLLING_INTERVAL` | Time between DNS propagation check | +| `DUCKDNS_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `DUCKDNS_SEQUENCE_INTERVAL` | Interval between iteration | +| `DUCKDNS_TTL` | The TTL of the TXT record used for the DNS challenge | + + + + +## More information + +- [API documentation](https://www.duckdns.org/spec.jsp) + + + + diff --git a/docs/content/dns/zz_gen_dyn.md b/docs/content/dns/zz_gen_dyn.md new file mode 100644 index 00000000..10271700 --- /dev/null +++ b/docs/content/dns/zz_gen_dyn.md @@ -0,0 +1,54 @@ +--- +title: "Dyn" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: dyn +--- + + + + + + +Configuration for [Dyn](https://dyn.com/). + + + + +- Code: `dyn` + +{{% notice note %}} +_Please contribute by adding a CLI example._ +{{% /notice %}} + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `DYN_CUSTOMER_NAME` | Customer name | +| `DYN_PASSWORD` | Paswword | +| `DYN_USER_NAME` | User name | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `DYN_HTTP_TIMEOUT` | API request timeout | +| `DYN_POLLING_INTERVAL` | Time between DNS propagation check | +| `DYN_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `DYN_TTL` | The TTL of the TXT record used for the DNS challenge | + + + + +## More information + +- [API documentation](https://help.dyn.com/rest/) + + + + diff --git a/docs/content/dns/zz_gen_exec.md b/docs/content/dns/zz_gen_exec.md new file mode 100644 index 00000000..e6114e6a --- /dev/null +++ b/docs/content/dns/zz_gen_exec.md @@ -0,0 +1,133 @@ +--- +title: "External program" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: exec +--- + + + + + +Solving the DNS-01 challenge using an external program. + + + + +- Code: `exec` + +Here is an example bash command using the External program provider: + +```bash +EXEC_PATH=/the/path/to/myscript.sh \ +lego --dns exec --domains my.domain.com --email my@email.com run +``` + + + + + +## Base Configuration + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `EXEC_MODE` | `RAW`, none | +| `EXEC_PATH` | TODO | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `EXEC_POLLING_INTERVAL` | Time between DNS propagation check | +| `EXEC_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | + + +## Description + +The file name of the external program is specified in the environment variable `EXEC_PATH`. + +When it is run by lego, three command-line parameters are passed to it: +The action ("present" or "cleanup"), the fully-qualified domain name and the value for the record. + +For example, requesting a certificate for the domain 'foo.example.com' can be achieved by calling lego as follows: + +```bash +EXEC_PATH=./update-dns.sh \ + lego --dns exec \ + --domains foo.example.com \ + --email invalid@example.com run +``` + +It will then call the program './update-dns.sh' with like this: + +```bash +./update-dns.sh "present" "_acme-challenge.foo.example.com." "MsijOYZxqyjGnFGwhjrhfg-Xgbl5r68WPda0J9EgqqI" +``` + +The program then needs to make sure the record is inserted. +When it returns an error via a non-zero exit code, lego aborts. + +When the record is to be removed again, +the program is called with the first command-line parameter set to `cleanup` instead of `present`. + +If you want to use the raw domain, token, and keyAuth values with your program, you can set `EXEC_MODE=RAW`: + +```bash +EXEC_MODE=RAW \ +EXEC_PATH=./update-dns.sh \ + lego --dns exec \ + --domains foo.example.com \ + --email invalid@example.com run +``` + +It will then call the program `./update-dns.sh` like this: + +```bash +./update-dns.sh "present" "foo.example.com." "--" "some-token" "KxAy-J3NwUmg9ZQuM-gP_Mq1nStaYSaP9tYQs5_-YsE.ksT-qywTd8058G-SHHWA3RAN72Pr0yWtPYmmY5UBpQ8" +``` + +## Commands + +{{% notice note %}} +The `--` is because the token MAY start with a `-`, and the called program may try and interpret a `-` as indicating a flag. +In the case of urfave, which is commonly used, +you can use the `--` delimiter to specify the start of positional arguments, and handle such a string safely. +{{% /notice %}} + +### Present + +| Mode | Command | +|---------|----------------------------------------------------| +| default | `myprogram present -- ` | +| `RAW` | `myprogram present -- ` | + +### Cleanup + +| Mode | Command | +|---------|----------------------------------------------------| +| default | `myprogram cleanup -- ` | +| `RAW` | `myprogram cleanup -- ` | + +### Timeout + +The command have to display propagation timeout and polling interval into Stdout. + +The values must be formatted as JSON, and times are in seconds. +Example: `{"timeout": 30, "interval": 5}` + +If an error occurs or if the command is not provided: +the default display propagation timeout and polling interval are used. + +| Mode | Command | +|---------|----------------------------------------------------| +| default | `myprogram timeout` | +| `RAW` | `myprogram timeout` | + + + + + + + + diff --git a/docs/content/dns/zz_gen_exoscale.md b/docs/content/dns/zz_gen_exoscale.md new file mode 100644 index 00000000..f764f3d4 --- /dev/null +++ b/docs/content/dns/zz_gen_exoscale.md @@ -0,0 +1,55 @@ +--- +title: "Exoscale" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: exoscale +--- + + + + + + +Configuration for [Exoscale](https://www.exoscale.com/). + + + + +- Code: `exoscale` + +{{% notice note %}} +_Please contribute by adding a CLI example._ +{{% /notice %}} + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `EXOSCALE_API_KEY` | API key | +| `EXOSCALE_API_SECRET` | API secret | +| `EXOSCALE_ENDPOINT` | API endpoint URL | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `EXOSCALE_HTTP_TIMEOUT` | API request timeout | +| `EXOSCALE_POLLING_INTERVAL` | Time between DNS propagation check | +| `EXOSCALE_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `EXOSCALE_TTL` | The TTL of the TXT record used for the DNS challenge | + + + + +## More information + +- [API documentation](https://community.exoscale.com/documentation/dns/api/) +- [Go client](https://github.com/exoscale/egoscale) + + + + diff --git a/docs/content/dns/zz_gen_fastdns.md b/docs/content/dns/zz_gen_fastdns.md new file mode 100644 index 00000000..c46f23b6 --- /dev/null +++ b/docs/content/dns/zz_gen_fastdns.md @@ -0,0 +1,55 @@ +--- +title: "FastDNS" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: fastdns +--- + + + + + + +Configuration for [FastDNS](https://www.akamai.com/us/en/products/security/fast-dns.jsp). + + + + +- Code: `fastdns` + +{{% notice note %}} +_Please contribute by adding a CLI example._ +{{% /notice %}} + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `AKAMAI_ACCESS_TOKEN` | Access token | +| `AKAMAI_CLIENT_SECRET` | Client secret | +| `AKAMAI_CLIENT_TOKEN` | Client token | +| `AKAMAI_HOST` | API host | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `AKAMAI_POLLING_INTERVAL` | Time between DNS propagation check | +| `AKAMAI_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `AKAMAI_TTL` | The TTL of the TXT record used for the DNS challenge | + + + + +## More information + +- [API documentation](https://developer.akamai.com/api/web_performance/fast_dns_record_management/v1.html) +- [Go client](https://github.com/akamai/AkamaiOPEN-edgegrid-golang) + + + + diff --git a/docs/content/dns/zz_gen_gandi.md b/docs/content/dns/zz_gen_gandi.md new file mode 100644 index 00000000..feb2aa4d --- /dev/null +++ b/docs/content/dns/zz_gen_gandi.md @@ -0,0 +1,52 @@ +--- +title: "Gandi" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: gandi +--- + + + + + + +Configuration for [Gandi](https://www.gandi.net). + + + + +- Code: `gandi` + +{{% notice note %}} +_Please contribute by adding a CLI example._ +{{% /notice %}} + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `GANDI_API_KEY` | API key | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `GANDI_HTTP_TIMEOUT` | API request timeout | +| `GANDI_POLLING_INTERVAL` | Time between DNS propagation check | +| `GANDI_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `GANDI_TTL` | The TTL of the TXT record used for the DNS challenge | + + + + +## More information + +- [API documentation](http://doc.rpc.gandi.net/index.html) + + + + diff --git a/docs/content/dns/zz_gen_gandiv5.md b/docs/content/dns/zz_gen_gandiv5.md new file mode 100644 index 00000000..b3b5ac1b --- /dev/null +++ b/docs/content/dns/zz_gen_gandiv5.md @@ -0,0 +1,52 @@ +--- +title: "Gandi Live DNS (v5)" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: gandiv5 +--- + + + + + + +Configuration for [Gandi Live DNS (v5)](https://www.gandi.net). + + + + +- Code: `gandiv5` + +{{% notice note %}} +_Please contribute by adding a CLI example._ +{{% /notice %}} + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `GANDIV5_API_KEY` | API key | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `GANDIV5_HTTP_TIMEOUT` | API request timeout | +| `GANDIV5_POLLING_INTERVAL` | Time between DNS propagation check | +| `GANDIV5_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `GANDIV5_TTL` | The TTL of the TXT record used for the DNS challenge | + + + + +## More information + +- [API documentation](http://doc.livedns.gandi.net) + + + + diff --git a/docs/content/dns/zz_gen_gcloud.md b/docs/content/dns/zz_gen_gcloud.md new file mode 100644 index 00000000..de08207a --- /dev/null +++ b/docs/content/dns/zz_gen_gcloud.md @@ -0,0 +1,55 @@ +--- +title: "Google Cloud" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: gcloud +--- + + + + + + +Configuration for [Google Cloud](https://cloud.google.com). + + + + +- Code: `gcloud` + +{{% notice note %}} +_Please contribute by adding a CLI example._ +{{% /notice %}} + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `Application Default Credentials` | [Documentation](https://cloud.google.com/docs/authentication/production#providing_credentials_to_your_application) | +| `GCE_PROJECT` | Project name | +| `GCE_SERVICE_ACCOUNT` | Account | +| `GCE_SERVICE_ACCOUNT_FILE` | Account file path | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `GCE_POLLING_INTERVAL` | Time between DNS propagation check | +| `GCE_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `GCE_TTL` | The TTL of the TXT record used for the DNS challenge | + + + + +## More information + +- [API documentation](https://community.exoscale.com/documentation/dns/api/) +- [Go client](https://github.com/googleapis/google-api-go-client) + + + + diff --git a/docs/content/dns/zz_gen_glesys.md b/docs/content/dns/zz_gen_glesys.md new file mode 100644 index 00000000..5413d90b --- /dev/null +++ b/docs/content/dns/zz_gen_glesys.md @@ -0,0 +1,53 @@ +--- +title: "Glesys" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: glesys +--- + + + + + + +Configuration for [Glesys](https://glesys.com/). + + + + +- Code: `glesys` + +{{% notice note %}} +_Please contribute by adding a CLI example._ +{{% /notice %}} + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `GLESYS_API_KEY` | API key | +| `GLESYS_API_USER` | API user | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `GLESYS_HTTP_TIMEOUT` | API request timeout | +| `GLESYS_POLLING_INTERVAL` | Time between DNS propagation check | +| `GLESYS_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `GLESYS_TTL` | The TTL of the TXT record used for the DNS challenge | + + + + +## More information + +- [API documentation](https://github.com/GleSYS/API/wiki/API-Documentation) + + + + diff --git a/docs/content/dns/zz_gen_godaddy.md b/docs/content/dns/zz_gen_godaddy.md new file mode 100644 index 00000000..1458ed15 --- /dev/null +++ b/docs/content/dns/zz_gen_godaddy.md @@ -0,0 +1,54 @@ +--- +title: "Go Daddy" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: godaddy +--- + + + + + + +Configuration for [Go Daddy](https://godaddy.com). + + + + +- Code: `godaddy` + +{{% notice note %}} +_Please contribute by adding a CLI example._ +{{% /notice %}} + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `GODADDY_API_KEY` | **TODO** | +| `GODADDY_API_SECRET` | **TODO** | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `GODADDY_HTTP_TIMEOUT` | API request timeout | +| `GODADDY_POLLING_INTERVAL` | Time between DNS propagation check | +| `GODADDY_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `GODADDY_SEQUENCE_INTERVAL` | Interval between iteration | +| `GODADDY_TTL` | The TTL of the TXT record used for the DNS challenge | + + + + +## More information + +- [API documentation](https://developer.godaddy.com/doc/endpoint/domains) + + + + diff --git a/docs/content/dns/zz_gen_hostingde.md b/docs/content/dns/zz_gen_hostingde.md new file mode 100644 index 00000000..5b927633 --- /dev/null +++ b/docs/content/dns/zz_gen_hostingde.md @@ -0,0 +1,53 @@ +--- +title: "Hosting.de" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: hostingde +--- + + + + + + +Configuration for [Hosting.de](https://www.hosting.de/). + + + + +- Code: `hostingde` + +{{% notice note %}} +_Please contribute by adding a CLI example._ +{{% /notice %}} + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `HOSTINGDE_API_KEY` | **TODO** | +| `HOSTINGDE_ZONE_NAME` | **TODO** | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `HOSTINGDE_HTTP_TIMEOUT` | API request timeout | +| `HOSTINGDE_POLLING_INTERVAL` | Time between DNS propagation check | +| `HOSTINGDE_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `HOSTINGDE_TTL` | The TTL of the TXT record used for the DNS challenge | + + + + +## More information + +- [API documentation](https://www.hosting.de/api/#dns) + + + + diff --git a/docs/content/dns/zz_gen_httpreq.md b/docs/content/dns/zz_gen_httpreq.md new file mode 100644 index 00000000..05c1cb63 --- /dev/null +++ b/docs/content/dns/zz_gen_httpreq.md @@ -0,0 +1,91 @@ +--- +title: "HTTP request" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: httpreq +--- + + + + + + +Configuration for [HTTP request](/dns/httpreq/). + + + + +- Code: `httpreq` + +Here is an example bash command using the HTTP request provider: + +```bash +HTTPREQ_ENDPOINT=http://my.server.com:9090 \ +lego --dns httpreq --domains my.domain.com --email my@email.com run +``` + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `HTTPREQ_ENDPOINT` | The URL of the server | +| `HTTPREQ_MODE` | `RAW`, none | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `HTTPREQ_HTTP_TIMEOUT` | API request timeout | +| `HTTPREQ_PASSWORD` | **TODO** | +| `HTTPREQ_POLLING_INTERVAL` | Time between DNS propagation check | +| `HTTPREQ_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `HTTPREQ_USERNAME` | **TODO** | + +## Description + +The server must provide: + +- `POST` `/present` +- `POST` `/cleanup` + +The URL of the server must be define by `HTTPREQ_ENDPOINT`. + +### Mode + +There are 2 modes (`HTTPREQ_MODE`): + +- default mode: +```json +{ + "fqdn": "_acme-challenge.domain.", + "value": "LHDhK3oGRvkiefQnx7OOczTY5Tic_xZ6HcMOc_gmtoM" +} +``` + +- `RAW` +```json +{ + "domain": "domain", + "token": "token", + "keyAuth": "key" +} +``` + +### Authentication + +Basic authentication (optional) can be set with some environment variables: + +- `HTTPREQ_USERNAME` and `HTTPREQ_PASSWORD` +- both values must be set, otherwise basic authentication is not defined. + + + + + + + + diff --git a/docs/content/dns/zz_gen_iij.md b/docs/content/dns/zz_gen_iij.md new file mode 100644 index 00000000..d1daea31 --- /dev/null +++ b/docs/content/dns/zz_gen_iij.md @@ -0,0 +1,54 @@ +--- +title: "Internet Initiative Japan" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: iij +--- + + + + + + +Configuration for [Internet Initiative Japan](https://www.iij.ad.jp/en/). + + + + +- Code: `iij` + +{{% notice note %}} +_Please contribute by adding a CLI example._ +{{% /notice %}} + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `IIJ_API_ACCESS_KEY` | API access key | +| `IIJ_API_SECRET_KEY` | API secret key | +| `IIJ_DO_SERVICE_CODE` | DO service code | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `IIJ_POLLING_INTERVAL` | Time between DNS propagation check | +| `IIJ_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `IIJ_TTL` | The TTL of the TXT record used for the DNS challenge | + + + + +## More information + +- [API documentation](http://manual.iij.jp/p2/pubapi/http://manual.iij.jp/p2/pubapi/) +- [Go client](https://github.com/iij/doapi) + + + + diff --git a/docs/content/dns/zz_gen_inwx.md b/docs/content/dns/zz_gen_inwx.md new file mode 100644 index 00000000..917a7e97 --- /dev/null +++ b/docs/content/dns/zz_gen_inwx.md @@ -0,0 +1,54 @@ +--- +title: "INWX" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: inwx +--- + + + + + + +Configuration for [INWX](https://www.inwx.de/en). + + + + +- Code: `inwx` + +{{% notice note %}} +_Please contribute by adding a CLI example._ +{{% /notice %}} + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `INWX_PASSWORD` | Password | +| `INWX_USERNAME` | Username | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `INWX_POLLING_INTERVAL` | Time between DNS propagation check | +| `INWX_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `INWX_SANDBOX` | Activate the sandbox (boolean) | +| `INWX_TTL` | The TTL of the TXT record used for the DNS challenge | + + + + +## More information + +- [API documentation](https://www.inwx.de/en/help/apidoc) +- [Go client](https://github.com/nrdcg/goinwx) + + + + diff --git a/docs/content/dns/zz_gen_lightsail.md b/docs/content/dns/zz_gen_lightsail.md new file mode 100644 index 00000000..4e904bf4 --- /dev/null +++ b/docs/content/dns/zz_gen_lightsail.md @@ -0,0 +1,53 @@ +--- +title: "Amazon Lightsail" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: lightsail +--- + + + + + + +Configuration for [Amazon Lightsail](https://aws.amazon.com/lightsail/). + + + + +- Code: `lightsail` + +{{% notice note %}} +_Please contribute by adding a CLI example._ +{{% /notice %}} + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `AWS_ACCESS_KEY_ID` | Access key ID | +| `AWS_SECRET_ACCESS_KEY` | Secret access key | +| `DNS_ZONE` | DNS zone | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `LIGHTSAIL_POLLING_INTERVAL` | Time between DNS propagation check | +| `LIGHTSAIL_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | + + + + +## More information + + +- [Go client](https://github.com/aws/aws-sdk-go/aws) + + + + diff --git a/docs/content/dns/zz_gen_linode.md b/docs/content/dns/zz_gen_linode.md new file mode 100644 index 00000000..6b6113b1 --- /dev/null +++ b/docs/content/dns/zz_gen_linode.md @@ -0,0 +1,52 @@ +--- +title: "Linode (deprecated)" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: linode +--- + + + + + + +Configuration for [Linode (deprecated)](https://www.linode.com/). + + + + +- Code: `linode` + +{{% notice note %}} +_Please contribute by adding a CLI example._ +{{% /notice %}} + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `LINODE_API_KEY` | API key | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `LINODE_HTTP_TIMEOUT` | API request timeout | +| `LINODE_POLLING_INTERVAL` | Time between DNS propagation check | +| `LINODE_TTL` | The TTL of the TXT record used for the DNS challenge | + + + + +## More information + +- [API documentation](https://www.linode.com/api/dns) +- [Go client](https://github.com/timewasted/linode) + + + + diff --git a/docs/content/dns/zz_gen_linodev4.md b/docs/content/dns/zz_gen_linodev4.md new file mode 100644 index 00000000..317b701a --- /dev/null +++ b/docs/content/dns/zz_gen_linodev4.md @@ -0,0 +1,52 @@ +--- +title: "Linode (v4)" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: linodev4 +--- + + + + + + +Configuration for [Linode (v4)](https://www.linode.com/). + + + + +- Code: `linodev4` + +{{% notice note %}} +_Please contribute by adding a CLI example._ +{{% /notice %}} + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `LINODE_TOKEN` | API token | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `LINODE_HTTP_TIMEOUT` | API request timeout | +| `LINODE_POLLING_INTERVAL` | Time between DNS propagation check | +| `LINODE_TTL` | The TTL of the TXT record used for the DNS challenge | + + + + +## More information + +- [API documentation](https://developers.linode.com/api/v4) +- [Go client](https://github.com/linode/linodego) + + + + diff --git a/docs/content/dns/zz_gen_mydnsjp.md b/docs/content/dns/zz_gen_mydnsjp.md new file mode 100644 index 00000000..ac996c02 --- /dev/null +++ b/docs/content/dns/zz_gen_mydnsjp.md @@ -0,0 +1,53 @@ +--- +title: "MyDNS.jp" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: mydnsjp +--- + + + + + + +Configuration for [MyDNS.jp](https://www.mydns.jp). + + + + +- Code: `mydnsjp` + +{{% notice note %}} +_Please contribute by adding a CLI example._ +{{% /notice %}} + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `MYDNSJP_MASTER_ID` | Master ID | +| `MYDNSJP_PASSWORD` | Password | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `MYDNSJP_HTTP_TIMEOUT` | API request timeout | +| `MYDNSJP_POLLING_INTERVAL` | Time between DNS propagation check | +| `MYDNSJP_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `MYDNSJP_TTL` | The TTL of the TXT record used for the DNS challenge | + + + + +## More information + +- [API documentation](https://www.mydns.jp/?MENU=030) + + + + diff --git a/docs/content/dns/zz_gen_namecheap.md b/docs/content/dns/zz_gen_namecheap.md new file mode 100644 index 00000000..ba4197dd --- /dev/null +++ b/docs/content/dns/zz_gen_namecheap.md @@ -0,0 +1,53 @@ +--- +title: "Namecheap" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: namecheap +--- + + + + + + +Configuration for [Namecheap](https://www.namecheap.com). + + + + +- Code: `namecheap` + +{{% notice note %}} +_Please contribute by adding a CLI example._ +{{% /notice %}} + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `NAMECHEAP_API_KEY` | API key | +| `NAMECHEAP_API_USER` | API user | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `NAMECHEAP_HTTP_TIMEOUT` | API request timeout | +| `NAMECHEAP_POLLING_INTERVAL` | Time between DNS propagation check | +| `NAMECHEAP_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `NAMECHEAP_TTL` | The TTL of the TXT record used for the DNS challenge | + + + + +## More information + +- [API documentation](https://www.namecheap.com/support/api/methods.aspx) + + + + diff --git a/docs/content/dns/zz_gen_namedotcom.md b/docs/content/dns/zz_gen_namedotcom.md new file mode 100644 index 00000000..7f7ba304 --- /dev/null +++ b/docs/content/dns/zz_gen_namedotcom.md @@ -0,0 +1,54 @@ +--- +title: "Name.com" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: namedotcom +--- + + + + + + +Configuration for [Name.com](https://www.name.com). + + + + +- Code: `namedotcom` + +{{% notice note %}} +_Please contribute by adding a CLI example._ +{{% /notice %}} + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `NAMECOM_API_TOKEN` | API token | +| `NAMECOM_USERNAME` | Username | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `NAMECOM_HTTP_TIMEOUT` | API request timeout | +| `NAMECOM_POLLING_INTERVAL` | Time between DNS propagation check | +| `NAMECOM_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `NAMECOM_TTL` | The TTL of the TXT record used for the DNS challenge | + + + + +## More information + +- [API documentation](https://www.name.com/api-docs/DNS) +- [Go client](https://github.com/namedotcom/go) + + + + diff --git a/docs/content/dns/zz_gen_netcup.md b/docs/content/dns/zz_gen_netcup.md new file mode 100644 index 00000000..4c42da06 --- /dev/null +++ b/docs/content/dns/zz_gen_netcup.md @@ -0,0 +1,54 @@ +--- +title: "Netcup" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: netcup +--- + + + + + + +Configuration for [Netcup](https://www.netcup.eu/). + + + + +- Code: `netcup` + +{{% notice note %}} +_Please contribute by adding a CLI example._ +{{% /notice %}} + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `NETCUP_API_KEY` | API key | +| `NETCUP_API_PASSWORD` | API password | +| `NETCUP_CUSTOMER_NUMBER` | Customer number | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `NETCUP_HTTP_TIMEOUT` | API request timeout | +| `NETCUP_POLLING_INTERVAL` | Time between DNS propagation check | +| `NETCUP_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `NETCUP_TTL` | The TTL of the TXT record used for the DNS challenge | + + + + +## More information + +- [API documentation](https://www.netcup-wiki.de/wiki/DNS_API) + + + + diff --git a/docs/content/dns/zz_gen_nifcloud.md b/docs/content/dns/zz_gen_nifcloud.md new file mode 100644 index 00000000..253b4f7a --- /dev/null +++ b/docs/content/dns/zz_gen_nifcloud.md @@ -0,0 +1,53 @@ +--- +title: "NIFCloud" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: nifcloud +--- + + + + + + +Configuration for [NIFCloud](https://www.nifcloud.com/). + + + + +- Code: `nifcloud` + +{{% notice note %}} +_Please contribute by adding a CLI example._ +{{% /notice %}} + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `NIFCLOUD_ACCESS_KEY_ID` | Access key | +| `NIFCLOUD_SECRET_ACCESS_KEY` | Secret access key | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `NIFCLOUD_HTTP_TIMEOUT` | API request timeout | +| `NIFCLOUD_POLLING_INTERVAL` | Time between DNS propagation check | +| `NIFCLOUD_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `NIFCLOUD_TTL` | The TTL of the TXT record used for the DNS challenge | + + + + +## More information + +- [API documentation](https://mbaas.nifcloud.com/doc/current/rest/common/format.html) + + + + diff --git a/docs/content/dns/zz_gen_ns1.md b/docs/content/dns/zz_gen_ns1.md new file mode 100644 index 00000000..609b1e41 --- /dev/null +++ b/docs/content/dns/zz_gen_ns1.md @@ -0,0 +1,53 @@ +--- +title: "NS1" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: ns1 +--- + + + + + + +Configuration for [NS1](https://ns1.com). + + + + +- Code: `ns1` + +{{% notice note %}} +_Please contribute by adding a CLI example._ +{{% /notice %}} + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `NS1_API_KEY` | API key | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `NS1_HTTP_TIMEOUT` | API request timeout | +| `NS1_POLLING_INTERVAL` | Time between DNS propagation check | +| `NS1_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `NS1_TTL` | The TTL of the TXT record used for the DNS challenge | + + + + +## More information + +- [API documentation](https://ns1.com/api) +- [Go client](https://github.com/ns1/ns1-go) + + + + diff --git a/docs/content/dns/zz_gen_oraclecloud.md b/docs/content/dns/zz_gen_oraclecloud.md new file mode 100644 index 00000000..42bad2ea --- /dev/null +++ b/docs/content/dns/zz_gen_oraclecloud.md @@ -0,0 +1,67 @@ +--- +title: "Oracle Cloud" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: oraclecloud +--- + + + + + + +Configuration for [Oracle Cloud](https://cloud.oracle.com/home). + + + + +- Code: `oraclecloud` + +Here is an example bash command using the Oracle Cloud provider: + +```bash +OCI_PRIVKEY_FILE="~/.oci/oci_api_key.pem" \ +OCI_PRIVKEY_PASS="secret" \ +OCI_TENANCY_OCID="ocid1.tenancy.oc1..secret" \ +OCI_USER_OCID="ocid1.user.oc1..secret" \ +OCI_PUBKEY_FINGERPRINT="00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00" \ +OCI_REGION="us-phoenix-1" \ +OCI_COMPARTMENT_OCID="ocid1.tenancy.oc1..secret" \ +lego --dns oraclecloud --domains my.domain.com --email my@email.com run +``` + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `OCI_COMPARTMENT_OCID` | Compartment OCID | +| `OCI_PRIVKEY_FILE` | Private key file | +| `OCI_PRIVKEY_PASS` | Private key password | +| `OCI_PUBKEY_FINGERPRINT` | Public key fingerprint | +| `OCI_REGION` | Region | +| `OCI_TENANCY_OCID` | Tenanct OCID | +| `OCI_USER_OCID` | User OCID | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `OCI_POLLING_INTERVAL` | Time between DNS propagation check | +| `OCI_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `OCI_TTL` | The TTL of the TXT record used for the DNS challenge | + + + + +## More information + +- [API documentation](https://docs.cloud.oracle.com/iaas/Content/DNS/Concepts/dnszonemanagement.htm) +- [Go client](https://github.com/oracle/oci-go-sdk) + + + + diff --git a/docs/content/dns/zz_gen_otc.md b/docs/content/dns/zz_gen_otc.md new file mode 100644 index 00000000..119bcfc5 --- /dev/null +++ b/docs/content/dns/zz_gen_otc.md @@ -0,0 +1,56 @@ +--- +title: "Open Telekom Cloud" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: otc +--- + + + + + + +Configuration for [Open Telekom Cloud](https://cloud.telekom.de/en). + + + + +- Code: `otc` + +{{% notice note %}} +_Please contribute by adding a CLI example._ +{{% /notice %}} + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `OTC_DOMAIN_NAME` | Domain name | +| `OTC_IDENTITY_ENDPOINT` | Identity endpoint URL | +| `OTC_PASSWORD` | Password | +| `OTC_PROJECT_NAME` | Project name | +| `OTC_USER_NAME` | User name | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `OTC_HTTP_TIMEOUT` | API request timeout | +| `OTC_POLLING_INTERVAL` | Time between DNS propagation check | +| `OTC_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `OTC_TTL` | The TTL of the TXT record used for the DNS challenge | + + + + +## More information + +- [API documentation](https://docs.otc.t-systems.com/en-us/dns/index.html) + + + + diff --git a/docs/content/dns/zz_gen_ovh.md b/docs/content/dns/zz_gen_ovh.md new file mode 100644 index 00000000..5b897df4 --- /dev/null +++ b/docs/content/dns/zz_gen_ovh.md @@ -0,0 +1,56 @@ +--- +title: "OVH" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: ovh +--- + + + + + + +Configuration for [OVH](https://www.ovh.com/). + + + + +- Code: `ovh` + +{{% notice note %}} +_Please contribute by adding a CLI example._ +{{% /notice %}} + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `OVH_APPLICATION_KEY` | Application key | +| `OVH_APPLICATION_SECRET` | Application secret | +| `OVH_CONSUMER_KEY` | Consumer key | +| `OVH_ENDPOINT` | Endpoint URL (ovh-eu or ovh-ca) | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `OVH_HTTP_TIMEOUT` | API request timeout | +| `OVH_POLLING_INTERVAL` | Time between DNS propagation check | +| `OVH_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `OVH_TTL` | The TTL of the TXT record used for the DNS challenge | + + + + +## More information + +- [API documentation](https://eu.api.ovh.com/) +- [Go client](https://github.com/ovh/go-ovh) + + + + diff --git a/docs/content/dns/zz_gen_pdns.md b/docs/content/dns/zz_gen_pdns.md new file mode 100644 index 00000000..f8834e9e --- /dev/null +++ b/docs/content/dns/zz_gen_pdns.md @@ -0,0 +1,60 @@ +--- +title: "PowerDNS" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: pdns +--- + + + + + + +Configuration for [PowerDNS](https://www.powerdns.com/). + + + + +- Code: `pdns` + +{{% notice note %}} +_Please contribute by adding a CLI example._ +{{% /notice %}} + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `PDNS_API_KEY` | API key | +| `PDNS_API_URL` | API url | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `PDNS_HTTP_TIMEOUT` | API request timeout | +| `PDNS_POLLING_INTERVAL` | Time between DNS propagation check | +| `PDNS_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `PDNS_TTL` | The TTL of the TXT record used for the DNS challenge | + +## Information + +Tested and confirmed to work with PowerDNS authoritative 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-EDIT-API` to `INCEPTION-INCREMENT` for the zone in the `domainmetadata` table + + + +## More information + +- [API documentation](https://doc.powerdns.com/md/httpapi/README/) + + + + diff --git a/docs/content/dns/zz_gen_rackspace.md b/docs/content/dns/zz_gen_rackspace.md new file mode 100644 index 00000000..67113418 --- /dev/null +++ b/docs/content/dns/zz_gen_rackspace.md @@ -0,0 +1,53 @@ +--- +title: "Rackspace" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: rackspace +--- + + + + + + +Configuration for [Rackspace](https://www.rackspace.com/). + + + + +- Code: `rackspace` + +{{% notice note %}} +_Please contribute by adding a CLI example._ +{{% /notice %}} + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `RACKSPACE_API_KEY` | API key | +| `RACKSPACE_USER` | API user | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `RACKSPACE_HTTP_TIMEOUT` | API request timeout | +| `RACKSPACE_POLLING_INTERVAL` | Time between DNS propagation check | +| `RACKSPACE_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `RACKSPACE_TTL` | The TTL of the TXT record used for the DNS challenge | + + + + +## More information + +- [API documentation](https://developer.rackspace.com/docs/cloud-dns/v1/) + + + + diff --git a/docs/content/dns/zz_gen_rfc2136.md b/docs/content/dns/zz_gen_rfc2136.md new file mode 100644 index 00000000..22140092 --- /dev/null +++ b/docs/content/dns/zz_gen_rfc2136.md @@ -0,0 +1,56 @@ +--- +title: "RFC2136" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: rfc2136 +--- + + + + + + +Configuration for [RFC2136](https://tools.ietf.org/html/rfc2136). + + + + +- Code: `rfc2136` + +{{% notice note %}} +_Please contribute by adding a CLI example._ +{{% /notice %}} + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `RFC2136_NAMESERVER` | Network address in the form "host" or "host:port" | +| `RFC2136_TSIG_ALGORITHM` | TSIG algorythm. See [miekg/dns#tsig.go](https://github.com/miekg/dns/blob/master/tsig.go) for supported values. To disable TSIG authentication, leave the `RFC2136_TSIG*` variables unset. | +| `RFC2136_TSIG_KEY` | Name of the secret key as defined in DNS server configuration. To disable TSIG authentication, leave the `RFC2136_TSIG*` variables unset. | +| `RFC2136_TSIG_SECRET` | Secret key payload. To disable TSIG authentication, leave the` RFC2136_TSIG*` variables unset. | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `RFC2136_DNS_TIMEOUT` | API request timeout | +| `RFC2136_POLLING_INTERVAL` | Time between DNS propagation check | +| `RFC2136_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `RFC2136_SEQUENCE_INTERVAL` | Interval between iteration | +| `RFC2136_TTL` | The TTL of the TXT record used for the DNS challenge | + + + + +## More information + +- [API documentation](https://tools.ietf.org/html/rfc2136) + + + + diff --git a/docs/content/dns/zz_gen_route53.md b/docs/content/dns/zz_gen_route53.md new file mode 100644 index 00000000..48ae58b7 --- /dev/null +++ b/docs/content/dns/zz_gen_route53.md @@ -0,0 +1,98 @@ +--- +title: "Amazon Route 53" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: route53 +--- + + + + + + +Configuration for [Amazon Route 53](https://aws.amazon.com/route53/). + + + + +- Code: `route53` + +{{% notice note %}} +_Please contribute by adding a CLI example._ +{{% /notice %}} + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `AWS_ACCESS_KEY_ID` | | +| `AWS_HOSTED_ZONE_ID` | | +| `AWS_REGION` | | +| `AWS_SECRET_ACCESS_KEY` | | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `AWS_POLLING_INTERVAL` | Time between DNS propagation check | +| `AWS_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `AWS_TTL` | The TTL of the TXT record used for the DNS challenge | + +## Description + +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: [configuring-sdk](https://github.com/aws/aws-sdk-go/wiki/configuring-sdk) + +## Policy + +The following AWS IAM policy document describes the permissions required for lego to complete the DNS challenge. + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "", + "Effect": "Allow", + "Action": [ + "route53:GetChange", + "route53:ChangeResourceRecordSets", + "route53:ListResourceRecordSets" + ], + "Resource": [ + "arn:aws:route53:::hostedzone/*", + "arn:aws:route53:::change/*" + ] + }, + { + "Sid": "", + "Effect": "Allow", + "Action": "route53:ListHostedZonesByName", + "Resource": "*" + } + ] +} +``` + + + + +## More information + +- [API documentation](https://docs.aws.amazon.com/Route53/latest/APIReference/API_Operations_Amazon_Route_53.html) +- [Go client](https://github.com/aws/aws-sdk-go/aws) + + + + diff --git a/docs/content/dns/zz_gen_sakuracloud.md b/docs/content/dns/zz_gen_sakuracloud.md new file mode 100644 index 00000000..69b734d3 --- /dev/null +++ b/docs/content/dns/zz_gen_sakuracloud.md @@ -0,0 +1,53 @@ +--- +title: "Sakura Cloud" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: sakuracloud +--- + + + + + + +Configuration for [Sakura Cloud](https://cloud.sakura.ad.jp/). + + + + +- Code: `sakuracloud` + +{{% notice note %}} +_Please contribute by adding a CLI example._ +{{% /notice %}} + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `SAKURACLOUD_ACCESS_TOKEN` | Access token | +| `SAKURACLOUD_ACCESS_TOKEN_SECRET` | Access token secret | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `SAKURACLOUD_POLLING_INTERVAL` | Time between DNS propagation check | +| `SAKURACLOUD_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `SAKURACLOUD_TTL` | The TTL of the TXT record used for the DNS challenge | + + + + +## More information + +- [API documentation](https://developer.sakura.ad.jp/cloud/api/1.1/) +- [Go client](https://github.com/sacloud/libsacloud) + + + + diff --git a/docs/content/dns/zz_gen_selectel.md b/docs/content/dns/zz_gen_selectel.md new file mode 100644 index 00000000..ba452036 --- /dev/null +++ b/docs/content/dns/zz_gen_selectel.md @@ -0,0 +1,53 @@ +--- +title: "Selectel" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: selectel +--- + + + + + + +Configuration for [Selectel](https://kb.selectel.com/). + + + + +- Code: `selectel` + +{{% notice note %}} +_Please contribute by adding a CLI example._ +{{% /notice %}} + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `SELECTEL_API_TOKEN` | API token | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `SELECTEL_BASE_URL` | API endpoint URL | +| `SELECTEL_HTTP_TIMEOUT` | API request timeout | +| `SELECTEL_POLLING_INTERVAL` | Time between DNS propagation check | +| `SELECTEL_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `SELECTEL_TTL` | The TTL of the TXT record used for the DNS challenge | + + + + +## More information + +- [API documentation](https://kb.selectel.com/23136054.html) + + + + diff --git a/docs/content/dns/zz_gen_stackpath.md b/docs/content/dns/zz_gen_stackpath.md new file mode 100644 index 00000000..04047729 --- /dev/null +++ b/docs/content/dns/zz_gen_stackpath.md @@ -0,0 +1,53 @@ +--- +title: "Stackpath" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: stackpath +--- + + + + + + +Configuration for [Stackpath](https://www.stackpath.com/). + + + + +- Code: `stackpath` + +{{% notice note %}} +_Please contribute by adding a CLI example._ +{{% /notice %}} + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `STACKPATH_CLIENT_ID` | Client ID | +| `STACKPATH_CLIENT_SECRET` | Client secret | +| `STACKPATH_STACK_ID` | Stack ID | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `STACKPATH_POLLING_INTERVAL` | Time between DNS propagation check | +| `STACKPATH_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `STACKPATH_TTL` | The TTL of the TXT record used for the DNS challenge | + + + + +## More information + +- [API documentation](https://developer.stackpath.com/en/api/dns/#tag/Zone) + + + + diff --git a/docs/content/dns/zz_gen_transip.md b/docs/content/dns/zz_gen_transip.md new file mode 100644 index 00000000..ad3457b1 --- /dev/null +++ b/docs/content/dns/zz_gen_transip.md @@ -0,0 +1,53 @@ +--- +title: "TransIP" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: transip +--- + + + + + + +Configuration for [TransIP](https://www.transip.nl/). + + + + +- Code: `transip` + +{{% notice note %}} +_Please contribute by adding a CLI example._ +{{% /notice %}} + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `TRANSIP_ACCOUNT_NAME` | Account name | +| `TRANSIP_PRIVATE_KEY_PATH` | Private key path | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `TRANSIP_POLLING_INTERVAL` | Time between DNS propagation check | +| `TRANSIP_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `TRANSIP_TTL` | The TTL of the TXT record used for the DNS challenge | + + + + +## More information + +- [API documentation](https://api.transip.nl/docs/transip.nl/package-Transip.html) +- [Go client](https://github.com/transip/gotransip) + + + + diff --git a/docs/content/dns/zz_gen_vegadns.md b/docs/content/dns/zz_gen_vegadns.md new file mode 100644 index 00000000..3958de8f --- /dev/null +++ b/docs/content/dns/zz_gen_vegadns.md @@ -0,0 +1,54 @@ +--- +title: "VegaDNS" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: vegadns +--- + + + + + + +Configuration for [VegaDNS](https://github.com/shupp/VegaDNS-API). + + + + +- Code: `vegadns` + +{{% notice note %}} +_Please contribute by adding a CLI example._ +{{% /notice %}} + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `SECRET_VEGADNS_KEY` | API key | +| `SECRET_VEGADNS_SECRET` | API secret | +| `VEGADNS_URL` | API endpoint URL | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `VEGADNS_POLLING_INTERVAL` | Time between DNS propagation check | +| `VEGADNS_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `VEGADNS_TTL` | The TTL of the TXT record used for the DNS challenge | + + + + +## More information + +- [API documentation](https://github.com/shupp/VegaDNS-API) +- [Go client](https://github.com/OpenDNS/vegadns2client) + + + + diff --git a/docs/content/dns/zz_gen_vscale.md b/docs/content/dns/zz_gen_vscale.md new file mode 100644 index 00000000..76e09c94 --- /dev/null +++ b/docs/content/dns/zz_gen_vscale.md @@ -0,0 +1,53 @@ +--- +title: "Vscale" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: vscale +--- + + + + + + +Configuration for [Vscale](https://vscale.io/). + + + + +- Code: `vscale` + +{{% notice note %}} +_Please contribute by adding a CLI example._ +{{% /notice %}} + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `VSCALE_API_TOKEN` | API token | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `VSCALE_BASE_URL` | API enddpoint URL | +| `VSCALE_HTTP_TIMEOUT` | API request timeout | +| `VSCALE_POLLING_INTERVAL` | Time between DNS propagation check | +| `VSCALE_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `VSCALE_TTL` | The TTL of the TXT record used for the DNS challenge | + + + + +## More information + +- [API documentation](https://developers.vscale.io/documentation/api/v1/#api-Domains_Records) + + + + diff --git a/docs/content/dns/zz_gen_vultr.md b/docs/content/dns/zz_gen_vultr.md new file mode 100644 index 00000000..8d7ad724 --- /dev/null +++ b/docs/content/dns/zz_gen_vultr.md @@ -0,0 +1,53 @@ +--- +title: "Vultr" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: vultr +--- + + + + + + +Configuration for [Vultr](https://www.vultr.com/). + + + + +- Code: `vultr` + +{{% notice note %}} +_Please contribute by adding a CLI example._ +{{% /notice %}} + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `VULTR_API_KEY` | API key | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `VULTR_HTTP_TIMEOUT` | API request timeout | +| `VULTR_POLLING_INTERVAL` | Time between DNS propagation check | +| `VULTR_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `VULTR_TTL` | The TTL of the TXT record used for the DNS challenge | + + + + +## More information + +- [API documentation](https://www.vultr.com/api/#dns) +- [Go client](https://github.com/JamesClonk/vultr) + + + + diff --git a/docs/content/dns/zz_gen_zoneee.md b/docs/content/dns/zz_gen_zoneee.md new file mode 100644 index 00000000..a04398fc --- /dev/null +++ b/docs/content/dns/zz_gen_zoneee.md @@ -0,0 +1,54 @@ +--- +title: "Zone.ee" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: zoneee +--- + + + + + + +Configuration for [Zone.ee](https://www.zone.ee/). + + + + +- Code: `zoneee` + +{{% notice note %}} +_Please contribute by adding a CLI example._ +{{% /notice %}} + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `ZONEEE_API_KEY` | API key | +| `ZONEEE_API_USER` | API user | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `ZONEEE_ENDPOINT` | API endpoint URL | +| `ZONEEE_HTTP_TIMEOUT` | API request timeout | +| `ZONEEE_POLLING_INTERVAL` | Time between DNS propagation check | +| `ZONEEE_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `ZONEEE_TTL` | The TTL of the TXT record used for the DNS challenge | + + + + +## More information + +- [API documentation](https://api.zone.eu/v2) + + + + diff --git a/docs/content/installation/_index.md b/docs/content/installation/_index.md new file mode 100644 index 00000000..4858945a --- /dev/null +++ b/docs/content/installation/_index.md @@ -0,0 +1,35 @@ +--- +title: "Installation" +date: 2019-03-03T16:39:46+01:00 +weight: 1 +draft: false +--- + +## Binaries + +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. + +## From Docker + +```bash +docker run xenolf/lego -h +``` + +## From package managers + +- [ArchLinux (AUR)](https://aur.archlinux.org/packages/lego): + +```bash +yay -S lego +``` + +**Note**: only the package manager for Arch Linux is officially supported by the lego team. + +## From sources + +To install from sources, just run: + +```bash +go get -u github.com/xenolf/lego/cmd/lego +``` diff --git a/docs/content/usage/_index.md b/docs/content/usage/_index.md new file mode 100644 index 00000000..7a4b3f48 --- /dev/null +++ b/docs/content/usage/_index.md @@ -0,0 +1,8 @@ +--- +title: "Usage" +date: 2019-03-03T16:39:46+01:00 +draft: false +weight: 2 +--- + +{{%children style="h2" description="true" %}} diff --git a/docs/content/usage/cli/_index.md b/docs/content/usage/cli/_index.md new file mode 100644 index 00000000..3b3c0e83 --- /dev/null +++ b/docs/content/usage/cli/_index.md @@ -0,0 +1,94 @@ +--- +title: "CLI" +date: 2019-03-03T16:39:46+01:00 +draft: false +--- + +Lego can be use as a CLI. + + + +## Usage + +{{%expand "CLI help" %}} +```slim +NAME: + lego - Let's Encrypt client written in Go + +USAGE: + lego [global options] command [command options] [arguments...] + +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 + list Display certificates and accounts information. + 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. + --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-v02.api.letsencrypt.org/directory") + --accept-tos, -a By setting this flag to true you indicate that you accept the current Let's Encrypt terms of service. + --email value, -m value Email used for registration and recovery contact. + --csr value, -c value Certificate signing request filename, if an external CSR is to be used. + --eab Use External Account Binding for account registration. Requires --kid and --hmac. + --kid value Key identifier from External CA. Used for External Account Binding. + --hmac value MAC key from External CA. Should be in Base64 URL Encoding without padding format. Used for External Account Binding. + --key-type value, -k value Key type to use for private keys. Supported: rsa2048, rsa4096, rsa8192, ec256, ec384. (default: "rsa2048") + --filename value (deprecated) Filename of the generated certificate. + --path value Directory to use for storing the data. (default: "./.lego") + --http Use the HTTP challenge to solve challenges. Can be mixed with other types of challenges. + --http.port value Set the port and interface to use for HTTP based challenges to listen on.Supported: interface:port or :port. (default: ":80") + --http.webroot value Set the webroot folder to use for HTTP based challenges to write directly in a file in .well-known/acme-challenge. + --http.memcached-host value Set the memcached host(s) to use for HTTP based challenges. Challenges will be written to all specified hosts. + --tls Use the TLS challenge to solve challenges. Can be mixed with other types of challenges. + --tls.port value Set the port and interface to use for TLS based challenges to listen on. Supported: interface:port or :port. (default: ":443") + --dns value Solve a DNS challenge using the specified provider. Can be mixed with other types of challenges. Run 'lego dnshelp' for help on usage. + --dns.disable-cp By setting this flag to true, disables the need to wait the propagation of the TXT record to all authoritative name servers. + --dns.resolvers value 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. + --http-timeout value Set the HTTP timeout value to a specific value in seconds. (default: 0) + --dns-timeout value Set the DNS timeout value to a specific value in seconds. Used only when performing authoritative name servers queries. (default: 10) + --pem Generate a .pem file by concatenating the .key and .crt files together. + --cert.timeout value Set the certificate timeout value to a specific value in seconds. Only used when obtaining certificates. (default: 30) + --help, -h show help + --version, -v print the version +``` +{{% /expand%}} + + +When using the standard `--path` option, all certificates and account configurations are saved to a folder `.lego` in the current working directory. + + +## Let's Encrypt ACME server + +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-v02.api.letsencrypt.org/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.port` or/and the `--tls.port` 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](usage/cli#port-usage)). +- Pass the `--http.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.port` and `--tls.port` 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-ALPN 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. diff --git a/docs/content/usage/cli/examples.md b/docs/content/usage/cli/examples.md new file mode 100644 index 00000000..9b6c6072 --- /dev/null +++ b/docs/content/usage/cli/examples.md @@ -0,0 +1,48 @@ +--- +title: "Examples" +date: 2019-03-03T16:39:46+01:00 +draft: false +--- + +## CLI Examples + +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](usage/cli#port-usage). + +### Obtain a certificate + +```bash +lego --email="foo@bar.com" --domains="example.com" --http 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" --http renew +``` + +### To renew the certificate only if it expires within 45 days + +```bash +lego --email="foo@bar.com" --domains="example.com" --http renew --days 45 +``` + +### Obtain a certificate using the DNS challenge + +```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 +``` + +### Obtain a certificate given a certificate signing request (CSR) generated by something else + +```bash +lego --email="foo@bar.com" --http --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.) diff --git a/docs/content/usage/library/Writing-a-Challenge-Solver.md b/docs/content/usage/library/Writing-a-Challenge-Solver.md new file mode 100644 index 00000000..f9edae2a --- /dev/null +++ b/docs/content/usage/library/Writing-a-Challenge-Solver.md @@ -0,0 +1,103 @@ +--- +title: "Writing a Challenge Solver" +date: 2019-03-03T16:39:46+01:00 +draft: false +--- + +Lego can solve multiple ACME challenge types out of the box, but sometimes you have custom requirements. + + + +For example, you may want to write a solver for the DNS-01 challenge that works with a different DNS provider (lego already supports CloudFlare, AWS, DigitalOcean, and others). + +The DNS-01 challenge is advantageous when other challenge types are impossible. +For example, the HTTP-01 challenge doesn't work well behind a load balancer or CDN and the TLS-ALPN-01 challenge breaks behind TLS termination. + +But even if using HTTP-01 or TLS-ALPN-01 challenges, you may have specific needs that lego does not consider by default. + +You can write something called a `challenge.Provider` that implements [this interface](https://godoc.org/github.com/xenolf/lego/challenge#Provider): + +```go +type Provider interface { + Present(domain, token, keyAuth string) error + CleanUp(domain, token, keyAuth string) error +} +``` + +This provides the means to solve a challenge. +First you present a token to the ACME server in a way defined by the challenge type you're solving for, then you "clean up" after the challenge finishes. + +## Writing a challenge.Provider + +Pretend we want to write our own DNS-01 challenge provider (other challenge types have different requirements but the same principles apply). + +This will let us prove ownership of domain names parked at a new, imaginary DNS service called BestDNS without having to start our own HTTP server. +BestDNS has an API that, given an authentication token, allows us to manipulate DNS records. + +This simplistic example has only one field to store the auth token, but in reality you may need to keep more state. + +```go +type DNSProviderBestDNS struct { + apiAuthToken string +} +``` + +We should provide a constructor that returns a *pointer* to the `struct`. +This is important in case we need to maintain state in the `struct`. + +```go +func NewDNSProviderBestDNS(apiAuthToken string) (*DNSProviderBestDNS, error) { + return &DNSProviderBestDNS{apiAuthToken: apiAuthToken}, nil +} +``` + +Now we need to implement the interface. +We'll start with the `Present` method. +You'll be passed the `domain` name for which you're proving ownership, a `token`, and a `keyAuth` string. +How your provider uses `token` and `keyAuth`, or if you even use them at all, depends on the challenge type. +For DNS-01, we'll just use `domain` and `keyAuth`. + +```go +func (d *DNSProviderBestDNS) Present(domain, token, keyAuth string) error { + fqdn, value := dns01.GetRecord(domain, keyAuth) + // make API request to set a TXT record on fqdn with value and ttl + return nil +} +``` + +After calling `dns01.GetRecord(domain, keyAuth)`, we now have the information we need to make our API request and set the TXT record: +- `fqdn` is the fully qualified domain name on which to set the TXT record. +- `value` is the record's value to set on the record. + +So then you make an API request to the DNS service according to their docs. +Once the TXT record is set on the domain, you may return and the challenge will proceed. + +The ACME server will then verify that you did what it required you to do, and once it is finished, lego will call your `CleanUp` method. +In our case, we want to remove the TXT record we just created. + +```go +func (d *DNSProviderBestDNS) CleanUp(domain, token, keyAuth string) error { + // clean up any state you created in Present, like removing the TXT record +} +``` + +In our case, we'd just make another API request to have the DNS record deleted; no need to keep it and clutter the zone file. + +## Using your new challenge.Provider + +To use your new challenge provider, call [`client.Challenge.SetDNS01Provider`](https://godoc.org/github.com/xenolf/lego/challenge/resolver#SolverManager.SetDNS01Provider) to tell lego, "For this challenge, use this provider". +In our case: + +```go +bestDNS, err := NewDNSProviderBestDNS("my-auth-token") +if err != nil { + return err +} + +client.Challenge.SetDNS01Provider(bestDNS) +``` + +Then, when this client tries to solve the DNS-01 challenge, it will use our new provider, which sets TXT records on a domain name hosted by BestDNS. + +That's really all there is to it. +Go make awesome things! diff --git a/docs/content/usage/library/_index.md b/docs/content/usage/library/_index.md new file mode 100644 index 00000000..a697511b --- /dev/null +++ b/docs/content/usage/library/_index.md @@ -0,0 +1,115 @@ +--- +title: "Library" +date: 2019-03-03T16:39:46+01:00 +draft: false +--- + +Lego can be use as a Go Library. + + + +## GoDoc + +The GoDoc can be found here: [GoDoc](https://godoc.org/github.com/xenolf/lego/acme) + +## Usage + +A valid, but bare-bones example use of the acme package: + +```go +package main + +import ( + "crypto" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "fmt" + "log" + + "github.com/xenolf/lego/certcrypto" + "github.com/xenolf/lego/certificate" + "github.com/xenolf/lego/challenge/http01" + "github.com/xenolf/lego/challenge/tlsalpn01" + "github.com/xenolf/lego/lego" + "github.com/xenolf/lego/registration" +) + +// You'll need a user or account type that implements acme.User +type MyUser struct { + Email string + Registration *registration.Resource + key crypto.PrivateKey +} + +func (u *MyUser) GetEmail() string { + return u.Email +} +func (u MyUser) GetRegistration() *registration.Resource { + return u.Registration +} +func (u *MyUser) GetPrivateKey() crypto.PrivateKey { + return u.key +} + +func main() { + + // Create a user. New accounts need an email and private key to start. + privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + log.Fatal(err) + } + + myUser := MyUser{ + Email: "you@yours.com", + key: privateKey, + } + + config := lego.NewConfig(&myUser) + + // This CA URL is configured for a local dev instance of Boulder running in Docker in a VM. + config.CADirURL = "http://192.168.99.100:4000/directory" + config.Certificate.KeyType = certcrypto.RSA2048 + + // A client facilitates communication with the CA server. + client, err := lego.NewClient(config) + 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 you still + // need to proxy challenge traffic to port 5002 and 5001. + err = client.Challenge.SetHTTP01Provider(http01.NewProviderServer("", "5002")) + if err != nil { + log.Fatal(err) + } + err = client.Challenge.SetTLSALPN01Provider(tlsalpn01.NewProviderServer("", "5001")) + if err != nil { + log.Fatal(err) + } + + // New users will need to register + reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true}) + if err != nil { + log.Fatal(err) + } + myUser.Registration = reg + + request := certificate.ObtainRequest{ + Domains: []string{"mydomain.com"}, + Bundle: true, + } + certificates, err := client.Certificate.Obtain(request) + if err != nil { + log.Fatal(err) + } + + // 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/docs/layouts/partials/logo.html b/docs/layouts/partials/logo.html new file mode 100644 index 00000000..f91d1ddd --- /dev/null +++ b/docs/layouts/partials/logo.html @@ -0,0 +1 @@ + diff --git a/docs/static/images/logo-white.png b/docs/static/images/logo-white.png new file mode 100644 index 00000000..598c5da7 Binary files /dev/null and b/docs/static/images/logo-white.png differ diff --git a/docs/static/images/logo.png b/docs/static/images/logo.png new file mode 100644 index 00000000..bee263a4 Binary files /dev/null and b/docs/static/images/logo.png differ diff --git a/internal/dnsdocs/dns.go.tmpl b/internal/dnsdocs/dns.go.tmpl new file mode 100644 index 00000000..6527775d --- /dev/null +++ b/internal/dnsdocs/dns.go.tmpl @@ -0,0 +1,53 @@ +package cmd + +// CODE GENERATED AUTOMATICALLY +// THIS FILE MUST NOT BE EDITED BY HAND + +import ( + "fmt" + "os" + "strings" + "text/tabwriter" + + "github.com/xenolf/lego/log" +) + +func allDNSCodes() string { + return strings.Join([]string{ +{{- range $provider := .Providers }} + "{{ $provider.Code }}", +{{- end}} + }, ", ") +} + +func displayDNSHelp(name string) { + w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0) + switch name { +{{ range $provider := .Providers }} + case "{{ $provider.Code }}": + // generated from: {{ .GeneratedFrom }} + fmt.Fprintln(w, `Configuration for {{ $provider.Name }}.`) + fmt.Fprintln(w, `Code: '{{ $provider.Code }}'`) + fmt.Fprintln(w) +{{if $provider.Configuration }}{{if $provider.Configuration.Credentials }} + fmt.Fprintln(w, `Credentials:`) +{{- range $k, $v := $provider.Configuration.Credentials }} + fmt.Fprintln(w,` - "{{ $k }}": {{ safe $v }}`) +{{- end}} + fmt.Fprintln(w) +{{end}}{{if $provider.Configuration.Additional }} + fmt.Fprintln(w, `Additional Configuration:`) +{{- range $k, $v := $provider.Configuration.Additional }} + fmt.Fprintln(w, ` - "{{ $k }}": {{ safe $v }}`) +{{- end}} +{{end}}{{end}} + fmt.Fprintln(w) + fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/{{ $provider.Code }}`) +{{end}} + case "manual": + fmt.Fprintln(w, `Solving the DNS-01 challenge using CLI prompt.`) + default: + log.Fatalf("%q is not yet supported.", name) + } + w.Flush() +} \ No newline at end of file diff --git a/internal/dnsdocs/dns.md.tmpl b/internal/dnsdocs/dns.md.tmpl new file mode 100644 index 00000000..4f01412c --- /dev/null +++ b/internal/dnsdocs/dns.md.tmpl @@ -0,0 +1,73 @@ +--- +title: "{{ .Name }}" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: {{ .Code }} +--- + + + + + +{{if .Description -}} +{{ .Description }} +{{else}} +Configuration for [{{ .Name }}]({{ .URL }}). +{{end}} + + + +- Code: `{{ .Code }}` +{{if .Example }} +Here is an example bash command using the {{ .Name }} provider: + +```bash +{{ .Example -}} +``` +{{else}} +{{ "{{" }}% notice note %}} +_Please contribute by adding a CLI example._ +{{ "{{" }}% /notice %}} +{{end}} + +{{if .Configuration }} +{{if .Configuration.Credentials }} +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +{{- range $k, $v := .Configuration.Credentials }} +| `{{$k}}` | {{$v}} | +{{- end}} + +{{- end}} + +{{if .Configuration.Additional }} +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +{{- range $k, $v := .Configuration.Additional }} +| `{{$k}}` | {{$v}} | +{{- end}} + +{{- end}} +{{- end}} + +{{ .Additional }} + +{{if .Links }} +## More information + +{{if .Links.API -}} +- [API documentation]({{ .Links.API }}) +{{- end}} +{{- if .Links.GoClient }} +- [Go client]({{ .Links.GoClient }}) +{{- end}} + +{{- end}} + + + + diff --git a/internal/dnsdocs/generator.go b/internal/dnsdocs/generator.go new file mode 100644 index 00000000..a2d1742e --- /dev/null +++ b/internal/dnsdocs/generator.go @@ -0,0 +1,136 @@ +package main + +//go:generate go run . + +import ( + "bytes" + "go/format" + "log" + "os" + "path/filepath" + "strings" + "text/template" + + "github.com/BurntSushi/toml" +) + +const ( + root = "../../" + dnsPackage = root + "providers/dns" + mdTemplate = root + "internal/dnsdocs/dns.md.tmpl" + cliTemplate = root + "internal/dnsdocs/dns.go.tmpl" + cliOutput = root + "cmd/zz_gen_cmd_dnshelp.go" + docOutput = root + "docs/content/dns" +) + +type Model struct { + Name string // Real name of the DNS provider + Code string // DNS code + URL string // DNS provider URL + Description string // Provider summary + Example string // CLI example + Configuration *Configuration // Environment variables + Links *Links // Links + Additional string // Extra documentation + GeneratedFrom string // Source file +} + +type Configuration struct { + Credentials map[string]string + Additional map[string]string +} + +type Links struct { + API string + GoClient string +} + +type Providers struct { + Providers []Model +} + +func main() { + models := &Providers{} + + err := filepath.Walk(dnsPackage, walker(models)) + if err != nil { + log.Fatal(err) + } + + // generate CLI help + err = generateCLIHelp(models) + if err != nil { + log.Fatal(err) + } +} + +func walker(prs *Providers) func(string, os.FileInfo, error) error { + return func(path string, _ os.FileInfo, err error) error { + if err != nil { + return err + } + + if filepath.Ext(path) == ".toml" { + m := Model{} + + m.GeneratedFrom, err = filepath.Rel(root, path) + if err != nil { + return err + } + + _, err := toml.DecodeFile(path, &m) + if err != nil { + return err + } + + prs.Providers = append(prs.Providers, m) + + // generate documentation + return generateDocumentation(m) + } + + return nil + } +} + +func generateDocumentation(m Model) error { + filename := filepath.Join(docOutput, "zz_gen_"+m.Code+".md") + + file, err := os.Create(filename) + if err != nil { + return err + } + + return template.Must(template.ParseFiles(mdTemplate)).Execute(file, m) +} + +func generateCLIHelp(models *Providers) error { + filename := filepath.Join(cliOutput) + + file, err := os.Create(filename) + if err != nil { + return err + } + + tlt := template.New(filepath.Base(cliTemplate)).Funcs(map[string]interface{}{ + "safe": func(src string) string { + return strings.ReplaceAll(src, "`", "'") + }, + }) + + b := &bytes.Buffer{} + err = template.Must(tlt.ParseFiles(cliTemplate)).Execute(b, models) + if err != nil { + return err + } + + // gofmt + source, err := format.Source(b.Bytes()) + if err != nil { + return err + } + + _, err = file.Write(source) + return err + +} diff --git a/providers/dns/acmedns/acmedns.toml b/providers/dns/acmedns/acmedns.toml new file mode 100644 index 00000000..da0372c0 --- /dev/null +++ b/providers/dns/acmedns/acmedns.toml @@ -0,0 +1,15 @@ +Name = "Joohoi's ACME-DNS" +Description = '''''' +URL = "https://github.com/joohoi/acme-dns" +Code = "acme-dns" + +Example = '''''' + +[Configuration] + [Configuration.Credentials] + ACME_DNS_API_BASE = "The ACME-DNS API address" + ACME_DNS_STORAGE_PATH = "The ACME-DNS JSON account data file. A per-domain account will be registered/persisted to this file and used for TXT updates." + +[Links] + API = "https://github.com/joohoi/acme-dns#api" + GoClient = "https://github.com/cpu/goacmedns" diff --git a/providers/dns/alidns/alidns.toml b/providers/dns/alidns/alidns.toml new file mode 100644 index 00000000..c3c9fb87 --- /dev/null +++ b/providers/dns/alidns/alidns.toml @@ -0,0 +1,20 @@ +Name = "Alibaba Cloud DNS" +Description = '''''' +URL = "https://www.alibabacloud.com/product/dns" +Code = "alidns" + +Example = '''''' + +[Configuration] + [Configuration.Credentials] + ALICLOUD_ACCESS_KEY = "Access key ID" + ALICLOUD_SECRET_KEY = "Access Key secret" + [Configuration.Additional] + ALICLOUD_POLLING_INTERVAL = "Time between DNS propagation check" + ALICLOUD_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + ALICLOUD_TTL = "The TTL of the TXT record used for the DNS challenge" + ALICLOUD_HTTP_TIMEOUT = "API request timeout" + +[Links] + API = "https://www.alibabacloud.com/help/doc-detail/42875.htm" + GoClient = "https://github.com/aliyun/alibaba-cloud-sdk-go" diff --git a/providers/dns/auroradns/auroradns.toml b/providers/dns/auroradns/auroradns.toml new file mode 100644 index 00000000..d9e065ab --- /dev/null +++ b/providers/dns/auroradns/auroradns.toml @@ -0,0 +1,20 @@ +Name = "Aurora DNS" +Description = '''''' +URL = "https://www.pcextreme.com/aurora/dns" +Code = "auroradns" + +Example = '''''' + +[Configuration] + [Configuration.Credentials] + AURORA_USER_ID = "User ID" + AURORA_KEY = "User API key" + AURORA_ENDPOINT = "API endpoint URL" + [Configuration.Additional] + AURORA_POLLING_INTERVAL = "Time between DNS propagation check" + AURORA_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + AURORA_TTL = "The TTL of the TXT record used for the DNS challenge" + +[Links] + API = "https://libcloud.readthedocs.io/en/latest/dns/drivers/auroradns.html#api-docs" + GoClient = "https://github.com/nrdcg/auroradns" diff --git a/providers/dns/azure/azure.toml b/providers/dns/azure/azure.toml new file mode 100644 index 00000000..0923d1b4 --- /dev/null +++ b/providers/dns/azure/azure.toml @@ -0,0 +1,24 @@ +Name = "Azure" +Description = '''''' +URL = "https://azure.microsoft.com/services/dns/" +Code = "azure" + +Example = '''''' + +[Configuration] + [Configuration.Credentials] + AZURE_CLIENT_ID = "Client ID" + AZURE_CLIENT_SECRET = "Client secret" + AZURE_SUBSCRIPTION_ID = "Subscription ID" + AZURE_TENANT_ID = "Tenant ID" + AZURE_RESOURCE_GROUP = "Resource group" + 'instance metadata service' = "If the credentials are **not** set via the environment, then it will attempt to get a bearer token via the [instance metadata service](https://docs.microsoft.com/en-us/azure/virtual-machines/windows/instance-metadata-service)." + [Configuration.Additional] + AZURE_POLLING_INTERVAL = "Time between DNS propagation check" + AZURE_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + AZURE_TTL = "The TTL of the TXT record used for the DNS challenge" + AZURE_METADATA_ENDPOINT = "Metadata Service endpoint URL" + +[Links] + API = "https://docs.microsoft.com/en-us/go/azure/" + GoClient = "https://github.com/Azure/azure-sdk-for-go" diff --git a/providers/dns/bluecat/bluecat.toml b/providers/dns/bluecat/bluecat.toml new file mode 100644 index 00000000..c8f43ce6 --- /dev/null +++ b/providers/dns/bluecat/bluecat.toml @@ -0,0 +1,19 @@ +Name = "Bluecat" +Description = '''''' +URL = "https://www.bluecatnetworks.com" +Code = "bluecat" + +Example = '''''' + +[Configuration] + [Configuration.Credentials] + BLUECAT_SERVER_URL = "The server URL, should have scheme, hostname, and port (if required) of the authoritative Bluecat BAM serve" + BLUECAT_USER_NAME = "API username" + BLUECAT_PASSWORD = "API password" + BLUECAT_CONFIG_NAME = "Configuration name" + BLUECAT_DNS_VIEW = "External DNS View Name" + [Configuration.Additional] + BLUECAT_POLLING_INTERVAL = "Time between DNS propagation check" + BLUECAT_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + BLUECAT_TTL = "The TTL of the TXT record used for the DNS challenge" + BLUECAT_HTTP_TIMEOUT = "API request timeout" diff --git a/providers/dns/cloudflare/cloudflare.toml b/providers/dns/cloudflare/cloudflare.toml new file mode 100644 index 00000000..c60f4999 --- /dev/null +++ b/providers/dns/cloudflare/cloudflare.toml @@ -0,0 +1,24 @@ +Name = "Cloudflare" +Description = '''''' +URL = "https://www.cloudflare.com/dns/" +Code = "cloudflare" + +Example = ''' +CLOUDFLARE_EMAIL=foo@bar.com \ +CLOUDFLARE_API_KEY=b9841238feb177a84330febba8a83208921177bffe733 \ +lego --dns cloudflare --domains my.domain.com --email my@email.com run +''' + +[Configuration] + [Configuration.Credentials] + CLOUDFLARE_EMAIL = "Account email" + CLOUDFLARE_API_KEY = "API key" + [Configuration.Additional] + CLOUDFLARE_POLLING_INTERVAL = "Time between DNS propagation check" + CLOUDFLARE_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + CLOUDFLARE_TTL = "The TTL of the TXT record used for the DNS challenge" + CLOUDFLARE_HTTP_TIMEOUT = "API request timeout" + +[Links] + API = "https://api.cloudflare.com/" + GoClient = "https://github.com/cloudflare/cloudflare-go" diff --git a/providers/dns/cloudns/cloudns.toml b/providers/dns/cloudns/cloudns.toml new file mode 100644 index 00000000..f8c2b83a --- /dev/null +++ b/providers/dns/cloudns/cloudns.toml @@ -0,0 +1,19 @@ +Name = "ClouDNS" +Description = '''''' +URL = "https://www.cloudns.net" +Code = "cloudns" + +Example = '''''' + +[Configuration] + [Configuration.Credentials] + CLOUDNS_AUTH_ID = "The API user ID" + CLOUDNS_AUTH_PASSWORD = "The password for API user ID" + [Configuration.Additional] + CLOUDNS_POLLING_INTERVAL = "Time between DNS propagation check" + CLOUDNS_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + CLOUDNS_TTL = "The TTL of the TXT record used for the DNS challenge" + CLOUDNS_HTTP_TIMEOUT = "API request timeout" + +[Links] + API = "https://www.cloudns.net/wiki/article/42/" diff --git a/providers/dns/cloudxns/cloudxns.toml b/providers/dns/cloudxns/cloudxns.toml new file mode 100644 index 00000000..58695142 --- /dev/null +++ b/providers/dns/cloudxns/cloudxns.toml @@ -0,0 +1,19 @@ +Name = "CloudXNS" +Description = """""" +URL = "https://www.cloudxns.net/" +Code = "cloudxns" + +Example = '''''' + +[Configuration] + [Configuration.Credentials] + CLOUDXNS_API_KEY = "The API key" + CLOUDXNS_SECRET_KEY = "THe API secret key" + [Configuration.Additional] + CLOUDXNS_POLLING_INTERVAL = "Time between DNS propagation check" + CLOUDXNS_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + CLOUDXNS_TTL = "The TTL of the TXT record used for the DNS challenge" + CLOUDXNS_HTTP_TIMEOUT = "API request timeout" + +[Links] + API = "https://www.cloudxns.net/Public/Doc/CloudXNS_api2.0_doc_zh-cn.zip" diff --git a/providers/dns/conoha/conoha.toml b/providers/dns/conoha/conoha.toml new file mode 100644 index 00000000..c5f003da --- /dev/null +++ b/providers/dns/conoha/conoha.toml @@ -0,0 +1,21 @@ +Name = "ConoHa" +Description = '''''' +URL = "https://www.conoha.jp/" +Code = "conoha" + +Example = '''''' + +[Configuration] + [Configuration.Credentials] + CONOHA_TENANT_ID = "Tenant ID" + CONOHA_API_USERNAME = "The API username" + CONOHA_API_PASSWORD = "The API password" + [Configuration.Additional] + CONOHA_POLLING_INTERVAL = "Time between DNS propagation check" + CONOHA_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + CONOHA_TTL = "The TTL of the TXT record used for the DNS challenge" + CONOHA_HTTP_TIMEOUT = "API request timeout" + CONOHA_REGION = "The region" + +[Links] + API = "https://www.conoha.jp/docs/" diff --git a/providers/dns/designate/designate.toml b/providers/dns/designate/designate.toml new file mode 100644 index 00000000..7c82d216 --- /dev/null +++ b/providers/dns/designate/designate.toml @@ -0,0 +1,22 @@ +Name = "Designate DNSaaS for Openstack" +Description = '''''' +URL = "https://docs.openstack.org/designate/latest/" +Code = "designate" + +Example = '''''' + +[Configuration] + [Configuration.Credentials] + OS_AUTH_URL = "Identity endpoint URL" + OS_USERNAME = "Username" + OS_PASSWORD = "Password" + OS_TENANT_NAME = "Tenant name" + OS_REGION_NAME = "Region name" + [Configuration.Additional] + DESIGNATE_POLLING_INTERVAL = "Time between DNS propagation check" + DESIGNATE_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + DESIGNATE_TTL = "The TTL of the TXT record used for the DNS challenge" + +[Links] + API = "https://docs.openstack.org/designate/latest/" + GoClient = "https://godoc.org/github.com/gophercloud/gophercloud/openstack/dns/v2" diff --git a/providers/dns/digitalocean/digitalocean.toml b/providers/dns/digitalocean/digitalocean.toml new file mode 100644 index 00000000..f4aaf9ee --- /dev/null +++ b/providers/dns/digitalocean/digitalocean.toml @@ -0,0 +1,18 @@ +Name = "Digital Ocean" +Description = '''''' +URL = "https://www.digitalocean.com/docs/networking/dns/" +Code = "digitalocean" + +Example = '''''' + +[Configuration] + [Configuration.Credentials] + DO_AUTH_TOKEN = "Authentication token" + [Configuration.Additional] + DO_POLLING_INTERVAL = "Time between DNS propagation check" + DO_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + DO_TTL = "The TTL of the TXT record used for the DNS challenge" + DO_HTTP_TIMEOUT = "API request timeout" + +[Links] + API = "https://developers.digitalocean.com/documentation/v2/#domain-records" diff --git a/providers/dns/dnsimple/dnsimple.toml b/providers/dns/dnsimple/dnsimple.toml new file mode 100644 index 00000000..a817e598 --- /dev/null +++ b/providers/dns/dnsimple/dnsimple.toml @@ -0,0 +1,19 @@ +Name = "DNSimple" +Description = '''''' +URL = "https://dnsimple.com/" +Code = "dnsimple" + +Example = '''''' + +[Configuration] + [Configuration.Credentials] + DNSIMPLE_OAUTH_TOKEN = "OAuth token" + DNSIMPLE_BASE_URL = "API endpoint URL" + [Configuration.Additional] + DNSIMPLE_POLLING_INTERVAL = "Time between DNS propagation check" + DNSIMPLE_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + DNSIMPLE_TTL = "The TTL of the TXT record used for the DNS challenge" + +[Links] + API = "https://developer.dnsimple.com/v2/" + GoClient = "https://github.com/dnsimple/dnsimple-go" diff --git a/providers/dns/dnsmadeeasy/dnsmadeeasy.toml b/providers/dns/dnsmadeeasy/dnsmadeeasy.toml new file mode 100644 index 00000000..53ec6014 --- /dev/null +++ b/providers/dns/dnsmadeeasy/dnsmadeeasy.toml @@ -0,0 +1,20 @@ +Name = "DNS Made Easy" +Description = '''''' +URL = "https://dnsmadeeasy.com/" +Code = "dnsmadeeasy" + +Example = '''''' + +[Configuration] + [Configuration.Credentials] + DNSMADEEASY_API_KEY = "The API key" + DNSMADEEASY_API_SECRET = "The API Secret key" + [Configuration.Additional] + DNSMADEEASY_SANDBOX = "Activate the sandbox (boolean)" + DNSMADEEASY_POLLING_INTERVAL = "Time between DNS propagation check" + DNSMADEEASY_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + DNSMADEEASY_TTL = "The TTL of the TXT record used for the DNS challenge" + DNSMADEEASY_HTTP_TIMEOUT = "API request timeout" + +[Links] + API = "https://api-docs.dnsmadeeasy.com/" diff --git a/providers/dns/dnspod/dnspod.toml b/providers/dns/dnspod/dnspod.toml new file mode 100644 index 00000000..7b468466 --- /dev/null +++ b/providers/dns/dnspod/dnspod.toml @@ -0,0 +1,19 @@ +Name = "DNSPod" +Description = '''''' +URL = "http://www.dnspod.com/" +Code = "dnspod" + +Example = '''''' + +[Configuration] + [Configuration.Credentials] + DNSPOD_API_KEY = "The user token" + [Configuration.Additional] + DNSPOD_POLLING_INTERVAL = "Time between DNS propagation check" + DNSPOD_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + DNSPOD_TTL = "The TTL of the TXT record used for the DNS challenge" + DNSPOD_HTTP_TIMEOUT = "API request timeout" + +[Links] + API = "https://www.dnspod.com/docs/index.html" + GoClient = "https://github.com/decker502/dnspod-go" diff --git a/providers/dns/dreamhost/dreamhost.toml b/providers/dns/dreamhost/dreamhost.toml new file mode 100644 index 00000000..7bfeff10 --- /dev/null +++ b/providers/dns/dreamhost/dreamhost.toml @@ -0,0 +1,18 @@ +Name = "DreamHost" +Description = '''''' +URL = "https://www.dreamhost.com" +Code = "dreamhost" + +Example = '''''' + +[Configuration] + [Configuration.Credentials] + DREAMHOST_API_KEY = "The API key" + [Configuration.Additional] + DREAMHOST_POLLING_INTERVAL = "Time between DNS propagation check" + DREAMHOST_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + DREAMHOST_TTL = "The TTL of the TXT record used for the DNS challenge" + DREAMHOST_HTTP_TIMEOUT = "API request timeout" + +[Links] + API = "https://help.dreamhost.com/hc/en-us/articles/217560167-API_overview" diff --git a/providers/dns/duckdns/duckdns.toml b/providers/dns/duckdns/duckdns.toml new file mode 100644 index 00000000..f99eb0ab --- /dev/null +++ b/providers/dns/duckdns/duckdns.toml @@ -0,0 +1,19 @@ +Name = "Duck DNS" +Description = '''''' +URL = "https://www.duckdns.org/" +Code = "duckdns" + +Example = '''''' + +[Configuration] + [Configuration.Credentials] + DUCKDNS_TOKEN = "Account token" + [Configuration.Additional] + DUCKDNS_POLLING_INTERVAL = "Time between DNS propagation check" + DUCKDNS_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + DUCKDNS_TTL = "The TTL of the TXT record used for the DNS challenge" + DUCKDNS_HTTP_TIMEOUT = "API request timeout" + DUCKDNS_SEQUENCE_INTERVAL = "Interval between iteration" + +[Links] + API = "https://www.duckdns.org/spec.jsp" diff --git a/providers/dns/dyn/dyn.toml b/providers/dns/dyn/dyn.toml new file mode 100644 index 00000000..c2a4b6b8 --- /dev/null +++ b/providers/dns/dyn/dyn.toml @@ -0,0 +1,20 @@ +Name = "Dyn" +Description = '''''' +URL = "https://dyn.com/" +Code = "dyn" + +Example = '''''' + +[Configuration] + [Configuration.Credentials] + DYN_CUSTOMER_NAME = "Customer name" + DYN_USER_NAME = "User name" + DYN_PASSWORD = "Paswword" + [Configuration.Additional] + DYN_POLLING_INTERVAL = "Time between DNS propagation check" + DYN_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + DYN_TTL = "The TTL of the TXT record used for the DNS challenge" + DYN_HTTP_TIMEOUT = "API request timeout" + +[Links] + API = "https://help.dyn.com/rest/" diff --git a/providers/dns/exec/readme.md b/providers/dns/exec/exec.toml similarity index 78% rename from providers/dns/exec/readme.md rename to providers/dns/exec/exec.toml index 3fa2f7cf..c44a4c93 100644 --- a/providers/dns/exec/readme.md +++ b/providers/dns/exec/exec.toml @@ -1,6 +1,30 @@ -# Execute an external program +Name = "External program" +Description = "Solving the DNS-01 challenge using an external program." +URL = "/dns/exec" +Code = "exec" + +Example = ''' +EXEC_PATH=/the/path/to/myscript.sh \ +lego --dns exec --domains my.domain.com --email my@email.com run +''' + +Additional = ''' + +## Base Configuration + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `EXEC_MODE` | `RAW`, none | +| `EXEC_PATH` | TODO | + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `EXEC_POLLING_INTERVAL` | Time between DNS propagation check | +| `EXEC_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | -Solving the DNS-01 challenge using an external program. ## Description @@ -48,6 +72,12 @@ It will then call the program `./update-dns.sh` like this: ## Commands +{{% notice note %}} +The `--` is because the token MAY start with a `-`, and the called program may try and interpret a `-` as indicating a flag. +In the case of urfave, which is commonly used, +you can use the `--` delimiter to specify the start of positional arguments, and handle such a string safely. +{{% /notice %}} + ### Present | Mode | Command | @@ -77,10 +107,4 @@ the default display propagation timeout and polling interval are used. | default | `myprogram timeout` | | `RAW` | `myprogram timeout` | - -## NOTE - -The `--` is because the token MAY start with a `-`, and the called program may try and interpret a - as indicating a flag. - -In the case of urfave, which is commonly used, -you can use the `--` delimiter to specify the start of positional arguments, and handle such a string safely. +''' diff --git a/providers/dns/exoscale/exoscale.toml b/providers/dns/exoscale/exoscale.toml new file mode 100644 index 00000000..0e992aba --- /dev/null +++ b/providers/dns/exoscale/exoscale.toml @@ -0,0 +1,21 @@ +Name = "Exoscale" +Description = '''''' +URL = "https://www.exoscale.com/" +Code = "exoscale" + +Example = '''''' + +[Configuration] + [Configuration.Credentials] + EXOSCALE_API_KEY = "API key" + EXOSCALE_API_SECRET = "API secret" + EXOSCALE_ENDPOINT = "API endpoint URL" + [Configuration.Additional] + EXOSCALE_POLLING_INTERVAL = "Time between DNS propagation check" + EXOSCALE_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + EXOSCALE_TTL = "The TTL of the TXT record used for the DNS challenge" + EXOSCALE_HTTP_TIMEOUT = "API request timeout" + +[Links] + API = "https://community.exoscale.com/documentation/dns/api/" + GoClient = "https://github.com/exoscale/egoscale" diff --git a/providers/dns/fastdns/fastdns.toml b/providers/dns/fastdns/fastdns.toml new file mode 100644 index 00000000..196994ba --- /dev/null +++ b/providers/dns/fastdns/fastdns.toml @@ -0,0 +1,23 @@ +Name = "FastDNS" +Description = '''''' +URL = "https://www.akamai.com/us/en/products/security/fast-dns.jsp" +Code = "fastdns" + +Example = '''''' + +[Configuration] + [Configuration.Credentials] + AKAMAI_HOST = "API host" + AKAMAI_CLIENT_TOKEN = "Client token" + AKAMAI_CLIENT_SECRET = "Client secret" + AKAMAI_ACCESS_TOKEN = "Access token" + [Configuration.Additional] + AKAMAI_POLLING_INTERVAL = "Time between DNS propagation check" + AKAMAI_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + AKAMAI_TTL = "The TTL of the TXT record used for the DNS challenge" + +[Links] + API = "https://developer.akamai.com/api/web_performance/fast_dns_record_management/v1.html" + GoClient = "https://github.com/akamai/AkamaiOPEN-edgegrid-golang" + + diff --git a/providers/dns/gandi/gandi.toml b/providers/dns/gandi/gandi.toml new file mode 100644 index 00000000..15db58ca --- /dev/null +++ b/providers/dns/gandi/gandi.toml @@ -0,0 +1,18 @@ +Name = "Gandi" +Description = """""" +URL = "https://www.gandi.net" +Code = "gandi" + +Example = '''''' + +[Configuration] + [Configuration.Credentials] + GANDI_API_KEY = "API key" + [Configuration.Additional] + GANDI_POLLING_INTERVAL = "Time between DNS propagation check" + GANDI_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + GANDI_TTL = "The TTL of the TXT record used for the DNS challenge" + GANDI_HTTP_TIMEOUT = "API request timeout" + +[Links] + API = "http://doc.rpc.gandi.net/index.html" diff --git a/providers/dns/gandiv5/gandiv5.toml b/providers/dns/gandiv5/gandiv5.toml new file mode 100644 index 00000000..9eaa567e --- /dev/null +++ b/providers/dns/gandiv5/gandiv5.toml @@ -0,0 +1,18 @@ +Name = "Gandi Live DNS (v5)" +Description = '''''' +URL = "https://www.gandi.net" +Code = "gandiv5" + +Example = '''''' + +[Configuration] + [Configuration.Credentials] + GANDIV5_API_KEY = "API key" + [Configuration.Additional] + GANDIV5_POLLING_INTERVAL = "Time between DNS propagation check" + GANDIV5_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + GANDIV5_TTL = "The TTL of the TXT record used for the DNS challenge" + GANDIV5_HTTP_TIMEOUT = "API request timeout" + +[Links] + API = "http://doc.livedns.gandi.net" diff --git a/providers/dns/gcloud/gcloud.toml b/providers/dns/gcloud/gcloud.toml new file mode 100644 index 00000000..ae43c965 --- /dev/null +++ b/providers/dns/gcloud/gcloud.toml @@ -0,0 +1,21 @@ +Name = "Google Cloud" +Description = '''''' +URL = "https://cloud.google.com" +Code = "gcloud" + +Example = '''''' + +[Configuration] + [Configuration.Credentials] + GCE_PROJECT = "Project name" + 'Application Default Credentials' = "[Documentation](https://cloud.google.com/docs/authentication/production#providing_credentials_to_your_application)" + GCE_SERVICE_ACCOUNT_FILE = "Account file path" + GCE_SERVICE_ACCOUNT = "Account" + [Configuration.Additional] + GCE_POLLING_INTERVAL = "Time between DNS propagation check" + GCE_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + GCE_TTL = "The TTL of the TXT record used for the DNS challenge" + +[Links] + API = "https://community.exoscale.com/documentation/dns/api/" + GoClient = "https://github.com/googleapis/google-api-go-client" diff --git a/providers/dns/glesys/glesys.go b/providers/dns/glesys/glesys.go index 98667d8d..c647a741 100644 --- a/providers/dns/glesys/glesys.go +++ b/providers/dns/glesys/glesys.go @@ -13,8 +13,6 @@ import ( "github.com/xenolf/lego/platform/config/env" ) -// GleSYS API reference: https://github.com/GleSYS/API/wiki/API-Documentation - const ( // defaultBaseURL is the GleSYS API endpoint used by Present and CleanUp. defaultBaseURL = "https://api.glesys.com/domain" diff --git a/providers/dns/glesys/glesys.toml b/providers/dns/glesys/glesys.toml new file mode 100644 index 00000000..daba9d7b --- /dev/null +++ b/providers/dns/glesys/glesys.toml @@ -0,0 +1,19 @@ +Name = "Glesys" +Description = '''''' +URL = "https://glesys.com/" +Code = "glesys" + +Example = '''''' + +[Configuration] + [Configuration.Credentials] + GLESYS_API_USER = "API user" + GLESYS_API_KEY = "API key" + [Configuration.Additional] + GLESYS_POLLING_INTERVAL = "Time between DNS propagation check" + GLESYS_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + GLESYS_TTL = "The TTL of the TXT record used for the DNS challenge" + GLESYS_HTTP_TIMEOUT = "API request timeout" + +[Links] + API = "https://github.com/GleSYS/API/wiki/API-Documentation" diff --git a/providers/dns/godaddy/godaddy.toml b/providers/dns/godaddy/godaddy.toml new file mode 100644 index 00000000..688bc665 --- /dev/null +++ b/providers/dns/godaddy/godaddy.toml @@ -0,0 +1,18 @@ +Name = "Go Daddy" +Description = '''''' +URL = "https://godaddy.com" +Code = "godaddy" + +[Configuration] + [Configuration.Credentials] + GODADDY_API_KEY = "**TODO**" + GODADDY_API_SECRET = "**TODO**" + [Configuration.Additional] + GODADDY_POLLING_INTERVAL = "Time between DNS propagation check" + GODADDY_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + GODADDY_TTL = "The TTL of the TXT record used for the DNS challenge" + GODADDY_HTTP_TIMEOUT = "API request timeout" + GODADDY_SEQUENCE_INTERVAL = "Interval between iteration" + +[Links] + API = "https://developer.godaddy.com/doc/endpoint/domains" diff --git a/providers/dns/hostingde/hostingde.toml b/providers/dns/hostingde/hostingde.toml new file mode 100644 index 00000000..1bf91b92 --- /dev/null +++ b/providers/dns/hostingde/hostingde.toml @@ -0,0 +1,19 @@ +Name = "Hosting.de" +Description = '''''' +URL = "https://www.hosting.de/" +Code = "hostingde" + +[Configuration] + [Configuration.Credentials] + HOSTINGDE_API_KEY = "**TODO**" + HOSTINGDE_ZONE_NAME = "**TODO**" + [Configuration.Additional] + HOSTINGDE_POLLING_INTERVAL = "Time between DNS propagation check" + HOSTINGDE_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + HOSTINGDE_TTL = "The TTL of the TXT record used for the DNS challenge" + HOSTINGDE_HTTP_TIMEOUT = "API request timeout" + +[Links] + API = "https://www.hosting.de/api/#dns" + + diff --git a/providers/dns/httpreq/httpreq.toml b/providers/dns/httpreq/httpreq.toml new file mode 100644 index 00000000..bf7892d3 --- /dev/null +++ b/providers/dns/httpreq/httpreq.toml @@ -0,0 +1,60 @@ +Name = "HTTP request" +Description = '''''' +URL = "/dns/httpreq/" +Code = "httpreq" + +Example = ''' +HTTPREQ_ENDPOINT=http://my.server.com:9090 \ +lego --dns httpreq --domains my.domain.com --email my@email.com run +''' + +Additional = ''' +## Description + +The server must provide: + +- `POST` `/present` +- `POST` `/cleanup` + +The URL of the server must be define by `HTTPREQ_ENDPOINT`. + +### Mode + +There are 2 modes (`HTTPREQ_MODE`): + +- default mode: +```json +{ + "fqdn": "_acme-challenge.domain.", + "value": "LHDhK3oGRvkiefQnx7OOczTY5Tic_xZ6HcMOc_gmtoM" +} +``` + +- `RAW` +```json +{ + "domain": "domain", + "token": "token", + "keyAuth": "key" +} +``` + +### Authentication + +Basic authentication (optional) can be set with some environment variables: + +- `HTTPREQ_USERNAME` and `HTTPREQ_PASSWORD` +- both values must be set, otherwise basic authentication is not defined. + +''' + +[Configuration] + [Configuration.Credentials] + HTTPREQ_MODE = "`RAW`, none" + HTTPREQ_ENDPOINT = "The URL of the server" + [Configuration.Additional] + HTTPREQ_USERNAME = "**TODO**" + HTTPREQ_PASSWORD = "**TODO**" + HTTPREQ_POLLING_INTERVAL = "Time between DNS propagation check" + HTTPREQ_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + HTTPREQ_HTTP_TIMEOUT = "API request timeout" diff --git a/providers/dns/httpreq/readme.md b/providers/dns/httpreq/readme.md deleted file mode 100644 index 51bc9a0b..00000000 --- a/providers/dns/httpreq/readme.md +++ /dev/null @@ -1,33 +0,0 @@ -# HTTP request - -The server must provide: -- `POST` `/present` -- `POST` `/cleanup` - -The URL of the server must be define by `HTTPREQ_ENDPOINT`. - -## Mode - -There are 2 modes (`HTTPREQ_MODE`): -- default mode: -```json -{ - "fqdn": "_acme-challenge.domain.", - "value": "LHDhK3oGRvkiefQnx7OOczTY5Tic_xZ6HcMOc_gmtoM" -} -``` - -- `RAW` -```json -{ - "domain": "domain", - "token": "token", - "keyAuth": "key" -} -``` - -## Authentication - -Basic authentication (optional) can be set with some environment variables: -- `HTTPREQ_USERNAME` and `HTTPREQ_PASSWORD` -- both values must be set, otherwise basic authentication is not defined. diff --git a/providers/dns/iij/iij.toml b/providers/dns/iij/iij.toml new file mode 100644 index 00000000..0beb5587 --- /dev/null +++ b/providers/dns/iij/iij.toml @@ -0,0 +1,20 @@ +Name = "Internet Initiative Japan" +Description = '''''' +URL = "https://www.iij.ad.jp/en/" +Code = "iij" + +Example = '''''' + +[Configuration] + [Configuration.Credentials] + IIJ_API_ACCESS_KEY = "API access key" + IIJ_API_SECRET_KEY = "API secret key" + IIJ_DO_SERVICE_CODE = "DO service code" + [Configuration.Additional] + IIJ_POLLING_INTERVAL = "Time between DNS propagation check" + IIJ_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + IIJ_TTL = "The TTL of the TXT record used for the DNS challenge" + +[Links] + API = "http://manual.iij.jp/p2/pubapi/http://manual.iij.jp/p2/pubapi/" + GoClient = "https://github.com/iij/doapi" diff --git a/providers/dns/inwx/inwx.toml b/providers/dns/inwx/inwx.toml new file mode 100644 index 00000000..f77265fc --- /dev/null +++ b/providers/dns/inwx/inwx.toml @@ -0,0 +1,20 @@ +Name = "INWX" +Description = '''''' +URL = "https://www.inwx.de/en" +Code = "inwx" + +Example = '''''' + +[Configuration] + [Configuration.Credentials] + INWX_USERNAME = "Username" + INWX_PASSWORD = "Password" + [Configuration.Additional] + INWX_POLLING_INTERVAL = "Time between DNS propagation check" + INWX_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + INWX_TTL = "The TTL of the TXT record used for the DNS challenge" + INWX_SANDBOX = "Activate the sandbox (boolean)" + +[Links] + API = "https://www.inwx.de/en/help/apidoc" + GoClient = "https://github.com/nrdcg/goinwx" diff --git a/providers/dns/lightsail/lightsail.toml b/providers/dns/lightsail/lightsail.toml new file mode 100644 index 00000000..191cb1d3 --- /dev/null +++ b/providers/dns/lightsail/lightsail.toml @@ -0,0 +1,18 @@ +Name = "Amazon Lightsail" +Description = '''''' +URL = "https://aws.amazon.com/lightsail/" +Code = "lightsail" + +Example = '''''' + +[Configuration] + [Configuration.Credentials] + AWS_ACCESS_KEY_ID = "Access key ID" + AWS_SECRET_ACCESS_KEY = "Secret access key" + DNS_ZONE = "DNS zone" + [Configuration.Additional] + LIGHTSAIL_POLLING_INTERVAL = "Time between DNS propagation check" + LIGHTSAIL_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + +[Links] + GoClient = "https://github.com/aws/aws-sdk-go/aws" diff --git a/providers/dns/linode/linode.toml b/providers/dns/linode/linode.toml new file mode 100644 index 00000000..d11021c0 --- /dev/null +++ b/providers/dns/linode/linode.toml @@ -0,0 +1,18 @@ +Name = "Linode (deprecated)" +Description = '''''' +URL = "https://www.linode.com/" +Code = "linode" + +Example = '''''' + +[Configuration] + [Configuration.Credentials] + LINODE_API_KEY = "API key" + [Configuration.Additional] + LINODE_POLLING_INTERVAL = "Time between DNS propagation check" + LINODE_TTL = "The TTL of the TXT record used for the DNS challenge" + LINODE_HTTP_TIMEOUT = "API request timeout" + +[Links] + API = "https://www.linode.com/api/dns" + GoClient = "https://github.com/timewasted/linode" diff --git a/providers/dns/linodev4/linodev4.toml b/providers/dns/linodev4/linodev4.toml new file mode 100644 index 00000000..49e853cf --- /dev/null +++ b/providers/dns/linodev4/linodev4.toml @@ -0,0 +1,18 @@ +Name = "Linode (v4)" +Description = '''''' +URL = "https://www.linode.com/" +Code = "linodev4" + +Example = '''''' + +[Configuration] + [Configuration.Credentials] + LINODE_TOKEN = "API token" + [Configuration.Additional] + LINODE_POLLING_INTERVAL = "Time between DNS propagation check" + LINODE_TTL = "The TTL of the TXT record used for the DNS challenge" + LINODE_HTTP_TIMEOUT = "API request timeout" + +[Links] + API = "https://developers.linode.com/api/v4" + GoClient = "https://github.com/linode/linodego" diff --git a/providers/dns/mydnsjp/mydnsjp.toml b/providers/dns/mydnsjp/mydnsjp.toml new file mode 100644 index 00000000..ec5392e2 --- /dev/null +++ b/providers/dns/mydnsjp/mydnsjp.toml @@ -0,0 +1,19 @@ +Name = "MyDNS.jp" +Description = '''''' +URL = "https://www.mydns.jp" +Code = "mydnsjp" + +Example = '''''' + +[Configuration] + [Configuration.Credentials] + MYDNSJP_MASTER_ID = "Master ID" + MYDNSJP_PASSWORD = "Password" + [Configuration.Additional] + MYDNSJP_POLLING_INTERVAL = "Time between DNS propagation check" + MYDNSJP_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + MYDNSJP_TTL = "The TTL of the TXT record used for the DNS challenge" + MYDNSJP_HTTP_TIMEOUT = "API request timeout" + +[Links] + API = "https://www.mydns.jp/?MENU=030" diff --git a/providers/dns/namecheap/namecheap.toml b/providers/dns/namecheap/namecheap.toml new file mode 100644 index 00000000..341ac6ad --- /dev/null +++ b/providers/dns/namecheap/namecheap.toml @@ -0,0 +1,19 @@ +Name = "Namecheap" +Description = '''''' +URL = "https://www.namecheap.com" +Code = "namecheap" + +Example = '''''' + +[Configuration] + [Configuration.Credentials] + NAMECHEAP_API_USER = "API user" + NAMECHEAP_API_KEY = "API key" + [Configuration.Additional] + NAMECHEAP_POLLING_INTERVAL = "Time between DNS propagation check" + NAMECHEAP_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + NAMECHEAP_TTL = "The TTL of the TXT record used for the DNS challenge" + NAMECHEAP_HTTP_TIMEOUT = "API request timeout" + +[Links] + API = "https://www.namecheap.com/support/api/methods.aspx" diff --git a/providers/dns/namedotcom/namedotcom.toml b/providers/dns/namedotcom/namedotcom.toml new file mode 100644 index 00000000..c69c45e7 --- /dev/null +++ b/providers/dns/namedotcom/namedotcom.toml @@ -0,0 +1,21 @@ +Name = "Name.com" +Description = '''''' +URL = "https://www.name.com" +Code = "namedotcom" + +Example = '''''' + +[Configuration] + [Configuration.Credentials] + NAMECOM_USERNAME = "Username" + NAMECOM_API_TOKEN = "API token" + [Configuration.Additional] + NAMECOM_POLLING_INTERVAL = "Time between DNS propagation check" + NAMECOM_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + NAMECOM_TTL = "The TTL of the TXT record used for the DNS challenge" + NAMECOM_HTTP_TIMEOUT = "API request timeout" + +[Links] + API = "https://www.name.com/api-docs/DNS" + GoClient = "https://github.com/namedotcom/go" + diff --git a/providers/dns/netcup/netcup.toml b/providers/dns/netcup/netcup.toml new file mode 100644 index 00000000..6ade9cf3 --- /dev/null +++ b/providers/dns/netcup/netcup.toml @@ -0,0 +1,20 @@ +Name = "Netcup" +Description = '''''' +URL = "https://www.netcup.eu/" +Code = "netcup" + +Example = '''''' + +[Configuration] + [Configuration.Credentials] + NETCUP_CUSTOMER_NUMBER = "Customer number" + NETCUP_API_KEY = "API key" + NETCUP_API_PASSWORD = "API password" + [Configuration.Additional] + NETCUP_POLLING_INTERVAL = "Time between DNS propagation check" + NETCUP_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + NETCUP_TTL = "The TTL of the TXT record used for the DNS challenge" + NETCUP_HTTP_TIMEOUT = "API request timeout" + +[Links] + API = "https://www.netcup-wiki.de/wiki/DNS_API" diff --git a/providers/dns/nifcloud/nifcloud.toml b/providers/dns/nifcloud/nifcloud.toml new file mode 100644 index 00000000..c4b6465d --- /dev/null +++ b/providers/dns/nifcloud/nifcloud.toml @@ -0,0 +1,19 @@ +Name = "NIFCloud" +Description = '''''' +URL = "https://www.nifcloud.com/" +Code = "nifcloud" + +Example = '''''' + +[Configuration] + [Configuration.Credentials] + NIFCLOUD_ACCESS_KEY_ID = "Access key" + NIFCLOUD_SECRET_ACCESS_KEY = "Secret access key" + [Configuration.Additional] + NIFCLOUD_POLLING_INTERVAL = "Time between DNS propagation check" + NIFCLOUD_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + NIFCLOUD_TTL = "The TTL of the TXT record used for the DNS challenge" + NIFCLOUD_HTTP_TIMEOUT = "API request timeout" + +[Links] + API = "https://mbaas.nifcloud.com/doc/current/rest/common/format.html" diff --git a/providers/dns/ns1/ns1.toml b/providers/dns/ns1/ns1.toml new file mode 100644 index 00000000..74807974 --- /dev/null +++ b/providers/dns/ns1/ns1.toml @@ -0,0 +1,19 @@ +Name = "NS1" +Description = '''''' +URL = "https://ns1.com" +Code = "ns1" + +Example = '''''' + +[Configuration] + [Configuration.Credentials] + NS1_API_KEY = "API key" + [Configuration.Additional] + NS1_POLLING_INTERVAL = "Time between DNS propagation check" + NS1_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + NS1_TTL = "The TTL of the TXT record used for the DNS challenge" + NS1_HTTP_TIMEOUT = "API request timeout" + +[Links] + API = "https://ns1.com/api" + GoClient = "https://github.com/ns1/ns1-go" diff --git a/providers/dns/oraclecloud/README.md b/providers/dns/oraclecloud/README.md deleted file mode 100644 index 476882e0..00000000 --- a/providers/dns/oraclecloud/README.md +++ /dev/null @@ -1,48 +0,0 @@ -# Export shell-env for OracleCloud - -## In Bash - -``` -export OCI_PRIVKEY_FILE="/.oci/oci_api_key.pem" -export OCI_PRIVKEY_PASS="secret" -export OCI_TENANCY_OCID="ocid1.tenancy.oc1..secret" -export OCI_USER_OCID="ocid1.user.oc1..secret" -export OCI_PUBKEY_FINGERPRINT="00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00" -export OCI_REGION="us-phoenix-1" -export OCI_COMPARTMENT_OCID="ocid1.tenancy.oc1..secret" -``` - -``` -export OCI_PRIVKEY=`base64 ~/.oci/oci_api_key.pem` -export OCI_PRIVKEY_PASS="secret" -export OCI_TENANCY_OCID="ocid1.tenancy.oc1..secret" -export OCI_USER_OCID="ocid1.user.oc1..secret" -export OCI_PUBKEY_FINGERPRINT="00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00" -export OCI_REGION="us-phoenix-1" -export OCI_COMPARTMENT_OCID="ocid1.tenancy.oc1..secret" -``` - -## In Fish - -``` -set -x OCI_PRIVKEY_FILE '~/.oci/oci_api_key.pem' -set -x OCI_PRIVKEY_PASS 'secret' -set -x OCI_TENANCY_OCID 'ocid1.tenancy.oc1..secret' -set -x OCI_USER_OCID 'ocid1.user.oc1..secret' -set -x OCI_PUBKEY_FINGERPRINT '00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00' -set -x OCI_REGION 'us-phoenix-1' -set -x OCI_COMPARTMENT_OCID 'ocid1.tenancy.oc1..secret' -``` - -``` -set IFS -set -x OCI_PRIVKEY (base64 ~/.oci/oci_api_key.pem) -set IFS \n" "\t - -set -x OCI_PRIVKEY_PASS 'secret' -set -x OCI_TENANCY_OCID 'ocid1.tenancy.oc1..secret' -set -x OCI_USER_OCID 'ocid1.user.oc1..secret' -set -x OCI_PUBKEY_FINGERPRINT '00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00' -set -x OCI_REGION 'us-phoenix-1' -set -x OCI_COMPARTMENT_OCID 'ocid1.tenancy.oc1..secret' -``` diff --git a/providers/dns/oraclecloud/oraclecloud.toml b/providers/dns/oraclecloud/oraclecloud.toml new file mode 100644 index 00000000..60786c3c --- /dev/null +++ b/providers/dns/oraclecloud/oraclecloud.toml @@ -0,0 +1,34 @@ +Name = "Oracle Cloud" +Description = '''''' +URL = "https://cloud.oracle.com/home" +Code = "oraclecloud" + +Example = ''' +OCI_PRIVKEY_FILE="~/.oci/oci_api_key.pem" \ +OCI_PRIVKEY_PASS="secret" \ +OCI_TENANCY_OCID="ocid1.tenancy.oc1..secret" \ +OCI_USER_OCID="ocid1.user.oc1..secret" \ +OCI_PUBKEY_FINGERPRINT="00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00" \ +OCI_REGION="us-phoenix-1" \ +OCI_COMPARTMENT_OCID="ocid1.tenancy.oc1..secret" \ +lego --dns oraclecloud --domains my.domain.com --email my@email.com run +''' + +[Configuration] + [Configuration.Credentials] + OCI_PRIVKEY_FILE = "Private key file" + OCI_PRIVKEY_PASS = "Private key password" + OCI_TENANCY_OCID = "Tenanct OCID" + OCI_USER_OCID = "User OCID" + OCI_PUBKEY_FINGERPRINT = "Public key fingerprint" + OCI_REGION = "Region" + OCI_COMPARTMENT_OCID = "Compartment OCID" + [Configuration.Additional] + OCI_POLLING_INTERVAL = "Time between DNS propagation check" + OCI_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + OCI_TTL = "The TTL of the TXT record used for the DNS challenge" + +[Links] + API = "https://docs.cloud.oracle.com/iaas/Content/DNS/Concepts/dnszonemanagement.htm" + GoClient = "https://github.com/oracle/oci-go-sdk" + diff --git a/providers/dns/otc/otc.toml b/providers/dns/otc/otc.toml new file mode 100644 index 00000000..24bb5008 --- /dev/null +++ b/providers/dns/otc/otc.toml @@ -0,0 +1,23 @@ +Name = "Open Telekom Cloud" +Description = '''''' +URL = "https://cloud.telekom.de/en" +Code = "otc" + +Example = '''''' + +[Configuration] + [Configuration.Credentials] + OTC_USER_NAME = "User name" + OTC_PASSWORD = "Password" + OTC_PROJECT_NAME = "Project name" + OTC_DOMAIN_NAME = "Domain name" + OTC_IDENTITY_ENDPOINT = "Identity endpoint URL" + [Configuration.Additional] + OTC_POLLING_INTERVAL = "Time between DNS propagation check" + OTC_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + OTC_TTL = "The TTL of the TXT record used for the DNS challenge" + OTC_HTTP_TIMEOUT = "API request timeout" + +[Links] + API = "https://docs.otc.t-systems.com/en-us/dns/index.html" + diff --git a/providers/dns/ovh/ovh.toml b/providers/dns/ovh/ovh.toml new file mode 100644 index 00000000..3c65e71a --- /dev/null +++ b/providers/dns/ovh/ovh.toml @@ -0,0 +1,22 @@ +Name = "OVH" +Description = '''''' +URL = "https://www.ovh.com/" +Code = "ovh" + +Example = '''''' + +[Configuration] + [Configuration.Credentials] + OVH_ENDPOINT = "Endpoint URL (ovh-eu or ovh-ca)" + OVH_APPLICATION_KEY = "Application key" + OVH_APPLICATION_SECRET = "Application secret" + OVH_CONSUMER_KEY = "Consumer key" + [Configuration.Additional] + OVH_POLLING_INTERVAL = "Time between DNS propagation check" + OVH_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + OVH_TTL = "The TTL of the TXT record used for the DNS challenge" + OVH_HTTP_TIMEOUT = "API request timeout" + +[Links] + API = "https://eu.api.ovh.com/" + GoClient = "https://github.com/ovh/go-ovh" diff --git a/providers/dns/pdns/README.md b/providers/dns/pdns/pdns.toml similarity index 51% rename from providers/dns/pdns/README.md rename to providers/dns/pdns/pdns.toml index c13e7f9d..393f52a6 100644 --- a/providers/dns/pdns/README.md +++ b/providers/dns/pdns/pdns.toml @@ -1,7 +1,29 @@ -## PowerDNS provider +Name = "PowerDNS" +Description = '''''' +URL = "https://www.powerdns.com/" +Code = "pdns" + +Example = '''''' + +Additional = ''' +## Information Tested and confirmed to work with PowerDNS authoritative 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-EDIT-API` to `INCEPTION-INCREMENT` for the zone in the `domainmetadata` table +''' + +[Configuration] + [Configuration.Credentials] + PDNS_API_KEY = "API key" + PDNS_API_URL = "API url" + [Configuration.Additional] + PDNS_POLLING_INTERVAL = "Time between DNS propagation check" + PDNS_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + PDNS_TTL = "The TTL of the TXT record used for the DNS challenge" + PDNS_HTTP_TIMEOUT = "API request timeout" + +[Links] + API = "https://doc.powerdns.com/md/httpapi/README/" diff --git a/providers/dns/rackspace/rackspace.toml b/providers/dns/rackspace/rackspace.toml new file mode 100644 index 00000000..caec8f72 --- /dev/null +++ b/providers/dns/rackspace/rackspace.toml @@ -0,0 +1,19 @@ +Name = "Rackspace" +Description = '''''' +URL = "https://www.rackspace.com/" +Code = "rackspace" + +Example = '''''' + +[Configuration] + [Configuration.Credentials] + RACKSPACE_USER = "API user" + RACKSPACE_API_KEY = "API key" + [Configuration.Additional] + RACKSPACE_POLLING_INTERVAL = "Time between DNS propagation check" + RACKSPACE_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + RACKSPACE_TTL = "The TTL of the TXT record used for the DNS challenge" + RACKSPACE_HTTP_TIMEOUT = "API request timeout" + +[Links] + API = "https://developer.rackspace.com/docs/cloud-dns/v1/" diff --git a/providers/dns/rfc2136/rfc2136.go b/providers/dns/rfc2136/rfc2136.go index 395e7dc9..b49daa4f 100644 --- a/providers/dns/rfc2136/rfc2136.go +++ b/providers/dns/rfc2136/rfc2136.go @@ -51,7 +51,7 @@ type DNSProvider struct { // 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) +// RFC2136_PROPAGATION_TIMEOUT: DNS propagation timeout in time.ParseDuration format. (60s) // To disable TSIG authentication, leave the RFC2136_TSIG* variables unset. func NewDNSProvider() (*DNSProvider, error) { values, err := env.Get("RFC2136_NAMESERVER") diff --git a/providers/dns/rfc2136/rfc2136.toml b/providers/dns/rfc2136/rfc2136.toml new file mode 100644 index 00000000..183f8ef5 --- /dev/null +++ b/providers/dns/rfc2136/rfc2136.toml @@ -0,0 +1,22 @@ +Name = "RFC2136" +Description = '''''' +URL = "https://tools.ietf.org/html/rfc2136" +Code = "rfc2136" + +Example = '''''' + +[Configuration] + [Configuration.Credentials] + RFC2136_TSIG_KEY = "Name of the secret key as defined in DNS server configuration. To disable TSIG authentication, leave the `RFC2136_TSIG*` variables unset." + RFC2136_TSIG_SECRET = "Secret key payload. To disable TSIG authentication, leave the` RFC2136_TSIG*` variables unset." + RFC2136_TSIG_ALGORITHM = "TSIG algorythm. See [miekg/dns#tsig.go](https://github.com/miekg/dns/blob/master/tsig.go) for supported values. To disable TSIG authentication, leave the `RFC2136_TSIG*` variables unset." + RFC2136_NAMESERVER = 'Network address in the form "host" or "host:port"' + [Configuration.Additional] + RFC2136_POLLING_INTERVAL = "Time between DNS propagation check" + RFC2136_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + RFC2136_TTL = "The TTL of the TXT record used for the DNS challenge" + RFC2136_DNS_TIMEOUT = "API request timeout" + RFC2136_SEQUENCE_INTERVAL = "Interval between iteration" + +[Links] + API = "https://tools.ietf.org/html/rfc2136" diff --git a/providers/dns/route53/route53.toml b/providers/dns/route53/route53.toml new file mode 100644 index 00000000..39b17223 --- /dev/null +++ b/providers/dns/route53/route53.toml @@ -0,0 +1,67 @@ +Name = "Amazon Route 53" +Description = '''''' +URL = "https://aws.amazon.com/route53/" +Code = "route53" + +Example = '''''' + +Additional = ''' +## Description + +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: [configuring-sdk](https://github.com/aws/aws-sdk-go/wiki/configuring-sdk) + +## Policy + +The following AWS IAM policy document describes the permissions required for lego to complete the DNS challenge. + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "", + "Effect": "Allow", + "Action": [ + "route53:GetChange", + "route53:ChangeResourceRecordSets", + "route53:ListResourceRecordSets" + ], + "Resource": [ + "arn:aws:route53:::hostedzone/*", + "arn:aws:route53:::change/*" + ] + }, + { + "Sid": "", + "Effect": "Allow", + "Action": "route53:ListHostedZonesByName", + "Resource": "*" + } + ] +} +``` + +''' + +[Configuration] + [Configuration.Credentials] + AWS_ACCESS_KEY_ID = "" + AWS_SECRET_ACCESS_KEY = "" + AWS_REGION = "" + AWS_HOSTED_ZONE_ID = "" + [Configuration.Additional] + AWS_POLLING_INTERVAL = "Time between DNS propagation check" + AWS_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + AWS_TTL = "The TTL of the TXT record used for the DNS challenge" + +[Links] + API = "https://docs.aws.amazon.com/Route53/latest/APIReference/API_Operations_Amazon_Route_53.html" + GoClient = "https://github.com/aws/aws-sdk-go/aws" diff --git a/providers/dns/sakuracloud/sakuracloud.toml b/providers/dns/sakuracloud/sakuracloud.toml new file mode 100644 index 00000000..b49506a8 --- /dev/null +++ b/providers/dns/sakuracloud/sakuracloud.toml @@ -0,0 +1,19 @@ +Name = "Sakura Cloud" +Description = '''''' +URL = "https://cloud.sakura.ad.jp/" +Code = "sakuracloud" + +Example = '''''' + +[Configuration] + [Configuration.Credentials] + SAKURACLOUD_ACCESS_TOKEN = "Access token" + SAKURACLOUD_ACCESS_TOKEN_SECRET = "Access token secret" + [Configuration.Additional] + SAKURACLOUD_POLLING_INTERVAL = "Time between DNS propagation check" + SAKURACLOUD_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + SAKURACLOUD_TTL = "The TTL of the TXT record used for the DNS challenge" + +[Links] + API = "https://developer.sakura.ad.jp/cloud/api/1.1/" + GoClient = "https://github.com/sacloud/libsacloud" diff --git a/providers/dns/selectel/selectel.toml b/providers/dns/selectel/selectel.toml new file mode 100644 index 00000000..2e756e9c --- /dev/null +++ b/providers/dns/selectel/selectel.toml @@ -0,0 +1,19 @@ +Name = "Selectel" +Description = '''''' +URL = "https://kb.selectel.com/" +Code = "selectel" + +Example = '''''' + +[Configuration] + [Configuration.Credentials] + SELECTEL_API_TOKEN = "API token" + [Configuration.Additional] + SELECTEL_BASE_URL = "API endpoint URL" + SELECTEL_POLLING_INTERVAL = "Time between DNS propagation check" + SELECTEL_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + SELECTEL_TTL = "The TTL of the TXT record used for the DNS challenge" + SELECTEL_HTTP_TIMEOUT = "API request timeout" + +[Links] + API = "https://kb.selectel.com/23136054.html" diff --git a/providers/dns/stackpath/stackpath.toml b/providers/dns/stackpath/stackpath.toml new file mode 100644 index 00000000..81b94191 --- /dev/null +++ b/providers/dns/stackpath/stackpath.toml @@ -0,0 +1,19 @@ +Name = "Stackpath" +Description = '''''' +URL = "https://www.stackpath.com/" +Code = "stackpath" + +Example = '''''' + +[Configuration] + [Configuration.Credentials] + STACKPATH_CLIENT_ID = "Client ID" + STACKPATH_CLIENT_SECRET = "Client secret" + STACKPATH_STACK_ID = "Stack ID" + [Configuration.Additional] + STACKPATH_POLLING_INTERVAL = "Time between DNS propagation check" + STACKPATH_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + STACKPATH_TTL = "The TTL of the TXT record used for the DNS challenge" + +[Links] + API = "https://developer.stackpath.com/en/api/dns/#tag/Zone" diff --git a/providers/dns/transip/transip.toml b/providers/dns/transip/transip.toml new file mode 100644 index 00000000..913806c3 --- /dev/null +++ b/providers/dns/transip/transip.toml @@ -0,0 +1,20 @@ +Name = "TransIP" +Description = '''''' +URL = "https://www.transip.nl/" +Code = "transip" + +Example = '''''' + +[Configuration] + [Configuration.Credentials] + TRANSIP_ACCOUNT_NAME = "Account name" + TRANSIP_PRIVATE_KEY_PATH = "Private key path" + [Configuration.Additional] + TRANSIP_POLLING_INTERVAL = "Time between DNS propagation check" + TRANSIP_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + TRANSIP_TTL = "The TTL of the TXT record used for the DNS challenge" + +[Links] + API = "https://api.transip.nl/docs/transip.nl/package-Transip.html" + GoClient = "https://github.com/transip/gotransip" + diff --git a/providers/dns/vegadns/vegadns.toml b/providers/dns/vegadns/vegadns.toml new file mode 100644 index 00000000..a223b6e6 --- /dev/null +++ b/providers/dns/vegadns/vegadns.toml @@ -0,0 +1,21 @@ +Name = "VegaDNS" +Description = '''''' +URL = "https://github.com/shupp/VegaDNS-API" +Code = "vegadns" + +Example = '''''' + +[Configuration] + [Configuration.Credentials] + SECRET_VEGADNS_KEY = "API key" + SECRET_VEGADNS_SECRET = "API secret" + VEGADNS_URL = "API endpoint URL" + [Configuration.Additional] + VEGADNS_POLLING_INTERVAL = "Time between DNS propagation check" + VEGADNS_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + VEGADNS_TTL = "The TTL of the TXT record used for the DNS challenge" + +[Links] + API = "https://github.com/shupp/VegaDNS-API" + GoClient = "https://github.com/OpenDNS/vegadns2client" + diff --git a/providers/dns/vscale/vscale.toml b/providers/dns/vscale/vscale.toml new file mode 100644 index 00000000..71a3350a --- /dev/null +++ b/providers/dns/vscale/vscale.toml @@ -0,0 +1,19 @@ +Name = "Vscale" +Description = '''''' +URL = "https://vscale.io/" +Code = "vscale" + +Example = '''''' + +[Configuration] + [Configuration.Credentials] + VSCALE_API_TOKEN = "API token" + [Configuration.Additional] + VSCALE_BASE_URL = "API enddpoint URL" + VSCALE_POLLING_INTERVAL = "Time between DNS propagation check" + VSCALE_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + VSCALE_TTL = "The TTL of the TXT record used for the DNS challenge" + VSCALE_HTTP_TIMEOUT = "API request timeout" + +[Links] + API = "https://developers.vscale.io/documentation/api/v1/#api-Domains_Records" diff --git a/providers/dns/vultr/vultr.toml b/providers/dns/vultr/vultr.toml new file mode 100644 index 00000000..23d4243e --- /dev/null +++ b/providers/dns/vultr/vultr.toml @@ -0,0 +1,19 @@ +Name = "Vultr" +Description = '''''' +URL = "https://www.vultr.com/" +Code = "vultr" + +Example = '''''' + +[Configuration] + [Configuration.Credentials] + VULTR_API_KEY = "API key" + [Configuration.Additional] + VULTR_POLLING_INTERVAL = "Time between DNS propagation check" + VULTR_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + VULTR_TTL = "The TTL of the TXT record used for the DNS challenge" + VULTR_HTTP_TIMEOUT = "API request timeout" + +[Links] + API = "https://www.vultr.com/api/#dns" + GoClient = "https://github.com/JamesClonk/vultr" diff --git a/providers/dns/zoneee/zoneee.toml b/providers/dns/zoneee/zoneee.toml new file mode 100644 index 00000000..fc564ce7 --- /dev/null +++ b/providers/dns/zoneee/zoneee.toml @@ -0,0 +1,20 @@ +Name = "Zone.ee" +Description = '''''' +URL = "https://www.zone.ee/" +Code = "zoneee" + +Example = '''''' + +[Configuration] + [Configuration.Credentials] + ZONEEE_API_USER = "API user" + ZONEEE_API_KEY = "API key" + [Configuration.Additional] + ZONEEE_ENDPOINT = "API endpoint URL" + ZONEEE_POLLING_INTERVAL = "Time between DNS propagation check" + ZONEEE_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + ZONEEE_TTL = "The TTL of the TXT record used for the DNS challenge" + ZONEEE_HTTP_TIMEOUT = "API request timeout" + +[Links] + API = "https://api.zone.eu/v2" diff --git a/vendor/github.com/BurntSushi/toml/COPYING b/vendor/github.com/BurntSushi/toml/COPYING new file mode 100644 index 00000000..01b57432 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/COPYING @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2013 TOML authors + +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/BurntSushi/toml/cmd/toml-test-decoder/COPYING b/vendor/github.com/BurntSushi/toml/cmd/toml-test-decoder/COPYING new file mode 100644 index 00000000..01b57432 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/cmd/toml-test-decoder/COPYING @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2013 TOML authors + +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/BurntSushi/toml/cmd/toml-test-encoder/COPYING b/vendor/github.com/BurntSushi/toml/cmd/toml-test-encoder/COPYING new file mode 100644 index 00000000..01b57432 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/cmd/toml-test-encoder/COPYING @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2013 TOML authors + +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/BurntSushi/toml/cmd/tomlv/COPYING b/vendor/github.com/BurntSushi/toml/cmd/tomlv/COPYING new file mode 100644 index 00000000..01b57432 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/cmd/tomlv/COPYING @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2013 TOML authors + +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/BurntSushi/toml/decode.go b/vendor/github.com/BurntSushi/toml/decode.go new file mode 100644 index 00000000..b0fd51d5 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/decode.go @@ -0,0 +1,509 @@ +package toml + +import ( + "fmt" + "io" + "io/ioutil" + "math" + "reflect" + "strings" + "time" +) + +func e(format string, args ...interface{}) error { + return fmt.Errorf("toml: "+format, args...) +} + +// Unmarshaler is the interface implemented by objects that can unmarshal a +// TOML description of themselves. +type Unmarshaler interface { + UnmarshalTOML(interface{}) error +} + +// Unmarshal decodes the contents of `p` in TOML format into a pointer `v`. +func Unmarshal(p []byte, v interface{}) error { + _, err := Decode(string(p), v) + return err +} + +// Primitive is a TOML value that hasn't been decoded into a Go value. +// When using the various `Decode*` functions, the type `Primitive` may +// be given to any value, and its decoding will be delayed. +// +// A `Primitive` value can be decoded using the `PrimitiveDecode` function. +// +// The underlying representation of a `Primitive` value is subject to change. +// Do not rely on it. +// +// N.B. Primitive values are still parsed, so using them will only avoid +// the overhead of reflection. They can be useful when you don't know the +// exact type of TOML data until run time. +type Primitive struct { + undecoded interface{} + context Key +} + +// DEPRECATED! +// +// Use MetaData.PrimitiveDecode instead. +func PrimitiveDecode(primValue Primitive, v interface{}) error { + md := MetaData{decoded: make(map[string]bool)} + return md.unify(primValue.undecoded, rvalue(v)) +} + +// PrimitiveDecode is just like the other `Decode*` functions, except it +// decodes a TOML value that has already been parsed. Valid primitive values +// can *only* be obtained from values filled by the decoder functions, +// including this method. (i.e., `v` may contain more `Primitive` +// values.) +// +// Meta data for primitive values is included in the meta data returned by +// the `Decode*` functions with one exception: keys returned by the Undecoded +// method will only reflect keys that were decoded. Namely, any keys hidden +// behind a Primitive will be considered undecoded. Executing this method will +// update the undecoded keys in the meta data. (See the example.) +func (md *MetaData) PrimitiveDecode(primValue Primitive, v interface{}) error { + md.context = primValue.context + defer func() { md.context = nil }() + return md.unify(primValue.undecoded, rvalue(v)) +} + +// Decode will decode the contents of `data` in TOML format into a pointer +// `v`. +// +// TOML hashes correspond to Go structs or maps. (Dealer's choice. They can be +// used interchangeably.) +// +// TOML arrays of tables correspond to either a slice of structs or a slice +// of maps. +// +// TOML datetimes correspond to Go `time.Time` values. +// +// All other TOML types (float, string, int, bool and array) correspond +// to the obvious Go types. +// +// An exception to the above rules is if a type implements the +// encoding.TextUnmarshaler interface. In this case, any primitive TOML value +// (floats, strings, integers, booleans and datetimes) will be converted to +// a byte string and given to the value's UnmarshalText method. See the +// Unmarshaler example for a demonstration with time duration strings. +// +// Key mapping +// +// TOML keys can map to either keys in a Go map or field names in a Go +// struct. The special `toml` struct tag may be used to map TOML keys to +// struct fields that don't match the key name exactly. (See the example.) +// A case insensitive match to struct names will be tried if an exact match +// can't be found. +// +// The mapping between TOML values and Go values is loose. That is, there +// may exist TOML values that cannot be placed into your representation, and +// there may be parts of your representation that do not correspond to +// TOML values. This loose mapping can be made stricter by using the IsDefined +// and/or Undecoded methods on the MetaData returned. +// +// This decoder will not handle cyclic types. If a cyclic type is passed, +// `Decode` will not terminate. +func Decode(data string, v interface{}) (MetaData, error) { + rv := reflect.ValueOf(v) + if rv.Kind() != reflect.Ptr { + return MetaData{}, e("Decode of non-pointer %s", reflect.TypeOf(v)) + } + if rv.IsNil() { + return MetaData{}, e("Decode of nil %s", reflect.TypeOf(v)) + } + p, err := parse(data) + if err != nil { + return MetaData{}, err + } + md := MetaData{ + p.mapping, p.types, p.ordered, + make(map[string]bool, len(p.ordered)), nil, + } + return md, md.unify(p.mapping, indirect(rv)) +} + +// DecodeFile is just like Decode, except it will automatically read the +// contents of the file at `fpath` and decode it for you. +func DecodeFile(fpath string, v interface{}) (MetaData, error) { + bs, err := ioutil.ReadFile(fpath) + if err != nil { + return MetaData{}, err + } + return Decode(string(bs), v) +} + +// DecodeReader is just like Decode, except it will consume all bytes +// from the reader and decode it for you. +func DecodeReader(r io.Reader, v interface{}) (MetaData, error) { + bs, err := ioutil.ReadAll(r) + if err != nil { + return MetaData{}, err + } + return Decode(string(bs), v) +} + +// unify performs a sort of type unification based on the structure of `rv`, +// which is the client representation. +// +// Any type mismatch produces an error. Finding a type that we don't know +// how to handle produces an unsupported type error. +func (md *MetaData) unify(data interface{}, rv reflect.Value) error { + + // Special case. Look for a `Primitive` value. + if rv.Type() == reflect.TypeOf((*Primitive)(nil)).Elem() { + // Save the undecoded data and the key context into the primitive + // value. + context := make(Key, len(md.context)) + copy(context, md.context) + rv.Set(reflect.ValueOf(Primitive{ + undecoded: data, + context: context, + })) + return nil + } + + // Special case. Unmarshaler Interface support. + if rv.CanAddr() { + if v, ok := rv.Addr().Interface().(Unmarshaler); ok { + return v.UnmarshalTOML(data) + } + } + + // Special case. Handle time.Time values specifically. + // TODO: Remove this code when we decide to drop support for Go 1.1. + // This isn't necessary in Go 1.2 because time.Time satisfies the encoding + // interfaces. + if rv.Type().AssignableTo(rvalue(time.Time{}).Type()) { + return md.unifyDatetime(data, rv) + } + + // Special case. Look for a value satisfying the TextUnmarshaler interface. + if v, ok := rv.Interface().(TextUnmarshaler); ok { + return md.unifyText(data, v) + } + // BUG(burntsushi) + // The behavior here is incorrect whenever a Go type satisfies the + // encoding.TextUnmarshaler interface but also corresponds to a TOML + // hash or array. In particular, the unmarshaler should only be applied + // to primitive TOML values. But at this point, it will be applied to + // all kinds of values and produce an incorrect error whenever those values + // are hashes or arrays (including arrays of tables). + + k := rv.Kind() + + // laziness + if k >= reflect.Int && k <= reflect.Uint64 { + return md.unifyInt(data, rv) + } + switch k { + case reflect.Ptr: + elem := reflect.New(rv.Type().Elem()) + err := md.unify(data, reflect.Indirect(elem)) + if err != nil { + return err + } + rv.Set(elem) + return nil + case reflect.Struct: + return md.unifyStruct(data, rv) + case reflect.Map: + return md.unifyMap(data, rv) + case reflect.Array: + return md.unifyArray(data, rv) + case reflect.Slice: + return md.unifySlice(data, rv) + case reflect.String: + return md.unifyString(data, rv) + case reflect.Bool: + return md.unifyBool(data, rv) + case reflect.Interface: + // we only support empty interfaces. + if rv.NumMethod() > 0 { + return e("unsupported type %s", rv.Type()) + } + return md.unifyAnything(data, rv) + case reflect.Float32: + fallthrough + case reflect.Float64: + return md.unifyFloat64(data, rv) + } + return e("unsupported type %s", rv.Kind()) +} + +func (md *MetaData) unifyStruct(mapping interface{}, rv reflect.Value) error { + tmap, ok := mapping.(map[string]interface{}) + if !ok { + if mapping == nil { + return nil + } + return e("type mismatch for %s: expected table but found %T", + rv.Type().String(), mapping) + } + + for key, datum := range tmap { + var f *field + fields := cachedTypeFields(rv.Type()) + for i := range fields { + ff := &fields[i] + if ff.name == key { + f = ff + break + } + if f == nil && strings.EqualFold(ff.name, key) { + f = ff + } + } + if f != nil { + subv := rv + for _, i := range f.index { + subv = indirect(subv.Field(i)) + } + if isUnifiable(subv) { + md.decoded[md.context.add(key).String()] = true + md.context = append(md.context, key) + if err := md.unify(datum, subv); err != nil { + return err + } + md.context = md.context[0 : len(md.context)-1] + } else if f.name != "" { + // Bad user! No soup for you! + return e("cannot write unexported field %s.%s", + rv.Type().String(), f.name) + } + } + } + return nil +} + +func (md *MetaData) unifyMap(mapping interface{}, rv reflect.Value) error { + tmap, ok := mapping.(map[string]interface{}) + if !ok { + if tmap == nil { + return nil + } + return badtype("map", mapping) + } + if rv.IsNil() { + rv.Set(reflect.MakeMap(rv.Type())) + } + for k, v := range tmap { + md.decoded[md.context.add(k).String()] = true + md.context = append(md.context, k) + + rvkey := indirect(reflect.New(rv.Type().Key())) + rvval := reflect.Indirect(reflect.New(rv.Type().Elem())) + if err := md.unify(v, rvval); err != nil { + return err + } + md.context = md.context[0 : len(md.context)-1] + + rvkey.SetString(k) + rv.SetMapIndex(rvkey, rvval) + } + return nil +} + +func (md *MetaData) unifyArray(data interface{}, rv reflect.Value) error { + datav := reflect.ValueOf(data) + if datav.Kind() != reflect.Slice { + if !datav.IsValid() { + return nil + } + return badtype("slice", data) + } + sliceLen := datav.Len() + if sliceLen != rv.Len() { + return e("expected array length %d; got TOML array of length %d", + rv.Len(), sliceLen) + } + return md.unifySliceArray(datav, rv) +} + +func (md *MetaData) unifySlice(data interface{}, rv reflect.Value) error { + datav := reflect.ValueOf(data) + if datav.Kind() != reflect.Slice { + if !datav.IsValid() { + return nil + } + return badtype("slice", data) + } + n := datav.Len() + if rv.IsNil() || rv.Cap() < n { + rv.Set(reflect.MakeSlice(rv.Type(), n, n)) + } + rv.SetLen(n) + return md.unifySliceArray(datav, rv) +} + +func (md *MetaData) unifySliceArray(data, rv reflect.Value) error { + sliceLen := data.Len() + for i := 0; i < sliceLen; i++ { + v := data.Index(i).Interface() + sliceval := indirect(rv.Index(i)) + if err := md.unify(v, sliceval); err != nil { + return err + } + } + return nil +} + +func (md *MetaData) unifyDatetime(data interface{}, rv reflect.Value) error { + if _, ok := data.(time.Time); ok { + rv.Set(reflect.ValueOf(data)) + return nil + } + return badtype("time.Time", data) +} + +func (md *MetaData) unifyString(data interface{}, rv reflect.Value) error { + if s, ok := data.(string); ok { + rv.SetString(s) + return nil + } + return badtype("string", data) +} + +func (md *MetaData) unifyFloat64(data interface{}, rv reflect.Value) error { + if num, ok := data.(float64); ok { + switch rv.Kind() { + case reflect.Float32: + fallthrough + case reflect.Float64: + rv.SetFloat(num) + default: + panic("bug") + } + return nil + } + return badtype("float", data) +} + +func (md *MetaData) unifyInt(data interface{}, rv reflect.Value) error { + if num, ok := data.(int64); ok { + if rv.Kind() >= reflect.Int && rv.Kind() <= reflect.Int64 { + switch rv.Kind() { + case reflect.Int, reflect.Int64: + // No bounds checking necessary. + case reflect.Int8: + if num < math.MinInt8 || num > math.MaxInt8 { + return e("value %d is out of range for int8", num) + } + case reflect.Int16: + if num < math.MinInt16 || num > math.MaxInt16 { + return e("value %d is out of range for int16", num) + } + case reflect.Int32: + if num < math.MinInt32 || num > math.MaxInt32 { + return e("value %d is out of range for int32", num) + } + } + rv.SetInt(num) + } else if rv.Kind() >= reflect.Uint && rv.Kind() <= reflect.Uint64 { + unum := uint64(num) + switch rv.Kind() { + case reflect.Uint, reflect.Uint64: + // No bounds checking necessary. + case reflect.Uint8: + if num < 0 || unum > math.MaxUint8 { + return e("value %d is out of range for uint8", num) + } + case reflect.Uint16: + if num < 0 || unum > math.MaxUint16 { + return e("value %d is out of range for uint16", num) + } + case reflect.Uint32: + if num < 0 || unum > math.MaxUint32 { + return e("value %d is out of range for uint32", num) + } + } + rv.SetUint(unum) + } else { + panic("unreachable") + } + return nil + } + return badtype("integer", data) +} + +func (md *MetaData) unifyBool(data interface{}, rv reflect.Value) error { + if b, ok := data.(bool); ok { + rv.SetBool(b) + return nil + } + return badtype("boolean", data) +} + +func (md *MetaData) unifyAnything(data interface{}, rv reflect.Value) error { + rv.Set(reflect.ValueOf(data)) + return nil +} + +func (md *MetaData) unifyText(data interface{}, v TextUnmarshaler) error { + var s string + switch sdata := data.(type) { + case TextMarshaler: + text, err := sdata.MarshalText() + if err != nil { + return err + } + s = string(text) + case fmt.Stringer: + s = sdata.String() + case string: + s = sdata + case bool: + s = fmt.Sprintf("%v", sdata) + case int64: + s = fmt.Sprintf("%d", sdata) + case float64: + s = fmt.Sprintf("%f", sdata) + default: + return badtype("primitive (string-like)", data) + } + if err := v.UnmarshalText([]byte(s)); err != nil { + return err + } + return nil +} + +// rvalue returns a reflect.Value of `v`. All pointers are resolved. +func rvalue(v interface{}) reflect.Value { + return indirect(reflect.ValueOf(v)) +} + +// indirect returns the value pointed to by a pointer. +// Pointers are followed until the value is not a pointer. +// New values are allocated for each nil pointer. +// +// An exception to this rule is if the value satisfies an interface of +// interest to us (like encoding.TextUnmarshaler). +func indirect(v reflect.Value) reflect.Value { + if v.Kind() != reflect.Ptr { + if v.CanSet() { + pv := v.Addr() + if _, ok := pv.Interface().(TextUnmarshaler); ok { + return pv + } + } + return v + } + if v.IsNil() { + v.Set(reflect.New(v.Type().Elem())) + } + return indirect(reflect.Indirect(v)) +} + +func isUnifiable(rv reflect.Value) bool { + if rv.CanSet() { + return true + } + if _, ok := rv.Interface().(TextUnmarshaler); ok { + return true + } + return false +} + +func badtype(expected string, data interface{}) error { + return e("cannot load TOML value of type %T into a Go %s", data, expected) +} diff --git a/vendor/github.com/BurntSushi/toml/decode_meta.go b/vendor/github.com/BurntSushi/toml/decode_meta.go new file mode 100644 index 00000000..b9914a67 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/decode_meta.go @@ -0,0 +1,121 @@ +package toml + +import "strings" + +// MetaData allows access to meta information about TOML data that may not +// be inferrable via reflection. In particular, whether a key has been defined +// and the TOML type of a key. +type MetaData struct { + mapping map[string]interface{} + types map[string]tomlType + keys []Key + decoded map[string]bool + context Key // Used only during decoding. +} + +// IsDefined returns true if the key given exists in the TOML data. The key +// should be specified hierarchially. e.g., +// +// // access the TOML key 'a.b.c' +// IsDefined("a", "b", "c") +// +// IsDefined will return false if an empty key given. Keys are case sensitive. +func (md *MetaData) IsDefined(key ...string) bool { + if len(key) == 0 { + return false + } + + var hash map[string]interface{} + var ok bool + var hashOrVal interface{} = md.mapping + for _, k := range key { + if hash, ok = hashOrVal.(map[string]interface{}); !ok { + return false + } + if hashOrVal, ok = hash[k]; !ok { + return false + } + } + return true +} + +// Type returns a string representation of the type of the key specified. +// +// Type will return the empty string if given an empty key or a key that +// does not exist. Keys are case sensitive. +func (md *MetaData) Type(key ...string) string { + fullkey := strings.Join(key, ".") + if typ, ok := md.types[fullkey]; ok { + return typ.typeString() + } + return "" +} + +// Key is the type of any TOML key, including key groups. Use (MetaData).Keys +// to get values of this type. +type Key []string + +func (k Key) String() string { + return strings.Join(k, ".") +} + +func (k Key) maybeQuotedAll() string { + var ss []string + for i := range k { + ss = append(ss, k.maybeQuoted(i)) + } + return strings.Join(ss, ".") +} + +func (k Key) maybeQuoted(i int) string { + quote := false + for _, c := range k[i] { + if !isBareKeyChar(c) { + quote = true + break + } + } + if quote { + return "\"" + strings.Replace(k[i], "\"", "\\\"", -1) + "\"" + } + return k[i] +} + +func (k Key) add(piece string) Key { + newKey := make(Key, len(k)+1) + copy(newKey, k) + newKey[len(k)] = piece + return newKey +} + +// Keys returns a slice of every key in the TOML data, including key groups. +// Each key is itself a slice, where the first element is the top of the +// hierarchy and the last is the most specific. +// +// The list will have the same order as the keys appeared in the TOML data. +// +// All keys returned are non-empty. +func (md *MetaData) Keys() []Key { + return md.keys +} + +// Undecoded returns all keys that have not been decoded in the order in which +// they appear in the original TOML document. +// +// This includes keys that haven't been decoded because of a Primitive value. +// Once the Primitive value is decoded, the keys will be considered decoded. +// +// Also note that decoding into an empty interface will result in no decoding, +// and so no keys will be considered decoded. +// +// In this sense, the Undecoded keys correspond to keys in the TOML document +// that do not have a concrete type in your representation. +func (md *MetaData) Undecoded() []Key { + undecoded := make([]Key, 0, len(md.keys)) + for _, key := range md.keys { + if !md.decoded[key.String()] { + undecoded = append(undecoded, key) + } + } + return undecoded +} diff --git a/vendor/github.com/BurntSushi/toml/doc.go b/vendor/github.com/BurntSushi/toml/doc.go new file mode 100644 index 00000000..b371f396 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/doc.go @@ -0,0 +1,27 @@ +/* +Package toml provides facilities for decoding and encoding TOML configuration +files via reflection. There is also support for delaying decoding with +the Primitive type, and querying the set of keys in a TOML document with the +MetaData type. + +The specification implemented: https://github.com/toml-lang/toml + +The sub-command github.com/BurntSushi/toml/cmd/tomlv can be used to verify +whether a file is a valid TOML document. It can also be used to print the +type of each key in a TOML document. + +Testing + +There are two important types of tests used for this package. The first is +contained inside '*_test.go' files and uses the standard Go unit testing +framework. These tests are primarily devoted to holistically testing the +decoder and encoder. + +The second type of testing is used to verify the implementation's adherence +to the TOML specification. These tests have been factored into their own +project: https://github.com/BurntSushi/toml-test + +The reason the tests are in a separate project is so that they can be used by +any implementation of TOML. Namely, it is language agnostic. +*/ +package toml diff --git a/vendor/github.com/BurntSushi/toml/encode.go b/vendor/github.com/BurntSushi/toml/encode.go new file mode 100644 index 00000000..d905c21a --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/encode.go @@ -0,0 +1,568 @@ +package toml + +import ( + "bufio" + "errors" + "fmt" + "io" + "reflect" + "sort" + "strconv" + "strings" + "time" +) + +type tomlEncodeError struct{ error } + +var ( + errArrayMixedElementTypes = errors.New( + "toml: cannot encode array with mixed element types") + errArrayNilElement = errors.New( + "toml: cannot encode array with nil element") + errNonString = errors.New( + "toml: cannot encode a map with non-string key type") + errAnonNonStruct = errors.New( + "toml: cannot encode an anonymous field that is not a struct") + errArrayNoTable = errors.New( + "toml: TOML array element cannot contain a table") + errNoKey = errors.New( + "toml: top-level values must be Go maps or structs") + errAnything = errors.New("") // used in testing +) + +var quotedReplacer = strings.NewReplacer( + "\t", "\\t", + "\n", "\\n", + "\r", "\\r", + "\"", "\\\"", + "\\", "\\\\", +) + +// Encoder controls the encoding of Go values to a TOML document to some +// io.Writer. +// +// The indentation level can be controlled with the Indent field. +type Encoder struct { + // A single indentation level. By default it is two spaces. + Indent string + + // hasWritten is whether we have written any output to w yet. + hasWritten bool + w *bufio.Writer +} + +// NewEncoder returns a TOML encoder that encodes Go values to the io.Writer +// given. By default, a single indentation level is 2 spaces. +func NewEncoder(w io.Writer) *Encoder { + return &Encoder{ + w: bufio.NewWriter(w), + Indent: " ", + } +} + +// Encode writes a TOML representation of the Go value to the underlying +// io.Writer. If the value given cannot be encoded to a valid TOML document, +// then an error is returned. +// +// The mapping between Go values and TOML values should be precisely the same +// as for the Decode* functions. Similarly, the TextMarshaler interface is +// supported by encoding the resulting bytes as strings. (If you want to write +// arbitrary binary data then you will need to use something like base64 since +// TOML does not have any binary types.) +// +// When encoding TOML hashes (i.e., Go maps or structs), keys without any +// sub-hashes are encoded first. +// +// If a Go map is encoded, then its keys are sorted alphabetically for +// deterministic output. More control over this behavior may be provided if +// there is demand for it. +// +// Encoding Go values without a corresponding TOML representation---like map +// types with non-string keys---will cause an error to be returned. Similarly +// for mixed arrays/slices, arrays/slices with nil elements, embedded +// non-struct types and nested slices containing maps or structs. +// (e.g., [][]map[string]string is not allowed but []map[string]string is OK +// and so is []map[string][]string.) +func (enc *Encoder) Encode(v interface{}) error { + rv := eindirect(reflect.ValueOf(v)) + if err := enc.safeEncode(Key([]string{}), rv); err != nil { + return err + } + return enc.w.Flush() +} + +func (enc *Encoder) safeEncode(key Key, rv reflect.Value) (err error) { + defer func() { + if r := recover(); r != nil { + if terr, ok := r.(tomlEncodeError); ok { + err = terr.error + return + } + panic(r) + } + }() + enc.encode(key, rv) + return nil +} + +func (enc *Encoder) encode(key Key, rv reflect.Value) { + // Special case. Time needs to be in ISO8601 format. + // Special case. If we can marshal the type to text, then we used that. + // Basically, this prevents the encoder for handling these types as + // generic structs (or whatever the underlying type of a TextMarshaler is). + switch rv.Interface().(type) { + case time.Time, TextMarshaler: + enc.keyEqElement(key, rv) + return + } + + k := rv.Kind() + switch k { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, + reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, + reflect.Uint64, + reflect.Float32, reflect.Float64, reflect.String, reflect.Bool: + enc.keyEqElement(key, rv) + case reflect.Array, reflect.Slice: + if typeEqual(tomlArrayHash, tomlTypeOfGo(rv)) { + enc.eArrayOfTables(key, rv) + } else { + enc.keyEqElement(key, rv) + } + case reflect.Interface: + if rv.IsNil() { + return + } + enc.encode(key, rv.Elem()) + case reflect.Map: + if rv.IsNil() { + return + } + enc.eTable(key, rv) + case reflect.Ptr: + if rv.IsNil() { + return + } + enc.encode(key, rv.Elem()) + case reflect.Struct: + enc.eTable(key, rv) + default: + panic(e("unsupported type for key '%s': %s", key, k)) + } +} + +// eElement encodes any value that can be an array element (primitives and +// arrays). +func (enc *Encoder) eElement(rv reflect.Value) { + switch v := rv.Interface().(type) { + case time.Time: + // Special case time.Time as a primitive. Has to come before + // TextMarshaler below because time.Time implements + // encoding.TextMarshaler, but we need to always use UTC. + enc.wf(v.UTC().Format("2006-01-02T15:04:05Z")) + return + case TextMarshaler: + // Special case. Use text marshaler if it's available for this value. + if s, err := v.MarshalText(); err != nil { + encPanic(err) + } else { + enc.writeQuoted(string(s)) + } + return + } + switch rv.Kind() { + case reflect.Bool: + enc.wf(strconv.FormatBool(rv.Bool())) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, + reflect.Int64: + enc.wf(strconv.FormatInt(rv.Int(), 10)) + case reflect.Uint, reflect.Uint8, reflect.Uint16, + reflect.Uint32, reflect.Uint64: + enc.wf(strconv.FormatUint(rv.Uint(), 10)) + case reflect.Float32: + enc.wf(floatAddDecimal(strconv.FormatFloat(rv.Float(), 'f', -1, 32))) + case reflect.Float64: + enc.wf(floatAddDecimal(strconv.FormatFloat(rv.Float(), 'f', -1, 64))) + case reflect.Array, reflect.Slice: + enc.eArrayOrSliceElement(rv) + case reflect.Interface: + enc.eElement(rv.Elem()) + case reflect.String: + enc.writeQuoted(rv.String()) + default: + panic(e("unexpected primitive type: %s", rv.Kind())) + } +} + +// By the TOML spec, all floats must have a decimal with at least one +// number on either side. +func floatAddDecimal(fstr string) string { + if !strings.Contains(fstr, ".") { + return fstr + ".0" + } + return fstr +} + +func (enc *Encoder) writeQuoted(s string) { + enc.wf("\"%s\"", quotedReplacer.Replace(s)) +} + +func (enc *Encoder) eArrayOrSliceElement(rv reflect.Value) { + length := rv.Len() + enc.wf("[") + for i := 0; i < length; i++ { + elem := rv.Index(i) + enc.eElement(elem) + if i != length-1 { + enc.wf(", ") + } + } + enc.wf("]") +} + +func (enc *Encoder) eArrayOfTables(key Key, rv reflect.Value) { + if len(key) == 0 { + encPanic(errNoKey) + } + for i := 0; i < rv.Len(); i++ { + trv := rv.Index(i) + if isNil(trv) { + continue + } + panicIfInvalidKey(key) + enc.newline() + enc.wf("%s[[%s]]", enc.indentStr(key), key.maybeQuotedAll()) + enc.newline() + enc.eMapOrStruct(key, trv) + } +} + +func (enc *Encoder) eTable(key Key, rv reflect.Value) { + panicIfInvalidKey(key) + if len(key) == 1 { + // Output an extra newline between top-level tables. + // (The newline isn't written if nothing else has been written though.) + enc.newline() + } + if len(key) > 0 { + enc.wf("%s[%s]", enc.indentStr(key), key.maybeQuotedAll()) + enc.newline() + } + enc.eMapOrStruct(key, rv) +} + +func (enc *Encoder) eMapOrStruct(key Key, rv reflect.Value) { + switch rv := eindirect(rv); rv.Kind() { + case reflect.Map: + enc.eMap(key, rv) + case reflect.Struct: + enc.eStruct(key, rv) + default: + panic("eTable: unhandled reflect.Value Kind: " + rv.Kind().String()) + } +} + +func (enc *Encoder) eMap(key Key, rv reflect.Value) { + rt := rv.Type() + if rt.Key().Kind() != reflect.String { + encPanic(errNonString) + } + + // Sort keys so that we have deterministic output. And write keys directly + // underneath this key first, before writing sub-structs or sub-maps. + var mapKeysDirect, mapKeysSub []string + for _, mapKey := range rv.MapKeys() { + k := mapKey.String() + if typeIsHash(tomlTypeOfGo(rv.MapIndex(mapKey))) { + mapKeysSub = append(mapKeysSub, k) + } else { + mapKeysDirect = append(mapKeysDirect, k) + } + } + + var writeMapKeys = func(mapKeys []string) { + sort.Strings(mapKeys) + for _, mapKey := range mapKeys { + mrv := rv.MapIndex(reflect.ValueOf(mapKey)) + if isNil(mrv) { + // Don't write anything for nil fields. + continue + } + enc.encode(key.add(mapKey), mrv) + } + } + writeMapKeys(mapKeysDirect) + writeMapKeys(mapKeysSub) +} + +func (enc *Encoder) eStruct(key Key, rv reflect.Value) { + // Write keys for fields directly under this key first, because if we write + // a field that creates a new table, then all keys under it will be in that + // table (not the one we're writing here). + rt := rv.Type() + var fieldsDirect, fieldsSub [][]int + var addFields func(rt reflect.Type, rv reflect.Value, start []int) + addFields = func(rt reflect.Type, rv reflect.Value, start []int) { + for i := 0; i < rt.NumField(); i++ { + f := rt.Field(i) + // skip unexported fields + if f.PkgPath != "" && !f.Anonymous { + continue + } + frv := rv.Field(i) + if f.Anonymous { + t := f.Type + switch t.Kind() { + case reflect.Struct: + // Treat anonymous struct fields with + // tag names as though they are not + // anonymous, like encoding/json does. + if getOptions(f.Tag).name == "" { + addFields(t, frv, f.Index) + continue + } + case reflect.Ptr: + if t.Elem().Kind() == reflect.Struct && + getOptions(f.Tag).name == "" { + if !frv.IsNil() { + addFields(t.Elem(), frv.Elem(), f.Index) + } + continue + } + // Fall through to the normal field encoding logic below + // for non-struct anonymous fields. + } + } + + if typeIsHash(tomlTypeOfGo(frv)) { + fieldsSub = append(fieldsSub, append(start, f.Index...)) + } else { + fieldsDirect = append(fieldsDirect, append(start, f.Index...)) + } + } + } + addFields(rt, rv, nil) + + var writeFields = func(fields [][]int) { + for _, fieldIndex := range fields { + sft := rt.FieldByIndex(fieldIndex) + sf := rv.FieldByIndex(fieldIndex) + if isNil(sf) { + // Don't write anything for nil fields. + continue + } + + opts := getOptions(sft.Tag) + if opts.skip { + continue + } + keyName := sft.Name + if opts.name != "" { + keyName = opts.name + } + if opts.omitempty && isEmpty(sf) { + continue + } + if opts.omitzero && isZero(sf) { + continue + } + + enc.encode(key.add(keyName), sf) + } + } + writeFields(fieldsDirect) + writeFields(fieldsSub) +} + +// tomlTypeName returns the TOML type name of the Go value's type. It is +// used to determine whether the types of array elements are mixed (which is +// forbidden). If the Go value is nil, then it is illegal for it to be an array +// element, and valueIsNil is returned as true. + +// Returns the TOML type of a Go value. The type may be `nil`, which means +// no concrete TOML type could be found. +func tomlTypeOfGo(rv reflect.Value) tomlType { + if isNil(rv) || !rv.IsValid() { + return nil + } + switch rv.Kind() { + case reflect.Bool: + return tomlBool + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, + reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, + reflect.Uint64: + return tomlInteger + case reflect.Float32, reflect.Float64: + return tomlFloat + case reflect.Array, reflect.Slice: + if typeEqual(tomlHash, tomlArrayType(rv)) { + return tomlArrayHash + } + return tomlArray + case reflect.Ptr, reflect.Interface: + return tomlTypeOfGo(rv.Elem()) + case reflect.String: + return tomlString + case reflect.Map: + return tomlHash + case reflect.Struct: + switch rv.Interface().(type) { + case time.Time: + return tomlDatetime + case TextMarshaler: + return tomlString + default: + return tomlHash + } + default: + panic("unexpected reflect.Kind: " + rv.Kind().String()) + } +} + +// tomlArrayType returns the element type of a TOML array. The type returned +// may be nil if it cannot be determined (e.g., a nil slice or a zero length +// slize). This function may also panic if it finds a type that cannot be +// expressed in TOML (such as nil elements, heterogeneous arrays or directly +// nested arrays of tables). +func tomlArrayType(rv reflect.Value) tomlType { + if isNil(rv) || !rv.IsValid() || rv.Len() == 0 { + return nil + } + firstType := tomlTypeOfGo(rv.Index(0)) + if firstType == nil { + encPanic(errArrayNilElement) + } + + rvlen := rv.Len() + for i := 1; i < rvlen; i++ { + elem := rv.Index(i) + switch elemType := tomlTypeOfGo(elem); { + case elemType == nil: + encPanic(errArrayNilElement) + case !typeEqual(firstType, elemType): + encPanic(errArrayMixedElementTypes) + } + } + // If we have a nested array, then we must make sure that the nested + // array contains ONLY primitives. + // This checks arbitrarily nested arrays. + if typeEqual(firstType, tomlArray) || typeEqual(firstType, tomlArrayHash) { + nest := tomlArrayType(eindirect(rv.Index(0))) + if typeEqual(nest, tomlHash) || typeEqual(nest, tomlArrayHash) { + encPanic(errArrayNoTable) + } + } + return firstType +} + +type tagOptions struct { + skip bool // "-" + name string + omitempty bool + omitzero bool +} + +func getOptions(tag reflect.StructTag) tagOptions { + t := tag.Get("toml") + if t == "-" { + return tagOptions{skip: true} + } + var opts tagOptions + parts := strings.Split(t, ",") + opts.name = parts[0] + for _, s := range parts[1:] { + switch s { + case "omitempty": + opts.omitempty = true + case "omitzero": + opts.omitzero = true + } + } + return opts +} + +func isZero(rv reflect.Value) bool { + switch rv.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return rv.Int() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return rv.Uint() == 0 + case reflect.Float32, reflect.Float64: + return rv.Float() == 0.0 + } + return false +} + +func isEmpty(rv reflect.Value) bool { + switch rv.Kind() { + case reflect.Array, reflect.Slice, reflect.Map, reflect.String: + return rv.Len() == 0 + case reflect.Bool: + return !rv.Bool() + } + return false +} + +func (enc *Encoder) newline() { + if enc.hasWritten { + enc.wf("\n") + } +} + +func (enc *Encoder) keyEqElement(key Key, val reflect.Value) { + if len(key) == 0 { + encPanic(errNoKey) + } + panicIfInvalidKey(key) + enc.wf("%s%s = ", enc.indentStr(key), key.maybeQuoted(len(key)-1)) + enc.eElement(val) + enc.newline() +} + +func (enc *Encoder) wf(format string, v ...interface{}) { + if _, err := fmt.Fprintf(enc.w, format, v...); err != nil { + encPanic(err) + } + enc.hasWritten = true +} + +func (enc *Encoder) indentStr(key Key) string { + return strings.Repeat(enc.Indent, len(key)-1) +} + +func encPanic(err error) { + panic(tomlEncodeError{err}) +} + +func eindirect(v reflect.Value) reflect.Value { + switch v.Kind() { + case reflect.Ptr, reflect.Interface: + return eindirect(v.Elem()) + default: + return v + } +} + +func isNil(rv reflect.Value) bool { + switch rv.Kind() { + case reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: + return rv.IsNil() + default: + return false + } +} + +func panicIfInvalidKey(key Key) { + for _, k := range key { + if len(k) == 0 { + encPanic(e("Key '%s' is not a valid table name. Key names "+ + "cannot be empty.", key.maybeQuotedAll())) + } + } +} + +func isValidKeyName(s string) bool { + return len(s) != 0 +} diff --git a/vendor/github.com/BurntSushi/toml/encoding_types.go b/vendor/github.com/BurntSushi/toml/encoding_types.go new file mode 100644 index 00000000..d36e1dd6 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/encoding_types.go @@ -0,0 +1,19 @@ +// +build go1.2 + +package toml + +// In order to support Go 1.1, we define our own TextMarshaler and +// TextUnmarshaler types. For Go 1.2+, we just alias them with the +// standard library interfaces. + +import ( + "encoding" +) + +// TextMarshaler is a synonym for encoding.TextMarshaler. It is defined here +// so that Go 1.1 can be supported. +type TextMarshaler encoding.TextMarshaler + +// TextUnmarshaler is a synonym for encoding.TextUnmarshaler. It is defined +// here so that Go 1.1 can be supported. +type TextUnmarshaler encoding.TextUnmarshaler diff --git a/vendor/github.com/BurntSushi/toml/encoding_types_1.1.go b/vendor/github.com/BurntSushi/toml/encoding_types_1.1.go new file mode 100644 index 00000000..e8d503d0 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/encoding_types_1.1.go @@ -0,0 +1,18 @@ +// +build !go1.2 + +package toml + +// These interfaces were introduced in Go 1.2, so we add them manually when +// compiling for Go 1.1. + +// TextMarshaler is a synonym for encoding.TextMarshaler. It is defined here +// so that Go 1.1 can be supported. +type TextMarshaler interface { + MarshalText() (text []byte, err error) +} + +// TextUnmarshaler is a synonym for encoding.TextUnmarshaler. It is defined +// here so that Go 1.1 can be supported. +type TextUnmarshaler interface { + UnmarshalText(text []byte) error +} diff --git a/vendor/github.com/BurntSushi/toml/lex.go b/vendor/github.com/BurntSushi/toml/lex.go new file mode 100644 index 00000000..e0a742a8 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/lex.go @@ -0,0 +1,953 @@ +package toml + +import ( + "fmt" + "strings" + "unicode" + "unicode/utf8" +) + +type itemType int + +const ( + itemError itemType = iota + itemNIL // used in the parser to indicate no type + itemEOF + itemText + itemString + itemRawString + itemMultilineString + itemRawMultilineString + itemBool + itemInteger + itemFloat + itemDatetime + itemArray // the start of an array + itemArrayEnd + itemTableStart + itemTableEnd + itemArrayTableStart + itemArrayTableEnd + itemKeyStart + itemCommentStart + itemInlineTableStart + itemInlineTableEnd +) + +const ( + eof = 0 + comma = ',' + tableStart = '[' + tableEnd = ']' + arrayTableStart = '[' + arrayTableEnd = ']' + tableSep = '.' + keySep = '=' + arrayStart = '[' + arrayEnd = ']' + commentStart = '#' + stringStart = '"' + stringEnd = '"' + rawStringStart = '\'' + rawStringEnd = '\'' + inlineTableStart = '{' + inlineTableEnd = '}' +) + +type stateFn func(lx *lexer) stateFn + +type lexer struct { + input string + start int + pos int + line int + state stateFn + items chan item + + // Allow for backing up up to three runes. + // This is necessary because TOML contains 3-rune tokens (""" and '''). + prevWidths [3]int + nprev int // how many of prevWidths are in use + // If we emit an eof, we can still back up, but it is not OK to call + // next again. + atEOF bool + + // A stack of state functions used to maintain context. + // The idea is to reuse parts of the state machine in various places. + // For example, values can appear at the top level or within arbitrarily + // nested arrays. The last state on the stack is used after a value has + // been lexed. Similarly for comments. + stack []stateFn +} + +type item struct { + typ itemType + val string + line int +} + +func (lx *lexer) nextItem() item { + for { + select { + case item := <-lx.items: + return item + default: + lx.state = lx.state(lx) + } + } +} + +func lex(input string) *lexer { + lx := &lexer{ + input: input, + state: lexTop, + line: 1, + items: make(chan item, 10), + stack: make([]stateFn, 0, 10), + } + return lx +} + +func (lx *lexer) push(state stateFn) { + lx.stack = append(lx.stack, state) +} + +func (lx *lexer) pop() stateFn { + if len(lx.stack) == 0 { + return lx.errorf("BUG in lexer: no states to pop") + } + last := lx.stack[len(lx.stack)-1] + lx.stack = lx.stack[0 : len(lx.stack)-1] + return last +} + +func (lx *lexer) current() string { + return lx.input[lx.start:lx.pos] +} + +func (lx *lexer) emit(typ itemType) { + lx.items <- item{typ, lx.current(), lx.line} + lx.start = lx.pos +} + +func (lx *lexer) emitTrim(typ itemType) { + lx.items <- item{typ, strings.TrimSpace(lx.current()), lx.line} + lx.start = lx.pos +} + +func (lx *lexer) next() (r rune) { + if lx.atEOF { + panic("next called after EOF") + } + if lx.pos >= len(lx.input) { + lx.atEOF = true + return eof + } + + if lx.input[lx.pos] == '\n' { + lx.line++ + } + lx.prevWidths[2] = lx.prevWidths[1] + lx.prevWidths[1] = lx.prevWidths[0] + if lx.nprev < 3 { + lx.nprev++ + } + r, w := utf8.DecodeRuneInString(lx.input[lx.pos:]) + lx.prevWidths[0] = w + lx.pos += w + return r +} + +// ignore skips over the pending input before this point. +func (lx *lexer) ignore() { + lx.start = lx.pos +} + +// backup steps back one rune. Can be called only twice between calls to next. +func (lx *lexer) backup() { + if lx.atEOF { + lx.atEOF = false + return + } + if lx.nprev < 1 { + panic("backed up too far") + } + w := lx.prevWidths[0] + lx.prevWidths[0] = lx.prevWidths[1] + lx.prevWidths[1] = lx.prevWidths[2] + lx.nprev-- + lx.pos -= w + if lx.pos < len(lx.input) && lx.input[lx.pos] == '\n' { + lx.line-- + } +} + +// accept consumes the next rune if it's equal to `valid`. +func (lx *lexer) accept(valid rune) bool { + if lx.next() == valid { + return true + } + lx.backup() + return false +} + +// peek returns but does not consume the next rune in the input. +func (lx *lexer) peek() rune { + r := lx.next() + lx.backup() + return r +} + +// skip ignores all input that matches the given predicate. +func (lx *lexer) skip(pred func(rune) bool) { + for { + r := lx.next() + if pred(r) { + continue + } + lx.backup() + lx.ignore() + return + } +} + +// errorf stops all lexing by emitting an error and returning `nil`. +// Note that any value that is a character is escaped if it's a special +// character (newlines, tabs, etc.). +func (lx *lexer) errorf(format string, values ...interface{}) stateFn { + lx.items <- item{ + itemError, + fmt.Sprintf(format, values...), + lx.line, + } + return nil +} + +// lexTop consumes elements at the top level of TOML data. +func lexTop(lx *lexer) stateFn { + r := lx.next() + if isWhitespace(r) || isNL(r) { + return lexSkip(lx, lexTop) + } + switch r { + case commentStart: + lx.push(lexTop) + return lexCommentStart + case tableStart: + return lexTableStart + case eof: + if lx.pos > lx.start { + return lx.errorf("unexpected EOF") + } + lx.emit(itemEOF) + return nil + } + + // At this point, the only valid item can be a key, so we back up + // and let the key lexer do the rest. + lx.backup() + lx.push(lexTopEnd) + return lexKeyStart +} + +// lexTopEnd is entered whenever a top-level item has been consumed. (A value +// or a table.) It must see only whitespace, and will turn back to lexTop +// upon a newline. If it sees EOF, it will quit the lexer successfully. +func lexTopEnd(lx *lexer) stateFn { + r := lx.next() + switch { + case r == commentStart: + // a comment will read to a newline for us. + lx.push(lexTop) + return lexCommentStart + case isWhitespace(r): + return lexTopEnd + case isNL(r): + lx.ignore() + return lexTop + case r == eof: + lx.emit(itemEOF) + return nil + } + return lx.errorf("expected a top-level item to end with a newline, "+ + "comment, or EOF, but got %q instead", r) +} + +// lexTable lexes the beginning of a table. Namely, it makes sure that +// it starts with a character other than '.' and ']'. +// It assumes that '[' has already been consumed. +// It also handles the case that this is an item in an array of tables. +// e.g., '[[name]]'. +func lexTableStart(lx *lexer) stateFn { + if lx.peek() == arrayTableStart { + lx.next() + lx.emit(itemArrayTableStart) + lx.push(lexArrayTableEnd) + } else { + lx.emit(itemTableStart) + lx.push(lexTableEnd) + } + return lexTableNameStart +} + +func lexTableEnd(lx *lexer) stateFn { + lx.emit(itemTableEnd) + return lexTopEnd +} + +func lexArrayTableEnd(lx *lexer) stateFn { + if r := lx.next(); r != arrayTableEnd { + return lx.errorf("expected end of table array name delimiter %q, "+ + "but got %q instead", arrayTableEnd, r) + } + lx.emit(itemArrayTableEnd) + return lexTopEnd +} + +func lexTableNameStart(lx *lexer) stateFn { + lx.skip(isWhitespace) + switch r := lx.peek(); { + case r == tableEnd || r == eof: + return lx.errorf("unexpected end of table name " + + "(table names cannot be empty)") + case r == tableSep: + return lx.errorf("unexpected table separator " + + "(table names cannot be empty)") + case r == stringStart || r == rawStringStart: + lx.ignore() + lx.push(lexTableNameEnd) + return lexValue // reuse string lexing + default: + return lexBareTableName + } +} + +// lexBareTableName lexes the name of a table. It assumes that at least one +// valid character for the table has already been read. +func lexBareTableName(lx *lexer) stateFn { + r := lx.next() + if isBareKeyChar(r) { + return lexBareTableName + } + lx.backup() + lx.emit(itemText) + return lexTableNameEnd +} + +// lexTableNameEnd reads the end of a piece of a table name, optionally +// consuming whitespace. +func lexTableNameEnd(lx *lexer) stateFn { + lx.skip(isWhitespace) + switch r := lx.next(); { + case isWhitespace(r): + return lexTableNameEnd + case r == tableSep: + lx.ignore() + return lexTableNameStart + case r == tableEnd: + return lx.pop() + default: + return lx.errorf("expected '.' or ']' to end table name, "+ + "but got %q instead", r) + } +} + +// lexKeyStart consumes a key name up until the first non-whitespace character. +// lexKeyStart will ignore whitespace. +func lexKeyStart(lx *lexer) stateFn { + r := lx.peek() + switch { + case r == keySep: + return lx.errorf("unexpected key separator %q", keySep) + case isWhitespace(r) || isNL(r): + lx.next() + return lexSkip(lx, lexKeyStart) + case r == stringStart || r == rawStringStart: + lx.ignore() + lx.emit(itemKeyStart) + lx.push(lexKeyEnd) + return lexValue // reuse string lexing + default: + lx.ignore() + lx.emit(itemKeyStart) + return lexBareKey + } +} + +// lexBareKey consumes the text of a bare key. Assumes that the first character +// (which is not whitespace) has not yet been consumed. +func lexBareKey(lx *lexer) stateFn { + switch r := lx.next(); { + case isBareKeyChar(r): + return lexBareKey + case isWhitespace(r): + lx.backup() + lx.emit(itemText) + return lexKeyEnd + case r == keySep: + lx.backup() + lx.emit(itemText) + return lexKeyEnd + default: + return lx.errorf("bare keys cannot contain %q", r) + } +} + +// lexKeyEnd consumes the end of a key and trims whitespace (up to the key +// separator). +func lexKeyEnd(lx *lexer) stateFn { + switch r := lx.next(); { + case r == keySep: + return lexSkip(lx, lexValue) + case isWhitespace(r): + return lexSkip(lx, lexKeyEnd) + default: + return lx.errorf("expected key separator %q, but got %q instead", + keySep, r) + } +} + +// lexValue starts the consumption of a value anywhere a value is expected. +// lexValue will ignore whitespace. +// After a value is lexed, the last state on the next is popped and returned. +func lexValue(lx *lexer) stateFn { + // We allow whitespace to precede a value, but NOT newlines. + // In array syntax, the array states are responsible for ignoring newlines. + r := lx.next() + switch { + case isWhitespace(r): + return lexSkip(lx, lexValue) + case isDigit(r): + lx.backup() // avoid an extra state and use the same as above + return lexNumberOrDateStart + } + switch r { + case arrayStart: + lx.ignore() + lx.emit(itemArray) + return lexArrayValue + case inlineTableStart: + lx.ignore() + lx.emit(itemInlineTableStart) + return lexInlineTableValue + case stringStart: + if lx.accept(stringStart) { + if lx.accept(stringStart) { + lx.ignore() // Ignore """ + return lexMultilineString + } + lx.backup() + } + lx.ignore() // ignore the '"' + return lexString + case rawStringStart: + if lx.accept(rawStringStart) { + if lx.accept(rawStringStart) { + lx.ignore() // Ignore """ + return lexMultilineRawString + } + lx.backup() + } + lx.ignore() // ignore the "'" + return lexRawString + case '+', '-': + return lexNumberStart + case '.': // special error case, be kind to users + return lx.errorf("floats must start with a digit, not '.'") + } + if unicode.IsLetter(r) { + // Be permissive here; lexBool will give a nice error if the + // user wrote something like + // x = foo + // (i.e. not 'true' or 'false' but is something else word-like.) + lx.backup() + return lexBool + } + return lx.errorf("expected value but found %q instead", r) +} + +// lexArrayValue consumes one value in an array. It assumes that '[' or ',' +// have already been consumed. All whitespace and newlines are ignored. +func lexArrayValue(lx *lexer) stateFn { + r := lx.next() + switch { + case isWhitespace(r) || isNL(r): + return lexSkip(lx, lexArrayValue) + case r == commentStart: + lx.push(lexArrayValue) + return lexCommentStart + case r == comma: + return lx.errorf("unexpected comma") + case r == arrayEnd: + // NOTE(caleb): The spec isn't clear about whether you can have + // a trailing comma or not, so we'll allow it. + return lexArrayEnd + } + + lx.backup() + lx.push(lexArrayValueEnd) + return lexValue +} + +// lexArrayValueEnd consumes everything between the end of an array value and +// the next value (or the end of the array): it ignores whitespace and newlines +// and expects either a ',' or a ']'. +func lexArrayValueEnd(lx *lexer) stateFn { + r := lx.next() + switch { + case isWhitespace(r) || isNL(r): + return lexSkip(lx, lexArrayValueEnd) + case r == commentStart: + lx.push(lexArrayValueEnd) + return lexCommentStart + case r == comma: + lx.ignore() + return lexArrayValue // move on to the next value + case r == arrayEnd: + return lexArrayEnd + } + return lx.errorf( + "expected a comma or array terminator %q, but got %q instead", + arrayEnd, r, + ) +} + +// lexArrayEnd finishes the lexing of an array. +// It assumes that a ']' has just been consumed. +func lexArrayEnd(lx *lexer) stateFn { + lx.ignore() + lx.emit(itemArrayEnd) + return lx.pop() +} + +// lexInlineTableValue consumes one key/value pair in an inline table. +// It assumes that '{' or ',' have already been consumed. Whitespace is ignored. +func lexInlineTableValue(lx *lexer) stateFn { + r := lx.next() + switch { + case isWhitespace(r): + return lexSkip(lx, lexInlineTableValue) + case isNL(r): + return lx.errorf("newlines not allowed within inline tables") + case r == commentStart: + lx.push(lexInlineTableValue) + return lexCommentStart + case r == comma: + return lx.errorf("unexpected comma") + case r == inlineTableEnd: + return lexInlineTableEnd + } + lx.backup() + lx.push(lexInlineTableValueEnd) + return lexKeyStart +} + +// lexInlineTableValueEnd consumes everything between the end of an inline table +// key/value pair and the next pair (or the end of the table): +// it ignores whitespace and expects either a ',' or a '}'. +func lexInlineTableValueEnd(lx *lexer) stateFn { + r := lx.next() + switch { + case isWhitespace(r): + return lexSkip(lx, lexInlineTableValueEnd) + case isNL(r): + return lx.errorf("newlines not allowed within inline tables") + case r == commentStart: + lx.push(lexInlineTableValueEnd) + return lexCommentStart + case r == comma: + lx.ignore() + return lexInlineTableValue + case r == inlineTableEnd: + return lexInlineTableEnd + } + return lx.errorf("expected a comma or an inline table terminator %q, "+ + "but got %q instead", inlineTableEnd, r) +} + +// lexInlineTableEnd finishes the lexing of an inline table. +// It assumes that a '}' has just been consumed. +func lexInlineTableEnd(lx *lexer) stateFn { + lx.ignore() + lx.emit(itemInlineTableEnd) + return lx.pop() +} + +// lexString consumes the inner contents of a string. It assumes that the +// beginning '"' has already been consumed and ignored. +func lexString(lx *lexer) stateFn { + r := lx.next() + switch { + case r == eof: + return lx.errorf("unexpected EOF") + case isNL(r): + return lx.errorf("strings cannot contain newlines") + case r == '\\': + lx.push(lexString) + return lexStringEscape + case r == stringEnd: + lx.backup() + lx.emit(itemString) + lx.next() + lx.ignore() + return lx.pop() + } + return lexString +} + +// lexMultilineString consumes the inner contents of a string. It assumes that +// the beginning '"""' has already been consumed and ignored. +func lexMultilineString(lx *lexer) stateFn { + switch lx.next() { + case eof: + return lx.errorf("unexpected EOF") + case '\\': + return lexMultilineStringEscape + case stringEnd: + if lx.accept(stringEnd) { + if lx.accept(stringEnd) { + lx.backup() + lx.backup() + lx.backup() + lx.emit(itemMultilineString) + lx.next() + lx.next() + lx.next() + lx.ignore() + return lx.pop() + } + lx.backup() + } + } + return lexMultilineString +} + +// lexRawString consumes a raw string. Nothing can be escaped in such a string. +// It assumes that the beginning "'" has already been consumed and ignored. +func lexRawString(lx *lexer) stateFn { + r := lx.next() + switch { + case r == eof: + return lx.errorf("unexpected EOF") + case isNL(r): + return lx.errorf("strings cannot contain newlines") + case r == rawStringEnd: + lx.backup() + lx.emit(itemRawString) + lx.next() + lx.ignore() + return lx.pop() + } + return lexRawString +} + +// lexMultilineRawString consumes a raw string. Nothing can be escaped in such +// a string. It assumes that the beginning "'''" has already been consumed and +// ignored. +func lexMultilineRawString(lx *lexer) stateFn { + switch lx.next() { + case eof: + return lx.errorf("unexpected EOF") + case rawStringEnd: + if lx.accept(rawStringEnd) { + if lx.accept(rawStringEnd) { + lx.backup() + lx.backup() + lx.backup() + lx.emit(itemRawMultilineString) + lx.next() + lx.next() + lx.next() + lx.ignore() + return lx.pop() + } + lx.backup() + } + } + return lexMultilineRawString +} + +// lexMultilineStringEscape consumes an escaped character. It assumes that the +// preceding '\\' has already been consumed. +func lexMultilineStringEscape(lx *lexer) stateFn { + // Handle the special case first: + if isNL(lx.next()) { + return lexMultilineString + } + lx.backup() + lx.push(lexMultilineString) + return lexStringEscape(lx) +} + +func lexStringEscape(lx *lexer) stateFn { + r := lx.next() + switch r { + case 'b': + fallthrough + case 't': + fallthrough + case 'n': + fallthrough + case 'f': + fallthrough + case 'r': + fallthrough + case '"': + fallthrough + case '\\': + return lx.pop() + case 'u': + return lexShortUnicodeEscape + case 'U': + return lexLongUnicodeEscape + } + return lx.errorf("invalid escape character %q; only the following "+ + "escape characters are allowed: "+ + `\b, \t, \n, \f, \r, \", \\, \uXXXX, and \UXXXXXXXX`, r) +} + +func lexShortUnicodeEscape(lx *lexer) stateFn { + var r rune + for i := 0; i < 4; i++ { + r = lx.next() + if !isHexadecimal(r) { + return lx.errorf(`expected four hexadecimal digits after '\u', `+ + "but got %q instead", lx.current()) + } + } + return lx.pop() +} + +func lexLongUnicodeEscape(lx *lexer) stateFn { + var r rune + for i := 0; i < 8; i++ { + r = lx.next() + if !isHexadecimal(r) { + return lx.errorf(`expected eight hexadecimal digits after '\U', `+ + "but got %q instead", lx.current()) + } + } + return lx.pop() +} + +// lexNumberOrDateStart consumes either an integer, a float, or datetime. +func lexNumberOrDateStart(lx *lexer) stateFn { + r := lx.next() + if isDigit(r) { + return lexNumberOrDate + } + switch r { + case '_': + return lexNumber + case 'e', 'E': + return lexFloat + case '.': + return lx.errorf("floats must start with a digit, not '.'") + } + return lx.errorf("expected a digit but got %q", r) +} + +// lexNumberOrDate consumes either an integer, float or datetime. +func lexNumberOrDate(lx *lexer) stateFn { + r := lx.next() + if isDigit(r) { + return lexNumberOrDate + } + switch r { + case '-': + return lexDatetime + case '_': + return lexNumber + case '.', 'e', 'E': + return lexFloat + } + + lx.backup() + lx.emit(itemInteger) + return lx.pop() +} + +// lexDatetime consumes a Datetime, to a first approximation. +// The parser validates that it matches one of the accepted formats. +func lexDatetime(lx *lexer) stateFn { + r := lx.next() + if isDigit(r) { + return lexDatetime + } + switch r { + case '-', 'T', ':', '.', 'Z', '+': + return lexDatetime + } + + lx.backup() + lx.emit(itemDatetime) + return lx.pop() +} + +// lexNumberStart consumes either an integer or a float. It assumes that a sign +// has already been read, but that *no* digits have been consumed. +// lexNumberStart will move to the appropriate integer or float states. +func lexNumberStart(lx *lexer) stateFn { + // We MUST see a digit. Even floats have to start with a digit. + r := lx.next() + if !isDigit(r) { + if r == '.' { + return lx.errorf("floats must start with a digit, not '.'") + } + return lx.errorf("expected a digit but got %q", r) + } + return lexNumber +} + +// lexNumber consumes an integer or a float after seeing the first digit. +func lexNumber(lx *lexer) stateFn { + r := lx.next() + if isDigit(r) { + return lexNumber + } + switch r { + case '_': + return lexNumber + case '.', 'e', 'E': + return lexFloat + } + + lx.backup() + lx.emit(itemInteger) + return lx.pop() +} + +// lexFloat consumes the elements of a float. It allows any sequence of +// float-like characters, so floats emitted by the lexer are only a first +// approximation and must be validated by the parser. +func lexFloat(lx *lexer) stateFn { + r := lx.next() + if isDigit(r) { + return lexFloat + } + switch r { + case '_', '.', '-', '+', 'e', 'E': + return lexFloat + } + + lx.backup() + lx.emit(itemFloat) + return lx.pop() +} + +// lexBool consumes a bool string: 'true' or 'false. +func lexBool(lx *lexer) stateFn { + var rs []rune + for { + r := lx.next() + if !unicode.IsLetter(r) { + lx.backup() + break + } + rs = append(rs, r) + } + s := string(rs) + switch s { + case "true", "false": + lx.emit(itemBool) + return lx.pop() + } + return lx.errorf("expected value but found %q instead", s) +} + +// lexCommentStart begins the lexing of a comment. It will emit +// itemCommentStart and consume no characters, passing control to lexComment. +func lexCommentStart(lx *lexer) stateFn { + lx.ignore() + lx.emit(itemCommentStart) + return lexComment +} + +// lexComment lexes an entire comment. It assumes that '#' has been consumed. +// It will consume *up to* the first newline character, and pass control +// back to the last state on the stack. +func lexComment(lx *lexer) stateFn { + r := lx.peek() + if isNL(r) || r == eof { + lx.emit(itemText) + return lx.pop() + } + lx.next() + return lexComment +} + +// lexSkip ignores all slurped input and moves on to the next state. +func lexSkip(lx *lexer, nextState stateFn) stateFn { + return func(lx *lexer) stateFn { + lx.ignore() + return nextState + } +} + +// isWhitespace returns true if `r` is a whitespace character according +// to the spec. +func isWhitespace(r rune) bool { + return r == '\t' || r == ' ' +} + +func isNL(r rune) bool { + return r == '\n' || r == '\r' +} + +func isDigit(r rune) bool { + return r >= '0' && r <= '9' +} + +func isHexadecimal(r rune) bool { + return (r >= '0' && r <= '9') || + (r >= 'a' && r <= 'f') || + (r >= 'A' && r <= 'F') +} + +func isBareKeyChar(r rune) bool { + return (r >= 'A' && r <= 'Z') || + (r >= 'a' && r <= 'z') || + (r >= '0' && r <= '9') || + r == '_' || + r == '-' +} + +func (itype itemType) String() string { + switch itype { + case itemError: + return "Error" + case itemNIL: + return "NIL" + case itemEOF: + return "EOF" + case itemText: + return "Text" + case itemString, itemRawString, itemMultilineString, itemRawMultilineString: + return "String" + case itemBool: + return "Bool" + case itemInteger: + return "Integer" + case itemFloat: + return "Float" + case itemDatetime: + return "DateTime" + case itemTableStart: + return "TableStart" + case itemTableEnd: + return "TableEnd" + case itemKeyStart: + return "KeyStart" + case itemArray: + return "Array" + case itemArrayEnd: + return "ArrayEnd" + case itemCommentStart: + return "CommentStart" + } + panic(fmt.Sprintf("BUG: Unknown type '%d'.", int(itype))) +} + +func (item item) String() string { + return fmt.Sprintf("(%s, %s)", item.typ.String(), item.val) +} diff --git a/vendor/github.com/BurntSushi/toml/parse.go b/vendor/github.com/BurntSushi/toml/parse.go new file mode 100644 index 00000000..50869ef9 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/parse.go @@ -0,0 +1,592 @@ +package toml + +import ( + "fmt" + "strconv" + "strings" + "time" + "unicode" + "unicode/utf8" +) + +type parser struct { + mapping map[string]interface{} + types map[string]tomlType + lx *lexer + + // A list of keys in the order that they appear in the TOML data. + ordered []Key + + // the full key for the current hash in scope + context Key + + // the base key name for everything except hashes + currentKey string + + // rough approximation of line number + approxLine int + + // A map of 'key.group.names' to whether they were created implicitly. + implicits map[string]bool +} + +type parseError string + +func (pe parseError) Error() string { + return string(pe) +} + +func parse(data string) (p *parser, err error) { + defer func() { + if r := recover(); r != nil { + var ok bool + if err, ok = r.(parseError); ok { + return + } + panic(r) + } + }() + + p = &parser{ + mapping: make(map[string]interface{}), + types: make(map[string]tomlType), + lx: lex(data), + ordered: make([]Key, 0), + implicits: make(map[string]bool), + } + for { + item := p.next() + if item.typ == itemEOF { + break + } + p.topLevel(item) + } + + return p, nil +} + +func (p *parser) panicf(format string, v ...interface{}) { + msg := fmt.Sprintf("Near line %d (last key parsed '%s'): %s", + p.approxLine, p.current(), fmt.Sprintf(format, v...)) + panic(parseError(msg)) +} + +func (p *parser) next() item { + it := p.lx.nextItem() + if it.typ == itemError { + p.panicf("%s", it.val) + } + return it +} + +func (p *parser) bug(format string, v ...interface{}) { + panic(fmt.Sprintf("BUG: "+format+"\n\n", v...)) +} + +func (p *parser) expect(typ itemType) item { + it := p.next() + p.assertEqual(typ, it.typ) + return it +} + +func (p *parser) assertEqual(expected, got itemType) { + if expected != got { + p.bug("Expected '%s' but got '%s'.", expected, got) + } +} + +func (p *parser) topLevel(item item) { + switch item.typ { + case itemCommentStart: + p.approxLine = item.line + p.expect(itemText) + case itemTableStart: + kg := p.next() + p.approxLine = kg.line + + var key Key + for ; kg.typ != itemTableEnd && kg.typ != itemEOF; kg = p.next() { + key = append(key, p.keyString(kg)) + } + p.assertEqual(itemTableEnd, kg.typ) + + p.establishContext(key, false) + p.setType("", tomlHash) + p.ordered = append(p.ordered, key) + case itemArrayTableStart: + kg := p.next() + p.approxLine = kg.line + + var key Key + for ; kg.typ != itemArrayTableEnd && kg.typ != itemEOF; kg = p.next() { + key = append(key, p.keyString(kg)) + } + p.assertEqual(itemArrayTableEnd, kg.typ) + + p.establishContext(key, true) + p.setType("", tomlArrayHash) + p.ordered = append(p.ordered, key) + case itemKeyStart: + kname := p.next() + p.approxLine = kname.line + p.currentKey = p.keyString(kname) + + val, typ := p.value(p.next()) + p.setValue(p.currentKey, val) + p.setType(p.currentKey, typ) + p.ordered = append(p.ordered, p.context.add(p.currentKey)) + p.currentKey = "" + default: + p.bug("Unexpected type at top level: %s", item.typ) + } +} + +// Gets a string for a key (or part of a key in a table name). +func (p *parser) keyString(it item) string { + switch it.typ { + case itemText: + return it.val + case itemString, itemMultilineString, + itemRawString, itemRawMultilineString: + s, _ := p.value(it) + return s.(string) + default: + p.bug("Unexpected key type: %s", it.typ) + panic("unreachable") + } +} + +// value translates an expected value from the lexer into a Go value wrapped +// as an empty interface. +func (p *parser) value(it item) (interface{}, tomlType) { + switch it.typ { + case itemString: + return p.replaceEscapes(it.val), p.typeOfPrimitive(it) + case itemMultilineString: + trimmed := stripFirstNewline(stripEscapedWhitespace(it.val)) + return p.replaceEscapes(trimmed), p.typeOfPrimitive(it) + case itemRawString: + return it.val, p.typeOfPrimitive(it) + case itemRawMultilineString: + return stripFirstNewline(it.val), p.typeOfPrimitive(it) + case itemBool: + switch it.val { + case "true": + return true, p.typeOfPrimitive(it) + case "false": + return false, p.typeOfPrimitive(it) + } + p.bug("Expected boolean value, but got '%s'.", it.val) + case itemInteger: + if !numUnderscoresOK(it.val) { + p.panicf("Invalid integer %q: underscores must be surrounded by digits", + it.val) + } + val := strings.Replace(it.val, "_", "", -1) + num, err := strconv.ParseInt(val, 10, 64) + if err != nil { + // Distinguish integer values. Normally, it'd be a bug if the lexer + // provides an invalid integer, but it's possible that the number is + // out of range of valid values (which the lexer cannot determine). + // So mark the former as a bug but the latter as a legitimate user + // error. + if e, ok := err.(*strconv.NumError); ok && + e.Err == strconv.ErrRange { + + p.panicf("Integer '%s' is out of the range of 64-bit "+ + "signed integers.", it.val) + } else { + p.bug("Expected integer value, but got '%s'.", it.val) + } + } + return num, p.typeOfPrimitive(it) + case itemFloat: + parts := strings.FieldsFunc(it.val, func(r rune) bool { + switch r { + case '.', 'e', 'E': + return true + } + return false + }) + for _, part := range parts { + if !numUnderscoresOK(part) { + p.panicf("Invalid float %q: underscores must be "+ + "surrounded by digits", it.val) + } + } + if !numPeriodsOK(it.val) { + // As a special case, numbers like '123.' or '1.e2', + // which are valid as far as Go/strconv are concerned, + // must be rejected because TOML says that a fractional + // part consists of '.' followed by 1+ digits. + p.panicf("Invalid float %q: '.' must be followed "+ + "by one or more digits", it.val) + } + val := strings.Replace(it.val, "_", "", -1) + num, err := strconv.ParseFloat(val, 64) + if err != nil { + if e, ok := err.(*strconv.NumError); ok && + e.Err == strconv.ErrRange { + + p.panicf("Float '%s' is out of the range of 64-bit "+ + "IEEE-754 floating-point numbers.", it.val) + } else { + p.panicf("Invalid float value: %q", it.val) + } + } + return num, p.typeOfPrimitive(it) + case itemDatetime: + var t time.Time + var ok bool + var err error + for _, format := range []string{ + "2006-01-02T15:04:05Z07:00", + "2006-01-02T15:04:05", + "2006-01-02", + } { + t, err = time.ParseInLocation(format, it.val, time.Local) + if err == nil { + ok = true + break + } + } + if !ok { + p.panicf("Invalid TOML Datetime: %q.", it.val) + } + return t, p.typeOfPrimitive(it) + case itemArray: + array := make([]interface{}, 0) + types := make([]tomlType, 0) + + for it = p.next(); it.typ != itemArrayEnd; it = p.next() { + if it.typ == itemCommentStart { + p.expect(itemText) + continue + } + + val, typ := p.value(it) + array = append(array, val) + types = append(types, typ) + } + return array, p.typeOfArray(types) + case itemInlineTableStart: + var ( + hash = make(map[string]interface{}) + outerContext = p.context + outerKey = p.currentKey + ) + + p.context = append(p.context, p.currentKey) + p.currentKey = "" + for it := p.next(); it.typ != itemInlineTableEnd; it = p.next() { + if it.typ != itemKeyStart { + p.bug("Expected key start but instead found %q, around line %d", + it.val, p.approxLine) + } + if it.typ == itemCommentStart { + p.expect(itemText) + continue + } + + // retrieve key + k := p.next() + p.approxLine = k.line + kname := p.keyString(k) + + // retrieve value + p.currentKey = kname + val, typ := p.value(p.next()) + // make sure we keep metadata up to date + p.setType(kname, typ) + p.ordered = append(p.ordered, p.context.add(p.currentKey)) + hash[kname] = val + } + p.context = outerContext + p.currentKey = outerKey + return hash, tomlHash + } + p.bug("Unexpected value type: %s", it.typ) + panic("unreachable") +} + +// numUnderscoresOK checks whether each underscore in s is surrounded by +// characters that are not underscores. +func numUnderscoresOK(s string) bool { + accept := false + for _, r := range s { + if r == '_' { + if !accept { + return false + } + accept = false + continue + } + accept = true + } + return accept +} + +// numPeriodsOK checks whether every period in s is followed by a digit. +func numPeriodsOK(s string) bool { + period := false + for _, r := range s { + if period && !isDigit(r) { + return false + } + period = r == '.' + } + return !period +} + +// establishContext sets the current context of the parser, +// where the context is either a hash or an array of hashes. Which one is +// set depends on the value of the `array` parameter. +// +// Establishing the context also makes sure that the key isn't a duplicate, and +// will create implicit hashes automatically. +func (p *parser) establishContext(key Key, array bool) { + var ok bool + + // Always start at the top level and drill down for our context. + hashContext := p.mapping + keyContext := make(Key, 0) + + // We only need implicit hashes for key[0:-1] + for _, k := range key[0 : len(key)-1] { + _, ok = hashContext[k] + keyContext = append(keyContext, k) + + // No key? Make an implicit hash and move on. + if !ok { + p.addImplicit(keyContext) + hashContext[k] = make(map[string]interface{}) + } + + // If the hash context is actually an array of tables, then set + // the hash context to the last element in that array. + // + // Otherwise, it better be a table, since this MUST be a key group (by + // virtue of it not being the last element in a key). + switch t := hashContext[k].(type) { + case []map[string]interface{}: + hashContext = t[len(t)-1] + case map[string]interface{}: + hashContext = t + default: + p.panicf("Key '%s' was already created as a hash.", keyContext) + } + } + + p.context = keyContext + if array { + // If this is the first element for this array, then allocate a new + // list of tables for it. + k := key[len(key)-1] + if _, ok := hashContext[k]; !ok { + hashContext[k] = make([]map[string]interface{}, 0, 5) + } + + // Add a new table. But make sure the key hasn't already been used + // for something else. + if hash, ok := hashContext[k].([]map[string]interface{}); ok { + hashContext[k] = append(hash, make(map[string]interface{})) + } else { + p.panicf("Key '%s' was already created and cannot be used as "+ + "an array.", keyContext) + } + } else { + p.setValue(key[len(key)-1], make(map[string]interface{})) + } + p.context = append(p.context, key[len(key)-1]) +} + +// setValue sets the given key to the given value in the current context. +// It will make sure that the key hasn't already been defined, account for +// implicit key groups. +func (p *parser) setValue(key string, value interface{}) { + var tmpHash interface{} + var ok bool + + hash := p.mapping + keyContext := make(Key, 0) + for _, k := range p.context { + keyContext = append(keyContext, k) + if tmpHash, ok = hash[k]; !ok { + p.bug("Context for key '%s' has not been established.", keyContext) + } + switch t := tmpHash.(type) { + case []map[string]interface{}: + // The context is a table of hashes. Pick the most recent table + // defined as the current hash. + hash = t[len(t)-1] + case map[string]interface{}: + hash = t + default: + p.bug("Expected hash to have type 'map[string]interface{}', but "+ + "it has '%T' instead.", tmpHash) + } + } + keyContext = append(keyContext, key) + + if _, ok := hash[key]; ok { + // Typically, if the given key has already been set, then we have + // to raise an error since duplicate keys are disallowed. However, + // it's possible that a key was previously defined implicitly. In this + // case, it is allowed to be redefined concretely. (See the + // `tests/valid/implicit-and-explicit-after.toml` test in `toml-test`.) + // + // But we have to make sure to stop marking it as an implicit. (So that + // another redefinition provokes an error.) + // + // Note that since it has already been defined (as a hash), we don't + // want to overwrite it. So our business is done. + if p.isImplicit(keyContext) { + p.removeImplicit(keyContext) + return + } + + // Otherwise, we have a concrete key trying to override a previous + // key, which is *always* wrong. + p.panicf("Key '%s' has already been defined.", keyContext) + } + hash[key] = value +} + +// setType sets the type of a particular value at a given key. +// It should be called immediately AFTER setValue. +// +// Note that if `key` is empty, then the type given will be applied to the +// current context (which is either a table or an array of tables). +func (p *parser) setType(key string, typ tomlType) { + keyContext := make(Key, 0, len(p.context)+1) + for _, k := range p.context { + keyContext = append(keyContext, k) + } + if len(key) > 0 { // allow type setting for hashes + keyContext = append(keyContext, key) + } + p.types[keyContext.String()] = typ +} + +// addImplicit sets the given Key as having been created implicitly. +func (p *parser) addImplicit(key Key) { + p.implicits[key.String()] = true +} + +// removeImplicit stops tagging the given key as having been implicitly +// created. +func (p *parser) removeImplicit(key Key) { + p.implicits[key.String()] = false +} + +// isImplicit returns true if the key group pointed to by the key was created +// implicitly. +func (p *parser) isImplicit(key Key) bool { + return p.implicits[key.String()] +} + +// current returns the full key name of the current context. +func (p *parser) current() string { + if len(p.currentKey) == 0 { + return p.context.String() + } + if len(p.context) == 0 { + return p.currentKey + } + return fmt.Sprintf("%s.%s", p.context, p.currentKey) +} + +func stripFirstNewline(s string) string { + if len(s) == 0 || s[0] != '\n' { + return s + } + return s[1:] +} + +func stripEscapedWhitespace(s string) string { + esc := strings.Split(s, "\\\n") + if len(esc) > 1 { + for i := 1; i < len(esc); i++ { + esc[i] = strings.TrimLeftFunc(esc[i], unicode.IsSpace) + } + } + return strings.Join(esc, "") +} + +func (p *parser) replaceEscapes(str string) string { + var replaced []rune + s := []byte(str) + r := 0 + for r < len(s) { + if s[r] != '\\' { + c, size := utf8.DecodeRune(s[r:]) + r += size + replaced = append(replaced, c) + continue + } + r += 1 + if r >= len(s) { + p.bug("Escape sequence at end of string.") + return "" + } + switch s[r] { + default: + p.bug("Expected valid escape code after \\, but got %q.", s[r]) + return "" + case 'b': + replaced = append(replaced, rune(0x0008)) + r += 1 + case 't': + replaced = append(replaced, rune(0x0009)) + r += 1 + case 'n': + replaced = append(replaced, rune(0x000A)) + r += 1 + case 'f': + replaced = append(replaced, rune(0x000C)) + r += 1 + case 'r': + replaced = append(replaced, rune(0x000D)) + r += 1 + case '"': + replaced = append(replaced, rune(0x0022)) + r += 1 + case '\\': + replaced = append(replaced, rune(0x005C)) + r += 1 + case 'u': + // At this point, we know we have a Unicode escape of the form + // `uXXXX` at [r, r+5). (Because the lexer guarantees this + // for us.) + escaped := p.asciiEscapeToUnicode(s[r+1 : r+5]) + replaced = append(replaced, escaped) + r += 5 + case 'U': + // At this point, we know we have a Unicode escape of the form + // `uXXXX` at [r, r+9). (Because the lexer guarantees this + // for us.) + escaped := p.asciiEscapeToUnicode(s[r+1 : r+9]) + replaced = append(replaced, escaped) + r += 9 + } + } + return string(replaced) +} + +func (p *parser) asciiEscapeToUnicode(bs []byte) rune { + s := string(bs) + hex, err := strconv.ParseUint(strings.ToLower(s), 16, 32) + if err != nil { + p.bug("Could not parse '%s' as a hexadecimal number, but the "+ + "lexer claims it's OK: %s", s, err) + } + if !utf8.ValidRune(rune(hex)) { + p.panicf("Escaped character '\\u%s' is not valid UTF-8.", s) + } + return rune(hex) +} + +func isStringType(ty itemType) bool { + return ty == itemString || ty == itemMultilineString || + ty == itemRawString || ty == itemRawMultilineString +} diff --git a/vendor/github.com/BurntSushi/toml/type_check.go b/vendor/github.com/BurntSushi/toml/type_check.go new file mode 100644 index 00000000..c73f8afc --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/type_check.go @@ -0,0 +1,91 @@ +package toml + +// tomlType represents any Go type that corresponds to a TOML type. +// While the first draft of the TOML spec has a simplistic type system that +// probably doesn't need this level of sophistication, we seem to be militating +// toward adding real composite types. +type tomlType interface { + typeString() string +} + +// typeEqual accepts any two types and returns true if they are equal. +func typeEqual(t1, t2 tomlType) bool { + if t1 == nil || t2 == nil { + return false + } + return t1.typeString() == t2.typeString() +} + +func typeIsHash(t tomlType) bool { + return typeEqual(t, tomlHash) || typeEqual(t, tomlArrayHash) +} + +type tomlBaseType string + +func (btype tomlBaseType) typeString() string { + return string(btype) +} + +func (btype tomlBaseType) String() string { + return btype.typeString() +} + +var ( + tomlInteger tomlBaseType = "Integer" + tomlFloat tomlBaseType = "Float" + tomlDatetime tomlBaseType = "Datetime" + tomlString tomlBaseType = "String" + tomlBool tomlBaseType = "Bool" + tomlArray tomlBaseType = "Array" + tomlHash tomlBaseType = "Hash" + tomlArrayHash tomlBaseType = "ArrayHash" +) + +// typeOfPrimitive returns a tomlType of any primitive value in TOML. +// Primitive values are: Integer, Float, Datetime, String and Bool. +// +// Passing a lexer item other than the following will cause a BUG message +// to occur: itemString, itemBool, itemInteger, itemFloat, itemDatetime. +func (p *parser) typeOfPrimitive(lexItem item) tomlType { + switch lexItem.typ { + case itemInteger: + return tomlInteger + case itemFloat: + return tomlFloat + case itemDatetime: + return tomlDatetime + case itemString: + return tomlString + case itemMultilineString: + return tomlString + case itemRawString: + return tomlString + case itemRawMultilineString: + return tomlString + case itemBool: + return tomlBool + } + p.bug("Cannot infer primitive type of lex item '%s'.", lexItem) + panic("unreachable") +} + +// typeOfArray returns a tomlType for an array given a list of types of its +// values. +// +// In the current spec, if an array is homogeneous, then its type is always +// "Array". If the array is not homogeneous, an error is generated. +func (p *parser) typeOfArray(types []tomlType) tomlType { + // Empty arrays are cool. + if len(types) == 0 { + return tomlArray + } + + theType := types[0] + for _, t := range types[1:] { + if !typeEqual(theType, t) { + p.panicf("Array contains values of type '%s' and '%s', but "+ + "arrays must be homogeneous.", theType, t) + } + } + return tomlArray +} diff --git a/vendor/github.com/BurntSushi/toml/type_fields.go b/vendor/github.com/BurntSushi/toml/type_fields.go new file mode 100644 index 00000000..608997c2 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/type_fields.go @@ -0,0 +1,242 @@ +package toml + +// Struct field handling is adapted from code in encoding/json: +// +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the Go distribution. + +import ( + "reflect" + "sort" + "sync" +) + +// A field represents a single field found in a struct. +type field struct { + name string // the name of the field (`toml` tag included) + tag bool // whether field has a `toml` tag + index []int // represents the depth of an anonymous field + typ reflect.Type // the type of the field +} + +// byName sorts field by name, breaking ties with depth, +// then breaking ties with "name came from toml tag", then +// breaking ties with index sequence. +type byName []field + +func (x byName) Len() int { return len(x) } + +func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] } + +func (x byName) Less(i, j int) bool { + if x[i].name != x[j].name { + return x[i].name < x[j].name + } + if len(x[i].index) != len(x[j].index) { + return len(x[i].index) < len(x[j].index) + } + if x[i].tag != x[j].tag { + return x[i].tag + } + return byIndex(x).Less(i, j) +} + +// byIndex sorts field by index sequence. +type byIndex []field + +func (x byIndex) Len() int { return len(x) } + +func (x byIndex) Swap(i, j int) { x[i], x[j] = x[j], x[i] } + +func (x byIndex) Less(i, j int) bool { + for k, xik := range x[i].index { + if k >= len(x[j].index) { + return false + } + if xik != x[j].index[k] { + return xik < x[j].index[k] + } + } + return len(x[i].index) < len(x[j].index) +} + +// typeFields returns a list of fields that TOML should recognize for the given +// type. The algorithm is breadth-first search over the set of structs to +// include - the top struct and then any reachable anonymous structs. +func typeFields(t reflect.Type) []field { + // Anonymous fields to explore at the current level and the next. + current := []field{} + next := []field{{typ: t}} + + // Count of queued names for current level and the next. + count := map[reflect.Type]int{} + nextCount := map[reflect.Type]int{} + + // Types already visited at an earlier level. + visited := map[reflect.Type]bool{} + + // Fields found. + var fields []field + + for len(next) > 0 { + current, next = next, current[:0] + count, nextCount = nextCount, map[reflect.Type]int{} + + for _, f := range current { + if visited[f.typ] { + continue + } + visited[f.typ] = true + + // Scan f.typ for fields to include. + for i := 0; i < f.typ.NumField(); i++ { + sf := f.typ.Field(i) + if sf.PkgPath != "" && !sf.Anonymous { // unexported + continue + } + opts := getOptions(sf.Tag) + if opts.skip { + continue + } + index := make([]int, len(f.index)+1) + copy(index, f.index) + index[len(f.index)] = i + + ft := sf.Type + if ft.Name() == "" && ft.Kind() == reflect.Ptr { + // Follow pointer. + ft = ft.Elem() + } + + // Record found field and index sequence. + if opts.name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct { + tagged := opts.name != "" + name := opts.name + if name == "" { + name = sf.Name + } + fields = append(fields, field{name, tagged, index, ft}) + if count[f.typ] > 1 { + // If there were multiple instances, add a second, + // so that the annihilation code will see a duplicate. + // It only cares about the distinction between 1 or 2, + // so don't bother generating any more copies. + fields = append(fields, fields[len(fields)-1]) + } + continue + } + + // Record new anonymous struct to explore in next round. + nextCount[ft]++ + if nextCount[ft] == 1 { + f := field{name: ft.Name(), index: index, typ: ft} + next = append(next, f) + } + } + } + } + + sort.Sort(byName(fields)) + + // Delete all fields that are hidden by the Go rules for embedded fields, + // except that fields with TOML tags are promoted. + + // The fields are sorted in primary order of name, secondary order + // of field index length. Loop over names; for each name, delete + // hidden fields by choosing the one dominant field that survives. + out := fields[:0] + for advance, i := 0, 0; i < len(fields); i += advance { + // One iteration per name. + // Find the sequence of fields with the name of this first field. + fi := fields[i] + name := fi.name + for advance = 1; i+advance < len(fields); advance++ { + fj := fields[i+advance] + if fj.name != name { + break + } + } + if advance == 1 { // Only one field with this name + out = append(out, fi) + continue + } + dominant, ok := dominantField(fields[i : i+advance]) + if ok { + out = append(out, dominant) + } + } + + fields = out + sort.Sort(byIndex(fields)) + + return fields +} + +// dominantField looks through the fields, all of which are known to +// have the same name, to find the single field that dominates the +// others using Go's embedding rules, modified by the presence of +// TOML tags. If there are multiple top-level fields, the boolean +// will be false: This condition is an error in Go and we skip all +// the fields. +func dominantField(fields []field) (field, bool) { + // The fields are sorted in increasing index-length order. The winner + // must therefore be one with the shortest index length. Drop all + // longer entries, which is easy: just truncate the slice. + length := len(fields[0].index) + tagged := -1 // Index of first tagged field. + for i, f := range fields { + if len(f.index) > length { + fields = fields[:i] + break + } + if f.tag { + if tagged >= 0 { + // Multiple tagged fields at the same level: conflict. + // Return no field. + return field{}, false + } + tagged = i + } + } + if tagged >= 0 { + return fields[tagged], true + } + // All remaining fields have the same length. If there's more than one, + // we have a conflict (two fields named "X" at the same level) and we + // return no field. + if len(fields) > 1 { + return field{}, false + } + return fields[0], true +} + +var fieldCache struct { + sync.RWMutex + m map[reflect.Type][]field +} + +// cachedTypeFields is like typeFields but uses a cache to avoid repeated work. +func cachedTypeFields(t reflect.Type) []field { + fieldCache.RLock() + f := fieldCache.m[t] + fieldCache.RUnlock() + if f != nil { + return f + } + + // Compute fields without lock. + // Might duplicate effort but won't hold other computations back. + f = typeFields(t) + if f == nil { + f = []field{} + } + + fieldCache.Lock() + if fieldCache.m == nil { + fieldCache.m = map[reflect.Type][]field{} + } + fieldCache.m[t] = f + fieldCache.Unlock() + return f +}