pax_global_header 0000666 0000000 0000000 00000000064 14340204635 0014513 g ustar 00root root 0000000 0000000 52 comment=df79f1302276a36da386c7fdf70cbacc284efab9
lego-4.9.1/ 0000775 0000000 0000000 00000000000 14340204635 0012454 5 ustar 00root root 0000000 0000000 lego-4.9.1/.dockerignore 0000664 0000000 0000000 00000000076 14340204635 0015133 0 ustar 00root root 0000000 0000000 lego.exe
.lego
.gitcookies
.idea
.vscode/
dist/
builds/
docs/
lego-4.9.1/.gitcookies.enc 0000664 0000000 0000000 00000000740 14340204635 0015362 0 ustar 00root root 0000000 0000000 [{<9^ mUmm喨LF7
o*/+G0R"47 Ie >rW
ND
Ho`l6xe
p:Ņ>Qn|_"QeYXCBN# '=
Ȗ(FKpwx\SbTz$udN(?$)z,a?d:MB_44@Nkq%p7,UM(t:D2}Wy9GO6ȡcSGIMLEwȘ:}Hj|{^"W]b;ԬR!UdhPg/Qtƃ~~îaCB,|}C%֛V=ES("E5-73#"î ) %qf^6&,"=zAUR?CN lego-4.9.1/.github/ 0000775 0000000 0000000 00000000000 14340204635 0014014 5 ustar 00root root 0000000 0000000 lego-4.9.1/.github/FUNDING.yml 0000664 0000000 0000000 00000000015 14340204635 0015625 0 ustar 00root root 0000000 0000000 github: ldez
lego-4.9.1/.github/ISSUE_TEMPLATE/ 0000775 0000000 0000000 00000000000 14340204635 0016177 5 ustar 00root root 0000000 0000000 lego-4.9.1/.github/ISSUE_TEMPLATE/bug_report.yml 0000664 0000000 0000000 00000004102 14340204635 0021067 0 ustar 00root root 0000000 0000000 name: Bug Report
description: Create a report to help us improve.
labels: [bug]
body:
- type: checkboxes
id: terms
attributes:
label: Welcome
options:
- label: Yes, I'm using a binary release within 2 latest releases.
required: true
- label: Yes, I've searched similar issues on GitHub and didn't find any.
required: true
- label: Yes, I've included all information below (version, config, etc).
required: true
- type: textarea
id: expected
attributes:
label: What did you expect to see?
placeholder: Description.
validations:
required: true
- type: textarea
id: current
attributes:
label: What did you see instead?
placeholder: Description.
validations:
required: true
- type: dropdown
id: type
attributes:
label: How do you use lego?
options:
- Library
- Binary
- Docker image
- Through Traefik
- Through Caddy
- Other
validations:
required: true
- type: textarea
id: steps
attributes:
label: Reproduction steps
description: "How do you trigger this bug? Please walk us through it step by step."
placeholder: |
1. ...
2. ...
3. ...
...
validations:
required: true
- type: textarea
id: version
attributes:
label: Version of lego
description: |-
```console
$ lego --version
```
placeholder: Paste output here
render: console
validations:
required: true
- type: textarea
id: logs
attributes:
label: Logs
value: |-
```console
# paste output here
```
validations:
required: true
- type: textarea
id: go-env
attributes:
label: Go environment (if applicable)
value: |-
```console
$ go version && go env
# paste output here
```
validations:
required: false
lego-4.9.1/.github/ISSUE_TEMPLATE/config.yml 0000664 0000000 0000000 00000000524 14340204635 0020170 0 ustar 00root root 0000000 0000000 blank_issues_enabled: false
contact_links:
- name: Questions
url: https://github.com/go-acme/lego/discussions
about: If you have a question, or are looking for advice, please post on our Discussions section!
- name: lego documentation
url: https://go-acme.github.io/lego/
about: Please take a look to our documentation.
lego-4.9.1/.github/ISSUE_TEMPLATE/feature_request.yml 0000664 0000000 0000000 00000001261 14340204635 0022125 0 ustar 00root root 0000000 0000000 name: Feature request
description: Suggest an idea for this project.
body:
- type: checkboxes
id: terms
attributes:
label: Welcome
options:
- label: Yes, I've searched similar issues on GitHub and didn't find any.
required: true
- type: dropdown
id: type
attributes:
label: How do you use lego?
options:
- Library
- Binary
- Docker image
- Through Traefik
- Through Caddy
- Other
validations:
required: true
- type: textarea
id: description
attributes:
label: Detailed Description
placeholder: Description.
validations:
required: true
lego-4.9.1/.github/ISSUE_TEMPLATE/new_dns_provider.yml 0000664 0000000 0000000 00000003124 14340204635 0022271 0 ustar 00root root 0000000 0000000 name: New DNS provider support
description: Request for the support of a new DNS provider.
title: "Support for provider: "
labels: [enhancement, new-provider]
body:
- type: checkboxes
id: terms
attributes:
label: Welcome
options:
- label: Yes, I've searched similar issues on GitHub and didn't find any.
required: true
- label: Yes, the DNS provider exposes a public API.
required: true
- label: Yes, I know that the lego maintainers don't have an account in all DNS providers in the world.
required: true
- label: Yes, I'm able to create a pull request and be able to maintain the implementation.
required: false
- label: Yes, I'm able to test an implementation if someone creates a pull request to add the support of this DNS provider.
required: false
- type: dropdown
id: type
attributes:
label: How do you use lego?
options:
- Library
- Binary
- Docker image
- Through Traefik
- Through Caddy
- Other
validations:
required: true
- type: input
id: provider-link
attributes:
label: Link to the DNS provider
placeholder: Put your link here.
validations:
required: true
- type: input
id: api-link
attributes:
label: Link to the API documentation
placeholder: Put your link here.
validations:
required: true
- type: textarea
id: expected
attributes:
label: Additional Notes
placeholder: Your notes.
validations:
required: false
lego-4.9.1/.github/workflows/ 0000775 0000000 0000000 00000000000 14340204635 0016051 5 ustar 00root root 0000000 0000000 lego-4.9.1/.github/workflows/documentation.yml 0000664 0000000 0000000 00000002332 14340204635 0021445 0 ustar 00root root 0000000 0000000 name: Documentation
on:
push:
branches:
- master
jobs:
doc:
name: Build and deploy documentation
runs-on: ubuntu-latest
env:
GO_VERSION: 1.19
HUGO_VERSION: 0.101.0
CGO_ENABLED: 0
steps:
# https://github.com/marketplace/actions/setup-go-environment
- name: Set up Go ${{ env.GO_VERSION }}
uses: actions/setup-go@v2
with:
go-version: ${{ env.GO_VERSION }}
# https://github.com/marketplace/actions/checkout
- name: Check out code
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Generate DNS docs
run: make generate-dns
- name: Install Hugo
run: |
wget -O /tmp/hugo.deb https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_${HUGO_VERSION}_Linux-64bit.deb
sudo dpkg -i /tmp/hugo.deb
- name: Build Documentation
run: make docs-build
# https://github.com/marketplace/actions/github-pages
- name: Deploy to GitHub Pages
uses: crazy-max/ghaction-github-pages@v2
with:
target_branch: gh-pages
build_dir: docs/public
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
lego-4.9.1/.github/workflows/go-cross.yml 0000664 0000000 0000000 00000002572 14340204635 0020336 0 ustar 00root root 0000000 0000000 name: Go Matrix
on:
push:
branches:
- master
pull_request:
jobs:
cross:
name: Go
runs-on: ${{ matrix.os }}
env:
CGO_ENABLED: 0
strategy:
matrix:
go-version: [ 1.18, 1.19, 1.x ]
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
# https://github.com/marketplace/actions/setup-go-environment
- name: Set up Go ${{ matrix.go-version }}
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go-version }}
# https://github.com/marketplace/actions/checkout
- name: Checkout code
uses: actions/checkout@v2
# https://github.com/marketplace/actions/cache
- name: Cache Go modules
uses: actions/cache@v3
with:
# In order:
# * Module download cache
# * Build cache (Linux)
# * Build cache (Mac)
# * Build cache (Windows)
path: |
~/go/pkg/mod
~/.cache/go-build
~/Library/Caches/go-build
%LocalAppData%\go-build
key: ${{ runner.os }}-${{ matrix.go-version }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-${{ matrix.go-version }}-go-
- name: Test
run: go test -v -cover ./...
- name: Build
run: go build -v -ldflags "-s -w" -trimpath -o ./dist/lego ./cmd/lego/
lego-4.9.1/.github/workflows/pr.yml 0000664 0000000 0000000 00000004521 14340204635 0017217 0 ustar 00root root 0000000 0000000 name: Main
on:
push:
branches:
- master
pull_request:
jobs:
main:
name: Main Process
runs-on: ubuntu-latest
env:
GO_VERSION: 1.19
GOLANGCI_LINT_VERSION: v1.49.0
HUGO_VERSION: 0.54.0
CGO_ENABLED: 0
LEGO_E2E_TESTS: CI
MEMCACHED_HOSTS: localhost:11211
steps:
# https://github.com/marketplace/actions/setup-go-environment
- name: Set up Go ${{ env.GO_VERSION }}
uses: actions/setup-go@v2
with:
go-version: ${{ env.GO_VERSION }}
# https://github.com/marketplace/actions/checkout
- name: Check out code
uses: actions/checkout@v2
with:
fetch-depth: 0
# https://github.com/marketplace/actions/cache
- name: Cache Go modules
uses: actions/cache@v3
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Check and get dependencies
run: |
go mod tidy
git diff --exit-code go.mod
git diff --exit-code go.sum
# https://golangci-lint.run/usage/install#other-ci
- name: Install golangci-lint ${{ env.GOLANGCI_LINT_VERSION }}
run: |
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin ${GOLANGCI_LINT_VERSION}
golangci-lint --version
- name: Install Pebble and challtestsrv
run: GO111MODULE=off go get -u github.com/letsencrypt/pebble/...
- name: Set up a Memcached server
uses: niden/actions-memcached@v7
- name: Setup /etc/hosts
run: |
echo "127.0.0.1 acme.wtf" | sudo tee -a /etc/hosts
echo "127.0.0.1 lego.wtf" | sudo tee -a /etc/hosts
echo "127.0.0.1 acme.lego.wtf" | sudo tee -a /etc/hosts
echo "127.0.0.1 légô.wtf" | sudo tee -a /etc/hosts
echo "127.0.0.1 xn--lg-bja9b.wtf" | sudo tee -a /etc/hosts
- name: Make
run: |
make
make clean
- name: Install Hugo
run: |
wget -O /tmp/hugo.deb https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_${HUGO_VERSION}_Linux-64bit.deb
sudo dpkg -i /tmp/hugo.deb
- name: Build Documentation
run: make docs-build
lego-4.9.1/.github/workflows/release.yml 0000664 0000000 0000000 00000003144 14340204635 0020216 0 ustar 00root root 0000000 0000000 name: Release
on:
push:
tags:
- v*
jobs:
release:
name: Release version
runs-on: ubuntu-latest
env:
GO_VERSION: 1.19
SEIHON_VERSION: v0.8.3
CGO_ENABLED: 0
steps:
- name: Set up Go ${{ env.GO_VERSION }}
uses: actions/setup-go@v2
with:
go-version: ${{ env.GO_VERSION }}
- name: Check out code
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Cache Go modules
uses: actions/cache@v3
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
# https://goreleaser.com/ci/actions/
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v2
with:
version: latest
args: release --rm-dist --timeout=60m
env:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN_REPO }}
# Install Docker image multi-arch builder
- name: Install Seihon ${{ env.SEIHON_VERSION }}
if: startsWith(github.ref, 'refs/tags/v')
run: |
curl -sSfL https://raw.githubusercontent.com/ldez/seihon/master/godownloader.sh | sh -s -- -b $(go env GOPATH)/bin ${SEIHON_VERSION}
seihon --version
- name: Docker Login
env:
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
run: echo "${DOCKER_PASSWORD}" | docker login --username "${DOCKER_USERNAME}" --password-stdin
- name: Publish Docker Images (Seihon)
run: make publish-images
lego-4.9.1/.gitignore 0000664 0000000 0000000 00000000057 14340204635 0014446 0 ustar 00root root 0000000 0000000 .lego
.gitcookies
.idea
.vscode/
dist/
builds/
lego-4.9.1/.golangci.toml 0000664 0000000 0000000 00000015637 14340204635 0015226 0 ustar 00root root 0000000 0000000 [run]
timeout = "7m"
skip-files = []
[linters-settings]
[linters-settings.govet]
check-shadowing = true
[linters-settings.gocyclo]
min-complexity = 12.0
[linters-settings.maligned]
suggest-new = true
[linters-settings.goconst]
min-len = 3.0
min-occurrences = 3.0
[linters-settings.funlen]
lines = -1
statements = 50
[linters-settings.misspell]
locale = "US"
ignore-words = ["internetbs"]
[linters-settings.depguard]
list-type = "denylist"
include-go-root = false
packages = ["github.com/pkg/errors"]
[linters-settings.godox]
keywords = ["FIXME"]
[linters-settings.gocritic]
enabled-tags = ["diagnostic", "style", "performance"]
disabled-checks= [
"paramTypeCombine", # already handle by gofumpt.extra-rules
"whyNoLint", # already handle by nonolint
"unnamedResult",
"hugeParam",
"sloppyReassign",
"rangeValCopy",
"octalLiteral",
"ptrToRefParam",
"appendAssign",
"ruleguard",
"httpNoBody",
"exposedSyncMutex",
]
[linters]
enable-all = true
disable = [
"deadcode", # deprecated
"exhaustivestruct", # deprecated
"golint", # deprecated
"ifshort", # deprecated
"interfacer", # deprecated
"maligned", # deprecated
"nosnakecase", # deprecated
"scopelint", # deprecated
"structcheck", # deprecated
"varcheck", # deprecated
"cyclop", # duplicate of gocyclo
"sqlclosecheck", # not relevant (SQL)
"rowserrcheck", # not relevant (SQL)
"execinquery", # not relevant (SQL)
"lll",
"gosec",
"dupl", # not relevant
"prealloc", # too many false-positive
"bodyclose", # too many false-positive
"gomnd",
"testpackage", # not relevant
"tparallel", # not relevant
"paralleltest", # not relevant
"nestif", # too many false-positive
"wrapcheck",
"goerr113", # not relevant
"nlreturn", # not relevant
"wsl", # not relevant
"exhaustive", # not relevant
"exhaustruct", # not relevant
"makezero", # not relevant
"forbidigo", # not relevant
"varnamelen", # not relevant
"nilnil", # not relevant
"ireturn", # not relevant
"contextcheck", # too many false-positive
"tenv", # we already have a test "framework" to handle env vars
"noctx",
"forcetypeassert",
"tagliatelle",
"errname",
"errchkjson",
"nonamedreturns",
]
[issues]
exclude-use-default = false
max-per-linter = 0
max-same-issues = 0
exclude = [
"Error return value of .((os\\.)?std(out|err)\\..*|.*Close|.*Flush|os\\.Remove(All)?|.*printf?|os\\.(Un)?Setenv). is not checked",
"exported (type|method|function) (.+) should have comment or be unexported",
"ST1000: at least one file in a package should have a package comment",
"package-comments: should have a package comment",
]
[[issues.exclude-rules]]
path = "(.+)_test.go"
linters = ["funlen", "goconst", "maintidx"]
[[issues.exclude-rules]]
path = "providers/dns/dns_providers.go"
linters = ["gocyclo"]
[[issues.exclude-rules]]
path = "providers/dns/gcloud/googlecloud_test.go"
text = "string `(lego\\.wtf|manhattan)` has (\\d+) occurrences, make it a constant"
[[issues.exclude-rules]]
path = "providers/dns/zoneee/zoneee_test.go"
text = "string `(bar|foo)` has (\\d+) occurrences, make it a constant"
[[issues.exclude-rules]]
path = "certcrypto/crypto.go"
text = "(tlsFeatureExtensionOID|ocspMustStapleFeature) is a global variable"
[[issues.exclude-rules]]
path = "challenge/dns01/nameserver.go"
text = "(defaultNameservers|recursiveNameservers|fqdnSoaCache|muFqdnSoaCache) is a global variable"
[[issues.exclude-rules]]
path = "challenge/dns01/nameserver_.+.go"
text = "dnsTimeout is a global variable"
[[issues.exclude-rules]]
path = "challenge/dns01/nameserver_test.go"
text = "findXByFqdnTestCases is a global variable"
[[issues.exclude-rules]]
path = "challenge/http01/domain_matcher.go"
text = "string `Host` has \\d occurrences, make it a constant"
[[issues.exclude-rules]]
path = "challenge/http01/domain_matcher.go"
text = "cyclomatic complexity \\d+ of func `parseForwardedHeader` is high"
[[issues.exclude-rules]]
path = "challenge/http01/domain_matcher.go"
text = "Function 'parseForwardedHeader' has too many statements"
[[issues.exclude-rules]]
path = "challenge/tlsalpn01/tls_alpn_challenge.go"
text = "idPeAcmeIdentifierV1 is a global variable"
[[issues.exclude-rules]]
path = "log/logger.go"
text = "Logger is a global variable"
[[issues.exclude-rules]]
path = "e2e/(dnschallenge/)?[\\d\\w]+_test.go"
text = "load is a global variable"
[[issues.exclude-rules]]
path = "providers/dns/([\\d\\w]+/)*[\\d\\w]+_test.go"
text = "envTest is a global variable"
[[issues.exclude-rules]]
path = "providers/dns/namecheap/namecheap_test.go"
text = "testCases is a global variable"
[[issues.exclude-rules]]
path = "providers/dns/acmedns/acmedns_test.go"
text = "egTestAccount is a global variable"
[[issues.exclude-rules]]
path = "providers/http/memcached/memcached_test.go"
text = "memcachedHosts is a global variable"
[[issues.exclude-rules]]
path = "providers/dns/sakuracloud/client_test.go"
text = "cyclomatic complexity 13 of func `(TestDNSProvider_cleanupTXTRecord_concurrent|TestDNSProvider_addTXTRecord_concurrent)` is high"
[[issues.exclude-rules]]
path = "providers/dns/dns_providers.go"
text = "Function 'NewDNSChallengeProviderByName' has too many statements"
[[issues.exclude-rules]]
path = "cmd/flags.go"
text = "Function 'CreateFlags' is too long"
[[issues.exclude-rules]]
path = "certificate/certificates.go"
text = "Function 'GetOCSP' is too long"
[[issues.exclude-rules]]
path = "providers/dns/otc/client.go"
text = "Function 'loginRequest' is too long"
[[issues.exclude-rules]]
path = "providers/dns/gandi/gandi.go"
text = "Function 'Present' is too long"
[[issues.exclude-rules]]
path = "cmd/zz_gen_cmd_dnshelp.go"
linters = ["gocyclo", "funlen"]
[[issues.exclude-rules]]
path = "providers/dns/checkdomain/client.go"
text = "`payed` is a misspelling of `paid`"
[[issues.exclude-rules]]
path = "providers/dns/namecheap/namecheap_test.go"
text = "cognitive complexity (\\d+) of func `TestDNSProvider_getHosts` is high"
[[issues.exclude-rules]]
path = "platform/tester/env_test.go"
linters = ["thelper"]
[[issues.exclude-rules]]
path = "providers/dns/oraclecloud/oraclecloud_test.go"
text = "SA1019: x509.EncryptPEMBlock has been deprecated since Go 1.16"
[[issues.exclude-rules]]
path = "challenge/http01/domain_matcher.go"
text = "yodaStyleExpr"
[[issues.exclude-rules]]
path = "providers/dns/dns_providers.go"
text = "Function name: NewDNSChallengeProviderByName,"
[[issues.exclude-rules]]
path = "providers/dns/sakuracloud/client.go"
text = "mu is a global variable"
lego-4.9.1/.goreleaser.yml 0000664 0000000 0000000 00000001611 14340204635 0015404 0 ustar 00root root 0000000 0000000 project_name: lego
builds:
- binary: lego
main: ./cmd/lego/main.go
env:
- CGO_ENABLED=0
flags:
- -trimpath
ldflags:
- -s -w -X main.version={{.Version}}
goos:
- windows
- darwin
- linux
- freebsd
- openbsd
- solaris
goarch:
- amd64
- 386
- arm
- arm64
- mips
- mipsle
- mips64
- mips64le
goarm:
- 7
- 6
- 5
gomips:
- hardfloat
- softfloat
ignore:
- goos: darwin
goarch: 386
- goos: openbsd
goarch: arm
archives:
- id: lego
name_template: '{{ .ProjectName }}_v{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}{{ if .Mips }}_{{ .Mips }}{{ end }}'
format: tar.gz
format_overrides:
- goos: windows
format: zip
files:
- LICENSE
- CHANGELOG.md
lego-4.9.1/CHANGELOG.md 0000664 0000000 0000000 00000102253 14340204635 0014270 0 ustar 00root root 0000000 0000000 # Changelog
## [v4.9.1] - 2022-11-25
### Changed:
-
- **[lib,cname]** cname: add log about CNAME entries
- **[dnsprovider]** regru: improve error handling
### Fixed:
-
- **[dnsprovider,cname]** fix CNAME support for multiple DNS providers
- **[dnsprovider,cname]** duckdns: fix CNAME support
- **[dnsprovider,cname]** oraclecloud: use fqdn to resolve zone
- **[dnsprovider]** hurricane: fix CNAME support
- **[lib,cname]** cname: stop trying to traverse cname if none have been found
## [v4.9.0] - 2022-10-03
### Added:
- **[dnsprovider]** Add DNS provider for CIVO
- **[dnsprovider]** Add DNS provider for VK Cloud
- **[dnsprovider]** Add DNS provider for YandexCloud
- **[dnsprovider]** digitalocean: configurable base URL
- **[dnsprovider]** loopia: add configurable API endpoint
- **[dnsprovider]** pdns: notify secondary servers after updates
### Changed:
- **[dnsprovider]** allinkl: removed deprecated sha1 hashing
- **[dnsprovider]** auroradns: update authentification
- **[dnsprovider]** dnspod: deprecated. Use Tencent Cloud instead.
- **[dnsprovider]** exoscale: migrate to API v2 endpoints
- **[dnsprovider]** gcloud: update golang.org/x/oauth2
- **[dnsprovider]** lightsail: cleanup
- **[dnsprovider]** sakuracloud: update api client library
- **[cname]** take out CNAME support from experimental features
- **[lib,cname]** add recursive CNAME lookup support
- **[lib]** Remove embedded issuer certificates from issued certificate if bundle is false
### Fixed:
- **[dnsprovider]** luadns: fix cname support
- **[dnsprovider]** njalla: fix record id unmarshal error
- **[dnsprovider]** tencentcloud: fix subdomain error
## [v4.8.0] - 2022-06-30
### Added
- **[dnsprovider]** Add DNS provider for Variomedia
- **[dnsprovider]** Add NearlyFreeSpeech DNS Provider
- **[cli]** Add a --user-agent flag to lego-cli
### Changed
- new logo
- **[cli]** feat: sleep at renewal
- **[cli]** cli/renew: skip random sleep if stdout is a terminal
- **[dnsprovider]** hetzner: set min TTL to 60s
- **[docs]** refactoring and cleanup
## [v4.7.0] - 2022-05-27
### Added:
- **[dnsprovider]** Add DNS provider for iwantmyname
- **[dnsprovider]** Add DNS Provider for IIJ DNS Platform Service
- **[dnsprovider]** Add DNS provider for Vercel
- **[dnsprovider]** route53: add assume role ARN
- **[dnsprovider]** dnsimple: add debug option
- **[cli]** feat: add `LEGO_CERT_PEM_PATH` and `LEGO_CERT_PFX_PATH`
### Changed:
- **[dnsprovider]** gcore: change dns api url
- **[dnsprovider]** bluecat: rewrite provider implementation
### Fixed:
- **[dnsprovider]** rfc2136: fix TSIG secret
- **[dnsprovider]** tencentcloud: fix InvalidParameter.DomainInvalid error when using DNS challenges
- **[lib]** fix: panic in certcrypto.ParsePEMPrivateKey
## [v4.6.0] - 2022-01-18
### Added
- **[dnsprovider]** Add DNS provider for UKFast SafeDNS
- **[dnsprovider]** Add DNS Provider for Tencent Cloud
- **[dnsprovider]** azure: add support for Azure Private Zone DNS
- **[dnsprovider]** exec: add sequence interval
- **[cli]** Add a `--pfx`, and `--pfx.pas`s option to generate a PKCS#12 (`.pfx`) file.
- **[lib]** Extended support of cert pool (`LEGO_CA_CERTIFICATES` and `LEGO_CA_SYSTEM_CERT_POOL`)
- **[lib,httpprovider]** added uds capability to http challenge server
### Changed
- **[lib]** Extend validity of TLS-ALPN-01 certificates to 365 days
- **[lib,cli]** Allows defining the reason for the certificate revocation
### Fixed
- **[dnsprovider]** mythicbeasts: fix token expiration
- **[dnsprovider]** rackspace: change zone ID to string
## [v4.5.3] - 2021-09-06
### Fixed:
- **[lib,cli]** fix: missing preferred chain param for renew request
## [v4.5.2] - 2021-09-01
### Added:
- **[dnsprovider]** Add DNS provider for all-inkl
- **[dnsprovider]** Add DNS provider for Epik
- **[dnsprovider]** Add DNS provider for freemyip.com
- **[dnsprovider]** Add DNS provider for g-core labs
- **[dnsprovider]** Add DNS provider for hosttech
- **[dnsprovider]** Add DNS Provider for IBM Cloud (SoftLayer)
- **[dnsprovider]** Add DNS provider for Internet.bs
- **[dnsprovider]** Add DNS provider for nicmanager
### Changed:
- **[dnsprovider]** alidns: support ECS instance RAM role
- **[dnsprovider]** alidns: support sts token credential
- **[dnsprovider]** azure: zone name as environment variable
- **[dnsprovider]** ovh: follow cname
- **[lib,cli]** Add AlwaysDeactivateAuthorizations flag to ObtainRequest
### Fixed:
- **[dnsprovider]** infomaniak: fix subzone support
- **[dnsprovider]** edgedns: fix Present and CleanUp logic
- **[dnsprovider]** lightsail: wrong Region env var name
- **[lib]** lib: fix backoff in SolverManager
- **[lib]** lib: use permanent error instead of context cancellation
- **[dnsprovider]** desec: bump to v0.6.0
## [v4.5.1] - 2021-09-01
Cancelled due to a CI issue, replaced by v4.5.2.
## [v4.5.0] - 2021-09-30
Cancelled due to a CI issue, replaced by v4.5.2.
## [v4.4.0] - 2021-06-08
### Added:
- **[dnsprovider]** Add DNS provider for Infoblox
- **[dnsprovider]** Add DNS provider for Porkbun
- **[dnsprovider]** Add DNS provider for Simply.com
- **[dnsprovider]** Add DNS provider for Sonic
- **[dnsprovider]** Add DNS provider for VinylDNS
- **[dnsprovider]** Add DNS provider for wedos
### Changed:
- **[cli]** log: Use stderr instead of stdout.
- **[dnsprovider]** hostingde: autodetection of the zone name.
- **[dnsprovider]** scaleway: use official SDK
- **[dnsprovider]** powerdns: several improvements
- **[lib]** lib: improve wait.For returns.
### Fixed:
- **[dnsprovider]** hurricane: add API rate limiter.
- **[dnsprovider]** hurricane: only treat first word of response body as response code
- **[dnsprovider]** exoscale: fix DNS provider debugging
- **[dnsprovider]** wedos: fix api call parameters
- **[dnsprovider]** nifcloud: Get zone info from dns01.FindZoneByFqdn
- **[cli,lib]** csr: Support the type `NEW CERTIFICATE REQUEST`
## [v4.3.1] - 2021-03-12
### Fixed:
- **[dnsprovider]** exoscale: fix dependency version.
## [v4.3.0] - 2021-03-10
### Added:
- **[dnsprovider]** Add DNS provider for Njalla
- **[dnsprovider]** Add DNS provider for Domeneshop
- **[dnsprovider]** Add DNS provider for Hurricane Electric
- **[dnsprovider]** designate: support for Openstack Application Credentials
- **[dnsprovider]** edgedns: support for .edgerc file
### Changed:
- **[dnsprovider]** infomaniak: Make error message more meaningful
- **[dnsprovider]** cloudns: Improve reliability
- **[dnsprovider]** rfc2163: Removed support for MD5 algorithm. The default algorithm is now SHA1.
### Fixed:
- **[dnsprovider]** desec: fix error with default TTL
- **[dnsprovider]** mythicbeasts: implement `ProviderTimeout`
- **[dnsprovider]** dnspod: improve search accuracy when a domain have more than 100 records
- **[lib]** Increase HTTP client timeouts
- **[lib]** preferred chain only match root name
## [v4.2.0] - 2021-01-24
### Added:
- **[dnsprovider]** Add DNS provider for Loopia
- **[dnsprovider]** Add DNS provider for Ionos.
### Changed:
- **[dnsprovider]** acme-dns: update cpu/goacmedns to v0.1.1.
- **[dnsprovider]** inwx: Increase propagation timeout to 360s to improve robustness
- **[dnsprovider]** vultr: Update to govultr v2 API
- **[dnsprovider]** pdns: get exact zone instead of all zones
### Fixed:
- **[dnsprovider]** vult, dnspod: fix default HTTP timeout.
- **[dnsprovider]** pdns: URL request creation.
- **[lib]** errors: Fix instance not being printed
## [v4.1.3] - 2020-11-25
### Fixed:
- **[dnsprovider]** azure: fix error handling.
## [v4.1.2] - 2020-11-21
### Fixed:
- **[lib]** fix: preferred chain support.
## [v4.1.1] - 2020-11-19
### Fixed:
- **[dnsprovider]** otc: select correct zone if multiple returned
- **[dnsprovider]** azure: fix target must be a non-nil pointer
## [v4.1.0] - 2020-11-06
### Added:
- **[dnsprovider]** Add DNS provider for Infomaniak
- **[dnsprovider]** joker: add support for SVC API
- **[dnsprovider]** gcloud: add an option to allow the use of private zones
### Changed:
- **[dnsprovider]** rfc2136: ensure TSIG algorithm is fully qualified
- **[dnsprovider]** designate: Deprecate OS_TENANT_NAME as required field
### Fixed:
- **[lib]** acme/api: use postAsGet instead of post for AccountService.Get
- **[lib]** fix: use http.Header.Set method instead of Add.
## [v4.0.1] - 2020-09-03
### Fixed:
- **[dnsprovider]** exoscale: change dependency version.
## [v4.0.0] - 2020-09-02
### Added:
- **[cli], [lib]** Support "alternate" certificate links for selecting different signing Chains
### Changed:
- **[cli]** Replaces `ec384` by `ec256` as default key-type
- **[lib]** Changes `ObtainForCSR` method signature
### Removed:
- **[dnsprovider]** Replaces FastDNS by EdgeDNS
- **[dnsprovider]** Removes old Linode provider
- **[lib]** Removes `AddPreCheck` function
## [v3.9.0] - 2020-09-01
### Added:
- **[dnsprovider]** Add Akamai Edgedns. Deprecate FastDNS
- **[dnsprovider]** Add DNS provider for HyperOne
### Changed:
- **[dnsprovider]** designate: add support for Openstack clouds.yaml
- **[dnsprovider]** azure: allow selecting environments
- **[dnsprovider]** desec: applies API rate limits.
### Fixed:
- **[dnsprovider]** namesilo: fix cleanup.
## [v3.8.0] - 2020-07-02
### Added:
- **[cli]** cli: add hook on the run command.
- **[dnsprovider]** inwx: Two-Factor-Authentication
- **[dnsprovider]** Add DNS provider for ArvanCloud
### Changed:
- **[dnsprovider]** vultr: bumping govultr version
- **[dnsprovider]** desec: improve error logs.
- **[lib]** Ensures the return of a location during account updates
- **[dnsprovider]** route53: Document all AWS credential environment variables
### Fixed:
- **[dnsprovider]** stackpath: fix subdomain support.
- **[dnsprovider]** arvandcloud: fix record name.
- **[dnsprovider]** fix: multi-va.
- **[dnsprovider]** constellix: fix search records API call.
- **[dnsprovider]** hetzner: fix record name.
- **[lib]** Registrar.ResolveAccountByKey: Fix malformed request
## [v3.7.0] - 2020-05-11
### Added:
- **[dnsprovider]** Add DNS provider for Netlify.
- **[dnsprovider]** Add DNS provider for deSEC.io
- **[dnsprovider]** Add DNS provider for LuaDNS
- **[dnsprovider]** Adding Hetzner DNS provider
- **[dnsprovider]** Add DNS provider for Mythic beasts DNSv2
- **[dnsprovider]** Add DNS provider for Yandex.
### Changed:
- **[dnsprovider]** Upgrade DNSimple client to 0.60.0
- **[dnsprovider]** update aws sdk
### Fixed:
- **[dnsprovider]** autodns: removes TXT records during CleanUp.
- **[dnsprovider]** Fix exoscale HTTP timeout
- **[cli]** fix: renew path information.
- **[cli]** Fix account storage location warning message
## [v3.6.0] - 2020-04-24
### Added:
- **[dnsprovider]** Add DNS provider for CloudDNS.
- **[dnsprovider]** alicloud: add support for domain with punycode
- **[dnsprovider]** cloudns: Add subuser support
- **[cli]** Information about renewed certificates are now passed to the renew hook
### Changed:
- **[dnsprovider]** acmedns: Update cpu/goacmedns v0.0.1 -> v0.0.2
- **[dnsprovider]** alicloud: update sdk dependency version to v1.61.112
- **[dnsprovider]** azure: Allow for the use of MSI
- **[dnsprovider]** constellix: improve challenge.
- **[dnsprovider]** godaddy: allow parallel solve.
- **[dnsprovider]** namedotcom: get the actual registered domain so we can remove just that from the hostname to be created
- **[dnsprovider]** transip: updated the client to v6
### Fixed:
- **[dnsprovider]** ns1: fix missing domain in log
- **[dnsprovider]** rimuhosting: use HTTP client from config.
## [v3.5.0] - 2020-03-15
### Added:
- **[dnsprovider]** Add DNS provider for Dynu.
- **[dnsprovider]** Add DNS provider for reg.ru
- **[dnsprovider]** Add DNS provider for Zonomi and RimuHosting.
- **[cli]** Building binaries for arm 6 and 5
- **[cli]** Uses CGO_ENABLED=0
- **[cli]** Multi-arch Docker image.
- **[cli]** Adds `--name` flag to list command.
### Changed:
- **[lib]** lib: Improve cleanup log messages.
- **[lib]** Wrap errors.
### Fixed:
- **[dnsprovider]** azure: pass AZURE_CLIENT_SECRET_FILE to autorest.Authorizer
- **[dnsprovider]** gcloud: fixes issues when used with GKE Workload Identity
- **[dnsprovider]** oraclecloud: fix subdomain support
## [v3.4.0] - 2020-02-25
### Added:
- **[dnsprovider]** Add DNS provider for Constellix
- **[dnsprovider]** Add DNS provider for Servercow.
- **[dnsprovider]** Add DNS provider for Scaleway
- **[cli]** Add "LEGO_PATH" environment variable
### Changed:
- **[dnsprovider]** route53: allow custom client to be provided
- **[dnsprovider]** namecheap: allow external domains
- **[dnsprovider]** namecheap: add sandbox support.
- **[dnsprovider]** ovh: Improve provider documentation
- **[dnsprovider]** route53: Improve provider documentation
### Fixed:
- **[dnsprovider]** zoneee: fix subdomains.
- **[dnsprovider]** designate: Don't clean up managed records like SOA and NS
- **[dnsprovider]** dnspod: update lib.
- **[lib]** crypto: Treat CommonName as optional
- **[lib]** chore: update cenkalti/backoff to v4.
## [v3.3.0] - 2020-01-08
### Added:
- **[dnsprovider]** Add DNS provider for Checkdomain
- **[lib]** Add support to update account
### Changed:
- **[dnsprovider]** gcloud: Auto-detection of the project ID.
- **[lib]** Successfully parse private key PEM blocks
### Fixed:
- **[dnsprovider]** Update dnspod, because of API breaking changes.
## [v3.2.0] - 2019-11-10
### Added:
- **[dnsprovider]** Add support for autodns
### Changed:
- **[dnsprovider]** httpreq: Allow use environment vars from a `_FILE` file
- **[lib]** Don't deactivate valid authorizations
- **[lib]** Expose more SOA fields found by dns01.FindZoneByFqdn
### Fixed:
- **[dnsprovider]** use token as unique ID.
## [v3.1.0] - 2019-10-07
### Added:
- **[dnsprovider]** Add DNS provider for Liquid Web
- **[dnsprovider]** cloudflare: add support for API tokens
- **[cli]** feat: ease operation behind proxy servers
### Changed:
- **[dnsprovider]** cloudflare: update client
- **[dnsprovider]** linodev4: propagation timeout configuration.
### Fixed:
- **[dnsprovider]** ovh: fix int overflow.
- **[dnsprovider]** bindman: fix client version.
## [v3.0.2] - 2019-08-15
### Fixed:
- Invalid pseudo version (related to Cloudflare client).
## [v3.0.1] - 2019-08-14
There was a problem when creating the tag v3.0.1, this tag has been invalidate.
## [v3.0.0] - 2019-08-05
### Changed:
- migrate to go module (new import github.com/go-acme/lego/v3/)
- update DNS clients
## [v2.7.2] - 2019-07-30
### Fixed:
- **[dnsprovider]** vultr: quote TXT record
## [v2.7.1] - 2019-07-22
### Fixed:
- **[dnsprovider]** vultr: invalid record type.
## [v2.7.0] - 2019-07-17
### Added:
- **[dnsprovider]** Add DNS provider for namesilo
- **[dnsprovider]** Add DNS provider for versio.nl
### Changed:
- **[dnsprovider]** Update DNS providers libs.
- **[dnsprovider]** joker: support username and password.
- **[dnsprovider]** Vultr: Switch to official client
### Fixed:
- **[dnsprovider]** otc: Prevent sending empty body.
## [v2.6.0] - 2019-05-27
### Added:
- **[dnsprovider]** Add support for Joker.com DMAPI
- **[dnsprovider]** Add support for Bindman DNS provider
- **[dnsprovider]** Add support for EasyDNS
- **[lib]** Get an existing certificate by URL
### Changed:
- **[dnsprovider]** digitalocean: LEGO_EXPERIMENTAL_CNAME_SUPPORT support
- **[dnsprovider]** gcloud: Use fqdn to get zone Present/CleanUp
- **[dnsprovider]** exec: serial behavior
- **[dnsprovider]** manual: serial behavior.
- **[dnsprovider]** Strip newlines when reading environment variables from `_FILE` suffixed files.
### Fixed:
- **[cli]** fix: cli disable-cp option.
- **[dnsprovider]** gcloud: fix zone visibility.
## [v2.5.0] - 2019-04-17
### Added:
- **[cli]** Adds renew hook
- **[dnsprovider]** Adds 'Since' to DNS providers documentation
### Changed:
- **[dnsprovider]** gcloud: use public DNS zones
- **[dnsprovider]** route53: enhance documentation.
### Fixed:
- **[dnsprovider]** cloudns: fix TTL and status validation
- **[dnsprovider]** sakuracloud: supports concurrent update
- **[dnsprovider]** Disable authz when solve fail.
- Add tzdata to the Docker image.
## [v2.4.0] - 2019-03-25
- Migrate from xenolf/lego to go-acme/lego.
### Added:
- **[dnsprovider]** Add DNS Provider for Domain Offensive (do.de)
- **[dnsprovider]** Adds information about '_FILE' suffix.
### Fixed:
- **[cli,dnsprovider]** Add 'manual' provider to the output of dnshelp
- **[dnsprovider]** hostingde: Use provided ZoneName instead of domain
- **[dnsprovider]** pdns: fix wildcard with SANs
## [v2.3.0] - 2019-03-11
### Added:
- **[dnsprovider]** Add DNS Provider for ClouDNS.net
- **[dnsprovider]** Add DNS Provider for Oracle Cloud
### Changed:
- **[cli]** Adds log when no renewal.
- **[dnsprovider,lib]** Add a mechanism to wrap a PreCheckFunc
- **[dnsprovider]** oraclecloud: better way to get private key.
- **[dnsprovider]** exoscale: update library
### Fixed:
- **[dnsprovider]** OVH: Refresh zone after deleting challenge record
- **[dnsprovider]** oraclecloud: ttl config and timeout
- **[dnsprovider]** hostingde: fix client fails if customer has no access to dns-groups
- **[dnsprovider]** vscale: getting sub-domain
- **[dnsprovider]** selectel: getting sub-domain
- **[dnsprovider]** vscale: fix TXT records clean up
- **[dnsprovider]** selectel: fix TXT records clean up
## [v2.2.0] - 2019-02-08
### Added:
- **[dnsprovider]** Add support for Openstack Designate as a DNS provider
- **[dnsprovider]** gcloud: Option to specify gcloud service account json by env as string
- **[experimental feature]** Resolve CNAME when creating dns-01 challenge. To enable: set `LEGO_EXPERIMENTAL_CNAME_SUPPORT` to `true`.
### Changed:
- **[cli]** Applies Let’s Encrypt’s recommendation about renew. The option `--days` of the command `renew` has a new default value (`30`)
- **[lib]** Uses a jittered exponential backoff
### Fixed:
- **[cli]** CLI and key type.
- **[dnsprovider]** httpreq: Endpoint with path.
- **[dnsprovider]** fastdns: Do not overwrite existing TXT records
- Log wildcard domain correctly in validation
## [v2.1.0] - 2019-01-24
### Added:
- **[dnsprovider]** Add support for zone.ee as a DNS provider.
### Changed:
- **[dnsprovider]** nifcloud: Change DNS base url.
- **[dnsprovider]** gcloud: More detailed information about Google Cloud DNS.
### Fixed:
- **[lib]** fix: OCSP, set HTTP client.
- **[dnsprovider]** alicloud: fix pagination.
- **[dnsprovider]** namecheap: fix panic.
## [v2.0.0] - 2019-01-09
### Added:
- **[cli,lib]** Option to disable the complete propagation Requirement
- **[lib,cli]** Support non-ascii domain name (punnycode)
- **[cli,lib]** Add configurable timeout when obtaining certificates
- **[cli]** Archive revoked certificates
- **[cli]** Add command to list certificates.
- **[cli]** support for renew with CSR
- **[cli]** add SAN on renew
- **[lib]** Adds `Remove` for challenges
- **[lib]** Add version to xenolf-acme in User-Agent.
- **[dnsprovider]** The ability for a DNS provider to solve the challenge sequentially
- **[dnsprovider]** Add DNS provider for "HTTP request".
- **[dnsprovider]** Add DNS Provider for Vscale
- **[dnsprovider]** Add DNS Provider for TransIP
- **[dnsprovider]** Add DNS Provider for inwx
- **[dnsprovider]** alidns: add support to handle more than 20 domains
### Changed:
- **[lib]** Check all challenges in a predictable order
- **[lib]** Poll authz URL instead of challenge URL
- **[lib]** Check all nameservers in a predictable order
- **[lib]** Logs every iteration of waiting for the propagation
- **[cli]** `--http`: enable HTTP challenge **important**
- **[cli]** `--http.port`: previously named `--http`
- **[cli]** `--http.webroot`: previously named `--webroot`
- **[cli]** `--http.memcached-host`: previously named `--memcached-host`
- **[cli]** `--tls`: enable TLS challenge **important**
- **[cli]** `--tls.port`: previously named `--tls`
- **[cli]** `--dns.resolvers`: previously named `--dns-resolvers`
- **[cli]** the option `--days` of the command `renew` has default value (`15`)
- **[dnsprovider]** gcloud: Use GCE_PROJECT for project always, if specified
### Removed:
- **[lib]** Remove `SetHTTP01Address`
- **[lib]** Remove `SetTLSALPN01Address`
- **[lib]** Remove `Exclude`
- **[cli]** Remove `--exclude`, `-x`
### Fixed:
- **[lib]** Fixes revocation for subdomains and non-ascii domains
- **[lib]** Disable pending authorizations
- **[dnsprovider]** transip: concurrent access to the API.
- **[dnsprovider]** gcloud: fix for wildcard
- **[dnsprovider]** Azure: Do not overwrite existing TXT records
- **[dnsprovider]** fix: Cloudflare error.
## [v1.2.0] - 2018-11-04
### Added:
- **[dnsprovider]** Add DNS Provider for ConoHa DNS
- **[dnsprovider]** Add DNS Provider for MyDNS.jp
- **[dnsprovider]** Add DNS Provider for Selectel
### Fixed:
- **[dnsprovider]** netcup: make unmarshalling of api-responses more lenient.
### Changed:
- **[dnsprovider]** aurora: change DNS client
- **[dnsprovider]** azure: update auth to support instance metadata service
- **[dnsprovider]** dnsmadeeasy: log response body on error
- **[lib]** TLS-ALPN-01: Update idPeAcmeIdentifierV1, draft refs.
- **[lib]** Do not send a JWS body when POSTing challenges.
- **[lib]** Support POST-as-GET.
## [v1.1.0] - 2018-10-16
### Added:
- **[lib]** TLS-ALPN-01 Challenge
- **[cli]** Add filename parameter
- **[dnsprovider]** Allow to configure TTL, interval and timeout
- **[dnsprovider]** Add support for reading DNS provider setup from files
- **[dnsprovider]** Add DNS Provider for ACME-DNS
- **[dnsprovider]** Add DNS Provider for ALIYUN DNS
- **[dnsprovider]** Add DNS Provider for DreamHost
- **[dnsprovider]** Add DNS provider for hosting.de
- **[dnsprovider]** Add DNS Provider for IIJ
- **[dnsprovider]** Add DNS Provider for netcup
- **[dnsprovider]** Add DNS Provider for NIFCLOUD DNS
- **[dnsprovider]** Add DNS Provider for SAKURA Cloud
- **[dnsprovider]** Add DNS Provider for Stackpath
- **[dnsprovider]** Add DNS Provider for VegaDNS
- **[dnsprovider]** exec: add EXEC_MODE=RAW support.
- **[dnsprovider]** cloudflare: support for CF_API_KEY and CF_API_EMAIL
### Fixed:
- **[lib]** Don't trust identifiers order.
- **[lib]** Fix missing issuer certificates from Let's Encrypt
- **[dnsprovider]** duckdns: fix TXT record update url
- **[dnsprovider]** duckdns: fix subsubdomain
- **[dnsprovider]** gcloud: update findTxtRecords to use Name=fqdn and Type=TXT
- **[dnsprovider]** lightsail: Fix Domain does not exist error
- **[dnsprovider]** ns1: use the authoritative zone and not the domain name
- **[dnsprovider]** ovh: check error to avoid panic due to nil client
### Changed:
- **[lib]** Submit all dns records up front, then validate serially
## [v1.0.0] - 2018-05-30
### Changed:
- **[lib]** ACME v2 Support.
- **[dnsprovider]** Renamed `/providers/dns/googlecloud` to `/providers/dns/gcloud`.
- **[dnsprovider]** Modified Google Cloud provider `gcloud.NewDNSProviderServiceAccount` function to extract the project id directly from the service account file.
- **[dnsprovider]** Made errors more verbose for the Cloudflare provider.
## [v0.5.0] - 2018-05-29
### Added:
- **[dnsprovider]** Add DNS challenge provider `exec`
- **[dnsprovider]** Add DNS Provider for Akamai FastDNS
- **[dnsprovider]** Add DNS Provider for Bluecat DNS
- **[dnsprovider]** Add DNS Provider for CloudXNS
- **[dnsprovider]** Add DNS Provider for Duck DNS
- **[dnsprovider]** Add DNS Provider for Gandi Beta Platform (LiveDNS)
- **[dnsprovider]** Add DNS Provider for GleSYS API
- **[dnsprovider]** Add DNS Provider for GoDaddy
- **[dnsprovider]** Add DNS Provider for Lightsail
- **[dnsprovider]** Add DNS Provider for Name.com
### Fixed:
- **[dnsprovider]** Azure: Added missing environment variable in the comments
- **[dnsprovider]** PowerDNS: Fix zone URL, add leading slash.
- **[dnsprovider]** DNSimple: Fix api
- **[cli]** Correct help text for `--dns-resolvers` default.
- **[cli]** renew/revoke - don't panic on wrong account.
- **[lib]** Fix zone detection for cross-zone cnames.
- **[lib]** Use proxies from environment when making outbound http connections.
### Changed:
- **[lib]** Users of an effective top-level domain can use the DNS challenge.
- **[dnsprovider]** Azure: Refactor to work with new Azure SDK version.
- **[dnsprovider]** Cloudflare and Azure: Adding output of which envvars are missing.
- **[dnsprovider]** Dyn DNS: Slightly improve provider error reporting.
- **[dnsprovider]** Exoscale: update to latest egoscale version.
- **[dnsprovider]** Route53: Use NewSessionWithOptions instead of deprecated New.
## [0.4.1] - 2017-09-26
### Added:
- lib: A new DNS provider for OTC.
- lib: The `AWS_HOSTED_ZONE_ID` environment variable for the Route53 DNS provider to directly specify the zone.
- lib: The `RFC2136_TIMEOUT` enviroment variable to make the timeout for the RFC2136 provider configurable.
- lib: The `GCE_SERVICE_ACCOUNT_FILE` environment variable to specify a service account file for the Google Cloud DNS provider.
### Fixed:
- lib: Fixed an authentication issue with the latest Azure SDK.
## [0.4.0] - 2017-07-13
### Added:
- CLI: The `--http-timeout` switch. This allows for an override of the default client HTTP timeout.
- lib: The `HTTPClient` field. This allows for an override of the default HTTP timeout for library HTTP requests.
- CLI: The `--dns-timeout` switch. This allows for an override of the default DNS timeout for library DNS requests.
- lib: The `DNSTimeout` switch. This allows for an override of the default client DNS timeout.
- lib: The `QueryRegistration` function on `acme.Client`. This performs a POST on the client registration's URI and gets the updated registration info.
- lib: The `DeleteRegistration` function on `acme.Client`. This deletes the registration as currently configured in the client.
- lib: The `ObtainCertificateForCSR` function on `acme.Client`. The function allows to request a certificate for an already existing CSR.
- CLI: The `--csr` switch. Allows to use already existing CSRs for certificate requests on the command line.
- CLI: The `--pem` flag. This will change the certificate output so it outputs a .pem file concatanating the .key and .crt files together.
- CLI: The `--dns-resolvers` flag. Allows for users to override the default DNS servers used for recursive lookup.
- lib: Added a memcached provider for the HTTP challenge.
- CLI: The `--memcached-host` flag. This allows to use memcached for challenge storage.
- CLI: The `--must-staple` flag. This enables OCSP must staple in the generated CSR.
- lib: The library will now honor entries in your resolv.conf.
- lib: Added a field `IssuerCertificate` to the `CertificateResource` struct.
- lib: A new DNS provider for OVH.
- lib: A new DNS provider for DNSMadeEasy.
- lib: A new DNS provider for Linode.
- lib: A new DNS provider for AuroraDNS.
- lib: A new DNS provider for NS1.
- lib: A new DNS provider for Azure DNS.
- lib: A new DNS provider for Rackspace DNS.
- lib: A new DNS provider for Exoscale DNS.
- lib: A new DNS provider for DNSPod.
### Changed:
- lib: Exported the `PreCheckDNS` field so library users can manage the DNS check in tests.
- lib: The library will now skip challenge solving if a valid Authz already exists.
### Removed:
- lib: The library will no longer check for auto renewed certificates. This has been removed from the spec and is not supported in Boulder.
### Fixed:
- lib: Fix a problem with the Route53 provider where it was possible the verification was published to a private zone.
- lib: Loading an account from file should fail if a integral part is nil
- lib: Fix a potential issue where the Dyn provider could resolve to an incorrect zone.
- lib: If a registration encounteres a conflict, the old registration is now recovered.
- CLI: The account.json file no longer has the executable flag set.
- lib: Made the client registration more robust in case of a 403 HTTP response.
- lib: Fixed an issue with zone lookups when they have a CNAME in another zone.
- lib: Fixed the lookup for the authoritative zone for Google Cloud.
- lib: Fixed a race condition in the nonce store.
- lib: The Google Cloud provider now removes old entries before trying to add new ones.
- lib: Fixed a condition where we could stall due to an early error condition.
- lib: Fixed an issue where Authz object could end up in an active state after an error condition.
## [0.3.1] - 2016-04-19
### Added:
- lib: A new DNS provider for Vultr.
### Fixed:
- lib: DNS Provider for DigitalOcean could not handle subdomains properly.
- lib: handleHTTPError should only try to JSON decode error messages with the right content type.
- lib: The propagation checker for the DNS challenge would not retry on send errors.
## [0.3.0] - 2016-03-19
### Added:
- CLI: The `--dns` switch. To include the DNS challenge for consideration. When using this switch, all other solvers are disabled. Supported are the following solvers: cloudflare, digitalocean, dnsimple, dyn, gandi, googlecloud, namecheap, route53, rfc2136 and manual.
- CLI: The `--accept-tos` switch. Indicates your acceptance of the Let's Encrypt terms of service without prompting you.
- CLI: The `--webroot` switch. The HTTP-01 challenge may now be completed by dropping a file into a webroot. When using this switch, all other solvers are disabled.
- CLI: The `--key-type` switch. This replaces the `--rsa-key-size` switch and supports the following key types: EC256, EC384, RSA2048, RSA4096 and RSA8192.
- CLI: The `--dnshelp` switch. This displays a more in-depth help topic for DNS solvers.
- CLI: The `--no-bundle` sub switch for the `run` and `renew` commands. When this switch is set, the CLI will not bundle the issuer certificate with your certificate.
- lib: A new type for challenge identifiers `Challenge`
- lib: A new interface for custom challenge providers `acme.ChallengeProvider`
- lib: A new interface for DNS-01 providers to allow for custom timeouts for the validation function `acme.ChallengeProviderTimeout`
- lib: SetChallengeProvider function. Pass a challenge identifier and a Provider to replace the default behaviour of a challenge.
- lib: The DNS-01 challenge has been implemented with modular solvers using the `ChallengeProvider` interface. Included solvers are: cloudflare, digitalocean, dnsimple, gandi, namecheap, route53, rfc2136 and manual.
- lib: The `acme.KeyType` type was added and is used for the configuration of crypto parameters for RSA and EC keys. Valid KeyTypes are: EC256, EC384, RSA2048, RSA4096 and RSA8192.
### Changed
- lib: ExcludeChallenges now expects to be passed an array of `Challenge` types.
- lib: HTTP-01 now supports custom solvers using the `ChallengeProvider` interface.
- lib: TLS-SNI-01 now supports custom solvers using the `ChallengeProvider` interface.
- lib: The `GetPrivateKey` function in the `acme.User` interface is now expected to return a `crypto.PrivateKey` instead of an `rsa.PrivateKey` for EC compat.
- lib: The `acme.NewClient` function now expects an `acme.KeyType` instead of the keyBits parameter.
### Removed
- CLI: The `rsa-key-size` switch was removed in favor of `key-type` to support EC keys.
### Fixed
- lib: Fixed a race condition in HTTP-01
- lib: Fixed an issue where status codes on ACME challenge responses could lead to no action being taken.
- lib: Fixed a regression when calling the Renew function with a SAN certificate.
## [0.2.0] - 2016-01-09
### Added:
- CLI: The `--exclude` or `-x` switch. To exclude a challenge from being solved.
- CLI: The `--http` switch. To set the listen address and port of HTTP based challenges. Supports `host:port` and `:port` for any interface.
- CLI: The `--tls` switch. To set the listen address and port of TLS based challenges. Supports `host:port` and `:port` for any interface.
- CLI: The `--reuse-key` switch for the `renew` operation. This lets you reuse an existing private key for renewals.
- lib: ExcludeChallenges function. Pass an array of challenge identifiers to exclude them from solving.
- lib: SetHTTPAddress function. Pass a port to set the listen port for HTTP based challenges.
- lib: SetTLSAddress function. Pass a port to set the listen port of TLS based challenges.
- lib: acme.UserAgent variable. Use this to customize the user agent on all requests sent by lego.
### Changed:
- lib: NewClient does no longer accept the optPort parameter
- lib: ObtainCertificate now returns a SAN certificate if you pass more then one domain.
- lib: GetOCSPForCert now returns the parsed OCSP response instead of just the status.
- lib: ObtainCertificate has a new parameter `privKey crypto.PrivateKey` which lets you reuse an existing private key for new certificates.
- lib: RenewCertificate now expects the PrivateKey property of the CertificateResource to be set only if you want to reuse the key.
### Removed:
- CLI: The `--port` switch was removed.
- lib: RenewCertificate does no longer offer to also revoke your old certificate.
### Fixed:
- CLI: Fix logic using the `--days` parameter for renew
## [0.1.1] - 2015-12-18
### Added:
- CLI: Added a way to automate renewal through a cronjob using the --days parameter to renew
### Changed:
- lib: Improved log output on challenge failures.
### Fixed:
- CLI: The short parameter for domains would not get accepted
- CLI: The cli did not return proper exit codes on error library errors.
- lib: RenewCertificate did not properly renew SAN certificates.
### Security
- lib: Fix possible DOS on GetOCSPForCert
## [0.1.0] - 2015-12-03
- Initial release
[0.3.1]: https://github.com/go-acme/lego/compare/v0.3.0...v0.3.1
[0.3.0]: https://github.com/go-acme/lego/compare/v0.2.0...v0.3.0
[0.2.0]: https://github.com/go-acme/lego/compare/v0.1.1...v0.2.0
[0.1.1]: https://github.com/go-acme/lego/compare/v0.1.0...v0.1.1
[0.1.0]: https://github.com/go-acme/lego/tree/v0.1.0
lego-4.9.1/CONTRIBUTING.md 0000664 0000000 0000000 00000005047 14340204635 0014713 0 ustar 00root root 0000000 0000000 # How to contribute to lego
Contributions in the form of patches and proposals are essential to keep lego great and to make it even better.
To ensure a great and easy experience for everyone, please review the few guidelines in this document.
## Bug reports
- Use the issue search to see if the issue has already been reported.
- Also look for closed issues to see if your issue has already been fixed.
- If both of the above do not apply create a new issue and include as much information as possible.
Bug reports should include all information a person could need to reproduce your problem without the need to
follow up for more information. If possible, provide detailed steps for us to reproduce it, the expected behaviour and the actual behaviour.
## Feature proposals and requests
Feature requests are welcome and should be discussed in an issue.
Please keep proposals focused on one thing at a time and be as detailed as possible.
It is up to you to make a strong point about your proposal and convince us of the merits and the added complexity of this feature.
## Pull requests
Patches, new features and improvements are a great way to help the project.
Please keep them focused on one thing and do not include unrelated commits.
All pull requests which alter the behaviour of the program, add new behaviour or somehow alter code in a non-trivial way should **always** include tests.
If you want to contribute a significant pull request (with a non-trivial workload for you) please **ask first**. We do not want you to spend
a lot of time on something the project's developers might not want to merge into the project.
**IMPORTANT**: By submitting a patch, you agree to allow the project
owners to license your work under the terms of the [MIT License](LICENSE).
### How to create a pull request
Requirements:
- `go` v1.15+
- environment variable: `GO111MODULE=on`
First, you have to install [GoLang](https://golang.org/doc/install) and [golangci-lint](https://github.com/golangci/golangci-lint#install).
```bash
# Create the root folder
mkdir -p $GOPATH/src/github.com/go-acme
cd $GOPATH/src/github.com/go-acme
# clone your fork
git clone git@github.com:YOUR_USERNAME/lego.git
cd lego
# Add the go-acme/lego remote
git remote add upstream git@github.com:go-acme/lego.git
git fetch upstream
```
```bash
# Create your branch
git checkout -b my-feature
## Create your code ##
```
```bash
# Format
make fmt
# Linters
make checks
# Tests
make test
# Compile
make build
```
```bash
# push your branch
git push -u origin my-feature
## create a pull request on GitHub ##
```
lego-4.9.1/Dockerfile 0000664 0000000 0000000 00000000637 14340204635 0014454 0 ustar 00root root 0000000 0000000 FROM golang:1-alpine as builder
RUN apk --no-cache --no-progress add make git
WORKDIR /go/lego
ENV GO111MODULE on
# Download go modules
COPY go.mod .
COPY go.sum .
RUN go mod download
COPY . .
RUN make build
FROM alpine:3.12
RUN apk update \
&& apk add --no-cache ca-certificates tzdata \
&& update-ca-certificates
COPY --from=builder /go/lego/dist/lego /usr/bin/lego
ENTRYPOINT [ "/usr/bin/lego" ]
lego-4.9.1/LICENSE 0000664 0000000 0000000 00000002100 14340204635 0013452 0 ustar 00root root 0000000 0000000 The MIT License (MIT)
Copyright (c) 2015-2017 Sebastian Erhart
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
lego-4.9.1/Makefile 0000664 0000000 0000000 00000003502 14340204635 0014114 0 ustar 00root root 0000000 0000000 .PHONY: clean checks test build image e2e fmt
export GO111MODULE=on
export CGO_ENABLED=0
LEGO_IMAGE := goacme/lego
MAIN_DIRECTORY := ./cmd/lego/
BIN_OUTPUT := $(if $(filter $(shell go env GOOS), windows), dist/lego.exe, dist/lego)
TAG_NAME := $(shell git tag -l --contains HEAD)
SHA := $(shell git rev-parse HEAD)
VERSION := $(if $(TAG_NAME),$(TAG_NAME),$(SHA))
default: clean generate-dns checks test build
clean:
@echo BIN_OUTPUT: ${BIN_OUTPUT}
rm -rf dist/ builds/ cover.out
build: clean
@echo Version: $(VERSION)
go build -trimpath -ldflags '-X "main.version=${VERSION}"' -o ${BIN_OUTPUT} ${MAIN_DIRECTORY}
image:
@echo Version: $(VERSION)
docker build -t $(LEGO_IMAGE) .
publish-images:
seihon publish -v "$(TAG_NAME)" -v "latest" --image-name="$(LEGO_IMAGE)" --dry-run=false
test: clean
go test -v -cover ./...
e2e: clean
LEGO_E2E_TESTS=local go test -count=1 -v ./e2e/...
checks:
golangci-lint run
# Release helper
.PHONY: patch minor major detach
patch:
go run internal/release.go release -m patch
minor:
go run internal/release.go release -m minor
major:
go run internal/release.go release -m 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
# DNS Documentation
.PHONY: generate-dns validate-doc
generate-dns:
go generate ./...
validate-doc: generate-dns
validate-doc: DOC_DIRECTORIES := ./docs/ ./cmd/
validate-doc:
if git diff --exit-code --quiet $(DOC_DIRECTORIES) 2>/dev/null; then \
echo 'All documentation changes are done the right way.'; \
else \
echo 'The documentation must be regenerated, please use `make generate-dns`.'; \
git status --porcelain -- $(DOC_DIRECTORIES) 2>/dev/null; \
exit 2; \
fi
lego-4.9.1/README.md 0000664 0000000 0000000 00000026141 14340204635 0013737 0 ustar 00root root 0000000 0000000
Automatic Certificates and HTTPS for everyone.
# Lego
Let's Encrypt client and ACME library written in Go.
[](https://pkg.go.dev/mod/github.com/go-acme/lego/v4)
[](https://github.com//go-acme/lego/actions)
[](https://hub.docker.com/r/goacme/lego/)
## Features
- ACME v2 [RFC 8555](https://www.rfc-editor.org/rfc/rfc8555.html)
- 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](https://go-acme.github.io/lego/dns)
- [Custom challenge solvers](https://go-acme.github.io/lego/usage/library/writing-a-challenge-solver/)
- Certificate bundling
- OCSP helper function
## Installation
How to [install](https://go-acme.github.io/lego/installation/).
## Usage
- as a [CLI](https://go-acme.github.io/lego/usage/cli)
- as a [library](https://go-acme.github.io/lego/usage/library)
## Documentation
Documentation is hosted live at https://go-acme.github.io/lego/.
## DNS providers
Detailed documentation is available [here](https://go-acme.github.io/lego/dns).
| | | | |
|---------------------------------------------------------------------------------|---------------------------------------------------------------------------------|---------------------------------------------------------------------------------|---------------------------------------------------------------------------------|
| [Akamai EdgeDNS](https://go-acme.github.io/lego/dns/edgedns/) | [Alibaba Cloud DNS](https://go-acme.github.io/lego/dns/alidns/) | [all-inkl](https://go-acme.github.io/lego/dns/allinkl/) | [Amazon Lightsail](https://go-acme.github.io/lego/dns/lightsail/) |
| [Amazon Route 53](https://go-acme.github.io/lego/dns/route53/) | [ArvanCloud](https://go-acme.github.io/lego/dns/arvancloud/) | [Aurora DNS](https://go-acme.github.io/lego/dns/auroradns/) | [Autodns](https://go-acme.github.io/lego/dns/autodns/) |
| [Azure](https://go-acme.github.io/lego/dns/azure/) | [Bindman](https://go-acme.github.io/lego/dns/bindman/) | [Bluecat](https://go-acme.github.io/lego/dns/bluecat/) | [Checkdomain](https://go-acme.github.io/lego/dns/checkdomain/) |
| [Civo](https://go-acme.github.io/lego/dns/civo/) | [CloudDNS](https://go-acme.github.io/lego/dns/clouddns/) | [Cloudflare](https://go-acme.github.io/lego/dns/cloudflare/) | [ClouDNS](https://go-acme.github.io/lego/dns/cloudns/) |
| [CloudXNS](https://go-acme.github.io/lego/dns/cloudxns/) | [ConoHa](https://go-acme.github.io/lego/dns/conoha/) | [Constellix](https://go-acme.github.io/lego/dns/constellix/) | [deSEC.io](https://go-acme.github.io/lego/dns/desec/) |
| [Designate DNSaaS for Openstack](https://go-acme.github.io/lego/dns/designate/) | [Digital Ocean](https://go-acme.github.io/lego/dns/digitalocean/) | [DNS Made Easy](https://go-acme.github.io/lego/dns/dnsmadeeasy/) | [DNSimple](https://go-acme.github.io/lego/dns/dnsimple/) |
| [DNSPod (deprecated)](https://go-acme.github.io/lego/dns/dnspod/) | [Domain Offensive (do.de)](https://go-acme.github.io/lego/dns/dode/) | [Domeneshop](https://go-acme.github.io/lego/dns/domeneshop/) | [DreamHost](https://go-acme.github.io/lego/dns/dreamhost/) |
| [Duck DNS](https://go-acme.github.io/lego/dns/duckdns/) | [Dyn](https://go-acme.github.io/lego/dns/dyn/) | [Dynu](https://go-acme.github.io/lego/dns/dynu/) | [EasyDNS](https://go-acme.github.io/lego/dns/easydns/) |
| [Epik](https://go-acme.github.io/lego/dns/epik/) | [Exoscale](https://go-acme.github.io/lego/dns/exoscale/) | [External program](https://go-acme.github.io/lego/dns/exec/) | [freemyip.com](https://go-acme.github.io/lego/dns/freemyip/) |
| [G-Core Labs](https://go-acme.github.io/lego/dns/gcore/) | [Gandi Live DNS (v5)](https://go-acme.github.io/lego/dns/gandiv5/) | [Gandi](https://go-acme.github.io/lego/dns/gandi/) | [Glesys](https://go-acme.github.io/lego/dns/glesys/) |
| [Go Daddy](https://go-acme.github.io/lego/dns/godaddy/) | [Google Cloud](https://go-acme.github.io/lego/dns/gcloud/) | [Hetzner](https://go-acme.github.io/lego/dns/hetzner/) | [Hosting.de](https://go-acme.github.io/lego/dns/hostingde/) |
| [Hosttech](https://go-acme.github.io/lego/dns/hosttech/) | [HTTP request](https://go-acme.github.io/lego/dns/httpreq/) | [Hurricane Electric DNS](https://go-acme.github.io/lego/dns/hurricane/) | [HyperOne](https://go-acme.github.io/lego/dns/hyperone/) |
| [IBM Cloud (SoftLayer)](https://go-acme.github.io/lego/dns/ibmcloud/) | [IIJ DNS Platform Service](https://go-acme.github.io/lego/dns/iijdpf/) | [Infoblox](https://go-acme.github.io/lego/dns/infoblox/) | [Infomaniak](https://go-acme.github.io/lego/dns/infomaniak/) |
| [Internet Initiative Japan](https://go-acme.github.io/lego/dns/iij/) | [Internet.bs](https://go-acme.github.io/lego/dns/internetbs/) | [INWX](https://go-acme.github.io/lego/dns/inwx/) | [Ionos](https://go-acme.github.io/lego/dns/ionos/) |
| [iwantmyname](https://go-acme.github.io/lego/dns/iwantmyname/) | [Joker](https://go-acme.github.io/lego/dns/joker/) | [Joohoi's ACME-DNS](https://go-acme.github.io/lego/dns/acme-dns/) | [Linode (v4)](https://go-acme.github.io/lego/dns/linode/) |
| [Liquid Web](https://go-acme.github.io/lego/dns/liquidweb/) | [Loopia](https://go-acme.github.io/lego/dns/loopia/) | [LuaDNS](https://go-acme.github.io/lego/dns/luadns/) | [Manual](https://go-acme.github.io/lego/dns/manual/) |
| [MyDNS.jp](https://go-acme.github.io/lego/dns/mydnsjp/) | [MythicBeasts](https://go-acme.github.io/lego/dns/mythicbeasts/) | [Name.com](https://go-acme.github.io/lego/dns/namedotcom/) | [Namecheap](https://go-acme.github.io/lego/dns/namecheap/) |
| [Namesilo](https://go-acme.github.io/lego/dns/namesilo/) | [NearlyFreeSpeech.NET](https://go-acme.github.io/lego/dns/nearlyfreespeech/) | [Netcup](https://go-acme.github.io/lego/dns/netcup/) | [Netlify](https://go-acme.github.io/lego/dns/netlify/) |
| [Nicmanager](https://go-acme.github.io/lego/dns/nicmanager/) | [NIFCloud](https://go-acme.github.io/lego/dns/nifcloud/) | [Njalla](https://go-acme.github.io/lego/dns/njalla/) | [NS1](https://go-acme.github.io/lego/dns/ns1/) |
| [Open Telekom Cloud](https://go-acme.github.io/lego/dns/otc/) | [Oracle Cloud](https://go-acme.github.io/lego/dns/oraclecloud/) | [OVH](https://go-acme.github.io/lego/dns/ovh/) | [Porkbun](https://go-acme.github.io/lego/dns/porkbun/) |
| [PowerDNS](https://go-acme.github.io/lego/dns/pdns/) | [Rackspace](https://go-acme.github.io/lego/dns/rackspace/) | [reg.ru](https://go-acme.github.io/lego/dns/regru/) | [RFC2136](https://go-acme.github.io/lego/dns/rfc2136/) |
| [RimuHosting](https://go-acme.github.io/lego/dns/rimuhosting/) | [Sakura Cloud](https://go-acme.github.io/lego/dns/sakuracloud/) | [Scaleway](https://go-acme.github.io/lego/dns/scaleway/) | [Selectel](https://go-acme.github.io/lego/dns/selectel/) |
| [Servercow](https://go-acme.github.io/lego/dns/servercow/) | [Simply.com](https://go-acme.github.io/lego/dns/simply/) | [Sonic](https://go-acme.github.io/lego/dns/sonic/) | [Stackpath](https://go-acme.github.io/lego/dns/stackpath/) |
| [Tencent Cloud DNS](https://go-acme.github.io/lego/dns/tencentcloud/) | [TransIP](https://go-acme.github.io/lego/dns/transip/) | [UKFast SafeDNS](https://go-acme.github.io/lego/dns/safedns/) | [Variomedia](https://go-acme.github.io/lego/dns/variomedia/) |
| [VegaDNS](https://go-acme.github.io/lego/dns/vegadns/) | [Vercel](https://go-acme.github.io/lego/dns/vercel/) | [Versio.[nl/eu/uk]](https://go-acme.github.io/lego/dns/versio/) | [VinylDNS](https://go-acme.github.io/lego/dns/vinyldns/) |
| [VK Cloud](https://go-acme.github.io/lego/dns/vkcloud/) | [Vscale](https://go-acme.github.io/lego/dns/vscale/) | [Vultr](https://go-acme.github.io/lego/dns/vultr/) | [WEDOS](https://go-acme.github.io/lego/dns/wedos/) |
| [Yandex Cloud](https://go-acme.github.io/lego/dns/yandexcloud/) | [Yandex PDD](https://go-acme.github.io/lego/dns/yandex/) | [Zone.ee](https://go-acme.github.io/lego/dns/zoneee/) | [Zonomi](https://go-acme.github.io/lego/dns/zonomi/) |
If your DNS provider is not supported, please open an [issue](https://github.com/go-acme/lego/issues/new?assignees=&labels=enhancement%2C+new-provider&template=new_dns_provider.md).
lego-4.9.1/acme/ 0000775 0000000 0000000 00000000000 14340204635 0013361 5 ustar 00root root 0000000 0000000 lego-4.9.1/acme/api/ 0000775 0000000 0000000 00000000000 14340204635 0014132 5 ustar 00root root 0000000 0000000 lego-4.9.1/acme/api/account.go 0000664 0000000 0000000 00000004214 14340204635 0016116 0 ustar 00root root 0000000 0000000 package api
import (
"encoding/base64"
"errors"
"fmt"
"github.com/go-acme/lego/v4/acme"
)
type AccountService service
// New Creates a new account.
func (a *AccountService) New(req acme.Account) (acme.ExtendedAccount, error) {
var account acme.Account
resp, err := a.core.post(a.core.GetDirectory().NewAccountURL, req, &account)
location := getLocation(resp)
if len(location) > 0 {
a.core.jws.SetKid(location)
}
if err != nil {
return acme.ExtendedAccount{Location: location}, err
}
return acme.ExtendedAccount{Account: account, Location: location}, nil
}
// NewEAB Creates a new account with an External Account Binding.
func (a *AccountService) NewEAB(accMsg acme.Account, kid, hmacEncoded string) (acme.ExtendedAccount, error) {
hmac, err := base64.RawURLEncoding.DecodeString(hmacEncoded)
if err != nil {
return acme.ExtendedAccount{}, fmt.Errorf("acme: could not decode hmac key: %w", err)
}
eabJWS, err := a.core.signEABContent(a.core.GetDirectory().NewAccountURL, kid, hmac)
if err != nil {
return acme.ExtendedAccount{}, fmt.Errorf("acme: error signing eab content: %w", err)
}
accMsg.ExternalAccountBinding = eabJWS
return a.New(accMsg)
}
// Get Retrieves an account.
func (a *AccountService) Get(accountURL string) (acme.Account, error) {
if accountURL == "" {
return acme.Account{}, errors.New("account[get]: empty URL")
}
var account acme.Account
_, err := a.core.postAsGet(accountURL, &account)
if err != nil {
return acme.Account{}, err
}
return account, nil
}
// Update Updates an account.
func (a *AccountService) Update(accountURL string, req acme.Account) (acme.Account, error) {
if accountURL == "" {
return acme.Account{}, errors.New("account[update]: empty URL")
}
var account acme.Account
_, err := a.core.post(accountURL, req, &account)
if err != nil {
return acme.Account{}, err
}
return account, nil
}
// Deactivate Deactivates an account.
func (a *AccountService) Deactivate(accountURL string) error {
if accountURL == "" {
return errors.New("account[deactivate]: empty URL")
}
req := acme.Account{Status: acme.StatusDeactivated}
_, err := a.core.post(accountURL, req, nil)
return err
}
lego-4.9.1/acme/api/api.go 0000664 0000000 0000000 00000010734 14340204635 0015237 0 ustar 00root root 0000000 0000000 package api
import (
"bytes"
"crypto"
"encoding/json"
"errors"
"fmt"
"net/http"
"time"
"github.com/cenkalti/backoff/v4"
"github.com/go-acme/lego/v4/acme"
"github.com/go-acme/lego/v4/acme/api/internal/nonces"
"github.com/go-acme/lego/v4/acme/api/internal/secure"
"github.com/go-acme/lego/v4/acme/api/internal/sender"
"github.com/go-acme/lego/v4/log"
)
// Core ACME/LE core API.
type Core struct {
doer *sender.Doer
nonceManager *nonces.Manager
jws *secure.JWS
directory acme.Directory
HTTPClient *http.Client
common service // Reuse a single struct instead of allocating one for each service on the heap.
Accounts *AccountService
Authorizations *AuthorizationService
Certificates *CertificateService
Challenges *ChallengeService
Orders *OrderService
}
// New Creates a new Core.
func New(httpClient *http.Client, userAgent, caDirURL, kid string, privateKey crypto.PrivateKey) (*Core, error) {
doer := sender.NewDoer(httpClient, userAgent)
dir, err := getDirectory(doer, caDirURL)
if err != nil {
return nil, err
}
nonceManager := nonces.NewManager(doer, dir.NewNonceURL)
jws := secure.NewJWS(privateKey, kid, nonceManager)
c := &Core{doer: doer, nonceManager: nonceManager, jws: jws, directory: dir, HTTPClient: httpClient}
c.common.core = c
c.Accounts = (*AccountService)(&c.common)
c.Authorizations = (*AuthorizationService)(&c.common)
c.Certificates = (*CertificateService)(&c.common)
c.Challenges = (*ChallengeService)(&c.common)
c.Orders = (*OrderService)(&c.common)
return c, nil
}
// post performs an HTTP POST request and parses the response body as JSON,
// into the provided respBody object.
func (a *Core) post(uri string, reqBody, response interface{}) (*http.Response, error) {
content, err := json.Marshal(reqBody)
if err != nil {
return nil, errors.New("failed to marshal message")
}
return a.retrievablePost(uri, content, response)
}
// postAsGet performs an HTTP POST ("POST-as-GET") request.
// https://www.rfc-editor.org/rfc/rfc8555.html#section-6.3
func (a *Core) postAsGet(uri string, response interface{}) (*http.Response, error) {
return a.retrievablePost(uri, []byte{}, response)
}
func (a *Core) retrievablePost(uri string, content []byte, response interface{}) (*http.Response, error) {
// during tests, allow to support ~90% of bad nonce with a minimum of attempts.
bo := backoff.NewExponentialBackOff()
bo.InitialInterval = 200 * time.Millisecond
bo.MaxInterval = 5 * time.Second
bo.MaxElapsedTime = 20 * time.Second
var resp *http.Response
operation := func() error {
var err error
resp, err = a.signedPost(uri, content, response)
if err != nil {
// Retry if the nonce was invalidated
var e *acme.NonceError
if errors.As(err, &e) {
return err
}
return backoff.Permanent(err)
}
return nil
}
notify := func(err error, duration time.Duration) {
log.Infof("retry due to: %v", err)
}
err := backoff.RetryNotify(operation, bo, notify)
if err != nil {
return resp, err
}
return resp, nil
}
func (a *Core) signedPost(uri string, content []byte, response interface{}) (*http.Response, error) {
signedContent, err := a.jws.SignContent(uri, content)
if err != nil {
return nil, fmt.Errorf("failed to post JWS message: failed to sign content: %w", err)
}
signedBody := bytes.NewBuffer([]byte(signedContent.FullSerialize()))
resp, err := a.doer.Post(uri, signedBody, "application/jose+json", response)
// nonceErr is ignored to keep the root error.
nonce, nonceErr := nonces.GetFromResponse(resp)
if nonceErr == nil {
a.nonceManager.Push(nonce)
}
return resp, err
}
func (a *Core) signEABContent(newAccountURL, kid string, hmac []byte) ([]byte, error) {
eabJWS, err := a.jws.SignEABContent(newAccountURL, kid, hmac)
if err != nil {
return nil, err
}
return []byte(eabJWS.FullSerialize()), nil
}
// GetKeyAuthorization Gets the key authorization.
func (a *Core) GetKeyAuthorization(token string) (string, error) {
return a.jws.GetKeyAuthorization(token)
}
func (a *Core) GetDirectory() acme.Directory {
return a.directory
}
func getDirectory(do *sender.Doer, caDirURL string) (acme.Directory, error) {
var dir acme.Directory
if _, err := do.Get(caDirURL, &dir); err != nil {
return dir, fmt.Errorf("get directory at '%s': %w", caDirURL, err)
}
if dir.NewAccountURL == "" {
return dir, errors.New("directory missing new registration URL")
}
if dir.NewOrderURL == "" {
return dir, errors.New("directory missing new order URL")
}
return dir, nil
}
lego-4.9.1/acme/api/authorization.go 0000664 0000000 0000000 00000001462 14340204635 0017364 0 ustar 00root root 0000000 0000000 package api
import (
"errors"
"github.com/go-acme/lego/v4/acme"
)
type AuthorizationService service
// Get Gets an authorization.
func (c *AuthorizationService) Get(authzURL string) (acme.Authorization, error) {
if authzURL == "" {
return acme.Authorization{}, errors.New("authorization[get]: empty URL")
}
var authz acme.Authorization
_, err := c.core.postAsGet(authzURL, &authz)
if err != nil {
return acme.Authorization{}, err
}
return authz, nil
}
// Deactivate Deactivates an authorization.
func (c *AuthorizationService) Deactivate(authzURL string) error {
if authzURL == "" {
return errors.New("authorization[deactivate]: empty URL")
}
var disabledAuth acme.Authorization
_, err := c.core.post(authzURL, acme.Authorization{Status: acme.StatusDeactivated}, &disabledAuth)
return err
}
lego-4.9.1/acme/api/certificate.go 0000664 0000000 0000000 00000007533 14340204635 0016753 0 ustar 00root root 0000000 0000000 package api
import (
"bytes"
"crypto/x509"
"encoding/pem"
"errors"
"io"
"net/http"
"github.com/go-acme/lego/v4/acme"
"github.com/go-acme/lego/v4/certcrypto"
"github.com/go-acme/lego/v4/log"
)
// maxBodySize is the maximum size of body that we will read.
const maxBodySize = 1024 * 1024
type CertificateService service
// Get Returns the certificate and the issuer certificate.
// 'bundle' is only applied if the issuer is provided by the 'up' link.
func (c *CertificateService) Get(certURL string, bundle bool) ([]byte, []byte, error) {
cert, _, err := c.get(certURL, bundle)
if err != nil {
return nil, nil, err
}
return cert.Cert, cert.Issuer, nil
}
// GetAll the certificates and the alternate certificates.
// bundle' is only applied if the issuer is provided by the 'up' link.
func (c *CertificateService) GetAll(certURL string, bundle bool) (map[string]*acme.RawCertificate, error) {
cert, headers, err := c.get(certURL, bundle)
if err != nil {
return nil, err
}
certs := map[string]*acme.RawCertificate{certURL: cert}
// URLs of "alternate" link relation
// - https://www.rfc-editor.org/rfc/rfc8555.html#section-7.4.2
alts := getLinks(headers, "alternate")
for _, alt := range alts {
altCert, _, err := c.get(alt, bundle)
if err != nil {
return nil, err
}
certs[alt] = altCert
}
return certs, nil
}
// Revoke Revokes a certificate.
func (c *CertificateService) Revoke(req acme.RevokeCertMessage) error {
_, err := c.core.post(c.core.GetDirectory().RevokeCertURL, req, nil)
return err
}
// get Returns the certificate and the "up" link.
func (c *CertificateService) get(certURL string, bundle bool) (*acme.RawCertificate, http.Header, error) {
if certURL == "" {
return nil, nil, errors.New("certificate[get]: empty URL")
}
resp, err := c.core.postAsGet(certURL, nil)
if err != nil {
return nil, nil, err
}
data, err := io.ReadAll(http.MaxBytesReader(nil, resp.Body, maxBodySize))
if err != nil {
return nil, resp.Header, err
}
cert := c.getCertificateChain(data, resp.Header, bundle, certURL)
return cert, resp.Header, err
}
// getCertificateChain Returns the certificate and the issuer certificate.
func (c *CertificateService) getCertificateChain(cert []byte, headers http.Header, bundle bool, certURL string) *acme.RawCertificate {
// Get issuerCert from bundled response from Let's Encrypt
// See https://community.letsencrypt.org/t/acme-v2-no-up-link-in-response/64962
_, issuer := pem.Decode(cert)
if issuer != nil {
// If bundle is false, we want to return a single certificate.
// To do this, we remove the issuer cert(s) from the issued cert.
if !bundle {
cert = bytes.TrimSuffix(cert, issuer)
}
return &acme.RawCertificate{Cert: cert, Issuer: issuer}
}
// The issuer certificate link may be supplied via an "up" link
// in the response headers of a new certificate.
// See https://www.rfc-editor.org/rfc/rfc8555.html#section-7.4.2
up := getLink(headers, "up")
issuer, err := c.getIssuerFromLink(up)
if err != nil {
// If we fail to acquire the issuer cert, return the issued certificate - do not fail.
log.Warnf("acme: Could not bundle issuer certificate [%s]: %v", certURL, err)
} else if len(issuer) > 0 {
// If bundle is true, we want to return a certificate bundle.
// To do this, we append the issuer cert to the issued cert.
if bundle {
cert = append(cert, issuer...)
}
}
return &acme.RawCertificate{Cert: cert, Issuer: issuer}
}
// getIssuerFromLink requests the issuer certificate.
func (c *CertificateService) getIssuerFromLink(up string) ([]byte, error) {
if up == "" {
return nil, nil
}
log.Infof("acme: Requesting issuer cert from %s", up)
cert, _, err := c.get(up, false)
if err != nil {
return nil, err
}
_, err = x509.ParseCertificate(cert.Cert)
if err != nil {
return nil, err
}
return certcrypto.PEMEncode(certcrypto.DERCertificateBytes(cert.Cert)), nil
}
lego-4.9.1/acme/api/certificate_test.go 0000664 0000000 0000000 00000012517 14340204635 0020010 0 ustar 00root root 0000000 0000000 package api
import (
"crypto/rand"
"crypto/rsa"
"encoding/pem"
"net/http"
"testing"
"github.com/go-acme/lego/v4/platform/tester"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
const certResponseMock = `-----BEGIN CERTIFICATE-----
MIIDEDCCAfigAwIBAgIHPhckqW5fPDANBgkqhkiG9w0BAQsFADAoMSYwJAYDVQQD
Ex1QZWJibGUgSW50ZXJtZWRpYXRlIENBIDM5NWU2MTAeFw0xODExMDcxNzQ2NTZa
Fw0yMzExMDcxNzQ2NTZaMBMxETAPBgNVBAMTCGFjbWUud3RmMIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwtLNKvZXD20XPUQCWYSK9rUSKxD9Eb0c9fag
bxOxOkLRTgL8LH6yln+bxc3MrHDou4PpDUdeo2CyOQu3CKsTS5mrH3NXYHu0H7p5
y3riOJTHnfkGKLT9LciGz7GkXd62nvNP57bOf5Sk4P2M+Qbxd0hPTSfu52740LSy
144cnxe2P1aDYehrEp6nYCESuyD/CtUHTo0qwJmzIy163Sp3rSs15BuCPyhySnE3
BJ8Ggv+qC6D5I1932DfSqyQJ79iq/HRm0Fn84am3KwvRlUfWxabmsUGARXoqCgnE
zcbJVOZKewv0zlQJpfac+b+Imj6Lvt1TGjIz2mVyefYgLx8gwwIDAQABo1QwUjAO
BgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwG
A1UdEwEB/wQCMAAwEwYDVR0RBAwwCoIIYWNtZS53dGYwDQYJKoZIhvcNAQELBQAD
ggEBABB/0iYhmfPSQot5RaeeovQnsqYjI5ryQK2cwzW6qcTJfv8N6+p6XkqF1+W4
jXZjrQP8MvgO9KNWlvx12vhINE6wubk88L+2piAi5uS2QejmZbXpyYB9s+oPqlk9
IDvfdlVYOqvYAhSx7ggGi+j73mjZVtjAavP6dKuu475ZCeq+NIC15RpbbikWKtYE
HBJ7BW8XQKx67iHGx8ygHTDLbREL80Bck3oUm7wIYGMoNijD6RBl25p4gYl9dzOd
TqGl5hW/1P5hMbgEzHbr4O3BfWqU2g7tV36TASy3jbC3ONFRNNYrpEZ1AL3+cUri
OPPkKtAKAbQkKbUIfsHpBZjKZMU=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDDDCCAfSgAwIBAgIIOV5hkYJx0JwwDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UE
AxMVUGViYmxlIFJvb3QgQ0EgNTBmZmJkMB4XDTE4MTEwNzE3NDY0N1oXDTQ4MTEw
NzE3NDY0N1owKDEmMCQGA1UEAxMdUGViYmxlIEludGVybWVkaWF0ZSBDQSAzOTVl
NjEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCacwXN4LmyRTgYS8TT
SZYgz758npHiPTBDKgeN5WVmkkwW0TuN4W2zXhEmcM82uxOEjWS2drvK0+iJKneh
0fQR8ZF35dIYFe8WXTg3kEmqcizSgh4LxlOntsXvatfX/6GU/ADo3xAFoBKCijen
SRBIY65yq5m00cWx3RMIcQq1B0X8nJS0O1P7MYE/Vvidz5St/36RXVu1oWLeS5Fx
HAezW0lqxEUzvC+uLTFWC6f/CilzmI7SsPAkZBk7dO5Qs0d7m/zWF588vlGS+0pt
D1on+lU85Ma2zuAd0qmB6LY66N8pEKKtMk93wF/o4Z5i58ahbwNvTKAzz4JSRWSu
mB9LAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIChDAdBgNVHSUEFjAUBggrBgEFBQcD
AQYIKwYBBQUHAwIwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA
upU0DjzvIvoCOYKbq1RRN7rPdYad39mfjxgkeV0iOF5JoIdO6y1C7XAm9lT69Wjm
iUPvnCTMDYft40N2SvmXuuMaPOm4zjNwn4K33jw5XBnpwxC7By/Y0oV+Sl10fBsd
QqXC6H7LcSGkv+4eJbgY33P4uH5ZAy+2TkHUuZDkpufkAshzBust7nDAjfv3AIuQ
wlPoyZfI11eqyiOqRzOq+B5dIBr1JzKnEzSL6n0JLNQiPO7iN03rud/wYD3gbmcv
rzFL1KZfz+HZdnFwFW2T2gVW8L3ii1l9AJDuKzlvjUH3p6bgihVq02sjT8mx+GM2
7R4IbHGnj0BJA2vMYC4hSw==
-----END CERTIFICATE-----
`
const issuerMock = `-----BEGIN CERTIFICATE-----
MIIDDDCCAfSgAwIBAgIIOV5hkYJx0JwwDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UE
AxMVUGViYmxlIFJvb3QgQ0EgNTBmZmJkMB4XDTE4MTEwNzE3NDY0N1oXDTQ4MTEw
NzE3NDY0N1owKDEmMCQGA1UEAxMdUGViYmxlIEludGVybWVkaWF0ZSBDQSAzOTVl
NjEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCacwXN4LmyRTgYS8TT
SZYgz758npHiPTBDKgeN5WVmkkwW0TuN4W2zXhEmcM82uxOEjWS2drvK0+iJKneh
0fQR8ZF35dIYFe8WXTg3kEmqcizSgh4LxlOntsXvatfX/6GU/ADo3xAFoBKCijen
SRBIY65yq5m00cWx3RMIcQq1B0X8nJS0O1P7MYE/Vvidz5St/36RXVu1oWLeS5Fx
HAezW0lqxEUzvC+uLTFWC6f/CilzmI7SsPAkZBk7dO5Qs0d7m/zWF588vlGS+0pt
D1on+lU85Ma2zuAd0qmB6LY66N8pEKKtMk93wF/o4Z5i58ahbwNvTKAzz4JSRWSu
mB9LAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIChDAdBgNVHSUEFjAUBggrBgEFBQcD
AQYIKwYBBQUHAwIwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA
upU0DjzvIvoCOYKbq1RRN7rPdYad39mfjxgkeV0iOF5JoIdO6y1C7XAm9lT69Wjm
iUPvnCTMDYft40N2SvmXuuMaPOm4zjNwn4K33jw5XBnpwxC7By/Y0oV+Sl10fBsd
QqXC6H7LcSGkv+4eJbgY33P4uH5ZAy+2TkHUuZDkpufkAshzBust7nDAjfv3AIuQ
wlPoyZfI11eqyiOqRzOq+B5dIBr1JzKnEzSL6n0JLNQiPO7iN03rud/wYD3gbmcv
rzFL1KZfz+HZdnFwFW2T2gVW8L3ii1l9AJDuKzlvjUH3p6bgihVq02sjT8mx+GM2
7R4IbHGnj0BJA2vMYC4hSw==
-----END CERTIFICATE-----
`
func TestCertificateService_Get_issuerRelUp(t *testing.T) {
mux, apiURL := tester.SetupFakeAPI(t)
mux.HandleFunc("/certificate", func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Link", "<"+apiURL+`/issuer>; rel="up"`)
_, err := w.Write([]byte(certResponseMock))
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
})
mux.HandleFunc("/issuer", func(w http.ResponseWriter, _ *http.Request) {
p, _ := pem.Decode([]byte(issuerMock))
_, err := w.Write(p.Bytes)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
})
key, err := rsa.GenerateKey(rand.Reader, 2048)
require.NoError(t, err, "Could not generate test key")
core, err := New(http.DefaultClient, "lego-test", apiURL+"/dir", "", key)
require.NoError(t, err)
cert, issuer, err := core.Certificates.Get(apiURL+"/certificate", true)
require.NoError(t, err)
assert.Equal(t, certResponseMock, string(cert), "Certificate")
assert.Equal(t, issuerMock, string(issuer), "IssuerCertificate")
}
func TestCertificateService_Get_embeddedIssuer(t *testing.T) {
mux, apiURL := tester.SetupFakeAPI(t)
mux.HandleFunc("/certificate", func(w http.ResponseWriter, _ *http.Request) {
_, err := w.Write([]byte(certResponseMock))
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
})
key, err := rsa.GenerateKey(rand.Reader, 2048)
require.NoError(t, err, "Could not generate test key")
core, err := New(http.DefaultClient, "lego-test", apiURL+"/dir", "", key)
require.NoError(t, err)
cert, issuer, err := core.Certificates.Get(apiURL+"/certificate", true)
require.NoError(t, err)
assert.Equal(t, certResponseMock, string(cert), "Certificate")
assert.Equal(t, issuerMock, string(issuer), "IssuerCertificate")
}
lego-4.9.1/acme/api/challenge.go 0000664 0000000 0000000 00000002307 14340204635 0016405 0 ustar 00root root 0000000 0000000 package api
import (
"errors"
"github.com/go-acme/lego/v4/acme"
)
type ChallengeService service
// New Creates a challenge.
func (c *ChallengeService) New(chlgURL string) (acme.ExtendedChallenge, error) {
if chlgURL == "" {
return acme.ExtendedChallenge{}, errors.New("challenge[new]: empty URL")
}
// Challenge initiation is done by sending a JWS payload containing the trivial JSON object `{}`.
// We use an empty struct instance as the postJSON payload here to achieve this result.
var chlng acme.ExtendedChallenge
resp, err := c.core.post(chlgURL, struct{}{}, &chlng)
if err != nil {
return acme.ExtendedChallenge{}, err
}
chlng.AuthorizationURL = getLink(resp.Header, "up")
chlng.RetryAfter = getRetryAfter(resp)
return chlng, nil
}
// Get Gets a challenge.
func (c *ChallengeService) Get(chlgURL string) (acme.ExtendedChallenge, error) {
if chlgURL == "" {
return acme.ExtendedChallenge{}, errors.New("challenge[get]: empty URL")
}
var chlng acme.ExtendedChallenge
resp, err := c.core.postAsGet(chlgURL, &chlng)
if err != nil {
return acme.ExtendedChallenge{}, err
}
chlng.AuthorizationURL = getLink(resp.Header, "up")
chlng.RetryAfter = getRetryAfter(resp)
return chlng, nil
}
lego-4.9.1/acme/api/internal/ 0000775 0000000 0000000 00000000000 14340204635 0015746 5 ustar 00root root 0000000 0000000 lego-4.9.1/acme/api/internal/nonces/ 0000775 0000000 0000000 00000000000 14340204635 0017233 5 ustar 00root root 0000000 0000000 lego-4.9.1/acme/api/internal/nonces/nonce_manager.go 0000664 0000000 0000000 00000002710 14340204635 0022356 0 ustar 00root root 0000000 0000000 package nonces
import (
"errors"
"fmt"
"net/http"
"sync"
"github.com/go-acme/lego/v4/acme/api/internal/sender"
)
// Manager Manages nonces.
type Manager struct {
do *sender.Doer
nonceURL string
nonces []string
sync.Mutex
}
// NewManager Creates a new Manager.
func NewManager(do *sender.Doer, nonceURL string) *Manager {
return &Manager{
do: do,
nonceURL: nonceURL,
}
}
// Pop Pops a nonce.
func (n *Manager) Pop() (string, bool) {
n.Lock()
defer n.Unlock()
if len(n.nonces) == 0 {
return "", false
}
nonce := n.nonces[len(n.nonces)-1]
n.nonces = n.nonces[:len(n.nonces)-1]
return nonce, true
}
// Push Pushes a nonce.
func (n *Manager) Push(nonce string) {
n.Lock()
defer n.Unlock()
n.nonces = append(n.nonces, nonce)
}
// Nonce implement jose.NonceSource.
func (n *Manager) Nonce() (string, error) {
if nonce, ok := n.Pop(); ok {
return nonce, nil
}
return n.getNonce()
}
func (n *Manager) getNonce() (string, error) {
resp, err := n.do.Head(n.nonceURL)
if err != nil {
return "", fmt.Errorf("failed to get nonce from HTTP HEAD: %w", err)
}
return GetFromResponse(resp)
}
// GetFromResponse Extracts a nonce from a HTTP response.
func GetFromResponse(resp *http.Response) (string, error) {
if resp == nil {
return "", errors.New("nil response")
}
nonce := resp.Header.Get("Replay-Nonce")
if nonce == "" {
return "", errors.New("server did not respond with a proper nonce header")
}
return nonce, nil
}
lego-4.9.1/acme/api/internal/nonces/nonce_manager_test.go 0000664 0000000 0000000 00000002371 14340204635 0023420 0 ustar 00root root 0000000 0000000 package nonces
import (
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/go-acme/lego/v4/acme"
"github.com/go-acme/lego/v4/acme/api/internal/sender"
"github.com/go-acme/lego/v4/platform/tester"
)
func TestNotHoldingLockWhileMakingHTTPRequests(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
time.Sleep(250 * time.Millisecond)
w.Header().Set("Replay-Nonce", "12345")
w.Header().Set("Retry-After", "0")
err := tester.WriteJSONResponse(w, &acme.Challenge{Type: "http-01", Status: "Valid", URL: "http://example.com/", Token: "token"})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}))
t.Cleanup(server.Close)
doer := sender.NewDoer(http.DefaultClient, "lego-test")
j := NewManager(doer, server.URL)
ch := make(chan bool)
resultCh := make(chan bool)
go func() {
_, errN := j.Nonce()
if errN != nil {
t.Log(errN)
}
ch <- true
}()
go func() {
_, errN := j.Nonce()
if errN != nil {
t.Log(errN)
}
ch <- true
}()
go func() {
<-ch
<-ch
resultCh <- true
}()
select {
case <-resultCh:
case <-time.After(500 * time.Millisecond):
t.Fatal("JWS is probably holding a lock while making HTTP request")
}
}
lego-4.9.1/acme/api/internal/secure/ 0000775 0000000 0000000 00000000000 14340204635 0017234 5 ustar 00root root 0000000 0000000 lego-4.9.1/acme/api/internal/secure/jws.go 0000664 0000000 0000000 00000005716 14340204635 0020377 0 ustar 00root root 0000000 0000000 package secure
import (
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rsa"
"encoding/base64"
"fmt"
"github.com/go-acme/lego/v4/acme/api/internal/nonces"
jose "gopkg.in/square/go-jose.v2"
)
// JWS Represents a JWS.
type JWS struct {
privKey crypto.PrivateKey
kid string // Key identifier
nonces *nonces.Manager
}
// NewJWS Create a new JWS.
func NewJWS(privateKey crypto.PrivateKey, kid string, nonceManager *nonces.Manager) *JWS {
return &JWS{
privKey: privateKey,
nonces: nonceManager,
kid: kid,
}
}
// SetKid Sets a key identifier.
func (j *JWS) SetKid(kid string) {
j.kid = kid
}
// SignContent Signs a content with the JWS.
func (j *JWS) SignContent(url string, content []byte) (*jose.JSONWebSignature, error) {
var alg jose.SignatureAlgorithm
switch k := j.privKey.(type) {
case *rsa.PrivateKey:
alg = jose.RS256
case *ecdsa.PrivateKey:
if k.Curve == elliptic.P256() {
alg = jose.ES256
} else if k.Curve == elliptic.P384() {
alg = jose.ES384
}
}
signKey := jose.SigningKey{
Algorithm: alg,
Key: jose.JSONWebKey{Key: j.privKey, KeyID: j.kid},
}
options := jose.SignerOptions{
NonceSource: j.nonces,
ExtraHeaders: map[jose.HeaderKey]interface{}{
"url": url,
},
}
if j.kid == "" {
options.EmbedJWK = true
}
signer, err := jose.NewSigner(signKey, &options)
if err != nil {
return nil, fmt.Errorf("failed to create jose signer: %w", err)
}
signed, err := signer.Sign(content)
if err != nil {
return nil, fmt.Errorf("failed to sign content: %w", err)
}
return signed, nil
}
// SignEABContent Signs an external account binding content with the JWS.
func (j *JWS) SignEABContent(url, kid string, hmac []byte) (*jose.JSONWebSignature, error) {
jwk := jose.JSONWebKey{Key: j.privKey}
jwkJSON, err := jwk.Public().MarshalJSON()
if err != nil {
return nil, fmt.Errorf("acme: error encoding eab jwk key: %w", err)
}
signer, err := jose.NewSigner(
jose.SigningKey{Algorithm: jose.HS256, Key: hmac},
&jose.SignerOptions{
EmbedJWK: false,
ExtraHeaders: map[jose.HeaderKey]interface{}{
"kid": kid,
"url": url,
},
},
)
if err != nil {
return nil, fmt.Errorf("failed to create External Account Binding jose signer: %w", err)
}
signed, err := signer.Sign(jwkJSON)
if err != nil {
return nil, fmt.Errorf("failed to External Account Binding sign content: %w", err)
}
return signed, nil
}
// GetKeyAuthorization Gets the key authorization for a token.
func (j *JWS) GetKeyAuthorization(token string) (string, error) {
var publicKey crypto.PublicKey
switch k := j.privKey.(type) {
case *ecdsa.PrivateKey:
publicKey = k.Public()
case *rsa.PrivateKey:
publicKey = k.Public()
}
// Generate the Key Authorization for the challenge
jwk := &jose.JSONWebKey{Key: publicKey}
thumbBytes, err := jwk.Thumbprint(crypto.SHA256)
if err != nil {
return "", err
}
// unpad the base64URL
keyThumb := base64.RawURLEncoding.EncodeToString(thumbBytes)
return token + "." + keyThumb, nil
}
lego-4.9.1/acme/api/internal/secure/jws_test.go 0000664 0000000 0000000 00000002467 14340204635 0021436 0 ustar 00root root 0000000 0000000 package secure
import (
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/go-acme/lego/v4/acme"
"github.com/go-acme/lego/v4/acme/api/internal/nonces"
"github.com/go-acme/lego/v4/acme/api/internal/sender"
"github.com/go-acme/lego/v4/platform/tester"
)
func TestNotHoldingLockWhileMakingHTTPRequests(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
time.Sleep(250 * time.Millisecond)
w.Header().Set("Replay-Nonce", "12345")
w.Header().Set("Retry-After", "0")
err := tester.WriteJSONResponse(w, &acme.Challenge{Type: "http-01", Status: "Valid", URL: "http://example.com/", Token: "token"})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}))
t.Cleanup(server.Close)
doer := sender.NewDoer(http.DefaultClient, "lego-test")
j := nonces.NewManager(doer, server.URL)
ch := make(chan bool)
resultCh := make(chan bool)
go func() {
_, errN := j.Nonce()
if errN != nil {
t.Log(errN)
}
ch <- true
}()
go func() {
_, errN := j.Nonce()
if errN != nil {
t.Log(errN)
}
ch <- true
}()
go func() {
<-ch
<-ch
resultCh <- true
}()
select {
case <-resultCh:
case <-time.After(500 * time.Millisecond):
t.Fatal("JWS is probably holding a lock while making HTTP request")
}
}
lego-4.9.1/acme/api/internal/sender/ 0000775 0000000 0000000 00000000000 14340204635 0017226 5 ustar 00root root 0000000 0000000 lego-4.9.1/acme/api/internal/sender/sender.go 0000664 0000000 0000000 00000007130 14340204635 0021036 0 ustar 00root root 0000000 0000000 package sender
import (
"encoding/json"
"fmt"
"io"
"net/http"
"runtime"
"strings"
"github.com/go-acme/lego/v4/acme"
)
type RequestOption func(*http.Request) error
func contentType(ct string) RequestOption {
return func(req *http.Request) error {
req.Header.Set("Content-Type", ct)
return nil
}
}
type Doer struct {
httpClient *http.Client
userAgent string
}
// NewDoer Creates a new Doer.
func NewDoer(client *http.Client, userAgent string) *Doer {
return &Doer{
httpClient: client,
userAgent: userAgent,
}
}
// Get performs a GET request with a proper User-Agent string.
// If "response" is not provided, callers should close resp.Body when done reading from it.
func (d *Doer) Get(url string, response interface{}) (*http.Response, error) {
req, err := d.newRequest(http.MethodGet, url, nil)
if err != nil {
return nil, err
}
return d.do(req, response)
}
// Head performs a HEAD request with a proper User-Agent string.
// The response body (resp.Body) is already closed when this function returns.
func (d *Doer) Head(url string) (*http.Response, error) {
req, err := d.newRequest(http.MethodHead, url, nil)
if err != nil {
return nil, err
}
return d.do(req, nil)
}
// Post performs a POST request with a proper User-Agent string.
// If "response" is not provided, callers should close resp.Body when done reading from it.
func (d *Doer) Post(url string, body io.Reader, bodyType string, response interface{}) (*http.Response, error) {
req, err := d.newRequest(http.MethodPost, url, body, contentType(bodyType))
if err != nil {
return nil, err
}
return d.do(req, response)
}
func (d *Doer) newRequest(method, uri string, body io.Reader, opts ...RequestOption) (*http.Request, error) {
req, err := http.NewRequest(method, uri, body)
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}
req.Header.Set("User-Agent", d.formatUserAgent())
for _, opt := range opts {
err = opt(req)
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}
}
return req, nil
}
func (d *Doer) do(req *http.Request, response interface{}) (*http.Response, error) {
resp, err := d.httpClient.Do(req)
if err != nil {
return nil, err
}
if err = checkError(req, resp); err != nil {
return resp, err
}
if response != nil {
raw, err := io.ReadAll(resp.Body)
if err != nil {
return resp, err
}
defer resp.Body.Close()
err = json.Unmarshal(raw, response)
if err != nil {
return resp, fmt.Errorf("failed to unmarshal %q to type %T: %w", raw, response, err)
}
}
return resp, nil
}
// formatUserAgent builds and returns the User-Agent string to use in requests.
func (d *Doer) formatUserAgent() string {
ua := fmt.Sprintf("%s %s (%s; %s; %s)", d.userAgent, ourUserAgent, ourUserAgentComment, runtime.GOOS, runtime.GOARCH)
return strings.TrimSpace(ua)
}
func checkError(req *http.Request, resp *http.Response) error {
if resp.StatusCode >= http.StatusBadRequest {
body, err := io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("%d :: %s :: %s :: %w", resp.StatusCode, req.Method, req.URL, err)
}
var errorDetails *acme.ProblemDetails
err = json.Unmarshal(body, &errorDetails)
if err != nil {
return fmt.Errorf("%d ::%s :: %s :: %w :: %s", resp.StatusCode, req.Method, req.URL, err, string(body))
}
errorDetails.Method = req.Method
errorDetails.URL = req.URL.String()
// Check for errors we handle specifically
if errorDetails.HTTPStatus == http.StatusBadRequest && errorDetails.Type == acme.BadNonceErr {
return &acme.NonceError{ProblemDetails: errorDetails}
}
return errorDetails
}
return nil
}
lego-4.9.1/acme/api/internal/sender/sender_test.go 0000664 0000000 0000000 00000002747 14340204635 0022106 0 ustar 00root root 0000000 0000000 package sender
import (
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestDo_UserAgentOnAllHTTPMethod(t *testing.T) {
var ua, method string
server := httptest.NewServer(http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) {
ua = r.Header.Get("User-Agent")
method = r.Method
}))
t.Cleanup(server.Close)
doer := NewDoer(http.DefaultClient, "")
testCases := []struct {
method string
call func(u string) (*http.Response, error)
}{
{
method: http.MethodGet,
call: func(u string) (*http.Response, error) {
return doer.Get(u, nil)
},
},
{
method: http.MethodHead,
call: doer.Head,
},
{
method: http.MethodPost,
call: func(u string) (*http.Response, error) {
return doer.Post(u, strings.NewReader("falalalala"), "text/plain", nil)
},
},
}
for _, test := range testCases {
t.Run(test.method, func(t *testing.T) {
_, err := test.call(server.URL)
require.NoError(t, err)
assert.Equal(t, test.method, method)
assert.Contains(t, ua, ourUserAgent, "User-Agent")
})
}
}
func TestDo_CustomUserAgent(t *testing.T) {
customUA := "MyApp/1.2.3"
doer := NewDoer(http.DefaultClient, customUA)
ua := doer.formatUserAgent()
assert.Contains(t, ua, ourUserAgent)
assert.Contains(t, ua, customUA)
if strings.HasSuffix(ua, " ") {
t.Errorf("UA should not have trailing spaces; got '%s'", ua)
}
assert.Len(t, strings.Split(ua, " "), 5)
}
lego-4.9.1/acme/api/internal/sender/useragent.go 0000664 0000000 0000000 00000000656 14340204635 0021561 0 ustar 00root root 0000000 0000000 package sender
// CODE GENERATED AUTOMATICALLY
// THIS FILE MUST NOT BE EDITED BY HAND
const (
// ourUserAgent is the User-Agent of this underlying library package.
ourUserAgent = "xenolf-acme/4.9.1"
// ourUserAgentComment is part of the UA comment linked to the version status of this underlying library package.
// values: detach|release
// NOTE: Update this with each tagged release.
ourUserAgentComment = "release"
)
lego-4.9.1/acme/api/order.go 0000664 0000000 0000000 00000002766 14340204635 0015607 0 ustar 00root root 0000000 0000000 package api
import (
"encoding/base64"
"errors"
"github.com/go-acme/lego/v4/acme"
)
type OrderService service
// New Creates a new order.
func (o *OrderService) New(domains []string) (acme.ExtendedOrder, error) {
var identifiers []acme.Identifier
for _, domain := range domains {
identifiers = append(identifiers, acme.Identifier{Type: "dns", Value: domain})
}
orderReq := acme.Order{Identifiers: identifiers}
var order acme.Order
resp, err := o.core.post(o.core.GetDirectory().NewOrderURL, orderReq, &order)
if err != nil {
return acme.ExtendedOrder{}, err
}
return acme.ExtendedOrder{
Order: order,
Location: resp.Header.Get("Location"),
}, nil
}
// Get Gets an order.
func (o *OrderService) Get(orderURL string) (acme.ExtendedOrder, error) {
if orderURL == "" {
return acme.ExtendedOrder{}, errors.New("order[get]: empty URL")
}
var order acme.Order
_, err := o.core.postAsGet(orderURL, &order)
if err != nil {
return acme.ExtendedOrder{}, err
}
return acme.ExtendedOrder{Order: order}, nil
}
// UpdateForCSR Updates an order for a CSR.
func (o *OrderService) UpdateForCSR(orderURL string, csr []byte) (acme.ExtendedOrder, error) {
csrMsg := acme.CSRMessage{
Csr: base64.RawURLEncoding.EncodeToString(csr),
}
var order acme.Order
_, err := o.core.post(orderURL, csrMsg, &order)
if err != nil {
return acme.ExtendedOrder{}, err
}
if order.Status == acme.StatusInvalid {
return acme.ExtendedOrder{}, order.Error
}
return acme.ExtendedOrder{Order: order}, nil
}
lego-4.9.1/acme/api/order_test.go 0000664 0000000 0000000 00000003747 14340204635 0016646 0 ustar 00root root 0000000 0000000 package api
import (
"crypto/rand"
"crypto/rsa"
"encoding/json"
"io"
"net/http"
"testing"
"github.com/go-acme/lego/v4/acme"
"github.com/go-acme/lego/v4/platform/tester"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gopkg.in/square/go-jose.v2"
)
func TestOrderService_New(t *testing.T) {
mux, apiURL := tester.SetupFakeAPI(t)
// small value keeps test fast
privateKey, errK := rsa.GenerateKey(rand.Reader, 512)
require.NoError(t, errK, "Could not generate test key")
mux.HandleFunc("/newOrder", func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
return
}
body, err := readSignedBody(r, privateKey)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
order := acme.Order{}
err = json.Unmarshal(body, &order)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
err = tester.WriteJSONResponse(w, acme.Order{
Status: acme.StatusValid,
Identifiers: order.Identifiers,
})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
})
core, err := New(http.DefaultClient, "lego-test", apiURL+"/dir", "", privateKey)
require.NoError(t, err)
order, err := core.Orders.New([]string{"example.com"})
require.NoError(t, err)
expected := acme.ExtendedOrder{
Order: acme.Order{
Status: "valid",
Identifiers: []acme.Identifier{{Type: "dns", Value: "example.com"}},
},
}
assert.Equal(t, expected, order)
}
func readSignedBody(r *http.Request, privateKey *rsa.PrivateKey) ([]byte, error) {
reqBody, err := io.ReadAll(r.Body)
if err != nil {
return nil, err
}
jws, err := jose.ParseSigned(string(reqBody))
if err != nil {
return nil, err
}
body, err := jws.Verify(&jose.JSONWebKey{
Key: privateKey.Public(),
Algorithm: "RSA",
})
if err != nil {
return nil, err
}
return body, nil
}
lego-4.9.1/acme/api/service.go 0000664 0000000 0000000 00000001743 14340204635 0016126 0 ustar 00root root 0000000 0000000 package api
import (
"net/http"
"regexp"
)
type service struct {
core *Core
}
// getLink get a rel into the Link header.
func getLink(header http.Header, rel string) string {
links := getLinks(header, rel)
if len(links) < 1 {
return ""
}
return links[0]
}
func getLinks(header http.Header, rel string) []string {
linkExpr := regexp.MustCompile(`<(.+?)>(?:;[^;]+)*?;\s*rel="(.+?)"`)
var links []string
for _, link := range header["Link"] {
for _, m := range linkExpr.FindAllStringSubmatch(link, -1) {
if len(m) != 3 {
continue
}
if m[2] == rel {
links = append(links, m[1])
}
}
}
return links
}
// getLocation get the value of the header Location.
func getLocation(resp *http.Response) string {
if resp == nil {
return ""
}
return resp.Header.Get("Location")
}
// getRetryAfter get the value of the header Retry-After.
func getRetryAfter(resp *http.Response) string {
if resp == nil {
return ""
}
return resp.Header.Get("Retry-After")
}
lego-4.9.1/acme/api/service_test.go 0000664 0000000 0000000 00000002312 14340204635 0017156 0 ustar 00root root 0000000 0000000 package api
import (
"net/http"
"testing"
"github.com/stretchr/testify/assert"
)
func Test_getLink(t *testing.T) {
testCases := []struct {
desc string
header http.Header
relName string
expected string
}{
{
desc: "success",
header: http.Header{
"Link": []string{`; rel="next", ; rel="up"`},
},
relName: "up",
expected: "https://acme-staging-v02.api.letsencrypt.org/up?query",
},
{
desc: "success several lines",
header: http.Header{
"Link": []string{`; rel="next"`, `; rel="up"`},
},
relName: "up",
expected: "https://acme-staging-v02.api.letsencrypt.org/up?query",
},
{
desc: "no link",
header: http.Header{},
relName: "up",
expected: "",
},
{
desc: "no header",
relName: "up",
expected: "",
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
link := getLink(test.header, test.relName)
assert.Equal(t, test.expected, link)
})
}
}
lego-4.9.1/acme/commons.go 0000664 0000000 0000000 00000031736 14340204635 0015375 0 ustar 00root root 0000000 0000000 // Package acme contains all objects related the ACME endpoints.
// https://www.rfc-editor.org/rfc/rfc8555.html
package acme
import (
"encoding/json"
"time"
)
// ACME status values of Account, Order, Authorization and Challenge objects.
// See https://www.rfc-editor.org/rfc/rfc8555.html#section-7.1.6 for details.
const (
StatusDeactivated = "deactivated"
StatusExpired = "expired"
StatusInvalid = "invalid"
StatusPending = "pending"
StatusProcessing = "processing"
StatusReady = "ready"
StatusRevoked = "revoked"
StatusUnknown = "unknown"
StatusValid = "valid"
)
// CRL reason codes as defined in RFC 5280.
// https://datatracker.ietf.org/doc/html/rfc5280#section-5.3.1
const (
CRLReasonUnspecified uint = 0
CRLReasonKeyCompromise uint = 1
CRLReasonCACompromise uint = 2
CRLReasonAffiliationChanged uint = 3
CRLReasonSuperseded uint = 4
CRLReasonCessationOfOperation uint = 5
CRLReasonCertificateHold uint = 6
CRLReasonRemoveFromCRL uint = 8
CRLReasonPrivilegeWithdrawn uint = 9
CRLReasonAACompromise uint = 10
)
// Directory the ACME directory object.
// - https://www.rfc-editor.org/rfc/rfc8555.html#section-7.1.1
type Directory struct {
NewNonceURL string `json:"newNonce"`
NewAccountURL string `json:"newAccount"`
NewOrderURL string `json:"newOrder"`
NewAuthzURL string `json:"newAuthz"`
RevokeCertURL string `json:"revokeCert"`
KeyChangeURL string `json:"keyChange"`
Meta Meta `json:"meta"`
}
// Meta the ACME meta object (related to Directory).
// - https://www.rfc-editor.org/rfc/rfc8555.html#section-7.1.1
type Meta struct {
// termsOfService (optional, string):
// A URL identifying the current terms of service.
TermsOfService string `json:"termsOfService"`
// website (optional, string):
// An HTTP or HTTPS URL locating a website providing more information about the ACME server.
Website string `json:"website"`
// caaIdentities (optional, array of string):
// The hostnames that the ACME server recognizes as referring to itself
// for the purposes of CAA record validation as defined in [RFC6844].
// Each string MUST represent the same sequence of ASCII code points
// that the server will expect to see as the "Issuer Domain Name" in a CAA issue or issuewild property tag.
// This allows clients to determine the correct issuer domain name to use when configuring CAA records.
CaaIdentities []string `json:"caaIdentities"`
// externalAccountRequired (optional, boolean):
// If this field is present and set to "true",
// then the CA requires that all new- account requests include an "externalAccountBinding" field
// associating the new account with an external account.
ExternalAccountRequired bool `json:"externalAccountRequired"`
}
// ExtendedAccount a extended Account.
type ExtendedAccount struct {
Account
// Contains the value of the response header `Location`
Location string `json:"-"`
}
// Account the ACME account Object.
// - https://www.rfc-editor.org/rfc/rfc8555.html#section-7.1.2
// - https://www.rfc-editor.org/rfc/rfc8555.html#section-7.3
type Account struct {
// status (required, string):
// The status of this account.
// Possible values are: "valid", "deactivated", and "revoked".
// The value "deactivated" should be used to indicate client-initiated deactivation
// whereas "revoked" should be used to indicate server- initiated deactivation. (See Section 7.1.6)
Status string `json:"status,omitempty"`
// contact (optional, array of string):
// An array of URLs that the server can use to contact the client for issues related to this account.
// For example, the server may wish to notify the client about server-initiated revocation or certificate expiration.
// For information on supported URL schemes, see Section 7.3
Contact []string `json:"contact,omitempty"`
// termsOfServiceAgreed (optional, boolean):
// Including this field in a new-account request,
// with a value of true, indicates the client's agreement with the terms of service.
// This field is not updateable by the client.
TermsOfServiceAgreed bool `json:"termsOfServiceAgreed,omitempty"`
// orders (required, string):
// A URL from which a list of orders submitted by this account can be fetched via a POST-as-GET request,
// as described in Section 7.1.2.1.
Orders string `json:"orders,omitempty"`
// onlyReturnExisting (optional, boolean):
// If this field is present with the value "true",
// then the server MUST NOT create a new account if one does not already exist.
// This allows a client to look up an account URL based on an account key (see Section 7.3.1).
OnlyReturnExisting bool `json:"onlyReturnExisting,omitempty"`
// externalAccountBinding (optional, object):
// An optional field for binding the new account with an existing non-ACME account (see Section 7.3.4).
ExternalAccountBinding json.RawMessage `json:"externalAccountBinding,omitempty"`
}
// ExtendedOrder a extended Order.
type ExtendedOrder struct {
Order
// The order URL, contains the value of the response header `Location`
Location string `json:"-"`
}
// Order the ACME order Object.
// - https://www.rfc-editor.org/rfc/rfc8555.html#section-7.1.3
type Order struct {
// status (required, string):
// The status of this order.
// Possible values are: "pending", "ready", "processing", "valid", and "invalid".
Status string `json:"status,omitempty"`
// expires (optional, string):
// The timestamp after which the server will consider this order invalid,
// encoded in the format specified in RFC 3339 [RFC3339].
// This field is REQUIRED for objects with "pending" or "valid" in the status field.
Expires string `json:"expires,omitempty"`
// identifiers (required, array of object):
// An array of identifier objects that the order pertains to.
Identifiers []Identifier `json:"identifiers"`
// notBefore (optional, string):
// The requested value of the notBefore field in the certificate,
// in the date format defined in [RFC3339].
NotBefore string `json:"notBefore,omitempty"`
// notAfter (optional, string):
// The requested value of the notAfter field in the certificate,
// in the date format defined in [RFC3339].
NotAfter string `json:"notAfter,omitempty"`
// error (optional, object):
// The error that occurred while processing the order, if any.
// This field is structured as a problem document [RFC7807].
Error *ProblemDetails `json:"error,omitempty"`
// authorizations (required, array of string):
// For pending orders,
// the authorizations that the client needs to complete before the requested certificate can be issued (see Section 7.5),
// including unexpired authorizations that the client has completed in the past for identifiers specified in the order.
// The authorizations required are dictated by server policy
// and there may not be a 1:1 relationship between the order identifiers and the authorizations required.
// For final orders (in the "valid" or "invalid" state), the authorizations that were completed.
// Each entry is a URL from which an authorization can be fetched with a POST-as-GET request.
Authorizations []string `json:"authorizations,omitempty"`
// finalize (required, string):
// A URL that a CSR must be POSTed to once all of the order's authorizations are satisfied to finalize the order.
// The result of a successful finalization will be the population of the certificate URL for the order.
Finalize string `json:"finalize,omitempty"`
// certificate (optional, string):
// A URL for the certificate that has been issued in response to this order
Certificate string `json:"certificate,omitempty"`
}
// Authorization the ACME authorization object.
// - https://www.rfc-editor.org/rfc/rfc8555.html#section-7.1.4
type Authorization struct {
// status (required, string):
// The status of this authorization.
// Possible values are: "pending", "valid", "invalid", "deactivated", "expired", and "revoked".
Status string `json:"status"`
// expires (optional, string):
// The timestamp after which the server will consider this authorization invalid,
// encoded in the format specified in RFC 3339 [RFC3339].
// This field is REQUIRED for objects with "valid" in the "status" field.
Expires time.Time `json:"expires,omitempty"`
// identifier (required, object):
// The identifier that the account is authorized to represent
Identifier Identifier `json:"identifier,omitempty"`
// challenges (required, array of objects):
// For pending authorizations, the challenges that the client can fulfill in order to prove possession of the identifier.
// For valid authorizations, the challenge that was validated.
// For invalid authorizations, the challenge that was attempted and failed.
// Each array entry is an object with parameters required to validate the challenge.
// A client should attempt to fulfill one of these challenges,
// and a server should consider any one of the challenges sufficient to make the authorization valid.
Challenges []Challenge `json:"challenges,omitempty"`
// wildcard (optional, boolean):
// For authorizations created as a result of a newOrder request containing a DNS identifier
// with a value that contained a wildcard prefix this field MUST be present, and true.
Wildcard bool `json:"wildcard,omitempty"`
}
// ExtendedChallenge a extended Challenge.
type ExtendedChallenge struct {
Challenge
// Contains the value of the response header `Retry-After`
RetryAfter string `json:"-"`
// Contains the value of the response header `Link` rel="up"
AuthorizationURL string `json:"-"`
}
// Challenge the ACME challenge object.
// - https://www.rfc-editor.org/rfc/rfc8555.html#section-7.1.5
// - https://www.rfc-editor.org/rfc/rfc8555.html#section-8
type Challenge struct {
// type (required, string):
// The type of challenge encoded in the object.
Type string `json:"type"`
// url (required, string):
// The URL to which a response can be posted.
URL string `json:"url"`
// status (required, string):
// The status of this challenge. Possible values are: "pending", "processing", "valid", and "invalid".
Status string `json:"status"`
// validated (optional, string):
// The time at which the server validated this challenge,
// encoded in the format specified in RFC 3339 [RFC3339].
// This field is REQUIRED if the "status" field is "valid".
Validated time.Time `json:"validated,omitempty"`
// error (optional, object):
// Error that occurred while the server was validating the challenge, if any,
// structured as a problem document [RFC7807].
// Multiple errors can be indicated by using subproblems Section 6.7.1.
// A challenge object with an error MUST have status equal to "invalid".
Error *ProblemDetails `json:"error,omitempty"`
// token (required, string):
// A random value that uniquely identifies the challenge.
// This value MUST have at least 128 bits of entropy.
// It MUST NOT contain any characters outside the base64url alphabet,
// and MUST NOT include base64 padding characters ("=").
// See [RFC4086] for additional information on randomness requirements.
// https://www.rfc-editor.org/rfc/rfc8555.html#section-8.3
// https://www.rfc-editor.org/rfc/rfc8555.html#section-8.4
Token string `json:"token"`
// https://www.rfc-editor.org/rfc/rfc8555.html#section-8.1
KeyAuthorization string `json:"keyAuthorization"`
}
// Identifier the ACME identifier object.
// - https://www.rfc-editor.org/rfc/rfc8555.html#section-9.7.7
type Identifier struct {
Type string `json:"type"`
Value string `json:"value"`
}
// CSRMessage Certificate Signing Request.
// - https://www.rfc-editor.org/rfc/rfc8555.html#section-7.4
type CSRMessage struct {
// csr (required, string):
// A CSR encoding the parameters for the certificate being requested [RFC2986].
// The CSR is sent in the base64url-encoded version of the DER format.
// (Note: Because this field uses base64url, and does not include headers, it is different from PEM.).
Csr string `json:"csr"`
}
// RevokeCertMessage a certificate revocation message.
// - https://www.rfc-editor.org/rfc/rfc8555.html#section-7.6
// - https://www.rfc-editor.org/rfc/rfc5280.html#section-5.3.1
type RevokeCertMessage struct {
// certificate (required, string):
// The certificate to be revoked, in the base64url-encoded version of the DER format.
// (Note: Because this field uses base64url, and does not include headers, it is different from PEM.)
Certificate string `json:"certificate"`
// reason (optional, int):
// One of the revocation reasonCodes defined in Section 5.3.1 of [RFC5280] to be used when generating OCSP responses and CRLs.
// If this field is not set the server SHOULD omit the reasonCode CRL entry extension when generating OCSP responses and CRLs.
// The server MAY disallow a subset of reasonCodes from being used by the user.
// If a request contains a disallowed reasonCode the server MUST reject it with the error type "urn:ietf:params:acme:error:badRevocationReason".
// The problem document detail SHOULD indicate which reasonCodes are allowed.
Reason *uint `json:"reason,omitempty"`
}
// RawCertificate raw data of a certificate.
type RawCertificate struct {
Cert []byte
Issuer []byte
}
lego-4.9.1/acme/errors.go 0000664 0000000 0000000 00000003127 14340204635 0015227 0 ustar 00root root 0000000 0000000 package acme
import (
"fmt"
)
// Errors types.
const (
errNS = "urn:ietf:params:acme:error:"
BadNonceErr = errNS + "badNonce"
)
// ProblemDetails the problem details object.
// - https://www.rfc-editor.org/rfc/rfc7807.html#section-3.1
// - https://www.rfc-editor.org/rfc/rfc8555.html#section-7.3.3
type ProblemDetails struct {
Type string `json:"type,omitempty"`
Detail string `json:"detail,omitempty"`
HTTPStatus int `json:"status,omitempty"`
Instance string `json:"instance,omitempty"`
SubProblems []SubProblem `json:"subproblems,omitempty"`
// additional values to have a better error message (Not defined by the RFC)
Method string `json:"method,omitempty"`
URL string `json:"url,omitempty"`
}
// SubProblem a "subproblems".
// - https://www.rfc-editor.org/rfc/rfc8555.html#section-6.7.1
type SubProblem struct {
Type string `json:"type,omitempty"`
Detail string `json:"detail,omitempty"`
Identifier Identifier `json:"identifier,omitempty"`
}
func (p ProblemDetails) Error() string {
msg := fmt.Sprintf("acme: error: %d", p.HTTPStatus)
if p.Method != "" || p.URL != "" {
msg += fmt.Sprintf(" :: %s :: %s", p.Method, p.URL)
}
msg += fmt.Sprintf(" :: %s :: %s", p.Type, p.Detail)
for _, sub := range p.SubProblems {
msg += fmt.Sprintf(", problem: %q :: %s", sub.Type, sub.Detail)
}
if p.Instance != "" {
msg += ", url: " + p.Instance
}
return msg
}
// NonceError represents the error which is returned
// if the nonce sent by the client was not accepted by the server.
type NonceError struct {
*ProblemDetails
}
lego-4.9.1/certcrypto/ 0000775 0000000 0000000 00000000000 14340204635 0014652 5 ustar 00root root 0000000 0000000 lego-4.9.1/certcrypto/crypto.go 0000664 0000000 0000000 00000017302 14340204635 0016524 0 ustar 00root root 0000000 0000000 package certcrypto
import (
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/pem"
"errors"
"fmt"
"math/big"
"strings"
"time"
"golang.org/x/crypto/ocsp"
)
// Constants for all key types we support.
const (
EC256 = KeyType("P256")
EC384 = KeyType("P384")
RSA2048 = KeyType("2048")
RSA4096 = KeyType("4096")
RSA8192 = KeyType("8192")
)
const (
// OCSPGood means that the certificate is valid.
OCSPGood = ocsp.Good
// OCSPRevoked means that the certificate has been deliberately revoked.
OCSPRevoked = ocsp.Revoked
// OCSPUnknown means that the OCSP responder doesn't know about the certificate.
OCSPUnknown = ocsp.Unknown
// OCSPServerFailed means that the OCSP responder failed to process the request.
OCSPServerFailed = ocsp.ServerFailed
)
// Constants for OCSP must staple.
var (
tlsFeatureExtensionOID = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 24}
ocspMustStapleFeature = []byte{0x30, 0x03, 0x02, 0x01, 0x05}
)
// KeyType represents the key algo as well as the key size or curve to use.
type KeyType string
type DERCertificateBytes []byte
// ParsePEMBundle parses a certificate bundle from top to bottom and returns
// a slice of x509 certificates. This function will error if no certificates are found.
func ParsePEMBundle(bundle []byte) ([]*x509.Certificate, error) {
var certificates []*x509.Certificate
var certDERBlock *pem.Block
for {
certDERBlock, bundle = pem.Decode(bundle)
if certDERBlock == nil {
break
}
if certDERBlock.Type == "CERTIFICATE" {
cert, err := x509.ParseCertificate(certDERBlock.Bytes)
if err != nil {
return nil, err
}
certificates = append(certificates, cert)
}
}
if len(certificates) == 0 {
return nil, errors.New("no certificates were found while parsing the bundle")
}
return certificates, nil
}
// ParsePEMPrivateKey parses a private key from key, which is a PEM block.
// Borrowed from Go standard library, to handle various private key and PEM block types.
// https://github.com/golang/go/blob/693748e9fa385f1e2c3b91ca9acbb6c0ad2d133d/src/crypto/tls/tls.go#L291-L308
// https://github.com/golang/go/blob/693748e9fa385f1e2c3b91ca9acbb6c0ad2d133d/src/crypto/tls/tls.go#L238)
func ParsePEMPrivateKey(key []byte) (crypto.PrivateKey, error) {
keyBlockDER, _ := pem.Decode(key)
if keyBlockDER == nil {
return nil, fmt.Errorf("invalid PEM block")
}
if keyBlockDER.Type != "PRIVATE KEY" && !strings.HasSuffix(keyBlockDER.Type, " PRIVATE KEY") {
return nil, fmt.Errorf("unknown PEM header %q", keyBlockDER.Type)
}
if key, err := x509.ParsePKCS1PrivateKey(keyBlockDER.Bytes); err == nil {
return key, nil
}
if key, err := x509.ParsePKCS8PrivateKey(keyBlockDER.Bytes); err == nil {
switch key := key.(type) {
case *rsa.PrivateKey, *ecdsa.PrivateKey, ed25519.PrivateKey:
return key, nil
default:
return nil, fmt.Errorf("found unknown private key type in PKCS#8 wrapping: %T", key)
}
}
if key, err := x509.ParseECPrivateKey(keyBlockDER.Bytes); err == nil {
return key, nil
}
return nil, errors.New("failed to parse private key")
}
func GeneratePrivateKey(keyType KeyType) (crypto.PrivateKey, error) {
switch keyType {
case EC256:
return ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
case EC384:
return ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
case RSA2048:
return rsa.GenerateKey(rand.Reader, 2048)
case RSA4096:
return rsa.GenerateKey(rand.Reader, 4096)
case RSA8192:
return rsa.GenerateKey(rand.Reader, 8192)
}
return nil, fmt.Errorf("invalid KeyType: %s", keyType)
}
func GenerateCSR(privateKey crypto.PrivateKey, domain string, san []string, mustStaple bool) ([]byte, error) {
template := x509.CertificateRequest{
Subject: pkix.Name{CommonName: domain},
DNSNames: san,
}
if mustStaple {
template.ExtraExtensions = append(template.ExtraExtensions, pkix.Extension{
Id: tlsFeatureExtensionOID,
Value: ocspMustStapleFeature,
})
}
return x509.CreateCertificateRequest(rand.Reader, &template, privateKey)
}
func PEMEncode(data interface{}) []byte {
return pem.EncodeToMemory(PEMBlock(data))
}
func PEMBlock(data interface{}) *pem.Block {
var pemBlock *pem.Block
switch key := data.(type) {
case *ecdsa.PrivateKey:
keyBytes, _ := x509.MarshalECPrivateKey(key)
pemBlock = &pem.Block{Type: "EC PRIVATE KEY", Bytes: keyBytes}
case *rsa.PrivateKey:
pemBlock = &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)}
case *x509.CertificateRequest:
pemBlock = &pem.Block{Type: "CERTIFICATE REQUEST", Bytes: key.Raw}
case DERCertificateBytes:
pemBlock = &pem.Block{Type: "CERTIFICATE", Bytes: []byte(data.(DERCertificateBytes))}
}
return pemBlock
}
func pemDecode(data []byte) (*pem.Block, error) {
pemBlock, _ := pem.Decode(data)
if pemBlock == nil {
return nil, errors.New("PEM decode did not yield a valid block. Is the certificate in the right format?")
}
return pemBlock, nil
}
func PemDecodeTox509CSR(data []byte) (*x509.CertificateRequest, error) {
pemBlock, err := pemDecode(data)
if pemBlock == nil {
return nil, err
}
if pemBlock.Type != "CERTIFICATE REQUEST" && pemBlock.Type != "NEW CERTIFICATE REQUEST" {
return nil, errors.New("PEM block is not a certificate request")
}
return x509.ParseCertificateRequest(pemBlock.Bytes)
}
// ParsePEMCertificate returns Certificate from a PEM encoded certificate.
// The certificate has to be PEM encoded. Any other encodings like DER will fail.
func ParsePEMCertificate(cert []byte) (*x509.Certificate, error) {
pemBlock, err := pemDecode(cert)
if pemBlock == nil {
return nil, err
}
// from a DER encoded certificate
return x509.ParseCertificate(pemBlock.Bytes)
}
func ExtractDomains(cert *x509.Certificate) []string {
var domains []string
if cert.Subject.CommonName != "" {
domains = append(domains, cert.Subject.CommonName)
}
// Check for SAN certificate
for _, sanDomain := range cert.DNSNames {
if sanDomain == cert.Subject.CommonName {
continue
}
domains = append(domains, sanDomain)
}
return domains
}
func ExtractDomainsCSR(csr *x509.CertificateRequest) []string {
var domains []string
if csr.Subject.CommonName != "" {
domains = append(domains, csr.Subject.CommonName)
}
// loop over the SubjectAltName DNS names
for _, sanName := range csr.DNSNames {
if containsSAN(domains, sanName) {
// Duplicate; skip this name
continue
}
// Name is unique
domains = append(domains, sanName)
}
return domains
}
func containsSAN(domains []string, sanName string) bool {
for _, existingName := range domains {
if existingName == sanName {
return true
}
}
return false
}
func GeneratePemCert(privateKey *rsa.PrivateKey, domain string, extensions []pkix.Extension) ([]byte, error) {
derBytes, err := generateDerCert(privateKey, time.Time{}, domain, extensions)
if err != nil {
return nil, err
}
return pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes}), nil
}
func generateDerCert(privateKey *rsa.PrivateKey, expiration time.Time, domain string, extensions []pkix.Extension) ([]byte, error) {
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
if err != nil {
return nil, err
}
if expiration.IsZero() {
expiration = time.Now().AddDate(1, 0, 0)
}
template := x509.Certificate{
SerialNumber: serialNumber,
Subject: pkix.Name{
CommonName: "ACME Challenge TEMP",
},
NotBefore: time.Now(),
NotAfter: expiration,
KeyUsage: x509.KeyUsageKeyEncipherment,
BasicConstraintsValid: true,
DNSNames: []string{domain},
ExtraExtensions: extensions,
}
return x509.CreateCertificate(rand.Reader, &template, &template, &privateKey.PublicKey, privateKey)
}
lego-4.9.1/certcrypto/crypto_test.go 0000664 0000000 0000000 00000011134 14340204635 0017560 0 ustar 00root root 0000000 0000000 package certcrypto
import (
"bytes"
"crypto"
"crypto/rand"
"crypto/rsa"
"encoding/pem"
"regexp"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestGeneratePrivateKey(t *testing.T) {
key, err := GeneratePrivateKey(RSA2048)
require.NoError(t, err, "Error generating private key")
assert.NotNil(t, key)
}
func TestGenerateCSR(t *testing.T) {
privateKey, err := rsa.GenerateKey(rand.Reader, 512)
require.NoError(t, err, "Error generating private key")
type expected struct {
len int
error bool
}
testCases := []struct {
desc string
privateKey crypto.PrivateKey
domain string
san []string
mustStaple bool
expected expected
}{
{
desc: "without SAN",
privateKey: privateKey,
domain: "lego.acme",
mustStaple: true,
expected: expected{len: 245},
},
{
desc: "without SAN",
privateKey: privateKey,
domain: "lego.acme",
san: []string{},
mustStaple: true,
expected: expected{len: 245},
},
{
desc: "with SAN",
privateKey: privateKey,
domain: "lego.acme",
san: []string{"a.lego.acme", "b.lego.acme", "c.lego.acme"},
mustStaple: true,
expected: expected{len: 296},
},
{
desc: "no domain",
privateKey: privateKey,
domain: "",
mustStaple: true,
expected: expected{len: 225},
},
{
desc: "no domain with SAN",
privateKey: privateKey,
domain: "",
san: []string{"a.lego.acme", "b.lego.acme", "c.lego.acme"},
mustStaple: true,
expected: expected{len: 276},
},
{
desc: "private key nil",
privateKey: nil,
domain: "fizz.buzz",
mustStaple: true,
expected: expected{error: true},
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
csr, err := GenerateCSR(test.privateKey, test.domain, test.san, test.mustStaple)
if test.expected.error {
require.Error(t, err)
} else {
require.NoError(t, err, "Error generating CSR")
assert.NotEmpty(t, csr)
assert.Len(t, csr, test.expected.len)
}
})
}
}
func TestPEMEncode(t *testing.T) {
buf := bytes.NewBufferString("TestingRSAIsSoMuchFun")
reader := MockRandReader{b: buf}
key, err := rsa.GenerateKey(reader, 32)
require.NoError(t, err, "Error generating private key")
data := PEMEncode(key)
require.NotNil(t, data)
exp := regexp.MustCompile(`^-----BEGIN RSA PRIVATE KEY-----\s+\S{60,}\s+-----END RSA PRIVATE KEY-----\s+`)
assert.Regexp(t, exp, string(data))
}
func TestParsePEMCertificate(t *testing.T) {
privateKey, err := GeneratePrivateKey(RSA2048)
require.NoError(t, err, "Error generating private key")
expiration := time.Now().Add(365).Round(time.Second)
certBytes, err := generateDerCert(privateKey.(*rsa.PrivateKey), expiration, "test.com", nil)
require.NoError(t, err, "Error generating cert")
buf := bytes.NewBufferString("TestingRSAIsSoMuchFun")
// Some random string should return an error.
cert, err := ParsePEMCertificate(buf.Bytes())
require.Errorf(t, err, "returned %v", cert)
// A DER encoded certificate should return an error.
_, err = ParsePEMCertificate(certBytes)
require.Error(t, err, "Expected to return an error for DER certificates")
// A PEM encoded certificate should work ok.
pemCert := PEMEncode(DERCertificateBytes(certBytes))
cert, err = ParsePEMCertificate(pemCert)
require.NoError(t, err)
assert.Equal(t, expiration.UTC(), cert.NotAfter)
}
func TestParsePEMPrivateKey(t *testing.T) {
privateKey, err := GeneratePrivateKey(RSA2048)
require.NoError(t, err, "Error generating private key")
pemPrivateKey := PEMEncode(privateKey)
// Decoding a key should work and create an identical key to the original
decoded, err := ParsePEMPrivateKey(pemPrivateKey)
require.NoError(t, err)
assert.Equal(t, decoded, privateKey)
// Decoding a PEM block that doesn't contain a private key should error
_, err = ParsePEMPrivateKey(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE"}))
require.Errorf(t, err, "Expected to return an error for non-private key input")
// Decoding a PEM block that doesn't actually contain a key should error
_, err = ParsePEMPrivateKey(pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY"}))
require.Errorf(t, err, "Expected to return an error for empty input")
// Decoding non-PEM input should return an error
_, err = ParsePEMPrivateKey([]byte("This is not PEM"))
require.Errorf(t, err, "Expected to return an error for non-PEM input")
}
type MockRandReader struct {
b *bytes.Buffer
}
func (r MockRandReader) Read(p []byte) (int, error) {
return r.b.Read(p)
}
lego-4.9.1/certificate/ 0000775 0000000 0000000 00000000000 14340204635 0014736 5 ustar 00root root 0000000 0000000 lego-4.9.1/certificate/authorization.go 0000664 0000000 0000000 00000004047 14340204635 0020172 0 ustar 00root root 0000000 0000000 package certificate
import (
"time"
"github.com/go-acme/lego/v4/acme"
"github.com/go-acme/lego/v4/log"
)
const (
// overallRequestLimit is the overall number of request per second
// limited on the "new-reg", "new-authz" and "new-cert" endpoints.
// From the documentation the limitation is 20 requests per second,
// but using 20 as value doesn't work but 18 do.
overallRequestLimit = 18
)
func (c *Certifier) getAuthorizations(order acme.ExtendedOrder) ([]acme.Authorization, error) {
resc, errc := make(chan acme.Authorization), make(chan domainError)
delay := time.Second / overallRequestLimit
for _, authzURL := range order.Authorizations {
time.Sleep(delay)
go func(authzURL string) {
authz, err := c.core.Authorizations.Get(authzURL)
if err != nil {
errc <- domainError{Domain: authz.Identifier.Value, Error: err}
return
}
resc <- authz
}(authzURL)
}
var responses []acme.Authorization
failures := make(obtainError)
for i := 0; i < len(order.Authorizations); i++ {
select {
case res := <-resc:
responses = append(responses, res)
case err := <-errc:
failures[err.Domain] = err.Error
}
}
for i, auth := range order.Authorizations {
log.Infof("[%s] AuthURL: %s", order.Identifiers[i].Value, auth)
}
close(resc)
close(errc)
// be careful to not return an empty failures map;
// even if empty, they become non-nil error values
if len(failures) > 0 {
return responses, failures
}
return responses, nil
}
func (c *Certifier) deactivateAuthorizations(order acme.ExtendedOrder, force bool) {
for _, authzURL := range order.Authorizations {
auth, err := c.core.Authorizations.Get(authzURL)
if err != nil {
log.Infof("Unable to get the authorization for: %s", authzURL)
continue
}
if auth.Status == acme.StatusValid && !force {
log.Infof("Skipping deactivating of valid auth: %s", authzURL)
continue
}
log.Infof("Deactivating auth: %s", authzURL)
if c.core.Authorizations.Deactivate(authzURL) != nil {
log.Infof("Unable to deactivate the authorization: %s", authzURL)
}
}
}
lego-4.9.1/certificate/certificates.go 0000664 0000000 0000000 00000044752 14340204635 0017746 0 ustar 00root root 0000000 0000000 package certificate
import (
"bytes"
"crypto"
"crypto/x509"
"encoding/base64"
"errors"
"fmt"
"io"
"net/http"
"strings"
"time"
"github.com/go-acme/lego/v4/acme"
"github.com/go-acme/lego/v4/acme/api"
"github.com/go-acme/lego/v4/certcrypto"
"github.com/go-acme/lego/v4/challenge"
"github.com/go-acme/lego/v4/log"
"github.com/go-acme/lego/v4/platform/wait"
"golang.org/x/crypto/ocsp"
"golang.org/x/net/idna"
)
// maxBodySize is the maximum size of body that we will read.
const maxBodySize = 1024 * 1024
// Resource represents a CA issued certificate.
// PrivateKey, Certificate and IssuerCertificate are all
// already PEM encoded and can be directly written to disk.
// Certificate may be a certificate bundle,
// depending on the options supplied to create it.
type Resource struct {
Domain string `json:"domain"`
CertURL string `json:"certUrl"`
CertStableURL string `json:"certStableUrl"`
PrivateKey []byte `json:"-"`
Certificate []byte `json:"-"`
IssuerCertificate []byte `json:"-"`
CSR []byte `json:"-"`
}
// ObtainRequest The request to obtain certificate.
//
// The first domain in domains is used for the CommonName field of the certificate,
// all other domains are added using the Subject Alternate Names extension.
//
// A new private key is generated for every invocation of the function Obtain.
// If you do not want that you can supply your own private key in the privateKey parameter.
// If this parameter is non-nil it will be used instead of generating a new one.
//
// If `Bundle` is true, the `[]byte` contains both the issuer certificate and your issued certificate as a bundle.
//
// If `AlwaysDeactivateAuthorizations` is true, the authorizations are also relinquished if the obtain request was successful.
// See https://datatracker.ietf.org/doc/html/rfc8555#section-7.5.2.
type ObtainRequest struct {
Domains []string
Bundle bool
PrivateKey crypto.PrivateKey
MustStaple bool
PreferredChain string
AlwaysDeactivateAuthorizations bool
}
// ObtainForCSRRequest The request to obtain a certificate matching the CSR passed into it.
//
// If `Bundle` is true, the `[]byte` contains both the issuer certificate and your issued certificate as a bundle.
//
// If `AlwaysDeactivateAuthorizations` is true, the authorizations are also relinquished if the obtain request was successful.
// See https://datatracker.ietf.org/doc/html/rfc8555#section-7.5.2.
type ObtainForCSRRequest struct {
CSR *x509.CertificateRequest
Bundle bool
PreferredChain string
AlwaysDeactivateAuthorizations bool
}
type resolver interface {
Solve(authorizations []acme.Authorization) error
}
type CertifierOptions struct {
KeyType certcrypto.KeyType
Timeout time.Duration
}
// Certifier A service to obtain/renew/revoke certificates.
type Certifier struct {
core *api.Core
resolver resolver
options CertifierOptions
}
// NewCertifier creates a Certifier.
func NewCertifier(core *api.Core, resolver resolver, options CertifierOptions) *Certifier {
return &Certifier{
core: core,
resolver: resolver,
options: options,
}
}
// Obtain tries to obtain a single certificate using all domains passed into it.
//
// This function will never return a partial certificate.
// If one domain in the list fails, the whole certificate will fail.
func (c *Certifier) Obtain(request ObtainRequest) (*Resource, error) {
if len(request.Domains) == 0 {
return nil, errors.New("no domains to obtain a certificate for")
}
domains := sanitizeDomain(request.Domains)
if request.Bundle {
log.Infof("[%s] acme: Obtaining bundled SAN certificate", strings.Join(domains, ", "))
} else {
log.Infof("[%s] acme: Obtaining SAN certificate", strings.Join(domains, ", "))
}
order, err := c.core.Orders.New(domains)
if err != nil {
return nil, err
}
authz, err := c.getAuthorizations(order)
if err != nil {
// If any challenge fails, return. Do not generate partial SAN certificates.
c.deactivateAuthorizations(order, request.AlwaysDeactivateAuthorizations)
return nil, err
}
err = c.resolver.Solve(authz)
if err != nil {
// If any challenge fails, return. Do not generate partial SAN certificates.
c.deactivateAuthorizations(order, request.AlwaysDeactivateAuthorizations)
return nil, err
}
log.Infof("[%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", "))
failures := make(obtainError)
cert, err := c.getForOrder(domains, order, request.Bundle, request.PrivateKey, request.MustStaple, request.PreferredChain)
if err != nil {
for _, auth := range authz {
failures[challenge.GetTargetedDomain(auth)] = err
}
}
if request.AlwaysDeactivateAuthorizations {
c.deactivateAuthorizations(order, true)
}
// Do not return an empty failures map, because
// it would still be a non-nil error value
if len(failures) > 0 {
return cert, failures
}
return cert, nil
}
// ObtainForCSR tries to obtain a certificate matching the CSR passed into it.
//
// The domains are inferred from the CommonName and SubjectAltNames, if any.
// The private key for this CSR is not required.
//
// If bundle is true, the []byte contains both the issuer certificate and your issued certificate as a bundle.
//
// This function will never return a partial certificate.
// If one domain in the list fails, the whole certificate will fail.
func (c *Certifier) ObtainForCSR(request ObtainForCSRRequest) (*Resource, error) {
if request.CSR == nil {
return nil, errors.New("cannot obtain resource for CSR: CSR is missing")
}
// figure out what domains it concerns
// start with the common name
domains := certcrypto.ExtractDomainsCSR(request.CSR)
if request.Bundle {
log.Infof("[%s] acme: Obtaining bundled SAN certificate given a CSR", strings.Join(domains, ", "))
} else {
log.Infof("[%s] acme: Obtaining SAN certificate given a CSR", strings.Join(domains, ", "))
}
order, err := c.core.Orders.New(domains)
if err != nil {
return nil, err
}
authz, err := c.getAuthorizations(order)
if err != nil {
// If any challenge fails, return. Do not generate partial SAN certificates.
c.deactivateAuthorizations(order, request.AlwaysDeactivateAuthorizations)
return nil, err
}
err = c.resolver.Solve(authz)
if err != nil {
// If any challenge fails, return. Do not generate partial SAN certificates.
c.deactivateAuthorizations(order, request.AlwaysDeactivateAuthorizations)
return nil, err
}
log.Infof("[%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", "))
failures := make(obtainError)
cert, err := c.getForCSR(domains, order, request.Bundle, request.CSR.Raw, nil, request.PreferredChain)
if err != nil {
for _, auth := range authz {
failures[challenge.GetTargetedDomain(auth)] = err
}
}
if request.AlwaysDeactivateAuthorizations {
c.deactivateAuthorizations(order, true)
}
if cert != nil {
// Add the CSR to the certificate so that it can be used for renewals.
cert.CSR = certcrypto.PEMEncode(request.CSR)
}
// Do not return an empty failures map,
// because it would still be a non-nil error value
if len(failures) > 0 {
return cert, failures
}
return cert, nil
}
func (c *Certifier) getForOrder(domains []string, order acme.ExtendedOrder, bundle bool, privateKey crypto.PrivateKey, mustStaple bool, preferredChain string) (*Resource, error) {
if privateKey == nil {
var err error
privateKey, err = certcrypto.GeneratePrivateKey(c.options.KeyType)
if err != nil {
return nil, err
}
}
// Determine certificate name(s) based on the authorization resources
commonName := domains[0]
// RFC8555 Section 7.4 "Applying for Certificate Issuance"
// https://www.rfc-editor.org/rfc/rfc8555.html#section-7.4
// says:
// Clients SHOULD NOT make any assumptions about the sort order of
// "identifiers" or "authorizations" elements in the returned order
// object.
san := []string{commonName}
for _, auth := range order.Identifiers {
if auth.Value != commonName {
san = append(san, auth.Value)
}
}
// TODO: should the CSR be customizable?
csr, err := certcrypto.GenerateCSR(privateKey, commonName, san, mustStaple)
if err != nil {
return nil, err
}
return c.getForCSR(domains, order, bundle, csr, certcrypto.PEMEncode(privateKey), preferredChain)
}
func (c *Certifier) getForCSR(domains []string, order acme.ExtendedOrder, bundle bool, csr, privateKeyPem []byte, preferredChain string) (*Resource, error) {
respOrder, err := c.core.Orders.UpdateForCSR(order.Finalize, csr)
if err != nil {
return nil, err
}
commonName := domains[0]
certRes := &Resource{
Domain: commonName,
CertURL: respOrder.Certificate,
PrivateKey: privateKeyPem,
}
if respOrder.Status == acme.StatusValid {
// if the certificate is available right away, short cut!
ok, errR := c.checkResponse(respOrder, certRes, bundle, preferredChain)
if errR != nil {
return nil, errR
}
if ok {
return certRes, nil
}
}
timeout := c.options.Timeout
if c.options.Timeout <= 0 {
timeout = 30 * time.Second
}
err = wait.For("certificate", timeout, timeout/60, func() (bool, error) {
ord, errW := c.core.Orders.Get(order.Location)
if errW != nil {
return false, errW
}
done, errW := c.checkResponse(ord, certRes, bundle, preferredChain)
if errW != nil {
return false, errW
}
return done, nil
})
return certRes, err
}
// checkResponse checks to see if the certificate is ready and a link is contained in the response.
//
// If so, loads it into certRes and returns true.
// If the cert is not yet ready, it returns false.
//
// The certRes input should already have the Domain (common name) field populated.
//
// If bundle is true, the certificate will be bundled with the issuer's cert.
func (c *Certifier) checkResponse(order acme.ExtendedOrder, certRes *Resource, bundle bool, preferredChain string) (bool, error) {
valid, err := checkOrderStatus(order)
if err != nil || !valid {
return valid, err
}
certs, err := c.core.Certificates.GetAll(order.Certificate, bundle)
if err != nil {
return false, err
}
// Set the default certificate
certRes.IssuerCertificate = certs[order.Certificate].Issuer
certRes.Certificate = certs[order.Certificate].Cert
certRes.CertURL = order.Certificate
certRes.CertStableURL = order.Certificate
if preferredChain == "" {
log.Infof("[%s] Server responded with a certificate.", certRes.Domain)
return true, nil
}
for link, cert := range certs {
ok, err := hasPreferredChain(cert.Issuer, preferredChain)
if err != nil {
return false, err
}
if ok {
log.Infof("[%s] Server responded with a certificate for the preferred certificate chains %q.", certRes.Domain, preferredChain)
certRes.IssuerCertificate = cert.Issuer
certRes.Certificate = cert.Cert
certRes.CertURL = link
certRes.CertStableURL = link
return true, nil
}
}
log.Infof("lego has been configured to prefer certificate chains with issuer %q, but no chain from the CA matched this issuer. Using the default certificate chain instead.", preferredChain)
return true, nil
}
// Revoke takes a PEM encoded certificate or bundle and tries to revoke it at the CA.
func (c *Certifier) Revoke(cert []byte) error {
return c.RevokeWithReason(cert, nil)
}
// RevokeWithReason takes a PEM encoded certificate or bundle and tries to revoke it at the CA.
func (c *Certifier) RevokeWithReason(cert []byte, reason *uint) error {
certificates, err := certcrypto.ParsePEMBundle(cert)
if err != nil {
return err
}
x509Cert := certificates[0]
if x509Cert.IsCA {
return errors.New("certificate bundle starts with a CA certificate")
}
revokeMsg := acme.RevokeCertMessage{
Certificate: base64.RawURLEncoding.EncodeToString(x509Cert.Raw),
Reason: reason,
}
return c.core.Certificates.Revoke(revokeMsg)
}
// Renew takes a Resource and tries to renew the certificate.
//
// If the renewal process succeeds, the new certificate will be returned in a new CertResource.
// Please be aware that this function will return a new certificate in ANY case that is not an error.
// If the server does not provide us with a new cert on a GET request to the CertURL
// this function will start a new-cert flow where a new certificate gets generated.
//
// If bundle is true, the []byte contains both the issuer certificate and your issued certificate as a bundle.
//
// For private key reuse the PrivateKey property of the passed in Resource should be non-nil.
func (c *Certifier) Renew(certRes Resource, bundle, mustStaple bool, preferredChain string) (*Resource, error) {
// Input certificate is PEM encoded.
// Decode it here as we may need the decoded cert later on in the renewal process.
// The input may be a bundle or a single certificate.
certificates, err := certcrypto.ParsePEMBundle(certRes.Certificate)
if err != nil {
return nil, err
}
x509Cert := certificates[0]
if x509Cert.IsCA {
return nil, fmt.Errorf("[%s] Certificate bundle starts with a CA certificate", certRes.Domain)
}
// This is just meant to be informal for the user.
timeLeft := x509Cert.NotAfter.Sub(time.Now().UTC())
log.Infof("[%s] acme: Trying renewal with %d hours remaining", certRes.Domain, int(timeLeft.Hours()))
// We always need to request a new certificate to renew.
// Start by checking to see if the certificate was based off a CSR,
// and use that if it's defined.
if len(certRes.CSR) > 0 {
csr, errP := certcrypto.PemDecodeTox509CSR(certRes.CSR)
if errP != nil {
return nil, errP
}
return c.ObtainForCSR(ObtainForCSRRequest{
CSR: csr,
Bundle: bundle,
PreferredChain: preferredChain,
})
}
var privateKey crypto.PrivateKey
if certRes.PrivateKey != nil {
privateKey, err = certcrypto.ParsePEMPrivateKey(certRes.PrivateKey)
if err != nil {
return nil, err
}
}
query := ObtainRequest{
Domains: certcrypto.ExtractDomains(x509Cert),
Bundle: bundle,
PrivateKey: privateKey,
MustStaple: mustStaple,
PreferredChain: preferredChain,
}
return c.Obtain(query)
}
// GetOCSP takes a PEM encoded cert or cert bundle returning the raw OCSP response,
// the parsed response, and an error, if any.
//
// The returned []byte can be passed directly into the OCSPStaple property of a tls.Certificate.
// If the bundle only contains the issued certificate,
// this function will try to get the issuer certificate from the IssuingCertificateURL in the certificate.
//
// If the []byte and/or ocsp.Response return values are nil, the OCSP status may be assumed OCSPUnknown.
func (c *Certifier) GetOCSP(bundle []byte) ([]byte, *ocsp.Response, error) {
certificates, err := certcrypto.ParsePEMBundle(bundle)
if err != nil {
return nil, nil, err
}
// We expect the certificate slice to be ordered downwards the chain.
// SRV CRT -> CA. We need to pull the leaf and issuer certs out of it,
// which should always be the first two certificates.
// If there's no OCSP server listed in the leaf cert, there's nothing to do.
// And if we have only one certificate so far, we need to get the issuer cert.
issuedCert := certificates[0]
if len(issuedCert.OCSPServer) == 0 {
return nil, nil, errors.New("no OCSP server specified in cert")
}
if len(certificates) == 1 {
// TODO: build fallback. If this fails, check the remaining array entries.
if len(issuedCert.IssuingCertificateURL) == 0 {
return nil, nil, errors.New("no issuing certificate URL")
}
resp, errC := c.core.HTTPClient.Get(issuedCert.IssuingCertificateURL[0])
if errC != nil {
return nil, nil, errC
}
defer resp.Body.Close()
issuerBytes, errC := io.ReadAll(http.MaxBytesReader(nil, resp.Body, maxBodySize))
if errC != nil {
return nil, nil, errC
}
issuerCert, errC := x509.ParseCertificate(issuerBytes)
if errC != nil {
return nil, nil, errC
}
// Insert it into the slice on position 0
// We want it ordered right SRV CRT -> CA
certificates = append(certificates, issuerCert)
}
issuerCert := certificates[1]
// Finally kick off the OCSP request.
ocspReq, err := ocsp.CreateRequest(issuedCert, issuerCert, nil)
if err != nil {
return nil, nil, err
}
resp, err := c.core.HTTPClient.Post(issuedCert.OCSPServer[0], "application/ocsp-request", bytes.NewReader(ocspReq))
if err != nil {
return nil, nil, err
}
defer resp.Body.Close()
ocspResBytes, err := io.ReadAll(http.MaxBytesReader(nil, resp.Body, maxBodySize))
if err != nil {
return nil, nil, err
}
ocspRes, err := ocsp.ParseResponse(ocspResBytes, issuerCert)
if err != nil {
return nil, nil, err
}
return ocspResBytes, ocspRes, nil
}
// Get attempts to fetch the certificate at the supplied URL.
// The URL is the same as what would normally be supplied at the Resource's CertURL.
//
// The returned Resource will not have the PrivateKey and CSR fields populated as these will not be available.
//
// If bundle is true, the Certificate field in the returned Resource includes the issuer certificate.
func (c *Certifier) Get(url string, bundle bool) (*Resource, error) {
cert, issuer, err := c.core.Certificates.Get(url, bundle)
if err != nil {
return nil, err
}
// Parse the returned cert bundle so that we can grab the domain from the common name.
x509Certs, err := certcrypto.ParsePEMBundle(cert)
if err != nil {
return nil, err
}
return &Resource{
Domain: x509Certs[0].Subject.CommonName,
Certificate: cert,
IssuerCertificate: issuer,
CertURL: url,
CertStableURL: url,
}, nil
}
func hasPreferredChain(issuer []byte, preferredChain string) (bool, error) {
certs, err := certcrypto.ParsePEMBundle(issuer)
if err != nil {
return false, err
}
topCert := certs[len(certs)-1]
if topCert.Issuer.CommonName == preferredChain {
return true, nil
}
return false, nil
}
func checkOrderStatus(order acme.ExtendedOrder) (bool, error) {
switch order.Status {
case acme.StatusValid:
return true, nil
case acme.StatusInvalid:
return false, order.Error
default:
return false, nil
}
}
// https://www.rfc-editor.org/rfc/rfc8555.html#section-7.1.4
// The domain name MUST be encoded in the form in which it would appear in a certificate.
// That is, it MUST be encoded according to the rules in Section 7 of [RFC5280].
//
// https://www.rfc-editor.org/rfc/rfc5280.html#section-7
func sanitizeDomain(domains []string) []string {
var sanitizedDomains []string
for _, domain := range domains {
sanitizedDomain, err := idna.ToASCII(domain)
if err != nil {
log.Infof("skip domain %q: unable to sanitize (punnycode): %v", domain, err)
} else {
sanitizedDomains = append(sanitizedDomains, sanitizedDomain)
}
}
return sanitizedDomains
}
lego-4.9.1/certificate/certificates_test.go 0000664 0000000 0000000 00000040057 14340204635 0020777 0 ustar 00root root 0000000 0000000 package certificate
import (
"crypto/rand"
"crypto/rsa"
"encoding/pem"
"fmt"
"net/http"
"testing"
"github.com/go-acme/lego/v4/acme"
"github.com/go-acme/lego/v4/acme/api"
"github.com/go-acme/lego/v4/certcrypto"
"github.com/go-acme/lego/v4/platform/tester"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
const certResponseNoBundleMock = `-----BEGIN CERTIFICATE-----
MIIDEDCCAfigAwIBAgIHPhckqW5fPDANBgkqhkiG9w0BAQsFADAoMSYwJAYDVQQD
Ex1QZWJibGUgSW50ZXJtZWRpYXRlIENBIDM5NWU2MTAeFw0xODExMDcxNzQ2NTZa
Fw0yMzExMDcxNzQ2NTZaMBMxETAPBgNVBAMTCGFjbWUud3RmMIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwtLNKvZXD20XPUQCWYSK9rUSKxD9Eb0c9fag
bxOxOkLRTgL8LH6yln+bxc3MrHDou4PpDUdeo2CyOQu3CKsTS5mrH3NXYHu0H7p5
y3riOJTHnfkGKLT9LciGz7GkXd62nvNP57bOf5Sk4P2M+Qbxd0hPTSfu52740LSy
144cnxe2P1aDYehrEp6nYCESuyD/CtUHTo0qwJmzIy163Sp3rSs15BuCPyhySnE3
BJ8Ggv+qC6D5I1932DfSqyQJ79iq/HRm0Fn84am3KwvRlUfWxabmsUGARXoqCgnE
zcbJVOZKewv0zlQJpfac+b+Imj6Lvt1TGjIz2mVyefYgLx8gwwIDAQABo1QwUjAO
BgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwG
A1UdEwEB/wQCMAAwEwYDVR0RBAwwCoIIYWNtZS53dGYwDQYJKoZIhvcNAQELBQAD
ggEBABB/0iYhmfPSQot5RaeeovQnsqYjI5ryQK2cwzW6qcTJfv8N6+p6XkqF1+W4
jXZjrQP8MvgO9KNWlvx12vhINE6wubk88L+2piAi5uS2QejmZbXpyYB9s+oPqlk9
IDvfdlVYOqvYAhSx7ggGi+j73mjZVtjAavP6dKuu475ZCeq+NIC15RpbbikWKtYE
HBJ7BW8XQKx67iHGx8ygHTDLbREL80Bck3oUm7wIYGMoNijD6RBl25p4gYl9dzOd
TqGl5hW/1P5hMbgEzHbr4O3BfWqU2g7tV36TASy3jbC3ONFRNNYrpEZ1AL3+cUri
OPPkKtAKAbQkKbUIfsHpBZjKZMU=
-----END CERTIFICATE-----
`
const certResponseMock = `-----BEGIN CERTIFICATE-----
MIIDEDCCAfigAwIBAgIHPhckqW5fPDANBgkqhkiG9w0BAQsFADAoMSYwJAYDVQQD
Ex1QZWJibGUgSW50ZXJtZWRpYXRlIENBIDM5NWU2MTAeFw0xODExMDcxNzQ2NTZa
Fw0yMzExMDcxNzQ2NTZaMBMxETAPBgNVBAMTCGFjbWUud3RmMIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwtLNKvZXD20XPUQCWYSK9rUSKxD9Eb0c9fag
bxOxOkLRTgL8LH6yln+bxc3MrHDou4PpDUdeo2CyOQu3CKsTS5mrH3NXYHu0H7p5
y3riOJTHnfkGKLT9LciGz7GkXd62nvNP57bOf5Sk4P2M+Qbxd0hPTSfu52740LSy
144cnxe2P1aDYehrEp6nYCESuyD/CtUHTo0qwJmzIy163Sp3rSs15BuCPyhySnE3
BJ8Ggv+qC6D5I1932DfSqyQJ79iq/HRm0Fn84am3KwvRlUfWxabmsUGARXoqCgnE
zcbJVOZKewv0zlQJpfac+b+Imj6Lvt1TGjIz2mVyefYgLx8gwwIDAQABo1QwUjAO
BgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwG
A1UdEwEB/wQCMAAwEwYDVR0RBAwwCoIIYWNtZS53dGYwDQYJKoZIhvcNAQELBQAD
ggEBABB/0iYhmfPSQot5RaeeovQnsqYjI5ryQK2cwzW6qcTJfv8N6+p6XkqF1+W4
jXZjrQP8MvgO9KNWlvx12vhINE6wubk88L+2piAi5uS2QejmZbXpyYB9s+oPqlk9
IDvfdlVYOqvYAhSx7ggGi+j73mjZVtjAavP6dKuu475ZCeq+NIC15RpbbikWKtYE
HBJ7BW8XQKx67iHGx8ygHTDLbREL80Bck3oUm7wIYGMoNijD6RBl25p4gYl9dzOd
TqGl5hW/1P5hMbgEzHbr4O3BfWqU2g7tV36TASy3jbC3ONFRNNYrpEZ1AL3+cUri
OPPkKtAKAbQkKbUIfsHpBZjKZMU=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDDDCCAfSgAwIBAgIIOV5hkYJx0JwwDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UE
AxMVUGViYmxlIFJvb3QgQ0EgNTBmZmJkMB4XDTE4MTEwNzE3NDY0N1oXDTQ4MTEw
NzE3NDY0N1owKDEmMCQGA1UEAxMdUGViYmxlIEludGVybWVkaWF0ZSBDQSAzOTVl
NjEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCacwXN4LmyRTgYS8TT
SZYgz758npHiPTBDKgeN5WVmkkwW0TuN4W2zXhEmcM82uxOEjWS2drvK0+iJKneh
0fQR8ZF35dIYFe8WXTg3kEmqcizSgh4LxlOntsXvatfX/6GU/ADo3xAFoBKCijen
SRBIY65yq5m00cWx3RMIcQq1B0X8nJS0O1P7MYE/Vvidz5St/36RXVu1oWLeS5Fx
HAezW0lqxEUzvC+uLTFWC6f/CilzmI7SsPAkZBk7dO5Qs0d7m/zWF588vlGS+0pt
D1on+lU85Ma2zuAd0qmB6LY66N8pEKKtMk93wF/o4Z5i58ahbwNvTKAzz4JSRWSu
mB9LAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIChDAdBgNVHSUEFjAUBggrBgEFBQcD
AQYIKwYBBQUHAwIwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA
upU0DjzvIvoCOYKbq1RRN7rPdYad39mfjxgkeV0iOF5JoIdO6y1C7XAm9lT69Wjm
iUPvnCTMDYft40N2SvmXuuMaPOm4zjNwn4K33jw5XBnpwxC7By/Y0oV+Sl10fBsd
QqXC6H7LcSGkv+4eJbgY33P4uH5ZAy+2TkHUuZDkpufkAshzBust7nDAjfv3AIuQ
wlPoyZfI11eqyiOqRzOq+B5dIBr1JzKnEzSL6n0JLNQiPO7iN03rud/wYD3gbmcv
rzFL1KZfz+HZdnFwFW2T2gVW8L3ii1l9AJDuKzlvjUH3p6bgihVq02sjT8mx+GM2
7R4IbHGnj0BJA2vMYC4hSw==
-----END CERTIFICATE-----
`
const issuerMock = `-----BEGIN CERTIFICATE-----
MIIDDDCCAfSgAwIBAgIIOV5hkYJx0JwwDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UE
AxMVUGViYmxlIFJvb3QgQ0EgNTBmZmJkMB4XDTE4MTEwNzE3NDY0N1oXDTQ4MTEw
NzE3NDY0N1owKDEmMCQGA1UEAxMdUGViYmxlIEludGVybWVkaWF0ZSBDQSAzOTVl
NjEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCacwXN4LmyRTgYS8TT
SZYgz758npHiPTBDKgeN5WVmkkwW0TuN4W2zXhEmcM82uxOEjWS2drvK0+iJKneh
0fQR8ZF35dIYFe8WXTg3kEmqcizSgh4LxlOntsXvatfX/6GU/ADo3xAFoBKCijen
SRBIY65yq5m00cWx3RMIcQq1B0X8nJS0O1P7MYE/Vvidz5St/36RXVu1oWLeS5Fx
HAezW0lqxEUzvC+uLTFWC6f/CilzmI7SsPAkZBk7dO5Qs0d7m/zWF588vlGS+0pt
D1on+lU85Ma2zuAd0qmB6LY66N8pEKKtMk93wF/o4Z5i58ahbwNvTKAzz4JSRWSu
mB9LAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIChDAdBgNVHSUEFjAUBggrBgEFBQcD
AQYIKwYBBQUHAwIwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA
upU0DjzvIvoCOYKbq1RRN7rPdYad39mfjxgkeV0iOF5JoIdO6y1C7XAm9lT69Wjm
iUPvnCTMDYft40N2SvmXuuMaPOm4zjNwn4K33jw5XBnpwxC7By/Y0oV+Sl10fBsd
QqXC6H7LcSGkv+4eJbgY33P4uH5ZAy+2TkHUuZDkpufkAshzBust7nDAjfv3AIuQ
wlPoyZfI11eqyiOqRzOq+B5dIBr1JzKnEzSL6n0JLNQiPO7iN03rud/wYD3gbmcv
rzFL1KZfz+HZdnFwFW2T2gVW8L3ii1l9AJDuKzlvjUH3p6bgihVq02sjT8mx+GM2
7R4IbHGnj0BJA2vMYC4hSw==
-----END CERTIFICATE-----
`
const certResponseMock2 = `
-----BEGIN CERTIFICATE-----
MIIFUzCCBDugAwIBAgISA/z9btaZCSo/qlVwmJrHpoyPMA0GCSqGSIb3DQEBCwUA
MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD
ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0yMDA3MjUwNjUxNDRaFw0y
MDEwMjMwNjUxNDRaMBgxFjAUBgNVBAMTDW5hdHVyZS5nbG9iYWwwggEiMA0GCSqG
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDN/PF8lWub3i+lO3CLl/HJAM86pQH9hWej
Whci1PPNzKyEByJq2psNLCO1W1mXK3ClWSyifptCf7+AAFAOoBojPMwjaKMziw1M
BxAQiX8MzZLv4Hr4Uk08cQX31QHiEpOv4pMHqB0UpodTYY10dZnDdyJHaGKzxfJh
nQPYIVto+UegcVu9iZIDow7ugoT2Gh8nB8jOAc4wtBgmylgeAFmYR6QZ4PYSYFh0
DLZGGB1WuU/4YC5OciwTDv5EiqP3KM3NdkmGhPY0A3jcTrjN+HhcE4pYBtG1wHi8
PEuqqKyCLa3AjHq4WrZyCCkCMXPbIDS1Qt7botDmUZr/26xJZnl5AgMBAAGjggJj
MIICXzAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUF
BwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFFm72Cv7LnjVhcLqUujrykUr70lF
MB8GA1UdIwQYMBaAFKhKamMEfd265tE5t6ZFZe/zqOyhMG8GCCsGAQUFBwEBBGMw
YTAuBggrBgEFBQcwAYYiaHR0cDovL29jc3AuaW50LXgzLmxldHNlbmNyeXB0Lm9y
ZzAvBggrBgEFBQcwAoYjaHR0cDovL2NlcnQuaW50LXgzLmxldHNlbmNyeXB0Lm9y
Zy8wGAYDVR0RBBEwD4INbmF0dXJlLmdsb2JhbDBMBgNVHSAERTBDMAgGBmeBDAEC
ATA3BgsrBgEEAYLfEwEBATAoMCYGCCsGAQUFBwIBFhpodHRwOi8vY3BzLmxldHNl
bmNyeXB0Lm9yZzCCAQUGCisGAQQB1nkCBAIEgfYEgfMA8QB3ALIeBcyLos2KIE6H
ZvkruYolIGdr2vpw57JJUy3vi5BeAAABc4T006IAAAQDAEgwRgIhAPEEvCEMkekD
8XLDaxHPnJ85UZL72JqGgNK+7I/NdFNuAiEA5D78b4V1YsD8wvWz/sk6Ks8VgjED
eKGl/TyXwKEpzEIAdgBvU3asMfAxGdiZAKRRFf93FRwR2QLBACkGjbIImjfZEwAA
AXOE9NPrAAAEAwBHMEUCIAu4YFfGZIN/P+0eRG0krSddHKCSf6rqr6aVqUWkJY3F
AiEAz0HkTe0alED1gW9nEAJ1qqK1MLMjRM8SsUv9Is86+CwwDQYJKoZIhvcNAQEL
BQADggEBAGriSVi9YuBnm50w84gjlinmeGdvxgugblIoEqKoXd3d5/zx0DvW9Tm6
YGfXsvAJUSCag7dZ/s/PEu23jKNdFoaBmDaUHHKnUwbWWF7/ptYZ+YuDVGOJo8PL
CULNfUMon20rPU9smzW4BFDBZ6KmX/r4Q8cQ7FLOqKdcng0yMcqIfq4cBxEvd0uQ
pHR3AwCjAIGpV6Q9WHHiHx+SEd/Xc18Z5pXa9m3Rz4i6Mfv+AYLtnsZDxcH81cVM
7rYp80vhXM9tFd4wyrqLuaVZgYD1ylxTYpTI7sijIq4Sl984f3IPA/olN+zK6E8d
EbiufIcKeju/aSellDzzBabEo80YT4o=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow
PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD
Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O
rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq
OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b
xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw
7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD
aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV
HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG
SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69
ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr
AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz
R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5
JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo
Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ
-----END CERTIFICATE-----
`
const issuerMock2 = `-----BEGIN CERTIFICATE-----
MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow
PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD
Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O
rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq
OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b
xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw
7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD
aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV
HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG
SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69
ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr
AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz
R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5
JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo
Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ
-----END CERTIFICATE-----
`
func Test_checkResponse(t *testing.T) {
mux, apiURL := tester.SetupFakeAPI(t)
mux.HandleFunc("/certificate", func(w http.ResponseWriter, _ *http.Request) {
_, err := w.Write([]byte(certResponseMock))
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
})
key, err := rsa.GenerateKey(rand.Reader, 2048)
require.NoError(t, err, "Could not generate test key")
core, err := api.New(http.DefaultClient, "lego-test", apiURL+"/dir", "", key)
require.NoError(t, err)
certifier := NewCertifier(core, &resolverMock{}, CertifierOptions{KeyType: certcrypto.RSA2048})
order := acme.ExtendedOrder{
Order: acme.Order{
Status: acme.StatusValid,
Certificate: apiURL + "/certificate",
},
}
certRes := &Resource{}
valid, err := certifier.checkResponse(order, certRes, true, "")
require.NoError(t, err)
assert.True(t, valid)
assert.NotNil(t, certRes)
assert.Equal(t, "", certRes.Domain)
assert.Contains(t, certRes.CertStableURL, "/certificate")
assert.Contains(t, certRes.CertURL, "/certificate")
assert.Nil(t, certRes.CSR)
assert.Nil(t, certRes.PrivateKey)
assert.Equal(t, certResponseMock, string(certRes.Certificate), "Certificate")
assert.Equal(t, issuerMock, string(certRes.IssuerCertificate), "IssuerCertificate")
}
func Test_checkResponse_issuerRelUp(t *testing.T) {
mux, apiURL := tester.SetupFakeAPI(t)
mux.HandleFunc("/certificate", func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Link", "<"+apiURL+`/issuer>; rel="up"`)
_, err := w.Write([]byte(certResponseMock))
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
})
mux.HandleFunc("/issuer", func(w http.ResponseWriter, _ *http.Request) {
p, _ := pem.Decode([]byte(issuerMock))
_, err := w.Write(p.Bytes)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
})
key, err := rsa.GenerateKey(rand.Reader, 2048)
require.NoError(t, err, "Could not generate test key")
core, err := api.New(http.DefaultClient, "lego-test", apiURL+"/dir", "", key)
require.NoError(t, err)
certifier := NewCertifier(core, &resolverMock{}, CertifierOptions{KeyType: certcrypto.RSA2048})
order := acme.ExtendedOrder{
Order: acme.Order{
Status: acme.StatusValid,
Certificate: apiURL + "/certificate",
},
}
certRes := &Resource{}
valid, err := certifier.checkResponse(order, certRes, true, "")
require.NoError(t, err)
assert.True(t, valid)
assert.NotNil(t, certRes)
assert.Equal(t, "", certRes.Domain)
assert.Contains(t, certRes.CertStableURL, "/certificate")
assert.Contains(t, certRes.CertURL, "/certificate")
assert.Nil(t, certRes.CSR)
assert.Nil(t, certRes.PrivateKey)
assert.Equal(t, certResponseMock, string(certRes.Certificate), "Certificate")
assert.Equal(t, issuerMock, string(certRes.IssuerCertificate), "IssuerCertificate")
}
func Test_checkResponse_no_bundle(t *testing.T) {
mux, apiURL := tester.SetupFakeAPI(t)
mux.HandleFunc("/certificate", func(w http.ResponseWriter, _ *http.Request) {
_, err := w.Write([]byte(certResponseMock))
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
})
key, err := rsa.GenerateKey(rand.Reader, 2048)
require.NoError(t, err, "Could not generate test key")
core, err := api.New(http.DefaultClient, "lego-test", apiURL+"/dir", "", key)
require.NoError(t, err)
certifier := NewCertifier(core, &resolverMock{}, CertifierOptions{KeyType: certcrypto.RSA2048})
order := acme.ExtendedOrder{
Order: acme.Order{
Status: acme.StatusValid,
Certificate: apiURL + "/certificate",
},
}
certRes := &Resource{}
valid, err := certifier.checkResponse(order, certRes, false, "")
require.NoError(t, err)
assert.True(t, valid)
assert.NotNil(t, certRes)
assert.Equal(t, "", certRes.Domain)
assert.Contains(t, certRes.CertStableURL, "/certificate")
assert.Contains(t, certRes.CertURL, "/certificate")
assert.Nil(t, certRes.CSR)
assert.Nil(t, certRes.PrivateKey)
assert.Equal(t, certResponseNoBundleMock, string(certRes.Certificate), "Certificate")
assert.Equal(t, issuerMock, string(certRes.IssuerCertificate), "IssuerCertificate")
}
func Test_checkResponse_alternate(t *testing.T) {
mux, apiURL := tester.SetupFakeAPI(t)
mux.HandleFunc("/certificate", func(w http.ResponseWriter, _ *http.Request) {
w.Header().Add("Link", fmt.Sprintf(`<%s/certificate/1>;title="foo";rel="alternate"`, apiURL))
_, err := w.Write([]byte(certResponseMock))
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
})
mux.HandleFunc("/certificate/1", func(w http.ResponseWriter, _ *http.Request) {
_, err := w.Write([]byte(certResponseMock2))
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
})
key, err := rsa.GenerateKey(rand.Reader, 2048)
require.NoError(t, err, "Could not generate test key")
core, err := api.New(http.DefaultClient, "lego-test", apiURL+"/dir", "", key)
require.NoError(t, err)
certifier := NewCertifier(core, &resolverMock{}, CertifierOptions{KeyType: certcrypto.RSA2048})
order := acme.ExtendedOrder{
Order: acme.Order{
Status: acme.StatusValid,
Certificate: apiURL + "/certificate",
},
}
certRes := &Resource{
Domain: "example.com",
}
valid, err := certifier.checkResponse(order, certRes, true, "DST Root CA X3")
require.NoError(t, err)
assert.True(t, valid)
assert.NotNil(t, certRes)
assert.Equal(t, "example.com", certRes.Domain)
assert.Contains(t, certRes.CertStableURL, "/certificate/1")
assert.Contains(t, certRes.CertURL, "/certificate/1")
assert.Nil(t, certRes.CSR)
assert.Nil(t, certRes.PrivateKey)
assert.Equal(t, certResponseMock2, string(certRes.Certificate), "Certificate")
assert.Equal(t, issuerMock2, string(certRes.IssuerCertificate), "IssuerCertificate")
}
func Test_Get(t *testing.T) {
mux, apiURL := tester.SetupFakeAPI(t)
mux.HandleFunc("/acme/cert/test-cert", func(w http.ResponseWriter, _ *http.Request) {
_, err := w.Write([]byte(certResponseMock))
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
})
key, err := rsa.GenerateKey(rand.Reader, 2048)
require.NoError(t, err, "Could not generate test key")
core, err := api.New(http.DefaultClient, "lego-test", apiURL+"/dir", "", key)
require.NoError(t, err)
certifier := NewCertifier(core, &resolverMock{}, CertifierOptions{KeyType: certcrypto.RSA2048})
certRes, err := certifier.Get(apiURL+"/acme/cert/test-cert", true)
require.NoError(t, err)
assert.NotNil(t, certRes)
assert.Equal(t, "acme.wtf", certRes.Domain)
assert.Equal(t, apiURL+"/acme/cert/test-cert", certRes.CertStableURL)
assert.Equal(t, apiURL+"/acme/cert/test-cert", certRes.CertURL)
assert.Nil(t, certRes.CSR)
assert.Nil(t, certRes.PrivateKey)
assert.Equal(t, certResponseMock, string(certRes.Certificate), "Certificate")
assert.Equal(t, issuerMock, string(certRes.IssuerCertificate), "IssuerCertificate")
}
type resolverMock struct {
error error
}
func (r *resolverMock) Solve(_ []acme.Authorization) error {
return r.error
}
lego-4.9.1/certificate/errors.go 0000664 0000000 0000000 00000001114 14340204635 0016576 0 ustar 00root root 0000000 0000000 package certificate
import (
"bytes"
"fmt"
"sort"
)
// obtainError is returned when there are specific errors available per domain.
type obtainError map[string]error
func (e obtainError) Error() string {
buffer := bytes.NewBufferString("error: one or more domains had a problem:\n")
var domains []string
for domain := range e {
domains = append(domains, domain)
}
sort.Strings(domains)
for _, domain := range domains {
buffer.WriteString(fmt.Sprintf("[%s] %s\n", domain, e[domain]))
}
return buffer.String()
}
type domainError struct {
Domain string
Error error
}
lego-4.9.1/challenge/ 0000775 0000000 0000000 00000000000 14340204635 0014376 5 ustar 00root root 0000000 0000000 lego-4.9.1/challenge/challenges.go 0000664 0000000 0000000 00000002326 14340204635 0017035 0 ustar 00root root 0000000 0000000 package challenge
import (
"fmt"
"github.com/go-acme/lego/v4/acme"
)
// Type is a string that identifies a particular challenge type and version of ACME challenge.
type Type string
const (
// HTTP01 is the "http-01" ACME challenge https://www.rfc-editor.org/rfc/rfc8555.html#section-8.3
// Note: ChallengePath returns the URL path to fulfill this challenge.
HTTP01 = Type("http-01")
// DNS01 is the "dns-01" ACME challenge https://www.rfc-editor.org/rfc/rfc8555.html#section-8.4
// Note: GetRecord returns a DNS record which will fulfill this challenge.
DNS01 = Type("dns-01")
// TLSALPN01 is the "tls-alpn-01" ACME challenge https://www.rfc-editor.org/rfc/rfc8737.html
TLSALPN01 = Type("tls-alpn-01")
)
func (t Type) String() string {
return string(t)
}
func FindChallenge(chlgType Type, authz acme.Authorization) (acme.Challenge, error) {
for _, chlg := range authz.Challenges {
if chlg.Type == string(chlgType) {
return chlg, nil
}
}
return acme.Challenge{}, fmt.Errorf("[%s] acme: unable to find challenge %s", GetTargetedDomain(authz), chlgType)
}
func GetTargetedDomain(authz acme.Authorization) string {
if authz.Wildcard {
return "*." + authz.Identifier.Value
}
return authz.Identifier.Value
}
lego-4.9.1/challenge/dns01/ 0000775 0000000 0000000 00000000000 14340204635 0015323 5 ustar 00root root 0000000 0000000 lego-4.9.1/challenge/dns01/cname.go 0000664 0000000 0000000 00000000436 14340204635 0016740 0 ustar 00root root 0000000 0000000 package dns01
import "github.com/miekg/dns"
// Update FQDN with CNAME if any.
func updateDomainWithCName(r *dns.Msg, fqdn string) string {
for _, rr := range r.Answer {
if cn, ok := rr.(*dns.CNAME); ok {
if cn.Hdr.Name == fqdn {
return cn.Target
}
}
}
return fqdn
}
lego-4.9.1/challenge/dns01/dns_challenge.go 0000664 0000000 0000000 00000012240 14340204635 0020437 0 ustar 00root root 0000000 0000000 package dns01
import (
"crypto/sha256"
"encoding/base64"
"fmt"
"os"
"strconv"
"time"
"github.com/go-acme/lego/v4/acme"
"github.com/go-acme/lego/v4/acme/api"
"github.com/go-acme/lego/v4/challenge"
"github.com/go-acme/lego/v4/log"
"github.com/go-acme/lego/v4/platform/wait"
"github.com/miekg/dns"
)
const (
// DefaultPropagationTimeout default propagation timeout.
DefaultPropagationTimeout = 60 * time.Second
// DefaultPollingInterval default polling interval.
DefaultPollingInterval = 2 * time.Second
// DefaultTTL default TTL.
DefaultTTL = 120
)
type ValidateFunc func(core *api.Core, domain string, chlng acme.Challenge) error
type ChallengeOption func(*Challenge) error
// CondOption Conditional challenge option.
func CondOption(condition bool, opt ChallengeOption) ChallengeOption {
if !condition {
// NoOp options
return func(*Challenge) error {
return nil
}
}
return opt
}
// Challenge implements the dns-01 challenge.
type Challenge struct {
core *api.Core
validate ValidateFunc
provider challenge.Provider
preCheck preCheck
dnsTimeout time.Duration
}
func NewChallenge(core *api.Core, validate ValidateFunc, provider challenge.Provider, opts ...ChallengeOption) *Challenge {
chlg := &Challenge{
core: core,
validate: validate,
provider: provider,
preCheck: newPreCheck(),
dnsTimeout: 10 * time.Second,
}
for _, opt := range opts {
err := opt(chlg)
if err != nil {
log.Infof("challenge option error: %v", err)
}
}
return chlg
}
// PreSolve just submits the txt record to the dns provider.
// It does not validate record propagation, or do anything at all with the acme server.
func (c *Challenge) PreSolve(authz acme.Authorization) error {
domain := challenge.GetTargetedDomain(authz)
log.Infof("[%s] acme: Preparing to solve DNS-01", domain)
chlng, err := challenge.FindChallenge(challenge.DNS01, authz)
if err != nil {
return err
}
if c.provider == nil {
return fmt.Errorf("[%s] acme: no DNS Provider configured", domain)
}
// Generate the Key Authorization for the challenge
keyAuth, err := c.core.GetKeyAuthorization(chlng.Token)
if err != nil {
return err
}
err = c.provider.Present(authz.Identifier.Value, chlng.Token, keyAuth)
if err != nil {
return fmt.Errorf("[%s] acme: error presenting token: %w", domain, err)
}
return nil
}
func (c *Challenge) Solve(authz acme.Authorization) error {
domain := challenge.GetTargetedDomain(authz)
log.Infof("[%s] acme: Trying to solve DNS-01", domain)
chlng, err := challenge.FindChallenge(challenge.DNS01, authz)
if err != nil {
return err
}
// Generate the Key Authorization for the challenge
keyAuth, err := c.core.GetKeyAuthorization(chlng.Token)
if err != nil {
return err
}
fqdn, value := GetRecord(authz.Identifier.Value, keyAuth)
var timeout, interval time.Duration
switch provider := c.provider.(type) {
case challenge.ProviderTimeout:
timeout, interval = provider.Timeout()
default:
timeout, interval = DefaultPropagationTimeout, DefaultPollingInterval
}
log.Infof("[%s] acme: Checking DNS record propagation using %+v", domain, recursiveNameservers)
time.Sleep(interval)
err = wait.For("propagation", timeout, interval, func() (bool, error) {
stop, errP := c.preCheck.call(domain, fqdn, value)
if !stop || errP != nil {
log.Infof("[%s] acme: Waiting for DNS record propagation.", domain)
}
return stop, errP
})
if err != nil {
return err
}
chlng.KeyAuthorization = keyAuth
return c.validate(c.core, domain, chlng)
}
// CleanUp cleans the challenge.
func (c *Challenge) CleanUp(authz acme.Authorization) error {
log.Infof("[%s] acme: Cleaning DNS-01 challenge", challenge.GetTargetedDomain(authz))
chlng, err := challenge.FindChallenge(challenge.DNS01, authz)
if err != nil {
return err
}
keyAuth, err := c.core.GetKeyAuthorization(chlng.Token)
if err != nil {
return err
}
return c.provider.CleanUp(authz.Identifier.Value, chlng.Token, keyAuth)
}
func (c *Challenge) Sequential() (bool, time.Duration) {
if p, ok := c.provider.(sequential); ok {
return ok, p.Sequential()
}
return false, 0
}
type sequential interface {
Sequential() time.Duration
}
// GetRecord returns a DNS record which will fulfill the `dns-01` challenge.
func GetRecord(domain, keyAuth string) (fqdn, value string) {
keyAuthShaBytes := sha256.Sum256([]byte(keyAuth))
// base64URL encoding without padding
value = base64.RawURLEncoding.EncodeToString(keyAuthShaBytes[:sha256.Size])
fqdn = getChallengeFqdn(domain)
return
}
func getChallengeFqdn(domain string) string {
fqdn := fmt.Sprintf("_acme-challenge.%s.", domain)
if ok, _ := strconv.ParseBool(os.Getenv("LEGO_DISABLE_CNAME_SUPPORT")); ok {
return fqdn
}
// recursion counter so it doesn't spin out of control
for limit := 0; limit < 50; limit++ {
// Keep following CNAMEs
r, err := dnsQuery(fqdn, dns.TypeCNAME, recursiveNameservers, true)
if err != nil || r.Rcode != dns.RcodeSuccess {
// No more CNAME records to follow, exit
break
}
// Check if the domain has CNAME then use that
cname := updateDomainWithCName(r, fqdn)
if cname == fqdn {
break
}
log.Infof("Found CNAME entry for %q: %q", fqdn, cname)
fqdn = cname
}
return fqdn
}
lego-4.9.1/challenge/dns01/dns_challenge_manual.go 0000664 0000000 0000000 00000002774 14340204635 0022007 0 ustar 00root root 0000000 0000000 package dns01
import (
"bufio"
"fmt"
"os"
"time"
)
const (
dnsTemplate = `%s %d IN TXT %q`
)
// DNSProviderManual is an implementation of the ChallengeProvider interface.
type DNSProviderManual struct{}
// NewDNSProviderManual returns a DNSProviderManual instance.
func NewDNSProviderManual() (*DNSProviderManual, error) {
return &DNSProviderManual{}, nil
}
// Present prints instructions for manually creating the TXT record.
func (*DNSProviderManual) Present(domain, token, keyAuth string) error {
fqdn, value := GetRecord(domain, keyAuth)
authZone, err := FindZoneByFqdn(fqdn)
if err != nil {
return err
}
fmt.Printf("lego: Please create the following TXT record in your %s zone:\n", authZone)
fmt.Printf(dnsTemplate+"\n", fqdn, DefaultTTL, value)
fmt.Printf("lego: Press 'Enter' when you are done\n")
_, err = bufio.NewReader(os.Stdin).ReadBytes('\n')
return err
}
// CleanUp prints instructions for manually removing the TXT record.
func (*DNSProviderManual) CleanUp(domain, token, keyAuth string) error {
fqdn, _ := GetRecord(domain, keyAuth)
authZone, err := FindZoneByFqdn(fqdn)
if err != nil {
return err
}
fmt.Printf("lego: You can now remove this TXT record from your %s zone:\n", authZone)
fmt.Printf(dnsTemplate+"\n", fqdn, DefaultTTL, "...")
return nil
}
// Sequential All DNS challenges for this provider will be resolved sequentially.
// Returns the interval between each iteration.
func (d *DNSProviderManual) Sequential() time.Duration {
return DefaultPropagationTimeout
}
lego-4.9.1/challenge/dns01/dns_challenge_manual_test.go 0000664 0000000 0000000 00000002172 14340204635 0023036 0 ustar 00root root 0000000 0000000 package dns01
import (
"io"
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestDNSProviderManual(t *testing.T) {
backupStdin := os.Stdin
defer func() { os.Stdin = backupStdin }()
testCases := []struct {
desc string
input string
expectError bool
}{
{
desc: "Press enter",
input: "ok\n",
},
{
desc: "Missing enter",
input: "ok",
expectError: true,
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
file, err := os.CreateTemp("", "lego_test")
assert.NoError(t, err)
defer func() { _ = os.Remove(file.Name()) }()
_, err = file.WriteString(test.input)
assert.NoError(t, err)
_, err = file.Seek(0, io.SeekStart)
assert.NoError(t, err)
os.Stdin = file
manualProvider, err := NewDNSProviderManual()
require.NoError(t, err)
err = manualProvider.Present("example.com", "", "")
if test.expectError {
require.Error(t, err)
} else {
require.NoError(t, err)
err = manualProvider.CleanUp("example.com", "", "")
require.NoError(t, err)
}
})
}
}
lego-4.9.1/challenge/dns01/dns_challenge_test.go 0000664 0000000 0000000 00000017615 14340204635 0021511 0 ustar 00root root 0000000 0000000 package dns01
import (
"crypto/rand"
"crypto/rsa"
"errors"
"net/http"
"testing"
"time"
"github.com/go-acme/lego/v4/acme"
"github.com/go-acme/lego/v4/acme/api"
"github.com/go-acme/lego/v4/challenge"
"github.com/go-acme/lego/v4/platform/tester"
"github.com/stretchr/testify/require"
)
type providerMock struct {
present, cleanUp error
}
func (p *providerMock) Present(domain, token, keyAuth string) error { return p.present }
func (p *providerMock) CleanUp(domain, token, keyAuth string) error { return p.cleanUp }
type providerTimeoutMock struct {
present, cleanUp error
timeout, interval time.Duration
}
func (p *providerTimeoutMock) Present(domain, token, keyAuth string) error { return p.present }
func (p *providerTimeoutMock) CleanUp(domain, token, keyAuth string) error { return p.cleanUp }
func (p *providerTimeoutMock) Timeout() (time.Duration, time.Duration) { return p.timeout, p.interval }
func TestChallenge_PreSolve(t *testing.T) {
_, apiURL := tester.SetupFakeAPI(t)
privateKey, err := rsa.GenerateKey(rand.Reader, 512)
require.NoError(t, err)
core, err := api.New(http.DefaultClient, "lego-test", apiURL+"/dir", "", privateKey)
require.NoError(t, err)
testCases := []struct {
desc string
validate ValidateFunc
preCheck WrapPreCheckFunc
provider challenge.Provider
expectError bool
}{
{
desc: "success",
validate: func(_ *api.Core, _ string, _ acme.Challenge) error { return nil },
preCheck: func(_, _, _ string, _ PreCheckFunc) (bool, error) { return true, nil },
provider: &providerMock{},
},
{
desc: "validate fail",
validate: func(_ *api.Core, _ string, _ acme.Challenge) error { return errors.New("OOPS") },
preCheck: func(_, _, _ string, _ PreCheckFunc) (bool, error) { return true, nil },
provider: &providerMock{
present: nil,
cleanUp: nil,
},
},
{
desc: "preCheck fail",
validate: func(_ *api.Core, _ string, _ acme.Challenge) error { return nil },
preCheck: func(_, _, _ string, _ PreCheckFunc) (bool, error) { return false, errors.New("OOPS") },
provider: &providerTimeoutMock{
timeout: 2 * time.Second,
interval: 500 * time.Millisecond,
},
},
{
desc: "present fail",
validate: func(_ *api.Core, _ string, _ acme.Challenge) error { return nil },
preCheck: func(_, _, _ string, _ PreCheckFunc) (bool, error) { return true, nil },
provider: &providerMock{
present: errors.New("OOPS"),
},
expectError: true,
},
{
desc: "cleanUp fail",
validate: func(_ *api.Core, _ string, _ acme.Challenge) error { return nil },
preCheck: func(_, _, _ string, _ PreCheckFunc) (bool, error) { return true, nil },
provider: &providerMock{
cleanUp: errors.New("OOPS"),
},
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
chlg := NewChallenge(core, test.validate, test.provider, WrapPreCheck(test.preCheck))
authz := acme.Authorization{
Identifier: acme.Identifier{
Value: "example.com",
},
Challenges: []acme.Challenge{
{Type: challenge.DNS01.String()},
},
}
err = chlg.PreSolve(authz)
if test.expectError {
require.Error(t, err)
} else {
require.NoError(t, err)
}
})
}
}
func TestChallenge_Solve(t *testing.T) {
_, apiURL := tester.SetupFakeAPI(t)
privateKey, err := rsa.GenerateKey(rand.Reader, 512)
require.NoError(t, err)
core, err := api.New(http.DefaultClient, "lego-test", apiURL+"/dir", "", privateKey)
require.NoError(t, err)
testCases := []struct {
desc string
validate ValidateFunc
preCheck WrapPreCheckFunc
provider challenge.Provider
expectError bool
}{
{
desc: "success",
validate: func(_ *api.Core, _ string, _ acme.Challenge) error { return nil },
preCheck: func(_, _, _ string, _ PreCheckFunc) (bool, error) { return true, nil },
provider: &providerMock{},
},
{
desc: "validate fail",
validate: func(_ *api.Core, _ string, _ acme.Challenge) error { return errors.New("OOPS") },
preCheck: func(_, _, _ string, _ PreCheckFunc) (bool, error) { return true, nil },
provider: &providerMock{
present: nil,
cleanUp: nil,
},
expectError: true,
},
{
desc: "preCheck fail",
validate: func(_ *api.Core, _ string, _ acme.Challenge) error { return nil },
preCheck: func(_, _, _ string, _ PreCheckFunc) (bool, error) { return false, errors.New("OOPS") },
provider: &providerTimeoutMock{
timeout: 2 * time.Second,
interval: 500 * time.Millisecond,
},
expectError: true,
},
{
desc: "present fail",
validate: func(_ *api.Core, _ string, _ acme.Challenge) error { return nil },
preCheck: func(_, _, _ string, _ PreCheckFunc) (bool, error) { return true, nil },
provider: &providerMock{
present: errors.New("OOPS"),
},
},
{
desc: "cleanUp fail",
validate: func(_ *api.Core, _ string, _ acme.Challenge) error { return nil },
preCheck: func(_, _, _ string, _ PreCheckFunc) (bool, error) { return true, nil },
provider: &providerMock{
cleanUp: errors.New("OOPS"),
},
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
var options []ChallengeOption
if test.preCheck != nil {
options = append(options, WrapPreCheck(test.preCheck))
}
chlg := NewChallenge(core, test.validate, test.provider, options...)
authz := acme.Authorization{
Identifier: acme.Identifier{
Value: "example.com",
},
Challenges: []acme.Challenge{
{Type: challenge.DNS01.String()},
},
}
err = chlg.Solve(authz)
if test.expectError {
require.Error(t, err)
} else {
require.NoError(t, err)
}
})
}
}
func TestChallenge_CleanUp(t *testing.T) {
_, apiURL := tester.SetupFakeAPI(t)
privateKey, err := rsa.GenerateKey(rand.Reader, 512)
require.NoError(t, err)
core, err := api.New(http.DefaultClient, "lego-test", apiURL+"/dir", "", privateKey)
require.NoError(t, err)
testCases := []struct {
desc string
validate ValidateFunc
preCheck WrapPreCheckFunc
provider challenge.Provider
expectError bool
}{
{
desc: "success",
validate: func(_ *api.Core, _ string, _ acme.Challenge) error { return nil },
preCheck: func(_, _, _ string, _ PreCheckFunc) (bool, error) { return true, nil },
provider: &providerMock{},
},
{
desc: "validate fail",
validate: func(_ *api.Core, _ string, _ acme.Challenge) error { return errors.New("OOPS") },
preCheck: func(_, _, _ string, _ PreCheckFunc) (bool, error) { return true, nil },
provider: &providerMock{
present: nil,
cleanUp: nil,
},
},
{
desc: "preCheck fail",
validate: func(_ *api.Core, _ string, _ acme.Challenge) error { return nil },
preCheck: func(_, _, _ string, _ PreCheckFunc) (bool, error) { return false, errors.New("OOPS") },
provider: &providerTimeoutMock{
timeout: 2 * time.Second,
interval: 500 * time.Millisecond,
},
},
{
desc: "present fail",
validate: func(_ *api.Core, _ string, _ acme.Challenge) error { return nil },
preCheck: func(_, _, _ string, _ PreCheckFunc) (bool, error) { return true, nil },
provider: &providerMock{
present: errors.New("OOPS"),
},
},
{
desc: "cleanUp fail",
validate: func(_ *api.Core, _ string, _ acme.Challenge) error { return nil },
preCheck: func(_, _, _ string, _ PreCheckFunc) (bool, error) { return true, nil },
provider: &providerMock{
cleanUp: errors.New("OOPS"),
},
expectError: true,
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
chlg := NewChallenge(core, test.validate, test.provider, WrapPreCheck(test.preCheck))
authz := acme.Authorization{
Identifier: acme.Identifier{
Value: "example.com",
},
Challenges: []acme.Challenge{
{Type: challenge.DNS01.String()},
},
}
err = chlg.CleanUp(authz)
if test.expectError {
require.Error(t, err)
} else {
require.NoError(t, err)
}
})
}
}
lego-4.9.1/challenge/dns01/fixtures/ 0000775 0000000 0000000 00000000000 14340204635 0017174 5 ustar 00root root 0000000 0000000 lego-4.9.1/challenge/dns01/fixtures/resolv.conf.1 0000664 0000000 0000000 00000000203 14340204635 0021507 0 ustar 00root root 0000000 0000000 domain company.com
nameserver 10.200.3.249
nameserver 10.200.3.250:5353
nameserver 2001:4860:4860::8844
nameserver [10.0.0.1]:5353
lego-4.9.1/challenge/dns01/fqdn.go 0000664 0000000 0000000 00000000607 14340204635 0016605 0 ustar 00root root 0000000 0000000 package dns01
// ToFqdn converts the name into a fqdn appending a trailing dot.
func ToFqdn(name string) string {
n := len(name)
if n == 0 || name[n-1] == '.' {
return name
}
return name + "."
}
// UnFqdn converts the fqdn into a name removing the trailing dot.
func UnFqdn(name string) string {
n := len(name)
if n != 0 && name[n-1] == '.' {
return name[:n-1]
}
return name
}
lego-4.9.1/challenge/dns01/fqdn_test.go 0000664 0000000 0000000 00000002047 14340204635 0017644 0 ustar 00root root 0000000 0000000 package dns01
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestToFqdn(t *testing.T) {
testCases := []struct {
desc string
domain string
expected string
}{
{
desc: "simple",
domain: "foo.bar.com",
expected: "foo.bar.com.",
},
{
desc: "already FQDN",
domain: "foo.bar.com.",
expected: "foo.bar.com.",
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
fqdn := ToFqdn(test.domain)
assert.Equal(t, test.expected, fqdn)
})
}
}
func TestUnFqdn(t *testing.T) {
testCases := []struct {
desc string
fqdn string
expected string
}{
{
desc: "simple",
fqdn: "foo.bar.com.",
expected: "foo.bar.com",
},
{
desc: "already domain",
fqdn: "foo.bar.com",
expected: "foo.bar.com",
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
domain := UnFqdn(test.fqdn)
assert.Equal(t, test.expected, domain)
})
}
}
lego-4.9.1/challenge/dns01/nameserver.go 0000664 0000000 0000000 00000016142 14340204635 0020025 0 ustar 00root root 0000000 0000000 package dns01
import (
"errors"
"fmt"
"net"
"strings"
"sync"
"time"
"github.com/miekg/dns"
)
const defaultResolvConf = "/etc/resolv.conf"
var (
fqdnSoaCache = map[string]*soaCacheEntry{}
muFqdnSoaCache sync.Mutex
)
var defaultNameservers = []string{
"google-public-dns-a.google.com:53",
"google-public-dns-b.google.com:53",
}
// recursiveNameservers are used to pre-check DNS propagation.
var recursiveNameservers = getNameservers(defaultResolvConf, defaultNameservers)
// soaCacheEntry holds a cached SOA record (only selected fields).
type soaCacheEntry struct {
zone string // zone apex (a domain name)
primaryNs string // primary nameserver for the zone apex
expires time.Time // time when this cache entry should be evicted
}
func newSoaCacheEntry(soa *dns.SOA) *soaCacheEntry {
return &soaCacheEntry{
zone: soa.Hdr.Name,
primaryNs: soa.Ns,
expires: time.Now().Add(time.Duration(soa.Refresh) * time.Second),
}
}
// isExpired checks whether a cache entry should be considered expired.
func (cache *soaCacheEntry) isExpired() bool {
return time.Now().After(cache.expires)
}
// ClearFqdnCache clears the cache of fqdn to zone mappings. Primarily used in testing.
func ClearFqdnCache() {
muFqdnSoaCache.Lock()
fqdnSoaCache = map[string]*soaCacheEntry{}
muFqdnSoaCache.Unlock()
}
func AddDNSTimeout(timeout time.Duration) ChallengeOption {
return func(_ *Challenge) error {
dnsTimeout = timeout
return nil
}
}
func AddRecursiveNameservers(nameservers []string) ChallengeOption {
return func(_ *Challenge) error {
recursiveNameservers = ParseNameservers(nameservers)
return nil
}
}
// getNameservers attempts to get systems nameservers before falling back to the defaults.
func getNameservers(path string, defaults []string) []string {
config, err := dns.ClientConfigFromFile(path)
if err != nil || len(config.Servers) == 0 {
return defaults
}
return ParseNameservers(config.Servers)
}
func ParseNameservers(servers []string) []string {
var resolvers []string
for _, resolver := range servers {
// ensure all servers have a port number
if _, _, err := net.SplitHostPort(resolver); err != nil {
resolvers = append(resolvers, net.JoinHostPort(resolver, "53"))
} else {
resolvers = append(resolvers, resolver)
}
}
return resolvers
}
// lookupNameservers returns the authoritative nameservers for the given fqdn.
func lookupNameservers(fqdn string) ([]string, error) {
var authoritativeNss []string
zone, err := FindZoneByFqdn(fqdn)
if err != nil {
return nil, fmt.Errorf("could not determine the zone: %w", err)
}
r, err := dnsQuery(zone, dns.TypeNS, recursiveNameservers, true)
if err != nil {
return nil, err
}
for _, rr := range r.Answer {
if ns, ok := rr.(*dns.NS); ok {
authoritativeNss = append(authoritativeNss, strings.ToLower(ns.Ns))
}
}
if len(authoritativeNss) > 0 {
return authoritativeNss, nil
}
return nil, errors.New("could not determine authoritative nameservers")
}
// FindPrimaryNsByFqdn determines the primary nameserver of the zone apex for the given fqdn
// by recursing up the domain labels until the nameserver returns a SOA record in the answer section.
func FindPrimaryNsByFqdn(fqdn string) (string, error) {
return FindPrimaryNsByFqdnCustom(fqdn, recursiveNameservers)
}
// FindPrimaryNsByFqdnCustom determines the primary nameserver of the zone apex for the given fqdn
// by recursing up the domain labels until the nameserver returns a SOA record in the answer section.
func FindPrimaryNsByFqdnCustom(fqdn string, nameservers []string) (string, error) {
soa, err := lookupSoaByFqdn(fqdn, nameservers)
if err != nil {
return "", err
}
return soa.primaryNs, nil
}
// FindZoneByFqdn determines the zone apex for the given fqdn
// by recursing up the domain labels until the nameserver returns a SOA record in the answer section.
func FindZoneByFqdn(fqdn string) (string, error) {
return FindZoneByFqdnCustom(fqdn, recursiveNameservers)
}
// FindZoneByFqdnCustom determines the zone apex for the given fqdn
// by recursing up the domain labels until the nameserver returns a SOA record in the answer section.
func FindZoneByFqdnCustom(fqdn string, nameservers []string) (string, error) {
soa, err := lookupSoaByFqdn(fqdn, nameservers)
if err != nil {
return "", err
}
return soa.zone, nil
}
func lookupSoaByFqdn(fqdn string, nameservers []string) (*soaCacheEntry, error) {
muFqdnSoaCache.Lock()
defer muFqdnSoaCache.Unlock()
// Do we have it cached and is it still fresh?
if ent := fqdnSoaCache[fqdn]; ent != nil && !ent.isExpired() {
return ent, nil
}
ent, err := fetchSoaByFqdn(fqdn, nameservers)
if err != nil {
return nil, err
}
fqdnSoaCache[fqdn] = ent
return ent, nil
}
func fetchSoaByFqdn(fqdn string, nameservers []string) (*soaCacheEntry, error) {
var err error
var in *dns.Msg
labelIndexes := dns.Split(fqdn)
for _, index := range labelIndexes {
domain := fqdn[index:]
in, err = dnsQuery(domain, dns.TypeSOA, nameservers, true)
if err != nil {
continue
}
if in == nil {
continue
}
switch in.Rcode {
case dns.RcodeSuccess:
// Check if we got a SOA RR in the answer section
if len(in.Answer) == 0 {
continue
}
// CNAME records cannot/should not exist at the root of a zone.
// So we skip a domain when a CNAME is found.
if dnsMsgContainsCNAME(in) {
continue
}
for _, ans := range in.Answer {
if soa, ok := ans.(*dns.SOA); ok {
return newSoaCacheEntry(soa), nil
}
}
case dns.RcodeNameError:
// NXDOMAIN
default:
// Any response code other than NOERROR and NXDOMAIN is treated as error
return nil, fmt.Errorf("unexpected response code '%s' for %s", dns.RcodeToString[in.Rcode], domain)
}
}
return nil, fmt.Errorf("could not find the start of authority for %s%s", fqdn, formatDNSError(in, err))
}
// dnsMsgContainsCNAME checks for a CNAME answer in msg.
func dnsMsgContainsCNAME(msg *dns.Msg) bool {
for _, ans := range msg.Answer {
if _, ok := ans.(*dns.CNAME); ok {
return true
}
}
return false
}
func dnsQuery(fqdn string, rtype uint16, nameservers []string, recursive bool) (*dns.Msg, error) {
m := createDNSMsg(fqdn, rtype, recursive)
var in *dns.Msg
var err error
for _, ns := range nameservers {
in, err = sendDNSQuery(m, ns)
if err == nil && len(in.Answer) > 0 {
break
}
}
return in, err
}
func createDNSMsg(fqdn string, rtype uint16, recursive bool) *dns.Msg {
m := new(dns.Msg)
m.SetQuestion(fqdn, rtype)
m.SetEdns0(4096, false)
if !recursive {
m.RecursionDesired = false
}
return m
}
func sendDNSQuery(m *dns.Msg, ns string) (*dns.Msg, error) {
udp := &dns.Client{Net: "udp", Timeout: dnsTimeout}
in, _, err := udp.Exchange(m, ns)
if in != nil && in.Truncated {
tcp := &dns.Client{Net: "tcp", Timeout: dnsTimeout}
// If the TCP request succeeds, the err will reset to nil
in, _, err = tcp.Exchange(m, ns)
}
return in, err
}
func formatDNSError(msg *dns.Msg, err error) string {
var parts []string
if msg != nil {
parts = append(parts, dns.RcodeToString[msg.Rcode])
}
if err != nil {
parts = append(parts, err.Error())
}
if len(parts) > 0 {
return ": " + strings.Join(parts, " ")
}
return ""
}
lego-4.9.1/challenge/dns01/nameserver_test.go 0000664 0000000 0000000 00000011266 14340204635 0021066 0 ustar 00root root 0000000 0000000 package dns01
import (
"sort"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestLookupNameserversOK(t *testing.T) {
testCases := []struct {
fqdn string
nss []string
}{
{
fqdn: "books.google.com.ng.",
nss: []string{"ns1.google.com.", "ns2.google.com.", "ns3.google.com.", "ns4.google.com."},
},
{
fqdn: "www.google.com.",
nss: []string{"ns1.google.com.", "ns2.google.com.", "ns3.google.com.", "ns4.google.com."},
},
{
fqdn: "physics.georgetown.edu.",
nss: []string{"ns4.georgetown.edu.", "ns5.georgetown.edu.", "ns6.georgetown.edu."},
},
}
for _, test := range testCases {
test := test
t.Run(test.fqdn, func(t *testing.T) {
t.Parallel()
nss, err := lookupNameservers(test.fqdn)
require.NoError(t, err)
sort.Strings(nss)
sort.Strings(test.nss)
assert.EqualValues(t, test.nss, nss)
})
}
}
func TestLookupNameserversErr(t *testing.T) {
testCases := []struct {
desc string
fqdn string
error string
}{
{
desc: "invalid tld",
fqdn: "_null.n0n0.",
error: "could not determine the zone",
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
_, err := lookupNameservers(test.fqdn)
require.Error(t, err)
assert.Contains(t, err.Error(), test.error)
})
}
}
var findXByFqdnTestCases = []struct {
desc string
fqdn string
zone string
primaryNs string
nameservers []string
expectedError string
}{
{
desc: "domain is a CNAME",
fqdn: "mail.google.com.",
zone: "google.com.",
primaryNs: "ns1.google.com.",
nameservers: recursiveNameservers,
},
{
desc: "domain is a non-existent subdomain",
fqdn: "foo.google.com.",
zone: "google.com.",
primaryNs: "ns1.google.com.",
nameservers: recursiveNameservers,
},
{
desc: "domain is a eTLD",
fqdn: "example.com.ac.",
zone: "ac.",
primaryNs: "a0.nic.ac.",
nameservers: recursiveNameservers,
},
{
desc: "domain is a cross-zone CNAME",
fqdn: "cross-zone-example.assets.sh.",
zone: "assets.sh.",
primaryNs: "gina.ns.cloudflare.com.",
nameservers: recursiveNameservers,
},
{
desc: "NXDOMAIN",
fqdn: "test.lego.zz.",
zone: "lego.zz.",
nameservers: []string{"8.8.8.8:53"},
expectedError: "could not find the start of authority for test.lego.zz.: NXDOMAIN",
},
{
desc: "several non existent nameservers",
fqdn: "mail.google.com.",
zone: "google.com.",
primaryNs: "ns1.google.com.",
nameservers: []string{":7053", ":8053", "8.8.8.8:53"},
},
{
desc: "only non-existent nameservers",
fqdn: "mail.google.com.",
zone: "google.com.",
nameservers: []string{":7053", ":8053", ":9053"},
expectedError: "could not find the start of authority for mail.google.com.: read udp",
},
{
desc: "no nameservers",
fqdn: "test.ldez.com.",
zone: "ldez.com.",
nameservers: []string{},
expectedError: "could not find the start of authority for test.ldez.com.",
},
}
func TestFindZoneByFqdnCustom(t *testing.T) {
for _, test := range findXByFqdnTestCases {
t.Run(test.desc, func(t *testing.T) {
ClearFqdnCache()
zone, err := FindZoneByFqdnCustom(test.fqdn, test.nameservers)
if test.expectedError != "" {
require.Error(t, err)
assert.Contains(t, err.Error(), test.expectedError)
} else {
require.NoError(t, err)
assert.Equal(t, test.zone, zone)
}
})
}
}
func TestFindPrimaryNsByFqdnCustom(t *testing.T) {
for _, test := range findXByFqdnTestCases {
t.Run(test.desc, func(t *testing.T) {
ClearFqdnCache()
ns, err := FindPrimaryNsByFqdnCustom(test.fqdn, test.nameservers)
if test.expectedError != "" {
require.Error(t, err)
assert.Contains(t, err.Error(), test.expectedError)
} else {
require.NoError(t, err)
assert.Equal(t, test.primaryNs, ns)
}
})
}
}
func TestResolveConfServers(t *testing.T) {
testCases := []struct {
fixture string
expected []string
defaults []string
}{
{
fixture: "fixtures/resolv.conf.1",
defaults: []string{"127.0.0.1:53"},
expected: []string{"10.200.3.249:53", "10.200.3.250:5353", "[2001:4860:4860::8844]:53", "[10.0.0.1]:5353"},
},
{
fixture: "fixtures/resolv.conf.nonexistant",
defaults: []string{"127.0.0.1:53"},
expected: []string{"127.0.0.1:53"},
},
}
for _, test := range testCases {
t.Run(test.fixture, func(t *testing.T) {
result := getNameservers(test.fixture, test.defaults)
sort.Strings(result)
sort.Strings(test.expected)
assert.Equal(t, test.expected, result)
})
}
}
lego-4.9.1/challenge/dns01/nameserver_unix.go 0000664 0000000 0000000 00000000236 14340204635 0021065 0 ustar 00root root 0000000 0000000 //go:build !windows
package dns01
import "time"
// dnsTimeout is used to override the default DNS timeout of 10 seconds.
var dnsTimeout = 10 * time.Second
lego-4.9.1/challenge/dns01/nameserver_windows.go 0000664 0000000 0000000 00000000235 14340204635 0021573 0 ustar 00root root 0000000 0000000 //go:build windows
package dns01
import "time"
// dnsTimeout is used to override the default DNS timeout of 20 seconds.
var dnsTimeout = 20 * time.Second
lego-4.9.1/challenge/dns01/precheck.go 0000664 0000000 0000000 00000005626 14340204635 0017447 0 ustar 00root root 0000000 0000000 package dns01
import (
"fmt"
"net"
"strings"
"github.com/miekg/dns"
)
// PreCheckFunc checks DNS propagation before notifying ACME that the DNS challenge is ready.
type PreCheckFunc func(fqdn, value string) (bool, error)
// WrapPreCheckFunc wraps a PreCheckFunc in order to do extra operations before or after
// the main check, put it in a loop, etc.
type WrapPreCheckFunc func(domain, fqdn, value string, check PreCheckFunc) (bool, error)
// WrapPreCheck Allow to define checks before notifying ACME that the DNS challenge is ready.
func WrapPreCheck(wrap WrapPreCheckFunc) ChallengeOption {
return func(chlg *Challenge) error {
chlg.preCheck.checkFunc = wrap
return nil
}
}
func DisableCompletePropagationRequirement() ChallengeOption {
return func(chlg *Challenge) error {
chlg.preCheck.requireCompletePropagation = false
return nil
}
}
type preCheck struct {
// checks DNS propagation before notifying ACME that the DNS challenge is ready.
checkFunc WrapPreCheckFunc
// require the TXT record to be propagated to all authoritative name servers
requireCompletePropagation bool
}
func newPreCheck() preCheck {
return preCheck{
requireCompletePropagation: true,
}
}
func (p preCheck) call(domain, fqdn, value string) (bool, error) {
if p.checkFunc == nil {
return p.checkDNSPropagation(fqdn, value)
}
return p.checkFunc(domain, fqdn, value, p.checkDNSPropagation)
}
// checkDNSPropagation checks if the expected TXT record has been propagated to all authoritative nameservers.
func (p preCheck) checkDNSPropagation(fqdn, value string) (bool, error) {
// Initial attempt to resolve at the recursive NS
r, err := dnsQuery(fqdn, dns.TypeTXT, recursiveNameservers, true)
if err != nil {
return false, err
}
if !p.requireCompletePropagation {
return true, nil
}
if r.Rcode == dns.RcodeSuccess {
fqdn = updateDomainWithCName(r, fqdn)
}
authoritativeNss, err := lookupNameservers(fqdn)
if err != nil {
return false, err
}
return checkAuthoritativeNss(fqdn, value, authoritativeNss)
}
// checkAuthoritativeNss queries each of the given nameservers for the expected TXT record.
func checkAuthoritativeNss(fqdn, value string, nameservers []string) (bool, error) {
for _, ns := range nameservers {
r, err := dnsQuery(fqdn, dns.TypeTXT, []string{net.JoinHostPort(ns, "53")}, false)
if err != nil {
return false, err
}
if r.Rcode != dns.RcodeSuccess {
return false, fmt.Errorf("NS %s returned %s for %s", ns, dns.RcodeToString[r.Rcode], fqdn)
}
var records []string
var found bool
for _, rr := range r.Answer {
if txt, ok := rr.(*dns.TXT); ok {
record := strings.Join(txt.Txt, "")
records = append(records, record)
if record == value {
found = true
break
}
}
}
if !found {
return false, fmt.Errorf("NS %s did not return the expected TXT record [fqdn: %s, value: %s]: %s", ns, fqdn, value, strings.Join(records, " ,"))
}
}
return true, nil
}
lego-4.9.1/challenge/dns01/precheck_test.go 0000664 0000000 0000000 00000005135 14340204635 0020501 0 ustar 00root root 0000000 0000000 package dns01
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestCheckDNSPropagation(t *testing.T) {
testCases := []struct {
desc string
fqdn string
value string
expectError bool
}{
{
desc: "success",
fqdn: "postman-echo.com.",
value: "postman-domain-verification=c85de626cb79d941310696e06558e2e790223802f3697dfbdcaf65510152d52c",
},
{
desc: "no TXT record",
fqdn: "acme-staging.api.letsencrypt.org.",
value: "fe01=",
expectError: true,
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
ClearFqdnCache()
check := newPreCheck()
ok, err := check.checkDNSPropagation(test.fqdn, test.value)
if test.expectError {
assert.Errorf(t, err, "PreCheckDNS must failed for %s", test.fqdn)
assert.False(t, ok, "PreCheckDNS must failed for %s", test.fqdn)
} else {
assert.NoErrorf(t, err, "PreCheckDNS failed for %s", test.fqdn)
assert.True(t, ok, "PreCheckDNS failed for %s", test.fqdn)
}
})
}
}
func TestCheckAuthoritativeNss(t *testing.T) {
testCases := []struct {
desc string
fqdn, value string
ns []string
expected bool
}{
{
desc: "TXT RR w/ expected value",
fqdn: "8.8.8.8.asn.routeviews.org.",
value: "151698.8.8.024",
ns: []string{"asnums.routeviews.org."},
expected: true,
},
{
desc: "No TXT RR",
fqdn: "ns1.google.com.",
ns: []string{"ns2.google.com."},
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
ClearFqdnCache()
ok, _ := checkAuthoritativeNss(test.fqdn, test.value, test.ns)
assert.Equal(t, test.expected, ok, test.fqdn)
})
}
}
func TestCheckAuthoritativeNssErr(t *testing.T) {
testCases := []struct {
desc string
fqdn, value string
ns []string
error string
}{
{
desc: "TXT RR /w unexpected value",
fqdn: "8.8.8.8.asn.routeviews.org.",
value: "fe01=",
ns: []string{"asnums.routeviews.org."},
error: "did not return the expected TXT record",
},
{
desc: "No TXT RR",
fqdn: "ns1.google.com.",
value: "fe01=",
ns: []string{"ns2.google.com."},
error: "did not return the expected TXT record",
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
ClearFqdnCache()
_, err := checkAuthoritativeNss(test.fqdn, test.value, test.ns)
require.Error(t, err)
assert.Contains(t, err.Error(), test.error)
})
}
}
lego-4.9.1/challenge/http01/ 0000775 0000000 0000000 00000000000 14340204635 0015516 5 ustar 00root root 0000000 0000000 lego-4.9.1/challenge/http01/domain_matcher.go 0000664 0000000 0000000 00000011265 14340204635 0021024 0 ustar 00root root 0000000 0000000 package http01
import (
"fmt"
"net/http"
"strings"
)
// A domainMatcher tries to match a domain (the one we're requesting a certificate for)
// in the HTTP request coming from the ACME validation servers.
// This step is part of DNS rebind attack prevention,
// where the webserver matches incoming requests to a list of domain the server acts authoritative for.
//
// The most simple check involves finding the domain in the HTTP Host header;
// this is what hostMatcher does.
// Use it, when the http01.ProviderServer is directly reachable from the internet,
// or when it operates behind a transparent proxy.
//
// In many (reverse) proxy setups, Apache and NGINX traditionally move the Host header to a new header named X-Forwarded-Host.
// Use arbitraryMatcher("X-Forwarded-Host") in this case,
// or the appropriate header name for other proxy servers.
//
// RFC7239 has standardized the different forwarding headers into a single header named Forwarded.
// The header value has a different format, so you should use forwardedMatcher
// when the http01.ProviderServer operates behind a RFC7239 compatible proxy.
// https://www.rfc-editor.org/rfc/rfc7239.html
//
// Note: RFC7239 also reminds us, "that an HTTP list [...] may be split over multiple header fields" (section 7.1),
// meaning that
//
// X-Header: a
// X-Header: b
//
// is equal to
//
// X-Header: a, b
//
// All matcher implementations (explicitly not excluding arbitraryMatcher!)
// have in common that they only match against the first value in such lists.
type domainMatcher interface {
// matches checks whether the request is valid for the given domain.
matches(request *http.Request, domain string) bool
// name returns the header name used in the check.
// This is primarily used to create meaningful error messages.
name() string
}
// hostMatcher checks whether (*net/http).Request.Host starts with a domain name.
type hostMatcher struct{}
func (m *hostMatcher) name() string {
return "Host"
}
func (m *hostMatcher) matches(r *http.Request, domain string) bool {
return strings.HasPrefix(r.Host, domain)
}
// hostMatcher checks whether the specified (*net/http.Request).Header value starts with a domain name.
type arbitraryMatcher string
func (m arbitraryMatcher) name() string {
return string(m)
}
func (m arbitraryMatcher) matches(r *http.Request, domain string) bool {
return strings.HasPrefix(r.Header.Get(m.name()), domain)
}
// forwardedMatcher checks whether the Forwarded header contains a "host" element starting with a domain name.
// See https://www.rfc-editor.org/rfc/rfc7239.html for details.
type forwardedMatcher struct{}
func (m *forwardedMatcher) name() string {
return "Forwarded"
}
func (m *forwardedMatcher) matches(r *http.Request, domain string) bool {
fwds, err := parseForwardedHeader(r.Header.Get(m.name()))
if err != nil {
return false
}
if len(fwds) == 0 {
return false
}
host := fwds[0]["host"]
return strings.HasPrefix(host, domain)
}
// parsing requires some form of state machine.
func parseForwardedHeader(s string) (elements []map[string]string, err error) {
cur := make(map[string]string)
key := ""
val := ""
inquote := false
pos := 0
l := len(s)
for i := 0; i < l; i++ {
r := rune(s[i])
if inquote {
if r == '"' {
cur[key] = s[pos:i]
key = ""
pos = i
inquote = false
}
continue
}
switch {
case r == '"': // start of quoted-string
if key == "" {
return nil, fmt.Errorf("unexpected quoted string as pos %d", i)
}
inquote = true
pos = i + 1
case r == ';': // end of forwarded-pair
cur[key] = s[pos:i]
key = ""
i = skipWS(s, i)
pos = i + 1
case r == '=': // end of token
key = strings.ToLower(strings.TrimFunc(s[pos:i], isWS))
i = skipWS(s, i)
pos = i + 1
case r == ',': // end of forwarded-element
if key != "" {
if val == "" {
val = s[pos:i]
}
cur[key] = val
}
elements = append(elements, cur)
cur = make(map[string]string)
key = ""
val = ""
i = skipWS(s, i)
pos = i + 1
case tchar(r) || isWS(r): // valid token character or whitespace
continue
default:
return nil, fmt.Errorf("invalid token character at pos %d: %c", i, r)
}
}
if inquote {
return nil, fmt.Errorf("unterminated quoted-string at pos %d", len(s))
}
if key != "" {
if pos < len(s) {
val = s[pos:]
}
cur[key] = val
}
if len(cur) > 0 {
elements = append(elements, cur)
}
return elements, nil
}
func tchar(r rune) bool {
return strings.ContainsRune("!#$%&'*+-.^_`|~", r) ||
'0' <= r && r <= '9' ||
'a' <= r && r <= 'z' ||
'A' <= r && r <= 'Z'
}
func skipWS(s string, i int) int {
for isWS(rune(s[i+1])) {
i++
}
return i
}
func isWS(r rune) bool {
return strings.ContainsRune(" \t\v\r\n", r)
}
lego-4.9.1/challenge/http01/domain_matcher_test.go 0000664 0000000 0000000 00000003165 14340204635 0022063 0 ustar 00root root 0000000 0000000 package http01
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestParseForwardedHeader(t *testing.T) {
testCases := []struct {
name string
input string
want []map[string]string
err string
}{
{
name: "empty input",
input: "",
want: nil,
},
{
name: "simple case",
input: `for=1.2.3.4;host=example.com; by=127.0.0.1`,
want: []map[string]string{
{"for": "1.2.3.4", "host": "example.com", "by": "127.0.0.1"},
},
},
{
name: "quoted-string",
input: `foo="bar"`,
want: []map[string]string{
{"foo": "bar"},
},
},
{
name: "multiple entries",
input: `a=1, b=2; c=3, d=4`,
want: []map[string]string{
{"a": "1"},
{"b": "2", "c": "3"},
{"d": "4"},
},
},
{
name: "whitespace",
input: " a = 1,\tb\n=\r\n2,c=\" untrimmed \"",
want: []map[string]string{
{"a": "1"},
{"b": "2"},
{"c": " untrimmed "},
},
},
{
name: "unterminated quote",
input: `x="y`,
err: "unterminated quoted-string",
},
{
name: "unexpected quote",
input: `"x=y"`,
err: "unexpected quote",
},
{
name: "invalid token",
input: `a=b, ipv6=[fe80::1], x=y`,
err: "invalid token character at pos 10: [",
},
}
for _, test := range testCases {
test := test
t.Run(test.name, func(t *testing.T) {
t.Parallel()
actual, err := parseForwardedHeader(test.input)
if test.err == "" {
require.NoError(t, err)
assert.EqualValues(t, test.want, actual)
} else {
require.Error(t, err)
assert.Contains(t, err.Error(), test.err)
}
})
}
}
lego-4.9.1/challenge/http01/http_challenge.go 0000664 0000000 0000000 00000003140 14340204635 0021024 0 ustar 00root root 0000000 0000000 package http01
import (
"fmt"
"github.com/go-acme/lego/v4/acme"
"github.com/go-acme/lego/v4/acme/api"
"github.com/go-acme/lego/v4/challenge"
"github.com/go-acme/lego/v4/log"
)
type ValidateFunc func(core *api.Core, domain string, chlng acme.Challenge) error
// ChallengePath returns the URL path for the `http-01` challenge.
func ChallengePath(token string) string {
return "/.well-known/acme-challenge/" + token
}
type Challenge struct {
core *api.Core
validate ValidateFunc
provider challenge.Provider
}
func NewChallenge(core *api.Core, validate ValidateFunc, provider challenge.Provider) *Challenge {
return &Challenge{
core: core,
validate: validate,
provider: provider,
}
}
func (c *Challenge) SetProvider(provider challenge.Provider) {
c.provider = provider
}
func (c *Challenge) Solve(authz acme.Authorization) error {
domain := challenge.GetTargetedDomain(authz)
log.Infof("[%s] acme: Trying to solve HTTP-01", domain)
chlng, err := challenge.FindChallenge(challenge.HTTP01, authz)
if err != nil {
return err
}
// Generate the Key Authorization for the challenge
keyAuth, err := c.core.GetKeyAuthorization(chlng.Token)
if err != nil {
return err
}
err = c.provider.Present(authz.Identifier.Value, chlng.Token, keyAuth)
if err != nil {
return fmt.Errorf("[%s] acme: error presenting token: %w", domain, err)
}
defer func() {
err := c.provider.CleanUp(authz.Identifier.Value, chlng.Token, keyAuth)
if err != nil {
log.Warnf("[%s] acme: cleaning up failed: %v", domain, err)
}
}()
chlng.KeyAuthorization = keyAuth
return c.validate(c.core, domain, chlng)
}
lego-4.9.1/challenge/http01/http_challenge_server.go 0000664 0000000 0000000 00000010716 14340204635 0022421 0 ustar 00root root 0000000 0000000 package http01
import (
"fmt"
"io/fs"
"net"
"net/http"
"net/textproto"
"os"
"strings"
"github.com/go-acme/lego/v4/log"
)
// ProviderServer implements ChallengeProvider for `http-01` challenge.
// It may be instantiated without using the NewProviderServer function if
// you want only to use the default values.
type ProviderServer struct {
address string
network string // must be valid argument to net.Listen
socketMode fs.FileMode
matcher domainMatcher
done chan bool
listener net.Listener
}
// NewProviderServer creates a new ProviderServer on the selected interface and port.
// Setting iface and / or port to an empty string will make the server fall back to
// the "any" interface and port 80 respectively.
func NewProviderServer(iface, port string) *ProviderServer {
if port == "" {
port = "80"
}
return &ProviderServer{network: "tcp", address: net.JoinHostPort(iface, port), matcher: &hostMatcher{}}
}
func NewUnixProviderServer(socketPath string, mode fs.FileMode) *ProviderServer {
return &ProviderServer{network: "unix", address: socketPath, socketMode: mode, matcher: &hostMatcher{}}
}
// Present starts a web server and makes the token available at `ChallengePath(token)` for web requests.
func (s *ProviderServer) Present(domain, token, keyAuth string) error {
var err error
s.listener, err = net.Listen(s.network, s.GetAddress())
if err != nil {
return fmt.Errorf("could not start HTTP server for challenge: %w", err)
}
if s.network == "unix" {
if err = os.Chmod(s.address, s.socketMode); err != nil {
return fmt.Errorf("chmod %s: %w", s.address, err)
}
}
s.done = make(chan bool)
go s.serve(domain, token, keyAuth)
return nil
}
func (s *ProviderServer) GetAddress() string {
return s.address
}
// CleanUp closes the HTTP server and removes the token from `ChallengePath(token)`.
func (s *ProviderServer) CleanUp(domain, token, keyAuth string) error {
if s.listener == nil {
return nil
}
s.listener.Close()
<-s.done
return nil
}
// SetProxyHeader changes the validation of incoming requests.
// By default, s matches the "Host" header value to the domain name.
//
// When the server runs behind a proxy server, this is not the correct place to look at;
// Apache and NGINX have traditionally moved the original Host header into a new header named "X-Forwarded-Host".
// Other webservers might use different names;
// and RFC7239 has standardized a new header named "Forwarded" (with slightly different semantics).
//
// The exact behavior depends on the value of headerName:
// - "" (the empty string) and "Host" will restore the default and only check the Host header
// - "Forwarded" will look for a Forwarded header, and inspect it according to https://www.rfc-editor.org/rfc/rfc7239.html
// - any other value will check the header value with the same name.
func (s *ProviderServer) SetProxyHeader(headerName string) {
switch h := textproto.CanonicalMIMEHeaderKey(headerName); h {
case "", "Host":
s.matcher = &hostMatcher{}
case "Forwarded":
s.matcher = &forwardedMatcher{}
default:
s.matcher = arbitraryMatcher(h)
}
}
func (s *ProviderServer) serve(domain, token, keyAuth string) {
path := ChallengePath(token)
// The incoming request will be validated to prevent DNS rebind attacks.
// We only respond with the keyAuth, when we're receiving a GET requests with
// the "Host" header matching the domain (the latter is configurable though SetProxyHeader).
mux := http.NewServeMux()
mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodGet && s.matcher.matches(r, domain) {
w.Header().Set("Content-Type", "text/plain")
_, err := w.Write([]byte(keyAuth))
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
log.Infof("[%s] Served key authentication", domain)
} else {
log.Warnf("Received request for domain %s with method %s but the domain did not match any challenge. Please ensure you are passing the %s header properly.", r.Host, r.Method, s.matcher.name())
_, err := w.Write([]byte("TEST"))
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
})
httpServer := &http.Server{Handler: mux}
// Once httpServer is shut down
// we don't want any lingering connections, so disable KeepAlives.
httpServer.SetKeepAlivesEnabled(false)
err := httpServer.Serve(s.listener)
if err != nil && !strings.Contains(err.Error(), "use of closed network connection") {
log.Println(err)
}
s.done <- true
}
lego-4.9.1/challenge/http01/http_challenge_test.go 0000664 0000000 0000000 00000024102 14340204635 0022064 0 ustar 00root root 0000000 0000000 package http01
import (
"context"
"crypto/rand"
"crypto/rsa"
"fmt"
"io"
"io/fs"
"net"
"net/http"
"net/textproto"
"os"
"path/filepath"
"runtime"
"testing"
"github.com/go-acme/lego/v4/acme"
"github.com/go-acme/lego/v4/acme/api"
"github.com/go-acme/lego/v4/challenge"
"github.com/go-acme/lego/v4/platform/tester"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestProviderServer_GetAddress(t *testing.T) {
dir := t.TempDir()
t.Cleanup(func() { _ = os.RemoveAll(dir) })
sock := filepath.Join(dir, "var", "run", "test")
testCases := []struct {
desc string
server *ProviderServer
expected string
}{
{
desc: "TCP default address",
server: NewProviderServer("", ""),
expected: ":80",
},
{
desc: "TCP with explicit port",
server: NewProviderServer("", "8080"),
expected: ":8080",
},
{
desc: "TCP with host and port",
server: NewProviderServer("localhost", "8080"),
expected: "localhost:8080",
},
{
desc: "UDS socket",
server: NewUnixProviderServer(sock, fs.ModeSocket|0o666),
expected: sock,
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
address := test.server.GetAddress()
assert.Equal(t, test.expected, address)
})
}
}
func TestChallenge(t *testing.T) {
_, apiURL := tester.SetupFakeAPI(t)
providerServer := NewProviderServer("", "23457")
validate := func(_ *api.Core, _ string, chlng acme.Challenge) error {
uri := "http://localhost" + providerServer.GetAddress() + ChallengePath(chlng.Token)
resp, err := http.DefaultClient.Get(uri)
if err != nil {
return err
}
defer resp.Body.Close()
if want := "text/plain"; resp.Header.Get("Content-Type") != want {
t.Errorf("Get(%q) Content-Type: got %q, want %q", uri, resp.Header.Get("Content-Type"), want)
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return err
}
bodyStr := string(body)
if bodyStr != chlng.KeyAuthorization {
t.Errorf("Get(%q) Body: got %q, want %q", uri, bodyStr, chlng.KeyAuthorization)
}
return nil
}
privateKey, err := rsa.GenerateKey(rand.Reader, 512)
require.NoError(t, err, "Could not generate test key")
core, err := api.New(http.DefaultClient, "lego-test", apiURL+"/dir", "", privateKey)
require.NoError(t, err)
solver := NewChallenge(core, validate, providerServer)
authz := acme.Authorization{
Identifier: acme.Identifier{
Value: "localhost:23457",
},
Challenges: []acme.Challenge{
{Type: challenge.HTTP01.String(), Token: "http1"},
},
}
err = solver.Solve(authz)
require.NoError(t, err)
}
func TestChallengeUnix(t *testing.T) {
if runtime.GOOS != "linux" {
t.Skip("only for UNIX systems")
}
_, apiURL := tester.SetupFakeAPI(t)
dir := t.TempDir()
t.Cleanup(func() { _ = os.RemoveAll(dir) })
socket := filepath.Join(dir, "lego-challenge-test.sock")
providerServer := NewUnixProviderServer(socket, fs.ModeSocket|0o666)
validate := func(_ *api.Core, _ string, chlng acme.Challenge) error {
// any uri will do, as we hijack the dial
uri := "http://localhost" + ChallengePath(chlng.Token)
client := &http.Client{Transport: &http.Transport{
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
return net.Dial("unix", socket)
},
}}
resp, err := client.Get(uri)
if err != nil {
return err
}
defer resp.Body.Close()
if want := "text/plain"; resp.Header.Get("Content-Type") != want {
t.Errorf("Get(%q) Content-Type: got %q, want %q", uri, resp.Header.Get("Content-Type"), want)
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return err
}
bodyStr := string(body)
if bodyStr != chlng.KeyAuthorization {
t.Errorf("Get(%q) Body: got %q, want %q", uri, bodyStr, chlng.KeyAuthorization)
}
return nil
}
privateKey, err := rsa.GenerateKey(rand.Reader, 512)
require.NoError(t, err, "Could not generate test key")
core, err := api.New(http.DefaultClient, "lego-test", apiURL+"/dir", "", privateKey)
require.NoError(t, err)
solver := NewChallenge(core, validate, providerServer)
authz := acme.Authorization{
Identifier: acme.Identifier{
Value: "localhost",
},
Challenges: []acme.Challenge{
{Type: challenge.HTTP01.String(), Token: "http1"},
},
}
err = solver.Solve(authz)
require.NoError(t, err)
}
func TestChallengeInvalidPort(t *testing.T) {
_, apiURL := tester.SetupFakeAPI(t)
privateKey, err := rsa.GenerateKey(rand.Reader, 128)
require.NoError(t, err, "Could not generate test key")
core, err := api.New(http.DefaultClient, "lego-test", apiURL+"/dir", "", privateKey)
require.NoError(t, err)
validate := func(_ *api.Core, _ string, _ acme.Challenge) error { return nil }
solver := NewChallenge(core, validate, NewProviderServer("", "123456"))
authz := acme.Authorization{
Identifier: acme.Identifier{
Value: "localhost:123456",
},
Challenges: []acme.Challenge{
{Type: challenge.HTTP01.String(), Token: "http2"},
},
}
err = solver.Solve(authz)
require.Error(t, err)
assert.Contains(t, err.Error(), "invalid port")
assert.Contains(t, err.Error(), "123456")
}
type testProxyHeader struct {
name string
values []string
}
func (h *testProxyHeader) update(r *http.Request) {
if h == nil || len(h.values) == 0 {
return
}
if h.name == "Host" {
r.Host = h.values[0]
} else if h.name != "" {
r.Header[h.name] = h.values
}
}
func TestChallengeWithProxy(t *testing.T) {
h := func(name string, values ...string) *testProxyHeader {
name = textproto.CanonicalMIMEHeaderKey(name)
return &testProxyHeader{name, values}
}
const (
ok = "localhost:23457"
nook = "example.com"
)
testCases := []struct {
name string
header *testProxyHeader
extra *testProxyHeader
isErr bool
}{
// tests for hostMatcher
{
name: "no proxy",
},
{
name: "empty string",
header: h(""),
},
{
name: "empty Host",
header: h("host"),
},
{
name: "matching Host",
header: h("host", ok),
},
{
name: "Host mismatch",
header: h("host", nook),
isErr: true,
},
{
name: "Host mismatch (ignoring forwarding header)",
header: h("host", nook),
extra: h("X-Forwarded-Host", ok),
isErr: true,
},
// test for arbitraryMatcher
{
name: "matching X-Forwarded-Host",
header: h("X-Forwarded-Host", ok),
},
{
name: "matching X-Forwarded-Host (multiple fields)",
header: h("X-Forwarded-Host", ok, nook),
},
{
name: "matching X-Forwarded-Host (chain value)",
header: h("X-Forwarded-Host", ok+", "+nook),
},
{
name: "X-Forwarded-Host mismatch",
header: h("X-Forwarded-Host", nook),
extra: h("host", ok),
isErr: true,
},
{
name: "X-Forwarded-Host mismatch (multiple fields)",
header: h("X-Forwarded-Host", nook, ok),
isErr: true,
},
{
name: "matching X-Something-Else",
header: h("X-Something-Else", ok),
},
{
name: "matching X-Something-Else (multiple fields)",
header: h("X-Something-Else", ok, nook),
},
{
name: "matching X-Something-Else (chain value)",
header: h("X-Something-Else", ok+", "+nook),
},
{
name: "X-Something-Else mismatch",
header: h("X-Something-Else", nook),
isErr: true,
},
{
name: "X-Something-Else mismatch (multiple fields)",
header: h("X-Something-Else", nook, ok),
isErr: true,
},
{
name: "X-Something-Else mismatch (chain value)",
header: h("X-Something-Else", nook+", "+ok),
isErr: true,
},
// tests for forwardedHeader
{
name: "matching Forwarded",
header: h("Forwarded", fmt.Sprintf("host=%q;foo=bar", ok)),
},
{
name: "matching Forwarded (multiple fields)",
header: h("Forwarded", fmt.Sprintf("host=%q", ok), "host="+nook),
},
{
name: "matching Forwarded (chain value)",
header: h("Forwarded", fmt.Sprintf("host=%q, host=%s", ok, nook)),
},
{
name: "Forwarded mismatch",
header: h("Forwarded", "host="+nook),
isErr: true,
},
{
name: "Forwarded mismatch (missing information)",
header: h("Forwarded", "for=127.0.0.1"),
isErr: true,
},
{
name: "Forwarded mismatch (multiple fields)",
header: h("Forwarded", "host="+nook, fmt.Sprintf("host=%q", ok)),
isErr: true,
},
{
name: "Forwarded mismatch (chain value)",
header: h("Forwarded", fmt.Sprintf("host=%s, host=%q", nook, ok)),
isErr: true,
},
}
for _, test := range testCases {
t.Run(test.name, func(t *testing.T) {
testServeWithProxy(t, test.header, test.extra, test.isErr)
})
}
}
func testServeWithProxy(t *testing.T, header, extra *testProxyHeader, expectError bool) {
t.Helper()
_, apiURL := tester.SetupFakeAPI(t)
providerServer := NewProviderServer("localhost", "23457")
if header != nil {
providerServer.SetProxyHeader(header.name)
}
validate := func(_ *api.Core, _ string, chlng acme.Challenge) error {
uri := "http://" + providerServer.GetAddress() + ChallengePath(chlng.Token)
req, err := http.NewRequest(http.MethodGet, uri, nil)
if err != nil {
return err
}
header.update(req)
extra.update(req)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if want := "text/plain"; resp.Header.Get("Content-Type") != want {
return fmt.Errorf("Get(%q) Content-Type: got %q, want %q", uri, resp.Header.Get("Content-Type"), want)
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return err
}
bodyStr := string(body)
if bodyStr != chlng.KeyAuthorization {
return fmt.Errorf("Get(%q) Body: got %q, want %q", uri, bodyStr, chlng.KeyAuthorization)
}
return nil
}
privateKey, err := rsa.GenerateKey(rand.Reader, 512)
require.NoError(t, err, "Could not generate test key")
core, err := api.New(http.DefaultClient, "lego-test", apiURL+"/dir", "", privateKey)
require.NoError(t, err)
solver := NewChallenge(core, validate, providerServer)
authz := acme.Authorization{
Identifier: acme.Identifier{
Value: "localhost:23457",
},
Challenges: []acme.Challenge{
{Type: challenge.HTTP01.String(), Token: "http1"},
},
}
err = solver.Solve(authz)
if expectError {
require.Error(t, err)
} else {
require.NoError(t, err)
}
}
lego-4.9.1/challenge/provider.go 0000664 0000000 0000000 00000002036 14340204635 0016560 0 ustar 00root root 0000000 0000000 package challenge
import "time"
// Provider enables implementing a custom challenge
// provider. Present presents the solution to a challenge available to
// be solved. CleanUp will be called by the challenge if Present ends
// in a non-error state.
type Provider interface {
Present(domain, token, keyAuth string) error
CleanUp(domain, token, keyAuth string) error
}
// ProviderTimeout allows for implementing a
// Provider where an unusually long timeout is required when
// waiting for an ACME challenge to be satisfied, such as when
// checking for DNS record propagation. If an implementor of a
// Provider provides a Timeout method, then the return values
// of the Timeout method will be used when appropriate by the acme
// package. The interval value is the time between checks.
//
// The default values used for timeout and interval are 60 seconds and
// 2 seconds respectively. These are used when no Timeout method is
// defined for the Provider.
type ProviderTimeout interface {
Provider
Timeout() (timeout, interval time.Duration)
}
lego-4.9.1/challenge/resolver/ 0000775 0000000 0000000 00000000000 14340204635 0016237 5 ustar 00root root 0000000 0000000 lego-4.9.1/challenge/resolver/errors.go 0000664 0000000 0000000 00000001017 14340204635 0020101 0 ustar 00root root 0000000 0000000 package resolver
import (
"bytes"
"fmt"
"sort"
)
// obtainError is returned when there are specific errors available per domain.
type obtainError map[string]error
func (e obtainError) Error() string {
buffer := bytes.NewBufferString("error: one or more domains had a problem:\n")
var domains []string
for domain := range e {
domains = append(domains, domain)
}
sort.Strings(domains)
for _, domain := range domains {
buffer.WriteString(fmt.Sprintf("[%s] %s\n", domain, e[domain]))
}
return buffer.String()
}
lego-4.9.1/challenge/resolver/prober.go 0000664 0000000 0000000 00000011100 14340204635 0020050 0 ustar 00root root 0000000 0000000 package resolver
import (
"fmt"
"time"
"github.com/go-acme/lego/v4/acme"
"github.com/go-acme/lego/v4/challenge"
"github.com/go-acme/lego/v4/log"
)
// Interface for all challenge solvers to implement.
type solver interface {
Solve(authorization acme.Authorization) error
}
// Interface for challenges like dns, where we can set a record in advance for ALL challenges.
// This saves quite a bit of time vs creating the records and solving them serially.
type preSolver interface {
PreSolve(authorization acme.Authorization) error
}
// Interface for challenges like dns, where we can solve all the challenges before to delete them.
type cleanup interface {
CleanUp(authorization acme.Authorization) error
}
type sequential interface {
Sequential() (bool, time.Duration)
}
// an authz with the solver we have chosen and the index of the challenge associated with it.
type selectedAuthSolver struct {
authz acme.Authorization
solver solver
}
type Prober struct {
solverManager *SolverManager
}
func NewProber(solverManager *SolverManager) *Prober {
return &Prober{
solverManager: solverManager,
}
}
// Solve Looks through the challenge combinations to find a solvable match.
// Then solves the challenges in series and returns.
func (p *Prober) Solve(authorizations []acme.Authorization) error {
failures := make(obtainError)
var authSolvers []*selectedAuthSolver
var authSolversSequential []*selectedAuthSolver
// Loop through the resources, basically through the domains.
// First pass just selects a solver for each authz.
for _, authz := range authorizations {
domain := challenge.GetTargetedDomain(authz)
if authz.Status == acme.StatusValid {
// Boulder might recycle recent validated authz (see issue #267)
log.Infof("[%s] acme: authorization already valid; skipping challenge", domain)
continue
}
if solvr := p.solverManager.chooseSolver(authz); solvr != nil {
authSolver := &selectedAuthSolver{authz: authz, solver: solvr}
switch s := solvr.(type) {
case sequential:
if ok, _ := s.Sequential(); ok {
authSolversSequential = append(authSolversSequential, authSolver)
} else {
authSolvers = append(authSolvers, authSolver)
}
default:
authSolvers = append(authSolvers, authSolver)
}
} else {
failures[domain] = fmt.Errorf("[%s] acme: could not determine solvers", domain)
}
}
parallelSolve(authSolvers, failures)
sequentialSolve(authSolversSequential, failures)
// Be careful not to return an empty failures map,
// for even an empty obtainError is a non-nil error value
if len(failures) > 0 {
return failures
}
return nil
}
func sequentialSolve(authSolvers []*selectedAuthSolver, failures obtainError) {
for i, authSolver := range authSolvers {
// Submit the challenge
domain := challenge.GetTargetedDomain(authSolver.authz)
if solvr, ok := authSolver.solver.(preSolver); ok {
err := solvr.PreSolve(authSolver.authz)
if err != nil {
failures[domain] = err
cleanUp(authSolver.solver, authSolver.authz)
continue
}
}
// Solve challenge
err := authSolver.solver.Solve(authSolver.authz)
if err != nil {
failures[domain] = err
cleanUp(authSolver.solver, authSolver.authz)
continue
}
// Clean challenge
cleanUp(authSolver.solver, authSolver.authz)
if len(authSolvers)-1 > i {
solvr := authSolver.solver.(sequential)
_, interval := solvr.Sequential()
log.Infof("sequence: wait for %s", interval)
time.Sleep(interval)
}
}
}
func parallelSolve(authSolvers []*selectedAuthSolver, failures obtainError) {
// For all valid preSolvers, first submit the challenges so they have max time to propagate
for _, authSolver := range authSolvers {
authz := authSolver.authz
if solvr, ok := authSolver.solver.(preSolver); ok {
err := solvr.PreSolve(authz)
if err != nil {
failures[challenge.GetTargetedDomain(authz)] = err
}
}
}
defer func() {
// Clean all created TXT records
for _, authSolver := range authSolvers {
cleanUp(authSolver.solver, authSolver.authz)
}
}()
// Finally solve all challenges for real
for _, authSolver := range authSolvers {
authz := authSolver.authz
domain := challenge.GetTargetedDomain(authz)
if failures[domain] != nil {
// already failed in previous loop
continue
}
err := authSolver.solver.Solve(authz)
if err != nil {
failures[domain] = err
}
}
}
func cleanUp(solvr solver, authz acme.Authorization) {
if solvr, ok := solvr.(cleanup); ok {
domain := challenge.GetTargetedDomain(authz)
err := solvr.CleanUp(authz)
if err != nil {
log.Warnf("[%s] acme: cleaning up failed: %v ", domain, err)
}
}
}
lego-4.9.1/challenge/resolver/prober_mock_test.go 0000664 0000000 0000000 00000001733 14340204635 0022133 0 ustar 00root root 0000000 0000000 package resolver
import (
"time"
"github.com/go-acme/lego/v4/acme"
"github.com/go-acme/lego/v4/challenge"
)
type preSolverMock struct {
preSolve map[string]error
solve map[string]error
cleanUp map[string]error
}
func (s *preSolverMock) PreSolve(authorization acme.Authorization) error {
return s.preSolve[authorization.Identifier.Value]
}
func (s *preSolverMock) Solve(authorization acme.Authorization) error {
return s.solve[authorization.Identifier.Value]
}
func (s *preSolverMock) CleanUp(authorization acme.Authorization) error {
return s.cleanUp[authorization.Identifier.Value]
}
func createStubAuthorizationHTTP01(domain, status string) acme.Authorization {
return acme.Authorization{
Status: status,
Expires: time.Now(),
Identifier: acme.Identifier{
Type: challenge.HTTP01.String(),
Value: domain,
},
Challenges: []acme.Challenge{
{
Type: challenge.HTTP01.String(),
Validated: time.Now(),
Error: nil,
},
},
}
}
lego-4.9.1/challenge/resolver/prober_test.go 0000664 0000000 0000000 00000006415 14340204635 0021124 0 ustar 00root root 0000000 0000000 package resolver
import (
"errors"
"testing"
"github.com/go-acme/lego/v4/acme"
"github.com/go-acme/lego/v4/challenge"
"github.com/stretchr/testify/require"
)
func TestProber_Solve(t *testing.T) {
testCases := []struct {
desc string
solvers map[challenge.Type]solver
authz []acme.Authorization
expectedError string
}{
{
desc: "success",
solvers: map[challenge.Type]solver{
challenge.HTTP01: &preSolverMock{
preSolve: map[string]error{},
solve: map[string]error{},
cleanUp: map[string]error{},
},
},
authz: []acme.Authorization{
createStubAuthorizationHTTP01("acme.wtf", acme.StatusProcessing),
createStubAuthorizationHTTP01("lego.wtf", acme.StatusProcessing),
createStubAuthorizationHTTP01("mydomain.wtf", acme.StatusProcessing),
},
},
{
desc: "already valid",
solvers: map[challenge.Type]solver{
challenge.HTTP01: &preSolverMock{
preSolve: map[string]error{},
solve: map[string]error{},
cleanUp: map[string]error{},
},
},
authz: []acme.Authorization{
createStubAuthorizationHTTP01("acme.wtf", acme.StatusValid),
createStubAuthorizationHTTP01("lego.wtf", acme.StatusValid),
createStubAuthorizationHTTP01("mydomain.wtf", acme.StatusValid),
},
},
{
desc: "when preSolve fail, auth is flagged as error and skipped",
solvers: map[challenge.Type]solver{
challenge.HTTP01: &preSolverMock{
preSolve: map[string]error{
"acme.wtf": errors.New("preSolve error acme.wtf"),
},
solve: map[string]error{
"acme.wtf": errors.New("solve error acme.wtf"),
},
cleanUp: map[string]error{
"acme.wtf": errors.New("clean error acme.wtf"),
},
},
},
authz: []acme.Authorization{
createStubAuthorizationHTTP01("acme.wtf", acme.StatusProcessing),
createStubAuthorizationHTTP01("lego.wtf", acme.StatusProcessing),
createStubAuthorizationHTTP01("mydomain.wtf", acme.StatusProcessing),
},
expectedError: `error: one or more domains had a problem:
[acme.wtf] preSolve error acme.wtf
`,
},
{
desc: "errors at different stages",
solvers: map[challenge.Type]solver{
challenge.HTTP01: &preSolverMock{
preSolve: map[string]error{
"acme.wtf": errors.New("preSolve error acme.wtf"),
},
solve: map[string]error{
"acme.wtf": errors.New("solve error acme.wtf"),
"lego.wtf": errors.New("solve error lego.wtf"),
},
cleanUp: map[string]error{
"mydomain.wtf": errors.New("clean error mydomain.wtf"),
},
},
},
authz: []acme.Authorization{
createStubAuthorizationHTTP01("acme.wtf", acme.StatusProcessing),
createStubAuthorizationHTTP01("lego.wtf", acme.StatusProcessing),
createStubAuthorizationHTTP01("mydomain.wtf", acme.StatusProcessing),
},
expectedError: `error: one or more domains had a problem:
[acme.wtf] preSolve error acme.wtf
[lego.wtf] solve error lego.wtf
`,
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
prober := &Prober{
solverManager: &SolverManager{solvers: test.solvers},
}
err := prober.Solve(test.authz)
if test.expectedError != "" {
require.EqualError(t, err, test.expectedError)
} else {
require.NoError(t, err)
}
})
}
}
lego-4.9.1/challenge/resolver/solver_manager.go 0000664 0000000 0000000 00000011471 14340204635 0021576 0 ustar 00root root 0000000 0000000 package resolver
import (
"errors"
"fmt"
"sort"
"strconv"
"time"
"github.com/cenkalti/backoff/v4"
"github.com/go-acme/lego/v4/acme"
"github.com/go-acme/lego/v4/acme/api"
"github.com/go-acme/lego/v4/challenge"
"github.com/go-acme/lego/v4/challenge/dns01"
"github.com/go-acme/lego/v4/challenge/http01"
"github.com/go-acme/lego/v4/challenge/tlsalpn01"
"github.com/go-acme/lego/v4/log"
)
type byType []acme.Challenge
func (a byType) Len() int { return len(a) }
func (a byType) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a byType) Less(i, j int) bool { return a[i].Type > a[j].Type }
type SolverManager struct {
core *api.Core
solvers map[challenge.Type]solver
}
func NewSolversManager(core *api.Core) *SolverManager {
return &SolverManager{
solvers: map[challenge.Type]solver{},
core: core,
}
}
// SetHTTP01Provider specifies a custom provider p that can solve the given HTTP-01 challenge.
func (c *SolverManager) SetHTTP01Provider(p challenge.Provider) error {
c.solvers[challenge.HTTP01] = http01.NewChallenge(c.core, validate, p)
return nil
}
// SetTLSALPN01Provider specifies a custom provider p that can solve the given TLS-ALPN-01 challenge.
func (c *SolverManager) SetTLSALPN01Provider(p challenge.Provider) error {
c.solvers[challenge.TLSALPN01] = tlsalpn01.NewChallenge(c.core, validate, p)
return nil
}
// SetDNS01Provider specifies a custom provider p that can solve the given DNS-01 challenge.
func (c *SolverManager) SetDNS01Provider(p challenge.Provider, opts ...dns01.ChallengeOption) error {
c.solvers[challenge.DNS01] = dns01.NewChallenge(c.core, validate, p, opts...)
return nil
}
// Remove Remove a challenge type from the available solvers.
func (c *SolverManager) Remove(chlgType challenge.Type) {
delete(c.solvers, chlgType)
}
// Checks all challenges from the server in order and returns the first matching solver.
func (c *SolverManager) chooseSolver(authz acme.Authorization) solver {
// Allow to have a deterministic challenge order
sort.Sort(byType(authz.Challenges))
domain := challenge.GetTargetedDomain(authz)
for _, chlg := range authz.Challenges {
if solvr, ok := c.solvers[challenge.Type(chlg.Type)]; ok {
log.Infof("[%s] acme: use %s solver", domain, chlg.Type)
return solvr
}
log.Infof("[%s] acme: Could not find solver for: %s", domain, chlg.Type)
}
return nil
}
func validate(core *api.Core, domain string, chlg acme.Challenge) error {
chlng, err := core.Challenges.New(chlg.URL)
if err != nil {
return fmt.Errorf("failed to initiate challenge: %w", err)
}
valid, err := checkChallengeStatus(chlng)
if err != nil {
return err
}
if valid {
log.Infof("[%s] The server validated our request", domain)
return nil
}
ra, err := strconv.Atoi(chlng.RetryAfter)
if err != nil {
// The ACME server MUST return a Retry-After.
// If it doesn't, we'll just poll hard.
// Boulder does not implement the ability to retry challenges or the Retry-After header.
// https://github.com/letsencrypt/boulder/blob/master/docs/acme-divergences.md#section-82
ra = 5
}
initialInterval := time.Duration(ra) * time.Second
bo := backoff.NewExponentialBackOff()
bo.InitialInterval = initialInterval
bo.MaxInterval = 10 * initialInterval
bo.MaxElapsedTime = 100 * initialInterval
// After the path is sent, the ACME server will access our server.
// Repeatedly check the server for an updated status on our request.
operation := func() error {
authz, err := core.Authorizations.Get(chlng.AuthorizationURL)
if err != nil {
return backoff.Permanent(err)
}
valid, err := checkAuthorizationStatus(authz)
if err != nil {
return backoff.Permanent(err)
}
if valid {
log.Infof("[%s] The server validated our request", domain)
return nil
}
return errors.New("the server didn't respond to our request")
}
return backoff.Retry(operation, bo)
}
func checkChallengeStatus(chlng acme.ExtendedChallenge) (bool, error) {
switch chlng.Status {
case acme.StatusValid:
return true, nil
case acme.StatusPending, acme.StatusProcessing:
return false, nil
case acme.StatusInvalid:
return false, chlng.Error
default:
return false, errors.New("the server returned an unexpected state")
}
}
func checkAuthorizationStatus(authz acme.Authorization) (bool, error) {
switch authz.Status {
case acme.StatusValid:
return true, nil
case acme.StatusPending, acme.StatusProcessing:
return false, nil
case acme.StatusDeactivated, acme.StatusExpired, acme.StatusRevoked:
return false, fmt.Errorf("the authorization state %s", authz.Status)
case acme.StatusInvalid:
for _, chlg := range authz.Challenges {
if chlg.Status == acme.StatusInvalid && chlg.Error != nil {
return false, chlg.Error
}
}
return false, fmt.Errorf("the authorization state %s", authz.Status)
default:
return false, errors.New("the server returned an unexpected state")
}
}
lego-4.9.1/challenge/resolver/solver_manager_test.go 0000664 0000000 0000000 00000010336 14340204635 0022634 0 ustar 00root root 0000000 0000000 package resolver
import (
"crypto/rand"
"crypto/rsa"
"fmt"
"io"
"net/http"
"sort"
"testing"
"github.com/go-acme/lego/v4/acme"
"github.com/go-acme/lego/v4/acme/api"
"github.com/go-acme/lego/v4/platform/tester"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gopkg.in/square/go-jose.v2"
)
func TestByType(t *testing.T) {
challenges := []acme.Challenge{
{Type: "dns-01"}, {Type: "tlsalpn-01"}, {Type: "http-01"},
}
sort.Sort(byType(challenges))
expected := []acme.Challenge{
{Type: "tlsalpn-01"}, {Type: "http-01"}, {Type: "dns-01"},
}
assert.Equal(t, expected, challenges)
}
func TestValidate(t *testing.T) {
mux, apiURL := tester.SetupFakeAPI(t)
var statuses []string
privateKey, _ := rsa.GenerateKey(rand.Reader, 512)
mux.HandleFunc("/chlg", func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
return
}
if err := validateNoBody(privateKey, r); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
w.Header().Set("Link", "<"+apiURL+`/my-authz>; rel="up"`)
st := statuses[0]
statuses = statuses[1:]
chlg := &acme.Challenge{Type: "http-01", Status: st, URL: "http://example.com/", Token: "token"}
if st == acme.StatusInvalid {
chlg.Error = &acme.ProblemDetails{}
}
err := tester.WriteJSONResponse(w, chlg)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
})
mux.HandleFunc("/my-authz", func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
return
}
st := statuses[0]
statuses = statuses[1:]
authorization := acme.Authorization{
Status: st,
Challenges: []acme.Challenge{},
}
if st == acme.StatusInvalid {
chlg := acme.Challenge{
Status: acme.StatusInvalid,
Error: &acme.ProblemDetails{},
}
authorization.Challenges = append(authorization.Challenges, chlg)
}
err := tester.WriteJSONResponse(w, authorization)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
})
core, err := api.New(http.DefaultClient, "lego-test", apiURL+"/dir", "", privateKey)
require.NoError(t, err)
testCases := []struct {
name string
statuses []string
want string
}{
{
name: "POST-unexpected",
statuses: []string{"weird"},
want: "unexpected",
},
{
name: "POST-valid",
statuses: []string{acme.StatusValid},
},
{
name: "POST-invalid",
statuses: []string{acme.StatusInvalid},
want: "error",
},
{
name: "POST-pending-unexpected",
statuses: []string{acme.StatusPending, "weird"},
want: "unexpected",
},
{
name: "POST-pending-valid",
statuses: []string{acme.StatusPending, acme.StatusValid},
},
{
name: "POST-pending-invalid",
statuses: []string{acme.StatusPending, acme.StatusInvalid},
want: "error",
},
}
for _, test := range testCases {
t.Run(test.name, func(t *testing.T) {
statuses = test.statuses
err := validate(core, "example.com", acme.Challenge{Type: "http-01", Token: "token", URL: apiURL + "/chlg"})
if test.want == "" {
require.NoError(t, err)
} else {
require.Error(t, err)
assert.Contains(t, err.Error(), test.want)
}
})
}
}
// validateNoBody reads the http.Request POST body, parses the JWS and validates it to read the body.
// If there is an error doing this,
// or if the JWS body is not the empty JSON payload "{}" or a POST-as-GET payload "" an error is returned.
// We use this to verify challenge POSTs to the ts below do not send a JWS body.
func validateNoBody(privateKey *rsa.PrivateKey, r *http.Request) error {
reqBody, err := io.ReadAll(r.Body)
if err != nil {
return err
}
jws, err := jose.ParseSigned(string(reqBody))
if err != nil {
return err
}
body, err := jws.Verify(&jose.JSONWebKey{
Key: privateKey.Public(),
Algorithm: "RSA",
})
if err != nil {
return err
}
if bodyStr := string(body); bodyStr != "{}" && bodyStr != "" {
return fmt.Errorf(`expected JWS POST body "{}" or "", got %q`, bodyStr)
}
return nil
}
lego-4.9.1/challenge/tlsalpn01/ 0000775 0000000 0000000 00000000000 14340204635 0016214 5 ustar 00root root 0000000 0000000 lego-4.9.1/challenge/tlsalpn01/tls_alpn_challenge.go 0000664 0000000 0000000 00000007305 14340204635 0022366 0 ustar 00root root 0000000 0000000 package tlsalpn01
import (
"crypto/rsa"
"crypto/sha256"
"crypto/tls"
"crypto/x509/pkix"
"encoding/asn1"
"fmt"
"github.com/go-acme/lego/v4/acme"
"github.com/go-acme/lego/v4/acme/api"
"github.com/go-acme/lego/v4/certcrypto"
"github.com/go-acme/lego/v4/challenge"
"github.com/go-acme/lego/v4/log"
)
// idPeAcmeIdentifierV1 is the SMI Security for PKIX Certification Extension OID referencing the ACME extension.
// Reference: https://www.rfc-editor.org/rfc/rfc8737.html#section-6.1
var idPeAcmeIdentifierV1 = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 31}
type ValidateFunc func(core *api.Core, domain string, chlng acme.Challenge) error
type Challenge struct {
core *api.Core
validate ValidateFunc
provider challenge.Provider
}
func NewChallenge(core *api.Core, validate ValidateFunc, provider challenge.Provider) *Challenge {
return &Challenge{
core: core,
validate: validate,
provider: provider,
}
}
func (c *Challenge) SetProvider(provider challenge.Provider) {
c.provider = provider
}
// Solve manages the provider to validate and solve the challenge.
func (c *Challenge) Solve(authz acme.Authorization) error {
domain := authz.Identifier.Value
log.Infof("[%s] acme: Trying to solve TLS-ALPN-01", challenge.GetTargetedDomain(authz))
chlng, err := challenge.FindChallenge(challenge.TLSALPN01, authz)
if err != nil {
return err
}
// Generate the Key Authorization for the challenge
keyAuth, err := c.core.GetKeyAuthorization(chlng.Token)
if err != nil {
return err
}
err = c.provider.Present(domain, chlng.Token, keyAuth)
if err != nil {
return fmt.Errorf("[%s] acme: error presenting token: %w", challenge.GetTargetedDomain(authz), err)
}
defer func() {
err := c.provider.CleanUp(domain, chlng.Token, keyAuth)
if err != nil {
log.Warnf("[%s] acme: cleaning up failed: %v", challenge.GetTargetedDomain(authz), err)
}
}()
chlng.KeyAuthorization = keyAuth
return c.validate(c.core, domain, chlng)
}
// ChallengeBlocks returns PEM blocks (certPEMBlock, keyPEMBlock) with the acmeValidation-v1 extension
// and domain name for the `tls-alpn-01` challenge.
func ChallengeBlocks(domain, keyAuth string) ([]byte, []byte, error) {
// Compute the SHA-256 digest of the key authorization.
zBytes := sha256.Sum256([]byte(keyAuth))
value, err := asn1.Marshal(zBytes[:sha256.Size])
if err != nil {
return nil, nil, err
}
// Add the keyAuth digest as the acmeValidation-v1 extension
// (marked as critical such that it won't be used by non-ACME software).
// Reference: https://www.rfc-editor.org/rfc/rfc8737.html#section-3
extensions := []pkix.Extension{
{
Id: idPeAcmeIdentifierV1,
Critical: true,
Value: value,
},
}
// Generate a new RSA key for the certificates.
tempPrivateKey, err := certcrypto.GeneratePrivateKey(certcrypto.RSA2048)
if err != nil {
return nil, nil, err
}
rsaPrivateKey := tempPrivateKey.(*rsa.PrivateKey)
// Generate the PEM certificate using the provided private key, domain, and extra extensions.
tempCertPEM, err := certcrypto.GeneratePemCert(rsaPrivateKey, domain, extensions)
if err != nil {
return nil, nil, err
}
// Encode the private key into a PEM format. We'll need to use it to generate the x509 keypair.
rsaPrivatePEM := certcrypto.PEMEncode(rsaPrivateKey)
return tempCertPEM, rsaPrivatePEM, nil
}
// ChallengeCert returns a certificate with the acmeValidation-v1 extension
// and domain name for the `tls-alpn-01` challenge.
func ChallengeCert(domain, keyAuth string) (*tls.Certificate, error) {
tempCertPEM, rsaPrivatePEM, err := ChallengeBlocks(domain, keyAuth)
if err != nil {
return nil, err
}
cert, err := tls.X509KeyPair(tempCertPEM, rsaPrivatePEM)
if err != nil {
return nil, err
}
return &cert, nil
}
lego-4.9.1/challenge/tlsalpn01/tls_alpn_challenge_server.go 0000664 0000000 0000000 00000005310 14340204635 0023746 0 ustar 00root root 0000000 0000000 package tlsalpn01
import (
"crypto/tls"
"errors"
"fmt"
"net"
"net/http"
"strings"
"github.com/go-acme/lego/v4/log"
)
const (
// ACMETLS1Protocol is the ALPN Protocol ID for the ACME-TLS/1 Protocol.
ACMETLS1Protocol = "acme-tls/1"
// defaultTLSPort is the port that the ProviderServer will default to
// when no other port is provided.
defaultTLSPort = "443"
)
// ProviderServer implements ChallengeProvider for `TLS-ALPN-01` challenge.
// It may be instantiated without using the NewProviderServer
// if you want only to use the default values.
type ProviderServer struct {
iface string
port string
listener net.Listener
}
// NewProviderServer creates a new ProviderServer on the selected interface and port.
// Setting iface and / or port to an empty string will make the server fall back to
// the "any" interface and port 443 respectively.
func NewProviderServer(iface, port string) *ProviderServer {
return &ProviderServer{iface: iface, port: port}
}
func (s *ProviderServer) GetAddress() string {
return net.JoinHostPort(s.iface, s.port)
}
// Present generates a certificate with a SHA-256 digest of the keyAuth provided
// as the acmeValidation-v1 extension value to conform to the ACME-TLS-ALPN spec.
func (s *ProviderServer) Present(domain, token, keyAuth string) error {
if s.port == "" {
// Fallback to port 443 if the port was not provided.
s.port = defaultTLSPort
}
// Generate the challenge certificate using the provided keyAuth and domain.
cert, err := ChallengeCert(domain, keyAuth)
if err != nil {
return err
}
// Place the generated certificate with the extension into the TLS config
// so that it can serve the correct details.
tlsConf := new(tls.Config)
tlsConf.Certificates = []tls.Certificate{*cert}
// We must set that the `acme-tls/1` application level protocol is supported
// so that the protocol negotiation can succeed. Reference:
// https://www.rfc-editor.org/rfc/rfc8737.html#section-6.2
tlsConf.NextProtos = []string{ACMETLS1Protocol}
// Create the listener with the created tls.Config.
s.listener, err = tls.Listen("tcp", s.GetAddress(), tlsConf)
if err != nil {
return fmt.Errorf("could not start HTTPS server for challenge: %w", err)
}
// Shut the server down when we're finished.
go func() {
err := http.Serve(s.listener, nil)
if err != nil && !strings.Contains(err.Error(), "use of closed network connection") {
log.Println(err)
}
}()
return nil
}
// CleanUp closes the HTTPS server.
func (s *ProviderServer) CleanUp(domain, token, keyAuth string) error {
if s.listener == nil {
return nil
}
// Server was created, close it.
if err := s.listener.Close(); err != nil && errors.Is(err, http.ErrServerClosed) {
return err
}
return nil
}
lego-4.9.1/challenge/tlsalpn01/tls_alpn_challenge_test.go 0000664 0000000 0000000 00000006643 14340204635 0023431 0 ustar 00root root 0000000 0000000 package tlsalpn01
import (
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/subtle"
"crypto/tls"
"encoding/asn1"
"net/http"
"testing"
"github.com/go-acme/lego/v4/acme"
"github.com/go-acme/lego/v4/acme/api"
"github.com/go-acme/lego/v4/challenge"
"github.com/go-acme/lego/v4/platform/tester"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestChallenge(t *testing.T) {
_, apiURL := tester.SetupFakeAPI(t)
domain := "localhost:23457"
mockValidate := func(_ *api.Core, _ string, chlng acme.Challenge) error {
conn, err := tls.Dial("tcp", domain, &tls.Config{
InsecureSkipVerify: true,
})
require.NoError(t, err, "Expected to connect to challenge server without an error")
// Expect the server to only return one certificate
connState := conn.ConnectionState()
assert.Len(t, connState.PeerCertificates, 1, "Expected the challenge server to return exactly one certificate")
remoteCert := connState.PeerCertificates[0]
assert.Len(t, remoteCert.DNSNames, 1, "Expected the challenge certificate to have exactly one DNSNames entry")
assert.Equal(t, domain, remoteCert.DNSNames[0], "challenge certificate DNSName ")
assert.NotEmpty(t, remoteCert.Extensions, "Expected the challenge certificate to contain extensions")
idx := -1
for i, ext := range remoteCert.Extensions {
if idPeAcmeIdentifierV1.Equal(ext.Id) {
idx = i
break
}
}
require.NotEqual(t, -1, idx, "Expected the challenge certificate to contain an extension with the id-pe-acmeIdentifier id,")
ext := remoteCert.Extensions[idx]
assert.True(t, ext.Critical, "Expected the challenge certificate id-pe-acmeIdentifier extension to be marked as critical")
zBytes := sha256.Sum256([]byte(chlng.KeyAuthorization))
value, err := asn1.Marshal(zBytes[:sha256.Size])
require.NoError(t, err, "Expected marshaling of the keyAuth to return no error")
if subtle.ConstantTimeCompare(value, ext.Value) != 1 {
t.Errorf("Expected the challenge certificate id-pe-acmeIdentifier extension to contain the SHA-256 digest of the keyAuth, %v, but was %v", zBytes[:], ext.Value)
}
return nil
}
privateKey, err := rsa.GenerateKey(rand.Reader, 512)
require.NoError(t, err, "Could not generate test key")
core, err := api.New(http.DefaultClient, "lego-test", apiURL+"/dir", "", privateKey)
require.NoError(t, err)
solver := NewChallenge(
core,
mockValidate,
&ProviderServer{port: "23457"},
)
authz := acme.Authorization{
Identifier: acme.Identifier{
Value: domain,
},
Challenges: []acme.Challenge{
{Type: challenge.TLSALPN01.String(), Token: "tlsalpn1"},
},
}
err = solver.Solve(authz)
require.NoError(t, err)
}
func TestChallengeInvalidPort(t *testing.T) {
_, apiURL := tester.SetupFakeAPI(t)
privateKey, err := rsa.GenerateKey(rand.Reader, 128)
require.NoError(t, err, "Could not generate test key")
core, err := api.New(http.DefaultClient, "lego-test", apiURL+"/dir", "", privateKey)
require.NoError(t, err)
solver := NewChallenge(
core,
func(_ *api.Core, _ string, _ acme.Challenge) error { return nil },
&ProviderServer{port: "123456"},
)
authz := acme.Authorization{
Identifier: acme.Identifier{
Value: "localhost:123456",
},
Challenges: []acme.Challenge{
{Type: challenge.TLSALPN01.String(), Token: "tlsalpn1"},
},
}
err = solver.Solve(authz)
require.Error(t, err)
assert.Contains(t, err.Error(), "invalid port")
assert.Contains(t, err.Error(), "123456")
}
lego-4.9.1/cmd/ 0000775 0000000 0000000 00000000000 14340204635 0013217 5 ustar 00root root 0000000 0000000 lego-4.9.1/cmd/account.go 0000664 0000000 0000000 00000001340 14340204635 0015200 0 ustar 00root root 0000000 0000000 package cmd
import (
"crypto"
"github.com/go-acme/lego/v4/registration"
)
// Account represents a users local saved credentials.
type Account struct {
Email string `json:"email"`
Registration *registration.Resource `json:"registration"`
key crypto.PrivateKey
}
/** Implementation of the registration.User interface **/
// GetEmail returns the email address for the account.
func (a *Account) GetEmail() string {
return a.Email
}
// GetPrivateKey returns the private RSA account key.
func (a *Account) GetPrivateKey() crypto.PrivateKey {
return a.key
}
// GetRegistration returns the server registration.
func (a *Account) GetRegistration() *registration.Resource {
return a.Registration
}
lego-4.9.1/cmd/accounts_storage.go 0000664 0000000 0000000 00000014657 14340204635 0017126 0 ustar 00root root 0000000 0000000 package cmd
import (
"crypto"
"crypto/x509"
"encoding/json"
"encoding/pem"
"errors"
"net/url"
"os"
"path/filepath"
"strings"
"github.com/go-acme/lego/v4/certcrypto"
"github.com/go-acme/lego/v4/lego"
"github.com/go-acme/lego/v4/log"
"github.com/go-acme/lego/v4/registration"
"github.com/urfave/cli/v2"
)
const (
baseAccountsRootFolderName = "accounts"
baseKeysFolderName = "keys"
accountFileName = "account.json"
)
// AccountsStorage A storage for account data.
//
// rootPath:
//
// ./.lego/accounts/
// │ └── root accounts directory
// └── "path" option
//
// rootUserPath:
//
// ./.lego/accounts/localhost_14000/hubert@hubert.com/
// │ │ │ └── userID ("email" option)
// │ │ └── CA server ("server" option)
// │ └── root accounts directory
// └── "path" option
//
// keysPath:
//
// ./.lego/accounts/localhost_14000/hubert@hubert.com/keys/
// │ │ │ │ └── root keys directory
// │ │ │ └── userID ("email" option)
// │ │ └── CA server ("server" option)
// │ └── root accounts directory
// └── "path" option
//
// accountFilePath:
//
// ./.lego/accounts/localhost_14000/hubert@hubert.com/account.json
// │ │ │ │ └── account file
// │ │ │ └── userID ("email" option)
// │ │ └── CA server ("server" option)
// │ └── root accounts directory
// └── "path" option
type AccountsStorage struct {
userID string
rootPath string
rootUserPath string
keysPath string
accountFilePath string
ctx *cli.Context
}
// NewAccountsStorage Creates a new AccountsStorage.
func NewAccountsStorage(ctx *cli.Context) *AccountsStorage {
// TODO: move to account struct? Currently MUST pass email.
email := getEmail(ctx)
serverURL, err := url.Parse(ctx.String("server"))
if err != nil {
log.Fatal(err)
}
rootPath := filepath.Join(ctx.String("path"), baseAccountsRootFolderName)
serverPath := strings.NewReplacer(":", "_", "/", string(os.PathSeparator)).Replace(serverURL.Host)
accountsPath := filepath.Join(rootPath, serverPath)
rootUserPath := filepath.Join(accountsPath, email)
return &AccountsStorage{
userID: email,
rootPath: rootPath,
rootUserPath: rootUserPath,
keysPath: filepath.Join(rootUserPath, baseKeysFolderName),
accountFilePath: filepath.Join(rootUserPath, accountFileName),
ctx: ctx,
}
}
func (s *AccountsStorage) ExistsAccountFilePath() bool {
accountFile := filepath.Join(s.rootUserPath, accountFileName)
if _, err := os.Stat(accountFile); os.IsNotExist(err) {
return false
} else if err != nil {
log.Fatal(err)
}
return true
}
func (s *AccountsStorage) GetRootPath() string {
return s.rootPath
}
func (s *AccountsStorage) GetRootUserPath() string {
return s.rootUserPath
}
func (s *AccountsStorage) GetUserID() string {
return s.userID
}
func (s *AccountsStorage) Save(account *Account) error {
jsonBytes, err := json.MarshalIndent(account, "", "\t")
if err != nil {
return err
}
return os.WriteFile(s.accountFilePath, jsonBytes, filePerm)
}
func (s *AccountsStorage) LoadAccount(privateKey crypto.PrivateKey) *Account {
fileBytes, err := os.ReadFile(s.accountFilePath)
if err != nil {
log.Fatalf("Could not load file for account %s: %v", s.userID, err)
}
var account Account
err = json.Unmarshal(fileBytes, &account)
if err != nil {
log.Fatalf("Could not parse file for account %s: %v", s.userID, err)
}
account.key = privateKey
if account.Registration == nil || account.Registration.Body.Status == "" {
reg, err := tryRecoverRegistration(s.ctx, privateKey)
if err != nil {
log.Fatalf("Could not load account for %s. Registration is nil: %#v", s.userID, err)
}
account.Registration = reg
err = s.Save(&account)
if err != nil {
log.Fatalf("Could not save account for %s. Registration is nil: %#v", s.userID, err)
}
}
return &account
}
func (s *AccountsStorage) GetPrivateKey(keyType certcrypto.KeyType) crypto.PrivateKey {
accKeyPath := filepath.Join(s.keysPath, s.userID+".key")
if _, err := os.Stat(accKeyPath); os.IsNotExist(err) {
log.Printf("No key found for account %s. Generating a %s key.", s.userID, keyType)
s.createKeysFolder()
privateKey, err := generatePrivateKey(accKeyPath, keyType)
if err != nil {
log.Fatalf("Could not generate RSA private account key for account %s: %v", s.userID, err)
}
log.Printf("Saved key to %s", accKeyPath)
return privateKey
}
privateKey, err := loadPrivateKey(accKeyPath)
if err != nil {
log.Fatalf("Could not load RSA private key from file %s: %v", accKeyPath, err)
}
return privateKey
}
func (s *AccountsStorage) createKeysFolder() {
if err := createNonExistingFolder(s.keysPath); err != nil {
log.Fatalf("Could not check/create directory for account %s: %v", s.userID, err)
}
}
func generatePrivateKey(file string, keyType certcrypto.KeyType) (crypto.PrivateKey, error) {
privateKey, err := certcrypto.GeneratePrivateKey(keyType)
if err != nil {
return nil, err
}
certOut, err := os.Create(file)
if err != nil {
return nil, err
}
defer certOut.Close()
pemKey := certcrypto.PEMBlock(privateKey)
err = pem.Encode(certOut, pemKey)
if err != nil {
return nil, err
}
return privateKey, nil
}
func loadPrivateKey(file string) (crypto.PrivateKey, error) {
keyBytes, err := os.ReadFile(file)
if err != nil {
return nil, err
}
keyBlock, _ := pem.Decode(keyBytes)
switch keyBlock.Type {
case "RSA PRIVATE KEY":
return x509.ParsePKCS1PrivateKey(keyBlock.Bytes)
case "EC PRIVATE KEY":
return x509.ParseECPrivateKey(keyBlock.Bytes)
}
return nil, errors.New("unknown private key type")
}
func tryRecoverRegistration(ctx *cli.Context, privateKey crypto.PrivateKey) (*registration.Resource, error) {
// couldn't load account but got a key. Try to look the account up.
config := lego.NewConfig(&Account{key: privateKey})
config.CADirURL = ctx.String("server")
config.UserAgent = getUserAgent(ctx)
client, err := lego.NewClient(config)
if err != nil {
return nil, err
}
reg, err := client.Registration.ResolveAccountByKey()
if err != nil {
return nil, err
}
return reg, nil
}
lego-4.9.1/cmd/certs_storage.go 0000664 0000000 0000000 00000017323 14340204635 0016420 0 ustar 00root root 0000000 0000000 package cmd
import (
"bytes"
"crypto"
"crypto/rand"
"crypto/x509"
"encoding/json"
"encoding/pem"
"fmt"
"os"
"path/filepath"
"strconv"
"strings"
"time"
"github.com/go-acme/lego/v4/certcrypto"
"github.com/go-acme/lego/v4/certificate"
"github.com/go-acme/lego/v4/log"
"github.com/urfave/cli/v2"
"golang.org/x/net/idna"
"software.sslmate.com/src/go-pkcs12"
)
const (
baseCertificatesFolderName = "certificates"
baseArchivesFolderName = "archives"
)
// CertificatesStorage a certificates' storage.
//
// rootPath:
//
// ./.lego/certificates/
// │ └── root certificates directory
// └── "path" option
//
// archivePath:
//
// ./.lego/archives/
// │ └── archived certificates directory
// └── "path" option
type CertificatesStorage struct {
rootPath string
archivePath string
pem bool
pfx bool
pfxPassword string
filename string // Deprecated
}
// NewCertificatesStorage create a new certificates storage.
func NewCertificatesStorage(ctx *cli.Context) *CertificatesStorage {
return &CertificatesStorage{
rootPath: filepath.Join(ctx.String("path"), baseCertificatesFolderName),
archivePath: filepath.Join(ctx.String("path"), baseArchivesFolderName),
pem: ctx.Bool("pem"),
pfx: ctx.Bool("pfx"),
pfxPassword: ctx.String("pfx.pass"),
filename: ctx.String("filename"),
}
}
func (s *CertificatesStorage) CreateRootFolder() {
err := createNonExistingFolder(s.rootPath)
if err != nil {
log.Fatalf("Could not check/create path: %v", err)
}
}
func (s *CertificatesStorage) CreateArchiveFolder() {
err := createNonExistingFolder(s.archivePath)
if err != nil {
log.Fatalf("Could not check/create path: %v", err)
}
}
func (s *CertificatesStorage) GetRootPath() string {
return s.rootPath
}
func (s *CertificatesStorage) SaveResource(certRes *certificate.Resource) {
domain := certRes.Domain
// We store the certificate, private key and metadata in different files
// as web servers would not be able to work with a combined file.
err := s.WriteFile(domain, ".crt", certRes.Certificate)
if err != nil {
log.Fatalf("Unable to save Certificate for domain %s\n\t%v", domain, err)
}
if certRes.IssuerCertificate != nil {
err = s.WriteFile(domain, ".issuer.crt", certRes.IssuerCertificate)
if err != nil {
log.Fatalf("Unable to save IssuerCertificate for domain %s\n\t%v", domain, err)
}
}
// if we were given a CSR, we don't know the private key
if certRes.PrivateKey != nil {
err = s.WriteCertificateFiles(domain, certRes)
if err != nil {
log.Fatalf("Unable to save PrivateKey for domain %s\n\t%v", domain, err)
}
} else if s.pem || s.pfx {
// we don't have the private key; can't write the .pem or .pfx file
log.Fatalf("Unable to save PEM or PFX without private key for domain %s. Are you using a CSR?", domain)
}
jsonBytes, err := json.MarshalIndent(certRes, "", "\t")
if err != nil {
log.Fatalf("Unable to marshal CertResource for domain %s\n\t%v", domain, err)
}
err = s.WriteFile(domain, ".json", jsonBytes)
if err != nil {
log.Fatalf("Unable to save CertResource for domain %s\n\t%v", domain, err)
}
}
func (s *CertificatesStorage) ReadResource(domain string) certificate.Resource {
raw, err := s.ReadFile(domain, ".json")
if err != nil {
log.Fatalf("Error while loading the meta data for domain %s\n\t%v", domain, err)
}
var resource certificate.Resource
if err = json.Unmarshal(raw, &resource); err != nil {
log.Fatalf("Error while marshaling the meta data for domain %s\n\t%v", domain, err)
}
return resource
}
func (s *CertificatesStorage) ExistsFile(domain, extension string) bool {
filePath := s.GetFileName(domain, extension)
if _, err := os.Stat(filePath); os.IsNotExist(err) {
return false
} else if err != nil {
log.Fatal(err)
}
return true
}
func (s *CertificatesStorage) ReadFile(domain, extension string) ([]byte, error) {
return os.ReadFile(s.GetFileName(domain, extension))
}
func (s *CertificatesStorage) GetFileName(domain, extension string) string {
filename := sanitizedDomain(domain) + extension
return filepath.Join(s.rootPath, filename)
}
func (s *CertificatesStorage) ReadCertificate(domain, extension string) ([]*x509.Certificate, error) {
content, err := s.ReadFile(domain, extension)
if err != nil {
return nil, err
}
// The input may be a bundle or a single certificate.
return certcrypto.ParsePEMBundle(content)
}
func (s *CertificatesStorage) WriteFile(domain, extension string, data []byte) error {
var baseFileName string
if s.filename != "" {
baseFileName = s.filename
} else {
baseFileName = sanitizedDomain(domain)
}
filePath := filepath.Join(s.rootPath, baseFileName+extension)
return os.WriteFile(filePath, data, filePerm)
}
func (s *CertificatesStorage) WriteCertificateFiles(domain string, certRes *certificate.Resource) error {
err := s.WriteFile(domain, ".key", certRes.PrivateKey)
if err != nil {
return fmt.Errorf("unable to save key file: %w", err)
}
if s.pem {
err = s.WriteFile(domain, ".pem", bytes.Join([][]byte{certRes.Certificate, certRes.PrivateKey}, nil))
if err != nil {
return fmt.Errorf("unable to save PEM file: %w", err)
}
}
if s.pfx {
err = s.WritePFXFile(domain, certRes)
if err != nil {
return fmt.Errorf("unable to save PFX file: %w", err)
}
}
return nil
}
func (s *CertificatesStorage) WritePFXFile(domain string, certRes *certificate.Resource) error {
certPemBlock, _ := pem.Decode(certRes.Certificate)
if certPemBlock == nil {
return fmt.Errorf("unable to parse Certificate for domain %s", domain)
}
cert, err := x509.ParseCertificate(certPemBlock.Bytes)
if err != nil {
return fmt.Errorf("unable to load Certificate for domain %s: %w", domain, err)
}
issuerCertPemBlock, _ := pem.Decode(certRes.IssuerCertificate)
if issuerCertPemBlock == nil {
return fmt.Errorf("unable to parse Issuer Certificate for domain %s", domain)
}
issuerCert, err := x509.ParseCertificate(issuerCertPemBlock.Bytes)
if err != nil {
return fmt.Errorf("unable to load Issuer Certificate for domain %s: %w", domain, err)
}
keyPemBlock, _ := pem.Decode(certRes.PrivateKey)
if keyPemBlock == nil {
return fmt.Errorf("unable to parse PrivateKey for domain %s", domain)
}
var privateKey crypto.Signer
var keyErr error
switch keyPemBlock.Type {
case "RSA PRIVATE KEY":
privateKey, keyErr = x509.ParsePKCS1PrivateKey(keyPemBlock.Bytes)
if keyErr != nil {
return fmt.Errorf("unable to load RSA PrivateKey for domain %s: %w", domain, keyErr)
}
case "EC PRIVATE KEY":
privateKey, keyErr = x509.ParseECPrivateKey(keyPemBlock.Bytes)
if keyErr != nil {
return fmt.Errorf("unable to load EC PrivateKey for domain %s: %w", domain, keyErr)
}
default:
return fmt.Errorf("unsupported PrivateKey type '%s' for domain %s", keyPemBlock.Type, domain)
}
pfxBytes, err := pkcs12.Encode(rand.Reader, privateKey, cert, []*x509.Certificate{issuerCert}, s.pfxPassword)
if err != nil {
return fmt.Errorf("unable to encode PFX data for domain %s: %w", domain, err)
}
return s.WriteFile(domain, ".pfx", pfxBytes)
}
func (s *CertificatesStorage) MoveToArchive(domain string) error {
matches, err := filepath.Glob(filepath.Join(s.rootPath, sanitizedDomain(domain)+".*"))
if err != nil {
return err
}
for _, oldFile := range matches {
date := strconv.FormatInt(time.Now().Unix(), 10)
filename := date + "." + filepath.Base(oldFile)
newFile := filepath.Join(s.archivePath, filename)
err = os.Rename(oldFile, newFile)
if err != nil {
return err
}
}
return nil
}
// sanitizedDomain Make sure no funny chars are in the cert names (like wildcards ;)).
func sanitizedDomain(domain string) string {
safe, err := idna.ToASCII(strings.ReplaceAll(domain, "*", "_"))
if err != nil {
log.Fatal(err)
}
return safe
}
lego-4.9.1/cmd/cmd.go 0000664 0000000 0000000 00000000365 14340204635 0014315 0 ustar 00root root 0000000 0000000 package cmd
import "github.com/urfave/cli/v2"
// CreateCommands Creates all CLI commands.
func CreateCommands() []*cli.Command {
return []*cli.Command{
createRun(),
createRevoke(),
createRenew(),
createDNSHelp(),
createList(),
}
}
lego-4.9.1/cmd/cmd_before.go 0000664 0000000 0000000 00000000764 14340204635 0015642 0 ustar 00root root 0000000 0000000 package cmd
import (
"github.com/go-acme/lego/v4/log"
"github.com/urfave/cli/v2"
)
func Before(ctx *cli.Context) error {
if ctx.String("path") == "" {
log.Fatal("Could not determine current working directory. Please pass --path.")
}
err := createNonExistingFolder(ctx.String("path"))
if err != nil {
log.Fatalf("Could not check/create path: %v", err)
}
if ctx.String("server") == "" {
log.Fatal("Could not determine current working server. Please pass --server.")
}
return nil
}
lego-4.9.1/cmd/cmd_dnshelp.go 0000664 0000000 0000000 00000002621 14340204635 0016027 0 ustar 00root root 0000000 0000000 package cmd
import (
"fmt"
"io"
"os"
"strings"
"text/tabwriter"
"github.com/urfave/cli/v2"
)
func createDNSHelp() *cli.Command {
return &cli.Command{
Name: "dnshelp",
Usage: "Shows additional help for the '--dns' global option",
Action: dnsHelp,
Flags: []cli.Flag{
&cli.StringFlag{
Name: "code",
Aliases: []string{"c"},
Usage: fmt.Sprintf("DNS code: %s", allDNSCodes()),
},
},
}
}
func dnsHelp(ctx *cli.Context) error {
code := ctx.String("code")
if code == "" {
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
ew := &errWriter{w: w}
ew.writeln(`Credentials for DNS providers must be passed through environment variables.`)
ew.writeln()
ew.writeln(`To display the documentation for a DNS providers:`)
ew.writeln()
ew.writeln("\t$ lego dnshelp -c code")
ew.writeln()
ew.writeln("All DNS codes:")
ew.writef("\t%s\n", allDNSCodes())
ew.writeln()
ew.writeln("More information: https://go-acme.github.io/lego/dns")
if ew.err != nil {
return ew.err
}
return w.Flush()
}
return displayDNSHelp(strings.ToLower(code))
}
type errWriter struct {
w io.Writer
err error
}
func (ew *errWriter) writeln(a ...interface{}) {
if ew.err != nil {
return
}
_, ew.err = fmt.Fprintln(ew.w, a...)
}
func (ew *errWriter) writef(format string, a ...interface{}) {
if ew.err != nil {
return
}
_, ew.err = fmt.Fprintf(ew.w, format, a...)
}
lego-4.9.1/cmd/cmd_list.go 0000664 0000000 0000000 00000005176 14340204635 0015355 0 ustar 00root root 0000000 0000000 package cmd
import (
"encoding/json"
"fmt"
"net/url"
"os"
"path/filepath"
"strings"
"github.com/go-acme/lego/v4/certcrypto"
"github.com/urfave/cli/v2"
)
func createList() *cli.Command {
return &cli.Command{
Name: "list",
Usage: "Display certificates and accounts information.",
Action: list,
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "accounts",
Aliases: []string{"a"},
Usage: "Display accounts.",
},
&cli.BoolFlag{
Name: "names",
Aliases: []string{"n"},
Usage: "Display certificate common names only.",
},
},
}
}
func list(ctx *cli.Context) error {
if ctx.Bool("accounts") && !ctx.Bool("names") {
if err := listAccount(ctx); err != nil {
return err
}
}
return listCertificates(ctx)
}
func listCertificates(ctx *cli.Context) error {
certsStorage := NewCertificatesStorage(ctx)
matches, err := filepath.Glob(filepath.Join(certsStorage.GetRootPath(), "*.crt"))
if err != nil {
return err
}
names := ctx.Bool("names")
if len(matches) == 0 {
if !names {
fmt.Println("No certificates found.")
}
return nil
}
if !names {
fmt.Println("Found the following certs:")
}
for _, filename := range matches {
if strings.HasSuffix(filename, ".issuer.crt") {
continue
}
data, err := os.ReadFile(filename)
if err != nil {
return err
}
pCert, err := certcrypto.ParsePEMCertificate(data)
if err != nil {
return err
}
if names {
fmt.Println(pCert.Subject.CommonName)
} else {
fmt.Println(" Certificate Name:", pCert.Subject.CommonName)
fmt.Println(" Domains:", strings.Join(pCert.DNSNames, ", "))
fmt.Println(" Expiry Date:", pCert.NotAfter)
fmt.Println(" Certificate Path:", filename)
fmt.Println()
}
}
return nil
}
func listAccount(ctx *cli.Context) error {
// fake email, needed by NewAccountsStorage
if err := ctx.Set("email", "unknown"); err != nil {
return err
}
accountsStorage := NewAccountsStorage(ctx)
matches, err := filepath.Glob(filepath.Join(accountsStorage.GetRootPath(), "*", "*", "*.json"))
if err != nil {
return err
}
if len(matches) == 0 {
fmt.Println("No accounts found.")
return nil
}
fmt.Println("Found the following accounts:")
for _, filename := range matches {
data, err := os.ReadFile(filename)
if err != nil {
return err
}
var account Account
err = json.Unmarshal(data, &account)
if err != nil {
return err
}
uri, err := url.Parse(account.Registration.URI)
if err != nil {
return err
}
fmt.Println(" Email:", account.Email)
fmt.Println(" Server:", uri.Host)
fmt.Println(" Path:", filepath.Dir(filename))
fmt.Println()
}
return nil
}
lego-4.9.1/cmd/cmd_renew.go 0000664 0000000 0000000 00000017707 14340204635 0015525 0 ustar 00root root 0000000 0000000 package cmd
import (
"crypto"
"crypto/x509"
"math/rand"
"os"
"time"
"github.com/go-acme/lego/v4/certcrypto"
"github.com/go-acme/lego/v4/certificate"
"github.com/go-acme/lego/v4/lego"
"github.com/go-acme/lego/v4/log"
"github.com/mattn/go-isatty"
"github.com/urfave/cli/v2"
)
const (
renewEnvAccountEmail = "LEGO_ACCOUNT_EMAIL"
renewEnvCertDomain = "LEGO_CERT_DOMAIN"
renewEnvCertPath = "LEGO_CERT_PATH"
renewEnvCertKeyPath = "LEGO_CERT_KEY_PATH"
renewEnvCertPEMPath = "LEGO_CERT_PEM_PATH"
renewEnvCertPFXPath = "LEGO_CERT_PFX_PATH"
)
func createRenew() *cli.Command {
return &cli.Command{
Name: "renew",
Usage: "Renew a certificate",
Action: renew,
Before: func(ctx *cli.Context) error {
// we require either domains or csr, but not both
hasDomains := len(ctx.StringSlice("domains")) > 0
hasCsr := len(ctx.String("csr")) > 0
if hasDomains && hasCsr {
log.Fatal("Please specify either --domains/-d or --csr/-c, but not both")
}
if !hasDomains && !hasCsr {
log.Fatal("Please specify --domains/-d (or --csr/-c if you already have a CSR)")
}
return nil
},
Flags: []cli.Flag{
&cli.IntFlag{
Name: "days",
Value: 30,
Usage: "The number of days left on a certificate to renew it.",
},
&cli.BoolFlag{
Name: "reuse-key",
Usage: "Used to indicate you want to reuse your current private key for the new certificate.",
},
&cli.BoolFlag{
Name: "no-bundle",
Usage: "Do not create a certificate bundle by adding the issuers certificate to the new certificate.",
},
&cli.BoolFlag{
Name: "must-staple",
Usage: "Include the OCSP must staple TLS extension in the CSR and generated certificate. Only works if the CSR is generated by lego.",
},
&cli.StringFlag{
Name: "renew-hook",
Usage: "Define a hook. The hook is executed only when the certificates are effectively renewed.",
},
&cli.StringFlag{
Name: "preferred-chain",
Usage: "If the CA offers multiple certificate chains, prefer the chain with an issuer matching this Subject Common Name. If no match, the default offered chain will be used.",
},
&cli.StringFlag{
Name: "always-deactivate-authorizations",
Usage: "Force the authorizations to be relinquished even if the certificate request was successful.",
},
&cli.BoolFlag{
Name: "no-random-sleep",
Usage: "Do not add a random sleep before the renewal. We do not recommend using this flag if you are doing your renewals in an automated way.",
},
},
}
}
func renew(ctx *cli.Context) error {
account, client := setup(ctx, NewAccountsStorage(ctx))
setupChallenges(ctx, client)
if account.Registration == nil {
log.Fatalf("Account %s is not registered. Use 'run' to register a new account.\n", account.Email)
}
certsStorage := NewCertificatesStorage(ctx)
bundle := !ctx.Bool("no-bundle")
meta := map[string]string{renewEnvAccountEmail: account.Email}
// CSR
if ctx.IsSet("csr") {
return renewForCSR(ctx, client, certsStorage, bundle, meta)
}
// Domains
return renewForDomains(ctx, client, certsStorage, bundle, meta)
}
func renewForDomains(ctx *cli.Context, client *lego.Client, certsStorage *CertificatesStorage, bundle bool, meta map[string]string) error {
domains := ctx.StringSlice("domains")
domain := domains[0]
// load the cert resource from files.
// We store the certificate, private key and metadata in different files
// as web servers would not be able to work with a combined file.
certificates, err := certsStorage.ReadCertificate(domain, ".crt")
if err != nil {
log.Fatalf("Error while loading the certificate for domain %s\n\t%v", domain, err)
}
cert := certificates[0]
if !needRenewal(cert, domain, ctx.Int("days")) {
return nil
}
// This is just meant to be informal for the user.
timeLeft := cert.NotAfter.Sub(time.Now().UTC())
log.Infof("[%s] acme: Trying renewal with %d hours remaining", domain, int(timeLeft.Hours()))
certDomains := certcrypto.ExtractDomains(cert)
var privateKey crypto.PrivateKey
if ctx.Bool("reuse-key") {
keyBytes, errR := certsStorage.ReadFile(domain, ".key")
if errR != nil {
log.Fatalf("Error while loading the private key for domain %s\n\t%v", domain, errR)
}
privateKey, errR = certcrypto.ParsePEMPrivateKey(keyBytes)
if errR != nil {
return errR
}
}
// https://github.com/go-acme/lego/issues/1656
// https://github.com/certbot/certbot/blob/284023a1b7672be2bd4018dd7623b3b92197d4b0/certbot/certbot/_internal/renewal.py#L435-L440
if !isatty.IsTerminal(os.Stdout.Fd()) && !ctx.Bool("no-random-sleep") {
// https://github.com/certbot/certbot/blob/284023a1b7672be2bd4018dd7623b3b92197d4b0/certbot/certbot/_internal/renewal.py#L472
const jitter = 8 * time.Minute
rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
sleepTime := time.Duration(rnd.Int63n(int64(jitter)))
log.Infof("renewal: random delay of %s", sleepTime)
time.Sleep(sleepTime)
}
request := certificate.ObtainRequest{
Domains: merge(certDomains, domains),
Bundle: bundle,
PrivateKey: privateKey,
MustStaple: ctx.Bool("must-staple"),
PreferredChain: ctx.String("preferred-chain"),
AlwaysDeactivateAuthorizations: ctx.Bool("always-deactivate-authorizations"),
}
certRes, err := client.Certificate.Obtain(request)
if err != nil {
log.Fatal(err)
}
certsStorage.SaveResource(certRes)
meta[renewEnvCertDomain] = domain
meta[renewEnvCertPath] = certsStorage.GetFileName(domain, ".crt")
meta[renewEnvCertKeyPath] = certsStorage.GetFileName(domain, ".key")
meta[renewEnvCertPEMPath] = certsStorage.GetFileName(domain, ".pem")
meta[renewEnvCertPFXPath] = certsStorage.GetFileName(domain, ".pfx")
return launchHook(ctx.String("renew-hook"), meta)
}
func renewForCSR(ctx *cli.Context, client *lego.Client, certsStorage *CertificatesStorage, bundle bool, meta map[string]string) error {
csr, err := readCSRFile(ctx.String("csr"))
if err != nil {
log.Fatal(err)
}
domain := csr.Subject.CommonName
// load the cert resource from files.
// We store the certificate, private key and metadata in different files
// as web servers would not be able to work with a combined file.
certificates, err := certsStorage.ReadCertificate(domain, ".crt")
if err != nil {
log.Fatalf("Error while loading the certificate for domain %s\n\t%v", domain, err)
}
cert := certificates[0]
if !needRenewal(cert, domain, ctx.Int("days")) {
return nil
}
// This is just meant to be informal for the user.
timeLeft := cert.NotAfter.Sub(time.Now().UTC())
log.Infof("[%s] acme: Trying renewal with %d hours remaining", domain, int(timeLeft.Hours()))
certRes, err := client.Certificate.ObtainForCSR(certificate.ObtainForCSRRequest{
CSR: csr,
Bundle: bundle,
PreferredChain: ctx.String("preferred-chain"),
AlwaysDeactivateAuthorizations: ctx.Bool("always-deactivate-authorizations"),
})
if err != nil {
log.Fatal(err)
}
certsStorage.SaveResource(certRes)
meta[renewEnvCertDomain] = domain
meta[renewEnvCertPath] = certsStorage.GetFileName(domain, ".crt")
meta[renewEnvCertKeyPath] = certsStorage.GetFileName(domain, ".key")
return launchHook(ctx.String("renew-hook"), meta)
}
func needRenewal(x509Cert *x509.Certificate, domain string, days int) bool {
if x509Cert.IsCA {
log.Fatalf("[%s] Certificate bundle starts with a CA certificate", domain)
}
if days >= 0 {
notAfter := int(time.Until(x509Cert.NotAfter).Hours() / 24.0)
if notAfter > days {
log.Printf("[%s] The certificate expires in %d days, the number of days defined to perform the renewal is %d: no renewal.",
domain, notAfter, days)
return false
}
}
return true
}
func merge(prevDomains, nextDomains []string) []string {
for _, next := range nextDomains {
var found bool
for _, prev := range prevDomains {
if prev == next {
found = true
break
}
}
if !found {
prevDomains = append(prevDomains, next)
}
}
return prevDomains
}
lego-4.9.1/cmd/cmd_renew_test.go 0000664 0000000 0000000 00000004634 14340204635 0016557 0 ustar 00root root 0000000 0000000 package cmd
import (
"crypto/x509"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func Test_merge(t *testing.T) {
testCases := []struct {
desc string
prevDomains []string
nextDomains []string
expected []string
}{
{
desc: "all empty",
prevDomains: []string{},
nextDomains: []string{},
expected: []string{},
},
{
desc: "next empty",
prevDomains: []string{"a", "b", "c"},
nextDomains: []string{},
expected: []string{"a", "b", "c"},
},
{
desc: "prev empty",
prevDomains: []string{},
nextDomains: []string{"a", "b", "c"},
expected: []string{"a", "b", "c"},
},
{
desc: "merge append",
prevDomains: []string{"a", "b", "c"},
nextDomains: []string{"a", "c", "d"},
expected: []string{"a", "b", "c", "d"},
},
{
desc: "merge same",
prevDomains: []string{"a", "b", "c"},
nextDomains: []string{"a", "b", "c"},
expected: []string{"a", "b", "c"},
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
actual := merge(test.prevDomains, test.nextDomains)
assert.Equal(t, test.expected, actual)
})
}
}
func Test_needRenewal(t *testing.T) {
testCases := []struct {
desc string
x509Cert *x509.Certificate
days int
expected bool
}{
{
desc: "30 days, NotAfter now",
x509Cert: &x509.Certificate{
NotAfter: time.Now(),
},
days: 30,
expected: true,
},
{
desc: "30 days, NotAfter 31 days",
x509Cert: &x509.Certificate{
NotAfter: time.Now().Add(31*24*time.Hour + 1*time.Second),
},
days: 30,
expected: false,
},
{
desc: "30 days, NotAfter 30 days",
x509Cert: &x509.Certificate{
NotAfter: time.Now().Add(30 * 24 * time.Hour),
},
days: 30,
expected: true,
},
{
desc: "0 days, NotAfter 30 days: only the day of the expiration",
x509Cert: &x509.Certificate{
NotAfter: time.Now().Add(30 * 24 * time.Hour),
},
days: 0,
expected: false,
},
{
desc: "-1 days, NotAfter 30 days: always renew",
x509Cert: &x509.Certificate{
NotAfter: time.Now().Add(30 * 24 * time.Hour),
},
days: -1,
expected: true,
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
actual := needRenewal(test.x509Cert, "foo.com", test.days)
assert.Equal(t, test.expected, actual)
})
}
}
lego-4.9.1/cmd/cmd_revoke.go 0000664 0000000 0000000 00000003576 14340204635 0015677 0 ustar 00root root 0000000 0000000 package cmd
import (
"github.com/go-acme/lego/v4/acme"
"github.com/go-acme/lego/v4/log"
"github.com/urfave/cli/v2"
)
func createRevoke() *cli.Command {
return &cli.Command{
Name: "revoke",
Usage: "Revoke a certificate",
Action: revoke,
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "keep",
Aliases: []string{"k"},
Usage: "Keep the certificates after the revocation instead of archiving them.",
},
&cli.UintFlag{
Name: "reason",
Usage: "Identifies the reason for the certificate revocation. See https://www.rfc-editor.org/rfc/rfc5280.html#section-5.3.1. 0(unspecified),1(keyCompromise),2(cACompromise),3(affiliationChanged),4(superseded),5(cessationOfOperation),6(certificateHold),8(removeFromCRL),9(privilegeWithdrawn),10(aACompromise)",
Value: acme.CRLReasonUnspecified,
},
},
}
}
func revoke(ctx *cli.Context) error {
acc, client := setup(ctx, NewAccountsStorage(ctx))
if acc.Registration == nil {
log.Fatalf("Account %s is not registered. Use 'run' to register a new account.\n", acc.Email)
}
certsStorage := NewCertificatesStorage(ctx)
certsStorage.CreateRootFolder()
for _, domain := range ctx.StringSlice("domains") {
log.Printf("Trying to revoke certificate for domain %s", domain)
certBytes, err := certsStorage.ReadFile(domain, ".crt")
if err != nil {
log.Fatalf("Error while revoking the certificate for domain %s\n\t%v", domain, err)
}
reason := ctx.Uint("reason")
err = client.Certificate.RevokeWithReason(certBytes, &reason)
if err != nil {
log.Fatalf("Error while revoking the certificate for domain %s\n\t%v", domain, err)
}
log.Println("Certificate was revoked.")
if ctx.Bool("keep") {
return nil
}
certsStorage.CreateArchiveFolder()
err = certsStorage.MoveToArchive(domain)
if err != nil {
return err
}
log.Println("Certificate was archived for domain:", domain)
}
return nil
}
lego-4.9.1/cmd/cmd_run.go 0000664 0000000 0000000 00000013230 14340204635 0015174 0 ustar 00root root 0000000 0000000 package cmd
import (
"bufio"
"fmt"
"os"
"strings"
"github.com/go-acme/lego/v4/certificate"
"github.com/go-acme/lego/v4/lego"
"github.com/go-acme/lego/v4/log"
"github.com/go-acme/lego/v4/registration"
"github.com/urfave/cli/v2"
)
func createRun() *cli.Command {
return &cli.Command{
Name: "run",
Usage: "Register an account, then create and install a certificate",
Before: func(ctx *cli.Context) error {
// we require either domains or csr, but not both
hasDomains := len(ctx.StringSlice("domains")) > 0
hasCsr := len(ctx.String("csr")) > 0
if hasDomains && hasCsr {
log.Fatal("Please specify either --domains/-d or --csr/-c, but not both")
}
if !hasDomains && !hasCsr {
log.Fatal("Please specify --domains/-d (or --csr/-c if you already have a CSR)")
}
return nil
},
Action: run,
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "no-bundle",
Usage: "Do not create a certificate bundle by adding the issuers certificate to the new certificate.",
},
&cli.BoolFlag{
Name: "must-staple",
Usage: "Include the OCSP must staple TLS extension in the CSR and generated certificate. Only works if the CSR is generated by lego.",
},
&cli.StringFlag{
Name: "run-hook",
Usage: "Define a hook. The hook is executed when the certificates are effectively created.",
},
&cli.StringFlag{
Name: "preferred-chain",
Usage: "If the CA offers multiple certificate chains, prefer the chain with an issuer matching this Subject Common Name. If no match, the default offered chain will be used.",
},
&cli.StringFlag{
Name: "always-deactivate-authorizations",
Usage: "Force the authorizations to be relinquished even if the certificate request was successful.",
},
},
}
}
const rootPathWarningMessage = `!!!! HEADS UP !!!!
Your account credentials have been saved in your Let's Encrypt
configuration directory at "%s".
You should make a secure backup of this folder now. This
configuration directory will also contain certificates and
private keys obtained from Let's Encrypt so making regular
backups of this folder is ideal.
`
func run(ctx *cli.Context) error {
accountsStorage := NewAccountsStorage(ctx)
account, client := setup(ctx, accountsStorage)
setupChallenges(ctx, client)
if account.Registration == nil {
reg, err := register(ctx, client)
if err != nil {
log.Fatalf("Could not complete registration\n\t%v", err)
}
account.Registration = reg
if err = accountsStorage.Save(account); err != nil {
log.Fatal(err)
}
fmt.Printf(rootPathWarningMessage, accountsStorage.GetRootPath())
}
certsStorage := NewCertificatesStorage(ctx)
certsStorage.CreateRootFolder()
cert, err := obtainCertificate(ctx, client)
if err != nil {
// Make sure to return a non-zero exit code if ObtainSANCertificate returned at least one error.
// Due to us not returning partial certificate we can just exit here instead of at the end.
log.Fatalf("Could not obtain certificates:\n\t%v", err)
}
certsStorage.SaveResource(cert)
meta := map[string]string{
renewEnvAccountEmail: account.Email,
renewEnvCertDomain: cert.Domain,
renewEnvCertPath: certsStorage.GetFileName(cert.Domain, ".crt"),
renewEnvCertKeyPath: certsStorage.GetFileName(cert.Domain, ".key"),
}
return launchHook(ctx.String("run-hook"), meta)
}
func handleTOS(ctx *cli.Context, client *lego.Client) bool {
// Check for a global accept override
if ctx.Bool("accept-tos") {
return true
}
reader := bufio.NewReader(os.Stdin)
log.Printf("Please review the TOS at %s", client.GetToSURL())
for {
fmt.Println("Do you accept the TOS? Y/n")
text, err := reader.ReadString('\n')
if err != nil {
log.Fatalf("Could not read from console: %v", err)
}
text = strings.Trim(text, "\r\n")
switch text {
case "", "y", "Y":
return true
case "n", "N":
return false
default:
fmt.Println("Your input was invalid. Please answer with one of Y/y, n/N or by pressing enter.")
}
}
}
func register(ctx *cli.Context, client *lego.Client) (*registration.Resource, error) {
accepted := handleTOS(ctx, client)
if !accepted {
log.Fatal("You did not accept the TOS. Unable to proceed.")
}
if ctx.Bool("eab") {
kid := ctx.String("kid")
hmacEncoded := ctx.String("hmac")
if kid == "" || hmacEncoded == "" {
log.Fatalf("Requires arguments --kid and --hmac.")
}
return client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{
TermsOfServiceAgreed: accepted,
Kid: kid,
HmacEncoded: hmacEncoded,
})
}
return client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
}
func obtainCertificate(ctx *cli.Context, client *lego.Client) (*certificate.Resource, error) {
bundle := !ctx.Bool("no-bundle")
domains := ctx.StringSlice("domains")
if len(domains) > 0 {
// obtain a certificate, generating a new private key
request := certificate.ObtainRequest{
Domains: domains,
Bundle: bundle,
MustStaple: ctx.Bool("must-staple"),
PreferredChain: ctx.String("preferred-chain"),
AlwaysDeactivateAuthorizations: ctx.Bool("always-deactivate-authorizations"),
}
return client.Certificate.Obtain(request)
}
// read the CSR
csr, err := readCSRFile(ctx.String("csr"))
if err != nil {
return nil, err
}
// obtain a certificate for this CSR
return client.Certificate.ObtainForCSR(certificate.ObtainForCSRRequest{
CSR: csr,
Bundle: bundle,
PreferredChain: ctx.String("preferred-chain"),
AlwaysDeactivateAuthorizations: ctx.Bool("always-deactivate-authorizations"),
})
}
lego-4.9.1/cmd/flags.go 0000664 0000000 0000000 00000011621 14340204635 0014643 0 ustar 00root root 0000000 0000000 package cmd
import (
"github.com/go-acme/lego/v4/lego"
"github.com/urfave/cli/v2"
"software.sslmate.com/src/go-pkcs12"
)
func CreateFlags(defaultPath string) []cli.Flag {
return []cli.Flag{
&cli.StringSliceFlag{
Name: "domains",
Aliases: []string{"d"},
Usage: "Add a domain to the process. Can be specified multiple times.",
},
&cli.StringFlag{
Name: "server",
Aliases: []string{"s"},
Usage: "CA hostname (and optionally :port). The server certificate must be trusted in order to avoid further modifications to the client.",
Value: lego.LEDirectoryProduction,
},
&cli.BoolFlag{
Name: "accept-tos",
Aliases: []string{"a"},
Usage: "By setting this flag to true you indicate that you accept the current Let's Encrypt terms of service.",
},
&cli.StringFlag{
Name: "email",
Aliases: []string{"m"},
Usage: "Email used for registration and recovery contact.",
},
&cli.StringFlag{
Name: "csr",
Aliases: []string{"c"},
Usage: "Certificate signing request filename, if an external CSR is to be used.",
},
&cli.BoolFlag{
Name: "eab",
Usage: "Use External Account Binding for account registration. Requires --kid and --hmac.",
},
&cli.StringFlag{
Name: "kid",
Usage: "Key identifier from External CA. Used for External Account Binding.",
},
&cli.StringFlag{
Name: "hmac",
Usage: "MAC key from External CA. Should be in Base64 URL Encoding without padding format. Used for External Account Binding.",
},
&cli.StringFlag{
Name: "key-type",
Aliases: []string{"k"},
Value: "ec256",
Usage: "Key type to use for private keys. Supported: rsa2048, rsa4096, rsa8192, ec256, ec384.",
},
&cli.StringFlag{
Name: "filename",
Usage: "(deprecated) Filename of the generated certificate.",
},
&cli.StringFlag{
Name: "path",
EnvVars: []string{"LEGO_PATH"},
Usage: "Directory to use for storing the data.",
Value: defaultPath,
},
&cli.BoolFlag{
Name: "http",
Usage: "Use the HTTP challenge to solve challenges. Can be mixed with other types of challenges.",
},
&cli.StringFlag{
Name: "http.port",
Usage: "Set the port and interface to use for HTTP based challenges to listen on.Supported: interface:port or :port.",
Value: ":80",
},
&cli.StringFlag{
Name: "http.proxy-header",
Usage: "Validate against this HTTP header when solving HTTP based challenges behind a reverse proxy.",
Value: "Host",
},
&cli.StringFlag{
Name: "http.webroot",
Usage: "Set the webroot folder to use for HTTP based challenges to write directly in a file in .well-known/acme-challenge. This disables the built-in server and expects the given directory to be publicly served with access to .well-known/acme-challenge",
},
&cli.StringSliceFlag{
Name: "http.memcached-host",
Usage: "Set the memcached host(s) to use for HTTP based challenges. Challenges will be written to all specified hosts.",
},
&cli.BoolFlag{
Name: "tls",
Usage: "Use the TLS challenge to solve challenges. Can be mixed with other types of challenges.",
},
&cli.StringFlag{
Name: "tls.port",
Usage: "Set the port and interface to use for TLS based challenges to listen on. Supported: interface:port or :port.",
Value: ":443",
},
&cli.StringFlag{
Name: "dns",
Usage: "Solve a DNS challenge using the specified provider. Can be mixed with other types of challenges. Run 'lego dnshelp' for help on usage.",
},
&cli.BoolFlag{
Name: "dns.disable-cp",
Usage: "By setting this flag to true, disables the need to wait the propagation of the TXT record to all authoritative name servers.",
},
&cli.StringSliceFlag{
Name: "dns.resolvers",
Usage: "Set the resolvers to use for performing recursive DNS queries. Supported: host:port. The default is to use the system resolvers, or Google's DNS resolvers if the system's cannot be determined.",
},
&cli.IntFlag{
Name: "http-timeout",
Usage: "Set the HTTP timeout value to a specific value in seconds.",
},
&cli.IntFlag{
Name: "dns-timeout",
Usage: "Set the DNS timeout value to a specific value in seconds. Used only when performing authoritative name servers queries.",
Value: 10,
},
&cli.BoolFlag{
Name: "pem",
Usage: "Generate a .pem file by concatenating the .key and .crt files together.",
},
&cli.BoolFlag{
Name: "pfx",
Usage: "Generate a .pfx (PKCS#12) file by with the .key and .crt and issuer .crt files together.",
},
&cli.StringFlag{
Name: "pfx.pass",
Usage: "The password used to encrypt the .pfx (PCKS#12) file.",
Value: pkcs12.DefaultPassword,
},
&cli.IntFlag{
Name: "cert.timeout",
Usage: "Set the certificate timeout value to a specific value in seconds. Only used when obtaining certificates.",
Value: 30,
},
&cli.StringFlag{
Name: "user-agent",
Usage: "Add to the user-agent sent to the CA to identify an application embedding lego-cli",
},
}
}
lego-4.9.1/cmd/hook.go 0000664 0000000 0000000 00000001422 14340204635 0014505 0 ustar 00root root 0000000 0000000 package cmd
import (
"context"
"errors"
"fmt"
"os"
"os/exec"
"strings"
"time"
)
func launchHook(hook string, meta map[string]string) error {
if hook == "" {
return nil
}
ctxCmd, cancel := context.WithTimeout(context.Background(), 120*time.Second)
defer cancel()
parts := strings.Fields(hook)
cmdCtx := exec.CommandContext(ctxCmd, parts[0], parts[1:]...)
cmdCtx.Env = append(os.Environ(), metaToEnv(meta)...)
output, err := cmdCtx.CombinedOutput()
if len(output) > 0 {
fmt.Println(string(output))
}
if errors.Is(ctxCmd.Err(), context.DeadlineExceeded) {
return errors.New("hook timed out")
}
return err
}
func metaToEnv(meta map[string]string) []string {
var envs []string
for k, v := range meta {
envs = append(envs, k+"="+v)
}
return envs
}
lego-4.9.1/cmd/lego/ 0000775 0000000 0000000 00000000000 14340204635 0014145 5 ustar 00root root 0000000 0000000 lego-4.9.1/cmd/lego/main.go 0000664 0000000 0000000 00000001606 14340204635 0015423 0 ustar 00root root 0000000 0000000 // Let's Encrypt client to go!
// CLI application for generating Let's Encrypt certificates using the ACME package.
package main
import (
"fmt"
"os"
"path/filepath"
"runtime"
"github.com/go-acme/lego/v4/cmd"
"github.com/go-acme/lego/v4/log"
"github.com/urfave/cli/v2"
)
var version = "dev"
func main() {
app := cli.NewApp()
app.Name = "lego"
app.HelpName = "lego"
app.Usage = "Let's Encrypt client written in Go"
app.EnableBashCompletion = true
app.Version = version
cli.VersionPrinter = func(c *cli.Context) {
fmt.Printf("lego version %s %s/%s\n", c.App.Version, runtime.GOOS, runtime.GOARCH)
}
var defaultPath string
cwd, err := os.Getwd()
if err == nil {
defaultPath = filepath.Join(cwd, ".lego")
}
app.Flags = cmd.CreateFlags(defaultPath)
app.Before = cmd.Before
app.Commands = cmd.CreateCommands()
err = app.Run(os.Args)
if err != nil {
log.Fatal(err)
}
}
lego-4.9.1/cmd/setup.go 0000664 0000000 0000000 00000006144 14340204635 0014713 0 ustar 00root root 0000000 0000000 package cmd
import (
"crypto/x509"
"encoding/pem"
"fmt"
"os"
"strings"
"time"
"github.com/go-acme/lego/v4/certcrypto"
"github.com/go-acme/lego/v4/lego"
"github.com/go-acme/lego/v4/log"
"github.com/go-acme/lego/v4/registration"
"github.com/urfave/cli/v2"
)
const filePerm os.FileMode = 0o600
func setup(ctx *cli.Context, accountsStorage *AccountsStorage) (*Account, *lego.Client) {
keyType := getKeyType(ctx)
privateKey := accountsStorage.GetPrivateKey(keyType)
var account *Account
if accountsStorage.ExistsAccountFilePath() {
account = accountsStorage.LoadAccount(privateKey)
} else {
account = &Account{Email: accountsStorage.GetUserID(), key: privateKey}
}
client := newClient(ctx, account, keyType)
return account, client
}
func newClient(ctx *cli.Context, acc registration.User, keyType certcrypto.KeyType) *lego.Client {
config := lego.NewConfig(acc)
config.CADirURL = ctx.String("server")
config.Certificate = lego.CertificateConfig{
KeyType: keyType,
Timeout: time.Duration(ctx.Int("cert.timeout")) * time.Second,
}
config.UserAgent = getUserAgent(ctx)
if ctx.IsSet("http-timeout") {
config.HTTPClient.Timeout = time.Duration(ctx.Int("http-timeout")) * time.Second
}
client, err := lego.NewClient(config)
if err != nil {
log.Fatalf("Could not create client: %v", err)
}
if client.GetExternalAccountRequired() && !ctx.IsSet("eab") {
log.Fatal("Server requires External Account Binding. Use --eab with --kid and --hmac.")
}
return client
}
// getKeyType the type from which private keys should be generated.
func getKeyType(ctx *cli.Context) certcrypto.KeyType {
keyType := ctx.String("key-type")
switch strings.ToUpper(keyType) {
case "RSA2048":
return certcrypto.RSA2048
case "RSA4096":
return certcrypto.RSA4096
case "RSA8192":
return certcrypto.RSA8192
case "EC256":
return certcrypto.EC256
case "EC384":
return certcrypto.EC384
}
log.Fatalf("Unsupported KeyType: %s", keyType)
return ""
}
func getEmail(ctx *cli.Context) string {
email := ctx.String("email")
if email == "" {
log.Fatal("You have to pass an account (email address) to the program using --email or -m")
}
return email
}
func getUserAgent(ctx *cli.Context) string {
return strings.TrimSpace(fmt.Sprintf("%s lego-cli/%s", ctx.String("user-agent"), ctx.App.Version))
}
func createNonExistingFolder(path string) error {
if _, err := os.Stat(path); os.IsNotExist(err) {
return os.MkdirAll(path, 0o700)
} else if err != nil {
return err
}
return nil
}
func readCSRFile(filename string) (*x509.CertificateRequest, error) {
bytes, err := os.ReadFile(filename)
if err != nil {
return nil, err
}
raw := bytes
// see if we can find a PEM-encoded CSR
var p *pem.Block
rest := bytes
for {
// decode a PEM block
p, rest = pem.Decode(rest)
// did we fail?
if p == nil {
break
}
// did we get a CSR?
if p.Type == "CERTIFICATE REQUEST" || p.Type == "NEW CERTIFICATE REQUEST" {
raw = p.Bytes
}
}
// no PEM-encoded CSR
// assume we were given a DER-encoded ASN.1 CSR
// (if this assumption is wrong, parsing these bytes will fail)
return x509.ParseCertificateRequest(raw)
}
lego-4.9.1/cmd/setup_challenges.go 0000664 0000000 0000000 00000006404 14340204635 0017077 0 ustar 00root root 0000000 0000000 package cmd
import (
"net"
"strings"
"time"
"github.com/go-acme/lego/v4/challenge"
"github.com/go-acme/lego/v4/challenge/dns01"
"github.com/go-acme/lego/v4/challenge/http01"
"github.com/go-acme/lego/v4/challenge/tlsalpn01"
"github.com/go-acme/lego/v4/lego"
"github.com/go-acme/lego/v4/log"
"github.com/go-acme/lego/v4/providers/dns"
"github.com/go-acme/lego/v4/providers/http/memcached"
"github.com/go-acme/lego/v4/providers/http/webroot"
"github.com/urfave/cli/v2"
)
func setupChallenges(ctx *cli.Context, client *lego.Client) {
if !ctx.Bool("http") && !ctx.Bool("tls") && !ctx.IsSet("dns") {
log.Fatal("No challenge selected. You must specify at least one challenge: `--http`, `--tls`, `--dns`.")
}
if ctx.Bool("http") {
err := client.Challenge.SetHTTP01Provider(setupHTTPProvider(ctx))
if err != nil {
log.Fatal(err)
}
}
if ctx.Bool("tls") {
err := client.Challenge.SetTLSALPN01Provider(setupTLSProvider(ctx))
if err != nil {
log.Fatal(err)
}
}
if ctx.IsSet("dns") {
setupDNS(ctx, client)
}
}
func setupHTTPProvider(ctx *cli.Context) challenge.Provider {
switch {
case ctx.IsSet("http.webroot"):
ps, err := webroot.NewHTTPProvider(ctx.String("http.webroot"))
if err != nil {
log.Fatal(err)
}
return ps
case ctx.IsSet("http.memcached-host"):
ps, err := memcached.NewMemcachedProvider(ctx.StringSlice("http.memcached-host"))
if err != nil {
log.Fatal(err)
}
return ps
case ctx.IsSet("http.port"):
iface := ctx.String("http.port")
if !strings.Contains(iface, ":") {
log.Fatalf("The --http switch only accepts interface:port or :port for its argument.")
}
host, port, err := net.SplitHostPort(iface)
if err != nil {
log.Fatal(err)
}
srv := http01.NewProviderServer(host, port)
if header := ctx.String("http.proxy-header"); header != "" {
srv.SetProxyHeader(header)
}
return srv
case ctx.Bool("http"):
srv := http01.NewProviderServer("", "")
if header := ctx.String("http.proxy-header"); header != "" {
srv.SetProxyHeader(header)
}
return srv
default:
log.Fatal("Invalid HTTP challenge options.")
return nil
}
}
func setupTLSProvider(ctx *cli.Context) challenge.Provider {
switch {
case ctx.IsSet("tls.port"):
iface := ctx.String("tls.port")
if !strings.Contains(iface, ":") {
log.Fatalf("The --tls switch only accepts interface:port or :port for its argument.")
}
host, port, err := net.SplitHostPort(iface)
if err != nil {
log.Fatal(err)
}
return tlsalpn01.NewProviderServer(host, port)
case ctx.Bool("tls"):
return tlsalpn01.NewProviderServer("", "")
default:
log.Fatal("Invalid HTTP challenge options.")
return nil
}
}
func setupDNS(ctx *cli.Context, client *lego.Client) {
provider, err := dns.NewDNSChallengeProviderByName(ctx.String("dns"))
if err != nil {
log.Fatal(err)
}
servers := ctx.StringSlice("dns.resolvers")
err = client.Challenge.SetDNS01Provider(provider,
dns01.CondOption(len(servers) > 0,
dns01.AddRecursiveNameservers(dns01.ParseNameservers(ctx.StringSlice("dns.resolvers")))),
dns01.CondOption(ctx.Bool("dns.disable-cp"),
dns01.DisableCompletePropagationRequirement()),
dns01.CondOption(ctx.IsSet("dns-timeout"),
dns01.AddDNSTimeout(time.Duration(ctx.Int("dns-timeout"))*time.Second)),
)
if err != nil {
log.Fatal(err)
}
}
lego-4.9.1/cmd/zz_gen_cmd_dnshelp.go 0000664 0000000 0000000 00000273523 14340204635 0017416 0 ustar 00root root 0000000 0000000 package cmd
// CODE GENERATED AUTOMATICALLY
// THIS FILE MUST NOT BE EDITED BY HAND
import (
"fmt"
"os"
"sort"
"strings"
"text/tabwriter"
)
func allDNSCodes() string {
providers := []string{
"manual",
"acme-dns",
"alidns",
"allinkl",
"arvancloud",
"auroradns",
"autodns",
"azure",
"bindman",
"bluecat",
"checkdomain",
"civo",
"clouddns",
"cloudflare",
"cloudns",
"cloudxns",
"conoha",
"constellix",
"desec",
"designate",
"digitalocean",
"dnsimple",
"dnsmadeeasy",
"dnspod",
"dode",
"domeneshop",
"dreamhost",
"duckdns",
"dyn",
"dynu",
"easydns",
"edgedns",
"epik",
"exec",
"exoscale",
"freemyip",
"gandi",
"gandiv5",
"gcloud",
"gcore",
"glesys",
"godaddy",
"hetzner",
"hostingde",
"hosttech",
"httpreq",
"hurricane",
"hyperone",
"ibmcloud",
"iij",
"iijdpf",
"infoblox",
"infomaniak",
"internetbs",
"inwx",
"ionos",
"iwantmyname",
"joker",
"lightsail",
"linode",
"liquidweb",
"loopia",
"luadns",
"mydnsjp",
"mythicbeasts",
"namecheap",
"namedotcom",
"namesilo",
"nearlyfreespeech",
"netcup",
"netlify",
"nicmanager",
"nifcloud",
"njalla",
"ns1",
"oraclecloud",
"otc",
"ovh",
"pdns",
"porkbun",
"rackspace",
"regru",
"rfc2136",
"rimuhosting",
"route53",
"safedns",
"sakuracloud",
"scaleway",
"selectel",
"servercow",
"simply",
"sonic",
"stackpath",
"tencentcloud",
"transip",
"variomedia",
"vegadns",
"vercel",
"versio",
"vinyldns",
"vkcloud",
"vscale",
"vultr",
"wedos",
"yandex",
"yandexcloud",
"zoneee",
"zonomi",
}
sort.Strings(providers)
return strings.Join(providers, ", ")
}
func displayDNSHelp(name string) error {
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
ew := &errWriter{w: w}
switch name {
case "acme-dns":
// generated from: providers/dns/acmedns/acmedns.toml
ew.writeln(`Configuration for Joohoi's ACME-DNS.`)
ew.writeln(`Code: 'acme-dns'`)
ew.writeln(`Since: 'v1.1.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "ACME_DNS_API_BASE": The ACME-DNS API address`)
ew.writeln(` - "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.`)
ew.writeln()
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/acme-dns`)
case "alidns":
// generated from: providers/dns/alidns/alidns.toml
ew.writeln(`Configuration for Alibaba Cloud DNS.`)
ew.writeln(`Code: 'alidns'`)
ew.writeln(`Since: 'v1.1.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "ALICLOUD_ACCESS_KEY": Access key ID`)
ew.writeln(` - "ALICLOUD_RAM_ROLE": Your instance RAM role (https://www.alibabacloud.com/help/doc-detail/54579.htm)`)
ew.writeln(` - "ALICLOUD_SECRET_KEY": Access Key secret`)
ew.writeln(` - "ALICLOUD_SECURITY_TOKEN": STS Security Token (optional)`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "ALICLOUD_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "ALICLOUD_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "ALICLOUD_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "ALICLOUD_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/alidns`)
case "allinkl":
// generated from: providers/dns/allinkl/allinkl.toml
ew.writeln(`Configuration for all-inkl.`)
ew.writeln(`Code: 'allinkl'`)
ew.writeln(`Since: 'v4.5.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "ALL_INKL_LOGIN": KAS login`)
ew.writeln(` - "ALL_INKL_PASSWORD": KAS password`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "ALL_INKL_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "ALL_INKL_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "ALL_INKL_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/allinkl`)
case "arvancloud":
// generated from: providers/dns/arvancloud/arvancloud.toml
ew.writeln(`Configuration for ArvanCloud.`)
ew.writeln(`Code: 'arvancloud'`)
ew.writeln(`Since: 'v3.8.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "ARVANCLOUD_API_KEY": API key`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "ARVANCLOUD_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "ARVANCLOUD_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "ARVANCLOUD_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "ARVANCLOUD_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/arvancloud`)
case "auroradns":
// generated from: providers/dns/auroradns/auroradns.toml
ew.writeln(`Configuration for Aurora DNS.`)
ew.writeln(`Code: 'auroradns'`)
ew.writeln(`Since: 'v0.4.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "AURORA_API_KEY": API key or username to used`)
ew.writeln(` - "AURORA_SECRET": Secret password to be used`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "AURORA_ENDPOINT": API endpoint URL`)
ew.writeln(` - "AURORA_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "AURORA_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "AURORA_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/auroradns`)
case "autodns":
// generated from: providers/dns/autodns/autodns.toml
ew.writeln(`Configuration for Autodns.`)
ew.writeln(`Code: 'autodns'`)
ew.writeln(`Since: 'v3.2.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "AUTODNS_API_PASSWORD": User Password`)
ew.writeln(` - "AUTODNS_API_USER": Username`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "AUTODNS_CONTEXT": API context (4 for production, 1 for testing. Defaults to 4)`)
ew.writeln(` - "AUTODNS_ENDPOINT": API endpoint URL, defaults to https://api.autodns.com/v1/`)
ew.writeln(` - "AUTODNS_HTTP_TIMEOUT": API request timeout, defaults to 30 seconds`)
ew.writeln(` - "AUTODNS_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "AUTODNS_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "AUTODNS_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/autodns`)
case "azure":
// generated from: providers/dns/azure/azure.toml
ew.writeln(`Configuration for Azure.`)
ew.writeln(`Code: 'azure'`)
ew.writeln(`Since: 'v0.4.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "AZURE_CLIENT_ID": Client ID`)
ew.writeln(` - "AZURE_CLIENT_SECRET": Client secret`)
ew.writeln(` - "AZURE_ENVIRONMENT": Azure environment, one of: public, usgovernment, german, and china`)
ew.writeln(` - "AZURE_RESOURCE_GROUP": Resource group`)
ew.writeln(` - "AZURE_SUBSCRIPTION_ID": Subscription ID`)
ew.writeln(` - "AZURE_TENANT_ID": Tenant ID`)
ew.writeln(` - "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).`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "AZURE_METADATA_ENDPOINT": Metadata Service endpoint URL`)
ew.writeln(` - "AZURE_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "AZURE_PRIVATE_ZONE": Set to true to use Azure Private DNS Zones and not public`)
ew.writeln(` - "AZURE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "AZURE_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln(` - "AZURE_ZONE_NAME": Zone name to use inside Azure DNS service to add the TXT record in`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/azure`)
case "bindman":
// generated from: providers/dns/bindman/bindman.toml
ew.writeln(`Configuration for Bindman.`)
ew.writeln(`Code: 'bindman'`)
ew.writeln(`Since: 'v2.6.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "BINDMAN_MANAGER_ADDRESS": The server URL, should have scheme, hostname, and port (if required) of the Bindman-DNS Manager server`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "BINDMAN_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "BINDMAN_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "BINDMAN_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/bindman`)
case "bluecat":
// generated from: providers/dns/bluecat/bluecat.toml
ew.writeln(`Configuration for Bluecat.`)
ew.writeln(`Code: 'bluecat'`)
ew.writeln(`Since: 'v0.5.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "BLUECAT_CONFIG_NAME": Configuration name`)
ew.writeln(` - "BLUECAT_DNS_VIEW": External DNS View Name`)
ew.writeln(` - "BLUECAT_PASSWORD": API password`)
ew.writeln(` - "BLUECAT_SERVER_URL": The server URL, should have scheme, hostname, and port (if required) of the authoritative Bluecat BAM serve`)
ew.writeln(` - "BLUECAT_USER_NAME": API username`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "BLUECAT_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "BLUECAT_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "BLUECAT_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "BLUECAT_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/bluecat`)
case "checkdomain":
// generated from: providers/dns/checkdomain/checkdomain.toml
ew.writeln(`Configuration for Checkdomain.`)
ew.writeln(`Code: 'checkdomain'`)
ew.writeln(`Since: 'v3.3.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "CHECKDOMAIN_TOKEN": API token`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "CHECKDOMAIN_ENDPOINT": API endpoint URL, defaults to https://api.checkdomain.de`)
ew.writeln(` - "CHECKDOMAIN_HTTP_TIMEOUT": API request timeout, defaults to 30 seconds`)
ew.writeln(` - "CHECKDOMAIN_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "CHECKDOMAIN_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "CHECKDOMAIN_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/checkdomain`)
case "civo":
// generated from: providers/dns/civo/civo.toml
ew.writeln(`Configuration for Civo.`)
ew.writeln(`Code: 'civo'`)
ew.writeln(`Since: 'v4.9.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "CIVO_TOKEN": Authentication token`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "CIVO_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "CIVO_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "CIVO_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/civo`)
case "clouddns":
// generated from: providers/dns/clouddns/clouddns.toml
ew.writeln(`Configuration for CloudDNS.`)
ew.writeln(`Code: 'clouddns'`)
ew.writeln(`Since: 'v3.6.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "CLOUDDNS_CLIENT_ID": Client ID`)
ew.writeln(` - "CLOUDDNS_EMAIL": Account email`)
ew.writeln(` - "CLOUDDNS_PASSWORD": Account password`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "CLOUDDNS_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "CLOUDDNS_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "CLOUDDNS_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "CLOUDDNS_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/clouddns`)
case "cloudflare":
// generated from: providers/dns/cloudflare/cloudflare.toml
ew.writeln(`Configuration for Cloudflare.`)
ew.writeln(`Code: 'cloudflare'`)
ew.writeln(`Since: 'v0.3.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "CF_API_EMAIL": Account email`)
ew.writeln(` - "CF_API_KEY": API key`)
ew.writeln(` - "CF_DNS_API_TOKEN": API token with DNS:Edit permission (since v3.1.0)`)
ew.writeln(` - "CF_ZONE_API_TOKEN": API token with Zone:Read permission (since v3.1.0)`)
ew.writeln(` - "CLOUDFLARE_API_KEY": Alias to CF_API_KEY`)
ew.writeln(` - "CLOUDFLARE_DNS_API_TOKEN": Alias to CF_DNS_API_TOKEN`)
ew.writeln(` - "CLOUDFLARE_EMAIL": Alias to CF_API_EMAIL`)
ew.writeln(` - "CLOUDFLARE_ZONE_API_TOKEN": Alias to CF_ZONE_API_TOKEN`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "CLOUDFLARE_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "CLOUDFLARE_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "CLOUDFLARE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "CLOUDFLARE_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/cloudflare`)
case "cloudns":
// generated from: providers/dns/cloudns/cloudns.toml
ew.writeln(`Configuration for ClouDNS.`)
ew.writeln(`Code: 'cloudns'`)
ew.writeln(`Since: 'v2.3.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "CLOUDNS_AUTH_ID": The API user ID`)
ew.writeln(` - "CLOUDNS_AUTH_PASSWORD": The password for API user ID`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "CLOUDNS_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "CLOUDNS_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "CLOUDNS_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "CLOUDNS_SUB_AUTH_ID": The API sub user ID`)
ew.writeln(` - "CLOUDNS_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/cloudns`)
case "cloudxns":
// generated from: providers/dns/cloudxns/cloudxns.toml
ew.writeln(`Configuration for CloudXNS.`)
ew.writeln(`Code: 'cloudxns'`)
ew.writeln(`Since: 'v0.5.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "CLOUDXNS_API_KEY": The API key`)
ew.writeln(` - "CLOUDXNS_SECRET_KEY": The API secret key`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "CLOUDXNS_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "CLOUDXNS_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "CLOUDXNS_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "CLOUDXNS_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/cloudxns`)
case "conoha":
// generated from: providers/dns/conoha/conoha.toml
ew.writeln(`Configuration for ConoHa.`)
ew.writeln(`Code: 'conoha'`)
ew.writeln(`Since: 'v1.2.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "CONOHA_API_PASSWORD": The API password`)
ew.writeln(` - "CONOHA_API_USERNAME": The API username`)
ew.writeln(` - "CONOHA_TENANT_ID": Tenant ID`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "CONOHA_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "CONOHA_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "CONOHA_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "CONOHA_REGION": The region`)
ew.writeln(` - "CONOHA_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/conoha`)
case "constellix":
// generated from: providers/dns/constellix/constellix.toml
ew.writeln(`Configuration for Constellix.`)
ew.writeln(`Code: 'constellix'`)
ew.writeln(`Since: 'v3.4.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "CONSTELLIX_API_KEY": User API key`)
ew.writeln(` - "CONSTELLIX_SECRET_KEY": User secret key`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "CONSTELLIX_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "CONSTELLIX_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "CONSTELLIX_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "CONSTELLIX_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/constellix`)
case "desec":
// generated from: providers/dns/desec/desec.toml
ew.writeln(`Configuration for deSEC.io.`)
ew.writeln(`Code: 'desec'`)
ew.writeln(`Since: 'v3.7.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "DESEC_TOKEN": Domain token`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "DESEC_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "DESEC_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "DESEC_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "DESEC_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/desec`)
case "designate":
// generated from: providers/dns/designate/designate.toml
ew.writeln(`Configuration for Designate DNSaaS for Openstack.`)
ew.writeln(`Code: 'designate'`)
ew.writeln(`Since: 'v2.2.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "OS_APPLICATION_CREDENTIAL_ID": Application credential ID`)
ew.writeln(` - "OS_APPLICATION_CREDENTIAL_NAME": Application credential name`)
ew.writeln(` - "OS_APPLICATION_CREDENTIAL_SECRET": Application credential secret`)
ew.writeln(` - "OS_AUTH_URL": Identity endpoint URL`)
ew.writeln(` - "OS_PASSWORD": Password`)
ew.writeln(` - "OS_PROJECT_NAME": Project name`)
ew.writeln(` - "OS_REGION_NAME": Region name`)
ew.writeln(` - "OS_USERNAME": Username`)
ew.writeln(` - "OS_USER_ID": User ID`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "DESIGNATE_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "DESIGNATE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "DESIGNATE_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln(` - "OS_PROJECT_ID": Project ID`)
ew.writeln(` - "OS_TENANT_NAME": Tenant name (deprecated see OS_PROJECT_NAME and OS_PROJECT_ID)`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/designate`)
case "digitalocean":
// generated from: providers/dns/digitalocean/digitalocean.toml
ew.writeln(`Configuration for Digital Ocean.`)
ew.writeln(`Code: 'digitalocean'`)
ew.writeln(`Since: 'v0.3.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "DO_AUTH_TOKEN": Authentication token`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "DO_API_URL": The URL of the API`)
ew.writeln(` - "DO_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "DO_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "DO_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "DO_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/digitalocean`)
case "dnsimple":
// generated from: providers/dns/dnsimple/dnsimple.toml
ew.writeln(`Configuration for DNSimple.`)
ew.writeln(`Code: 'dnsimple'`)
ew.writeln(`Since: 'v0.3.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "DNSIMPLE_OAUTH_TOKEN": OAuth token`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "DNSIMPLE_BASE_URL": API endpoint URL`)
ew.writeln(` - "DNSIMPLE_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "DNSIMPLE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "DNSIMPLE_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/dnsimple`)
case "dnsmadeeasy":
// generated from: providers/dns/dnsmadeeasy/dnsmadeeasy.toml
ew.writeln(`Configuration for DNS Made Easy.`)
ew.writeln(`Code: 'dnsmadeeasy'`)
ew.writeln(`Since: 'v0.4.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "DNSMADEEASY_API_KEY": The API key`)
ew.writeln(` - "DNSMADEEASY_API_SECRET": The API Secret key`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "DNSMADEEASY_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "DNSMADEEASY_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "DNSMADEEASY_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "DNSMADEEASY_SANDBOX": Activate the sandbox (boolean)`)
ew.writeln(` - "DNSMADEEASY_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/dnsmadeeasy`)
case "dnspod":
// generated from: providers/dns/dnspod/dnspod.toml
ew.writeln(`Configuration for DNSPod (deprecated).`)
ew.writeln(`Code: 'dnspod'`)
ew.writeln(`Since: 'v0.4.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "DNSPOD_API_KEY": The user token`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "DNSPOD_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "DNSPOD_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "DNSPOD_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "DNSPOD_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/dnspod`)
case "dode":
// generated from: providers/dns/dode/dode.toml
ew.writeln(`Configuration for Domain Offensive (do.de).`)
ew.writeln(`Code: 'dode'`)
ew.writeln(`Since: 'v2.4.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "DODE_TOKEN": API token`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "DODE_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "DODE_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "DODE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "DODE_SEQUENCE_INTERVAL": Time between sequential requests`)
ew.writeln(` - "DODE_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/dode`)
case "domeneshop":
// generated from: providers/dns/domeneshop/domeneshop.toml
ew.writeln(`Configuration for Domeneshop.`)
ew.writeln(`Code: 'domeneshop'`)
ew.writeln(`Since: 'v4.3.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "DOMENESHOP_API_SECRET": API secret`)
ew.writeln(` - "DOMENESHOP_API_TOKEN": API token`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "DOMENESHOP_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "DOMENESHOP_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "DOMENESHOP_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/domeneshop`)
case "dreamhost":
// generated from: providers/dns/dreamhost/dreamhost.toml
ew.writeln(`Configuration for DreamHost.`)
ew.writeln(`Code: 'dreamhost'`)
ew.writeln(`Since: 'v1.1.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "DREAMHOST_API_KEY": The API key`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "DREAMHOST_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "DREAMHOST_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "DREAMHOST_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "DREAMHOST_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/dreamhost`)
case "duckdns":
// generated from: providers/dns/duckdns/duckdns.toml
ew.writeln(`Configuration for Duck DNS.`)
ew.writeln(`Code: 'duckdns'`)
ew.writeln(`Since: 'v0.5.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "DUCKDNS_TOKEN": Account token`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "DUCKDNS_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "DUCKDNS_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "DUCKDNS_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "DUCKDNS_SEQUENCE_INTERVAL": Time between sequential requests`)
ew.writeln(` - "DUCKDNS_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/duckdns`)
case "dyn":
// generated from: providers/dns/dyn/dyn.toml
ew.writeln(`Configuration for Dyn.`)
ew.writeln(`Code: 'dyn'`)
ew.writeln(`Since: 'v0.3.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "DYN_CUSTOMER_NAME": Customer name`)
ew.writeln(` - "DYN_PASSWORD": Password`)
ew.writeln(` - "DYN_USER_NAME": User name`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "DYN_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "DYN_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "DYN_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "DYN_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/dyn`)
case "dynu":
// generated from: providers/dns/dynu/dynu.toml
ew.writeln(`Configuration for Dynu.`)
ew.writeln(`Code: 'dynu'`)
ew.writeln(`Since: 'v3.5.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "DYNU_API_KEY": API key`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "DYNU_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "DYNU_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "DYNU_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "DYNU_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/dynu`)
case "easydns":
// generated from: providers/dns/easydns/easydns.toml
ew.writeln(`Configuration for EasyDNS.`)
ew.writeln(`Code: 'easydns'`)
ew.writeln(`Since: 'v2.6.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "EASYDNS_KEY": API Key`)
ew.writeln(` - "EASYDNS_TOKEN": API Token`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "EASYDNS_ENDPOINT": The endpoint URL of the API Server`)
ew.writeln(` - "EASYDNS_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "EASYDNS_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "EASYDNS_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "EASYDNS_SEQUENCE_INTERVAL": Time between sequential requests`)
ew.writeln(` - "EASYDNS_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/easydns`)
case "edgedns":
// generated from: providers/dns/edgedns/edgedns.toml
ew.writeln(`Configuration for Akamai EdgeDNS.`)
ew.writeln(`Code: 'edgedns'`)
ew.writeln(`Since: 'v3.9.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "AKAMAI_ACCESS_TOKEN": Access token, managed by the Akamai EdgeGrid client`)
ew.writeln(` - "AKAMAI_CLIENT_SECRET": Client secret, managed by the Akamai EdgeGrid client`)
ew.writeln(` - "AKAMAI_CLIENT_TOKEN": Client token, managed by the Akamai EdgeGrid client`)
ew.writeln(` - "AKAMAI_EDGERC": Path to the .edgerc file, managed by the Akamai EdgeGrid client`)
ew.writeln(` - "AKAMAI_EDGERC_SECTION": Configuration section, managed by the Akamai EdgeGrid client`)
ew.writeln(` - "AKAMAI_HOST": API host, managed by the Akamai EdgeGrid client`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "AKAMAI_POLLING_INTERVAL": Time between DNS propagation check. Default: 15 seconds`)
ew.writeln(` - "AKAMAI_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation. Default: 3 minutes`)
ew.writeln(` - "AKAMAI_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/edgedns`)
case "epik":
// generated from: providers/dns/epik/epik.toml
ew.writeln(`Configuration for Epik.`)
ew.writeln(`Code: 'epik'`)
ew.writeln(`Since: 'v4.5.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "EPIK_SIGNATURE": Epik API signature (https://registrar.epik.com/account/api-settings/)`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "EPIK_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "EPIK_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "EPIK_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "EPIK_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/epik`)
case "exec":
// generated from: providers/dns/exec/exec.toml
ew.writeln(`Configuration for External program.`)
ew.writeln(`Code: 'exec'`)
ew.writeln(`Since: 'v0.5.0'`)
ew.writeln()
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/exec`)
case "exoscale":
// generated from: providers/dns/exoscale/exoscale.toml
ew.writeln(`Configuration for Exoscale.`)
ew.writeln(`Code: 'exoscale'`)
ew.writeln(`Since: 'v0.4.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "EXOSCALE_API_KEY": API key`)
ew.writeln(` - "EXOSCALE_API_SECRET": API secret`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "EXOSCALE_API_ZONE": API zone`)
ew.writeln(` - "EXOSCALE_ENDPOINT": API endpoint URL`)
ew.writeln(` - "EXOSCALE_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "EXOSCALE_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "EXOSCALE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "EXOSCALE_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/exoscale`)
case "freemyip":
// generated from: providers/dns/freemyip/freemyip.toml
ew.writeln(`Configuration for freemyip.com.`)
ew.writeln(`Code: 'freemyip'`)
ew.writeln(`Since: 'v4.5.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "FREEMYIP_TOKEN": Account token`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "FREEMYIP_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "FREEMYIP_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "FREEMYIP_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "FREEMYIP_SEQUENCE_INTERVAL": Time between sequential requests`)
ew.writeln(` - "FREEMYIP_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/freemyip`)
case "gandi":
// generated from: providers/dns/gandi/gandi.toml
ew.writeln(`Configuration for Gandi.`)
ew.writeln(`Code: 'gandi'`)
ew.writeln(`Since: 'v0.3.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "GANDI_API_KEY": API key`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "GANDI_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "GANDI_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "GANDI_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "GANDI_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/gandi`)
case "gandiv5":
// generated from: providers/dns/gandiv5/gandiv5.toml
ew.writeln(`Configuration for Gandi Live DNS (v5).`)
ew.writeln(`Code: 'gandiv5'`)
ew.writeln(`Since: 'v0.5.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "GANDIV5_API_KEY": API key`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "GANDIV5_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "GANDIV5_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "GANDIV5_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "GANDIV5_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/gandiv5`)
case "gcloud":
// generated from: providers/dns/gcloud/gcloud.toml
ew.writeln(`Configuration for Google Cloud.`)
ew.writeln(`Code: 'gcloud'`)
ew.writeln(`Since: 'v0.3.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "Application Default Credentials": [Documentation](https://cloud.google.com/docs/authentication/production#providing_credentials_to_your_application)`)
ew.writeln(` - "GCE_PROJECT": Project name (by default, the project name is auto-detected by using the metadata service)`)
ew.writeln(` - "GCE_SERVICE_ACCOUNT": Account`)
ew.writeln(` - "GCE_SERVICE_ACCOUNT_FILE": Account file path`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "GCE_ALLOW_PRIVATE_ZONE": Allows requested domain to be in private DNS zone, works only with a private ACME server (by default: false)`)
ew.writeln(` - "GCE_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "GCE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "GCE_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/gcloud`)
case "gcore":
// generated from: providers/dns/gcore/gcore.toml
ew.writeln(`Configuration for G-Core Labs.`)
ew.writeln(`Code: 'gcore'`)
ew.writeln(`Since: 'v4.5.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "GCORE_PERMANENT_API_TOKEN": Permanent API tokene (https://gcorelabs.com/blog/permanent-api-token-explained/)`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "GCORE_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "GCORE_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "GCORE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "GCORE_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/gcore`)
case "glesys":
// generated from: providers/dns/glesys/glesys.toml
ew.writeln(`Configuration for Glesys.`)
ew.writeln(`Code: 'glesys'`)
ew.writeln(`Since: 'v0.5.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "GLESYS_API_KEY": API key`)
ew.writeln(` - "GLESYS_API_USER": API user`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "GLESYS_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "GLESYS_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "GLESYS_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "GLESYS_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/glesys`)
case "godaddy":
// generated from: providers/dns/godaddy/godaddy.toml
ew.writeln(`Configuration for Go Daddy.`)
ew.writeln(`Code: 'godaddy'`)
ew.writeln(`Since: 'v0.5.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "GODADDY_API_KEY": API key`)
ew.writeln(` - "GODADDY_API_SECRET": API secret`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "GODADDY_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "GODADDY_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "GODADDY_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "GODADDY_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/godaddy`)
case "hetzner":
// generated from: providers/dns/hetzner/hetzner.toml
ew.writeln(`Configuration for Hetzner.`)
ew.writeln(`Code: 'hetzner'`)
ew.writeln(`Since: 'v3.7.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "HETZNER_API_KEY": API key`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "HETZNER_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "HETZNER_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "HETZNER_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "HETZNER_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/hetzner`)
case "hostingde":
// generated from: providers/dns/hostingde/hostingde.toml
ew.writeln(`Configuration for Hosting.de.`)
ew.writeln(`Code: 'hostingde'`)
ew.writeln(`Since: 'v1.1.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "HOSTINGDE_API_KEY": API key`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "HOSTINGDE_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "HOSTINGDE_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "HOSTINGDE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "HOSTINGDE_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln(` - "HOSTINGDE_ZONE_NAME": Zone name in ACE format`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/hostingde`)
case "hosttech":
// generated from: providers/dns/hosttech/hosttech.toml
ew.writeln(`Configuration for Hosttech.`)
ew.writeln(`Code: 'hosttech'`)
ew.writeln(`Since: 'v4.5.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "HOSTTECH_API_KEY": API login`)
ew.writeln(` - "HOSTTECH_PASSWORD": API password`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "HOSTTECH_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "HOSTTECH_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "HOSTTECH_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "HOSTTECH_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/hosttech`)
case "httpreq":
// generated from: providers/dns/httpreq/httpreq.toml
ew.writeln(`Configuration for HTTP request.`)
ew.writeln(`Code: 'httpreq'`)
ew.writeln(`Since: 'v2.0.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "HTTPREQ_ENDPOINT": The URL of the server`)
ew.writeln(` - "HTTPREQ_MODE": 'RAW', none`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "HTTPREQ_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "HTTPREQ_PASSWORD": Basic authentication password`)
ew.writeln(` - "HTTPREQ_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "HTTPREQ_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "HTTPREQ_USERNAME": Basic authentication username`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/httpreq`)
case "hurricane":
// generated from: providers/dns/hurricane/hurricane.toml
ew.writeln(`Configuration for Hurricane Electric DNS.`)
ew.writeln(`Code: 'hurricane'`)
ew.writeln(`Since: 'v4.3.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "HURRICANE_TOKENS": TXT record names and tokens`)
ew.writeln()
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/hurricane`)
case "hyperone":
// generated from: providers/dns/hyperone/hyperone.toml
ew.writeln(`Configuration for HyperOne.`)
ew.writeln(`Code: 'hyperone'`)
ew.writeln(`Since: 'v3.9.0'`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "HYPERONE_API_URL": Allows to pass custom API Endpoint to be used in the challenge (default https://api.hyperone.com/v2)`)
ew.writeln(` - "HYPERONE_LOCATION_ID": Specifies location (region) to be used in API calls. (default pl-waw-1)`)
ew.writeln(` - "HYPERONE_PASSPORT_LOCATION": Allows to pass custom passport file location (default ~/.h1/passport.json)`)
ew.writeln(` - "HYPERONE_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "HYPERONE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "HYPERONE_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/hyperone`)
case "ibmcloud":
// generated from: providers/dns/ibmcloud/ibmcloud.toml
ew.writeln(`Configuration for IBM Cloud (SoftLayer).`)
ew.writeln(`Code: 'ibmcloud'`)
ew.writeln(`Since: 'v4.5.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "SOFTLAYER_API_KEY": Classic Infrastructure API key`)
ew.writeln(` - "SOFTLAYER_USERNAME": User name (IBM Cloud is _)`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "SOFTLAYER_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "SOFTLAYER_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "SOFTLAYER_TIMEOUT": API request timeout`)
ew.writeln(` - "SOFTLAYER_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/ibmcloud`)
case "iij":
// generated from: providers/dns/iij/iij.toml
ew.writeln(`Configuration for Internet Initiative Japan.`)
ew.writeln(`Code: 'iij'`)
ew.writeln(`Since: 'v1.1.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "IIJ_API_ACCESS_KEY": API access key`)
ew.writeln(` - "IIJ_API_SECRET_KEY": API secret key`)
ew.writeln(` - "IIJ_DO_SERVICE_CODE": DO service code`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "IIJ_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "IIJ_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "IIJ_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/iij`)
case "iijdpf":
// generated from: providers/dns/iijdpf/iijdpf.toml
ew.writeln(`Configuration for IIJ DNS Platform Service.`)
ew.writeln(`Code: 'iijdpf'`)
ew.writeln(`Since: 'v4.7.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "IIJ_DPF_API_TOKEN": API token`)
ew.writeln(` - "IIJ_DPF_DPM_SERVICE_CODE": IIJ Managed DNS Service's service code`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "IIJ_DPF_API_ENDPOINT": API endpoint URL, defaults to https://api.dns-platform.jp/dpf/v1`)
ew.writeln(` - "IIJ_DPF_POLLING_INTERVAL": Time between DNS propagation check, defaults to 5 second`)
ew.writeln(` - "IIJ_DPF_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation, defaults to 660 second`)
ew.writeln(` - "IIJ_DPF_TTL": The TTL of the TXT record used for the DNS challenge, default to 300`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/iijdpf`)
case "infoblox":
// generated from: providers/dns/infoblox/infoblox.toml
ew.writeln(`Configuration for Infoblox.`)
ew.writeln(`Code: 'infoblox'`)
ew.writeln(`Since: 'v4.4.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "INFOBLOX_HOST": Host URI`)
ew.writeln(` - "INFOBLOX_PASSWORD": Account Password`)
ew.writeln(` - "INFOBLOX_USERNAME": Account Username`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "INFOBLOX_DNS_VIEW": The view for the TXT records, default: External`)
ew.writeln(` - "INFOBLOX_HTTP_TIMEOUT": HTTP request timeout`)
ew.writeln(` - "INFOBLOX_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "INFOBLOX_PORT": The port for the infoblox grid manager, default: 443`)
ew.writeln(` - "INFOBLOX_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "INFOBLOX_SSL_VERIFY": Whether or not to verify the TLS certificate, default: true`)
ew.writeln(` - "INFOBLOX_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln(` - "INFOBLOX_WAPI_VERSION": The version of WAPI being used, default: 2.11`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/infoblox`)
case "infomaniak":
// generated from: providers/dns/infomaniak/infomaniak.toml
ew.writeln(`Configuration for Infomaniak.`)
ew.writeln(`Code: 'infomaniak'`)
ew.writeln(`Since: 'v4.1.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "INFOMANIAK_ACCESS_TOKEN": Access token`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "INFOMANIAK_ENDPOINT": https://api.infomaniak.com`)
ew.writeln(` - "INFOMANIAK_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "INFOMANIAK_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "INFOMANIAK_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "INFOMANIAK_TTL": The TTL of the TXT record used for the DNS challenge in seconds`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/infomaniak`)
case "internetbs":
// generated from: providers/dns/internetbs/internetbs.toml
ew.writeln(`Configuration for Internet.bs.`)
ew.writeln(`Code: 'internetbs'`)
ew.writeln(`Since: 'v4.5.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "INTERNET_BS_API_KEY": API key`)
ew.writeln(` - "INTERNET_BS_PASSWORD": API password`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "INTERNET_BS_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "INTERNET_BS_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "INTERNET_BS_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "INTERNET_BS_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/internetbs`)
case "inwx":
// generated from: providers/dns/inwx/inwx.toml
ew.writeln(`Configuration for INWX.`)
ew.writeln(`Code: 'inwx'`)
ew.writeln(`Since: 'v2.0.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "INWX_PASSWORD": Password`)
ew.writeln(` - "INWX_USERNAME": Username`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "INWX_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "INWX_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation (default 360s)`)
ew.writeln(` - "INWX_SANDBOX": Activate the sandbox (boolean)`)
ew.writeln(` - "INWX_SHARED_SECRET": shared secret related to 2FA`)
ew.writeln(` - "INWX_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/inwx`)
case "ionos":
// generated from: providers/dns/ionos/ionos.toml
ew.writeln(`Configuration for Ionos.`)
ew.writeln(`Code: 'ionos'`)
ew.writeln(`Since: 'v4.2.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "IONOS_API_KEY": API key '.' https://developer.hosting.ionos.com/docs/getstarted`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "IONOS_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "IONOS_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "IONOS_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "IONOS_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/ionos`)
case "iwantmyname":
// generated from: providers/dns/iwantmyname/iwantmyname.toml
ew.writeln(`Configuration for iwantmyname.`)
ew.writeln(`Code: 'iwantmyname'`)
ew.writeln(`Since: 'v4.7.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "IWANTMYNAME_PASSWORD": API password`)
ew.writeln(` - "IWANTMYNAME_USERNAME": API username`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "IWANTMYNAME_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "IWANTMYNAME_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "IWANTMYNAME_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "IWANTMYNAME_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/iwantmyname`)
case "joker":
// generated from: providers/dns/joker/joker.toml
ew.writeln(`Configuration for Joker.`)
ew.writeln(`Code: 'joker'`)
ew.writeln(`Since: 'v2.6.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "JOKER_API_KEY": API key (only with DMAPI mode)`)
ew.writeln(` - "JOKER_API_MODE": 'DMAPI' or 'SVC'. DMAPI is for resellers accounts. (Default: DMAPI)`)
ew.writeln(` - "JOKER_PASSWORD": Joker.com password`)
ew.writeln(` - "JOKER_USERNAME": Joker.com username`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "JOKER_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "JOKER_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "JOKER_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "JOKER_SEQUENCE_INTERVAL": Time between sequential requests (only with 'SVC' mode)`)
ew.writeln(` - "JOKER_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/joker`)
case "lightsail":
// generated from: providers/dns/lightsail/lightsail.toml
ew.writeln(`Configuration for Amazon Lightsail.`)
ew.writeln(`Code: 'lightsail'`)
ew.writeln(`Since: 'v0.5.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "AWS_ACCESS_KEY_ID": Managed by the AWS client. Access key ID ('AWS_ACCESS_KEY_ID_FILE' is not supported, use 'AWS_SHARED_CREDENTIALS_FILE' instead)`)
ew.writeln(` - "AWS_SECRET_ACCESS_KEY": Managed by the AWS client. Secret access key ('AWS_SECRET_ACCESS_KEY_FILE' is not supported, use 'AWS_SHARED_CREDENTIALS_FILE' instead)`)
ew.writeln(` - "DNS_ZONE": Domain name of the DNS zone`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "AWS_SHARED_CREDENTIALS_FILE": Managed by the AWS client. Shared credentials file.`)
ew.writeln(` - "LIGHTSAIL_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "LIGHTSAIL_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/lightsail`)
case "linode":
// generated from: providers/dns/linode/linode.toml
ew.writeln(`Configuration for Linode (v4).`)
ew.writeln(`Code: 'linode'`)
ew.writeln(`Since: 'v1.1.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "LINODE_TOKEN": API token`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "LINODE_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "LINODE_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "LINODE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "LINODE_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/linode`)
case "liquidweb":
// generated from: providers/dns/liquidweb/liquidweb.toml
ew.writeln(`Configuration for Liquid Web.`)
ew.writeln(`Code: 'liquidweb'`)
ew.writeln(`Since: 'v3.1.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "LIQUID_WEB_PASSWORD": Storm API Password`)
ew.writeln(` - "LIQUID_WEB_USERNAME": Storm API Username`)
ew.writeln(` - "LIQUID_WEB_ZONE": DNS Zone`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "LIQUID_WEB_HTTP_TIMEOUT": Maximum waiting time for the DNS records to be created (not verified)`)
ew.writeln(` - "LIQUID_WEB_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "LIQUID_WEB_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "LIQUID_WEB_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln(` - "LIQUID_WEB_URL": Storm API endpoint`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/liquidweb`)
case "loopia":
// generated from: providers/dns/loopia/loopia.toml
ew.writeln(`Configuration for Loopia.`)
ew.writeln(`Code: 'loopia'`)
ew.writeln(`Since: 'v4.2.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "LOOPIA_API_PASSWORD": API password`)
ew.writeln(` - "LOOPIA_API_USER": API username`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "LOOPIA_API_URL": API endpoint. Ex: https://api.loopia.se/RPCSERV or https://api.loopia.rs/RPCSERV`)
ew.writeln(` - "LOOPIA_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "LOOPIA_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "LOOPIA_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "LOOPIA_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/loopia`)
case "luadns":
// generated from: providers/dns/luadns/luadns.toml
ew.writeln(`Configuration for LuaDNS.`)
ew.writeln(`Code: 'luadns'`)
ew.writeln(`Since: 'v3.7.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "LUADNS_API_TOKEN": API token`)
ew.writeln(` - "LUADNS_API_USERNAME": Username (your email)`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "LUADNS_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "LUADNS_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "LUADNS_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "LUADNS_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/luadns`)
case "mydnsjp":
// generated from: providers/dns/mydnsjp/mydnsjp.toml
ew.writeln(`Configuration for MyDNS.jp.`)
ew.writeln(`Code: 'mydnsjp'`)
ew.writeln(`Since: 'v1.2.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "MYDNSJP_MASTER_ID": Master ID`)
ew.writeln(` - "MYDNSJP_PASSWORD": Password`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "MYDNSJP_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "MYDNSJP_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "MYDNSJP_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "MYDNSJP_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/mydnsjp`)
case "mythicbeasts":
// generated from: providers/dns/mythicbeasts/mythicbeasts.toml
ew.writeln(`Configuration for MythicBeasts.`)
ew.writeln(`Code: 'mythicbeasts'`)
ew.writeln(`Since: 'v0.3.7'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "MYTHICBEASTS_PASSWORD": Password`)
ew.writeln(` - "MYTHICBEASTS_USERNAME": User name`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "MYTHICBEASTS_API_ENDPOINT": The endpoint for the API (must implement v2)`)
ew.writeln(` - "MYTHICBEASTS_AUTH_API_ENDPOINT": The endpoint for Mythic Beasts' Authentication`)
ew.writeln(` - "MYTHICBEASTS_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "MYTHICBEASTS_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "MYTHICBEASTS_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "MYTHICBEASTS_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/mythicbeasts`)
case "namecheap":
// generated from: providers/dns/namecheap/namecheap.toml
ew.writeln(`Configuration for Namecheap.`)
ew.writeln(`Code: 'namecheap'`)
ew.writeln(`Since: 'v0.3.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "NAMECHEAP_API_KEY": API key`)
ew.writeln(` - "NAMECHEAP_API_USER": API user`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "NAMECHEAP_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "NAMECHEAP_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "NAMECHEAP_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "NAMECHEAP_SANDBOX": Activate the sandbox (boolean)`)
ew.writeln(` - "NAMECHEAP_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/namecheap`)
case "namedotcom":
// generated from: providers/dns/namedotcom/namedotcom.toml
ew.writeln(`Configuration for Name.com.`)
ew.writeln(`Code: 'namedotcom'`)
ew.writeln(`Since: 'v0.5.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "NAMECOM_API_TOKEN": API token`)
ew.writeln(` - "NAMECOM_USERNAME": Username`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "NAMECOM_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "NAMECOM_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "NAMECOM_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "NAMECOM_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/namedotcom`)
case "namesilo":
// generated from: providers/dns/namesilo/namesilo.toml
ew.writeln(`Configuration for Namesilo.`)
ew.writeln(`Code: 'namesilo'`)
ew.writeln(`Since: 'v2.7.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "NAMESILO_API_KEY": Client ID`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "NAMESILO_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "NAMESILO_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation, it is better to set larger than 15m`)
ew.writeln(` - "NAMESILO_TTL": The TTL of the TXT record used for the DNS challenge, should be in [3600, 2592000]`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/namesilo`)
case "nearlyfreespeech":
// generated from: providers/dns/nearlyfreespeech/nearlyfreespeech.toml
ew.writeln(`Configuration for NearlyFreeSpeech.NET.`)
ew.writeln(`Code: 'nearlyfreespeech'`)
ew.writeln(`Since: 'v4.8.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "NEARLYFREESPEECH_API_KEY": API Key for API requests`)
ew.writeln(` - "NEARLYFREESPEECH_LOGIN": Username for API requests`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "NEARLYFREESPEECH_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "NEARLYFREESPEECH_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "NEARLYFREESPEECH_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "NEARLYFREESPEECH_SEQUENCE_INTERVAL": Time between sequential requests`)
ew.writeln(` - "NEARLYFREESPEECH_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/nearlyfreespeech`)
case "netcup":
// generated from: providers/dns/netcup/netcup.toml
ew.writeln(`Configuration for Netcup.`)
ew.writeln(`Code: 'netcup'`)
ew.writeln(`Since: 'v1.1.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "NETCUP_API_KEY": API key`)
ew.writeln(` - "NETCUP_API_PASSWORD": API password`)
ew.writeln(` - "NETCUP_CUSTOMER_NUMBER": Customer number`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "NETCUP_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "NETCUP_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "NETCUP_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "NETCUP_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/netcup`)
case "netlify":
// generated from: providers/dns/netlify/netlify.toml
ew.writeln(`Configuration for Netlify.`)
ew.writeln(`Code: 'netlify'`)
ew.writeln(`Since: 'v3.7.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "NETLIFY_TOKEN": Token`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "NETLIFY_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "NETLIFY_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "NETLIFY_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "NETLIFY_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/netlify`)
case "nicmanager":
// generated from: providers/dns/nicmanager/nicmanager.toml
ew.writeln(`Configuration for Nicmanager.`)
ew.writeln(`Code: 'nicmanager'`)
ew.writeln(`Since: 'v4.5.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "NICMANAGER_API_EMAIL": Email-based login`)
ew.writeln(` - "NICMANAGER_API_LOGIN": Login, used for Username-based login`)
ew.writeln(` - "NICMANAGER_API_PASSWORD": Password, always required`)
ew.writeln(` - "NICMANAGER_API_USERNAME": Username, used for Username-based login`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "NICMANAGER_API_MODE": mode: 'anycast' or 'zone' (default: 'anycast')`)
ew.writeln(` - "NICMANAGER_API_OTP": TOTP Secret (optional)`)
ew.writeln(` - "NICMANAGER_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "NICMANAGER_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "NICMANAGER_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "NICMANAGER_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/nicmanager`)
case "nifcloud":
// generated from: providers/dns/nifcloud/nifcloud.toml
ew.writeln(`Configuration for NIFCloud.`)
ew.writeln(`Code: 'nifcloud'`)
ew.writeln(`Since: 'v1.1.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "NIFCLOUD_ACCESS_KEY_ID": Access key`)
ew.writeln(` - "NIFCLOUD_SECRET_ACCESS_KEY": Secret access key`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "NIFCLOUD_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "NIFCLOUD_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "NIFCLOUD_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "NIFCLOUD_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/nifcloud`)
case "njalla":
// generated from: providers/dns/njalla/njalla.toml
ew.writeln(`Configuration for Njalla.`)
ew.writeln(`Code: 'njalla'`)
ew.writeln(`Since: 'v4.3.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "NJALLA_TOKEN": API token`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "NJALLA_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "NJALLA_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "NJALLA_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "NJALLA_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/njalla`)
case "ns1":
// generated from: providers/dns/ns1/ns1.toml
ew.writeln(`Configuration for NS1.`)
ew.writeln(`Code: 'ns1'`)
ew.writeln(`Since: 'v0.4.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "NS1_API_KEY": API key`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "NS1_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "NS1_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "NS1_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "NS1_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/ns1`)
case "oraclecloud":
// generated from: providers/dns/oraclecloud/oraclecloud.toml
ew.writeln(`Configuration for Oracle Cloud.`)
ew.writeln(`Code: 'oraclecloud'`)
ew.writeln(`Since: 'v2.3.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "OCI_COMPARTMENT_OCID": Compartment OCID`)
ew.writeln(` - "OCI_PRIVKEY_FILE": Private key file`)
ew.writeln(` - "OCI_PRIVKEY_PASS": Private key password`)
ew.writeln(` - "OCI_PUBKEY_FINGERPRINT": Public key fingerprint`)
ew.writeln(` - "OCI_REGION": Region`)
ew.writeln(` - "OCI_TENANCY_OCID": Tenancy OCID`)
ew.writeln(` - "OCI_USER_OCID": User OCID`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "OCI_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "OCI_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "OCI_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/oraclecloud`)
case "otc":
// generated from: providers/dns/otc/otc.toml
ew.writeln(`Configuration for Open Telekom Cloud.`)
ew.writeln(`Code: 'otc'`)
ew.writeln(`Since: 'v0.4.1'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "OTC_DOMAIN_NAME": Domain name`)
ew.writeln(` - "OTC_IDENTITY_ENDPOINT": Identity endpoint URL`)
ew.writeln(` - "OTC_PASSWORD": Password`)
ew.writeln(` - "OTC_PROJECT_NAME": Project name`)
ew.writeln(` - "OTC_USER_NAME": User name`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "OTC_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "OTC_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "OTC_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "OTC_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/otc`)
case "ovh":
// generated from: providers/dns/ovh/ovh.toml
ew.writeln(`Configuration for OVH.`)
ew.writeln(`Code: 'ovh'`)
ew.writeln(`Since: 'v0.4.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "OVH_APPLICATION_KEY": Application key`)
ew.writeln(` - "OVH_APPLICATION_SECRET": Application secret`)
ew.writeln(` - "OVH_CONSUMER_KEY": Consumer key`)
ew.writeln(` - "OVH_ENDPOINT": Endpoint URL (ovh-eu or ovh-ca)`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "OVH_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "OVH_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "OVH_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "OVH_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/ovh`)
case "pdns":
// generated from: providers/dns/pdns/pdns.toml
ew.writeln(`Configuration for PowerDNS.`)
ew.writeln(`Code: 'pdns'`)
ew.writeln(`Since: 'v0.4.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "PDNS_API_KEY": API key`)
ew.writeln(` - "PDNS_API_URL": API URL`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "PDNS_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "PDNS_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "PDNS_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "PDNS_SERVER_NAME": Name of the server in the URL, 'localhost' by default`)
ew.writeln(` - "PDNS_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/pdns`)
case "porkbun":
// generated from: providers/dns/porkbun/porkbun.toml
ew.writeln(`Configuration for Porkbun.`)
ew.writeln(`Code: 'porkbun'`)
ew.writeln(`Since: 'v4.4.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "PORKBUN_API_KEY": API key`)
ew.writeln(` - "PORKBUN_SECRET_API_KEY": secret API key`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "PORKBUN_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "PORKBUN_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "PORKBUN_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "PORKBUN_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/porkbun`)
case "rackspace":
// generated from: providers/dns/rackspace/rackspace.toml
ew.writeln(`Configuration for Rackspace.`)
ew.writeln(`Code: 'rackspace'`)
ew.writeln(`Since: 'v0.4.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "RACKSPACE_API_KEY": API key`)
ew.writeln(` - "RACKSPACE_USER": API user`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "RACKSPACE_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "RACKSPACE_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "RACKSPACE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "RACKSPACE_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/rackspace`)
case "regru":
// generated from: providers/dns/regru/regru.toml
ew.writeln(`Configuration for reg.ru.`)
ew.writeln(`Code: 'regru'`)
ew.writeln(`Since: 'v3.5.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "REGRU_PASSWORD": API password`)
ew.writeln(` - "REGRU_USERNAME": API username`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "REGRU_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "REGRU_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "REGRU_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "REGRU_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/regru`)
case "rfc2136":
// generated from: providers/dns/rfc2136/rfc2136.toml
ew.writeln(`Configuration for RFC2136.`)
ew.writeln(`Code: 'rfc2136'`)
ew.writeln(`Since: 'v0.3.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "RFC2136_NAMESERVER": Network address in the form "host" or "host:port"`)
ew.writeln(` - "RFC2136_TSIG_ALGORITHM": TSIG algorithm. 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.`)
ew.writeln(` - "RFC2136_TSIG_KEY": Name of the secret key as defined in DNS server configuration. To disable TSIG authentication, leave the 'RFC2136_TSIG*' variables unset.`)
ew.writeln(` - "RFC2136_TSIG_SECRET": Secret key payload. To disable TSIG authentication, leave the' RFC2136_TSIG*' variables unset.`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "RFC2136_DNS_TIMEOUT": API request timeout`)
ew.writeln(` - "RFC2136_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "RFC2136_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "RFC2136_SEQUENCE_INTERVAL": Time between sequential requests`)
ew.writeln(` - "RFC2136_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/rfc2136`)
case "rimuhosting":
// generated from: providers/dns/rimuhosting/rimuhosting.toml
ew.writeln(`Configuration for RimuHosting.`)
ew.writeln(`Code: 'rimuhosting'`)
ew.writeln(`Since: 'v0.3.5'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "RIMUHOSTING_API_KEY": User API key`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "RIMUHOSTING_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "RIMUHOSTING_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "RIMUHOSTING_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "RIMUHOSTING_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/rimuhosting`)
case "route53":
// generated from: providers/dns/route53/route53.toml
ew.writeln(`Configuration for Amazon Route 53.`)
ew.writeln(`Code: 'route53'`)
ew.writeln(`Since: 'v0.3.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "AWS_ACCESS_KEY_ID": Managed by the AWS client. Access key ID ('AWS_ACCESS_KEY_ID_FILE' is not supported, use 'AWS_SHARED_CREDENTIALS_FILE' instead)`)
ew.writeln(` - "AWS_ASSUME_ROLE_ARN": Managed by the AWS Role ARN ('AWS_ASSUME_ROLE_ARN' is not supported)`)
ew.writeln(` - "AWS_HOSTED_ZONE_ID": Override the hosted zone ID.`)
ew.writeln(` - "AWS_PROFILE": Managed by the AWS client ('AWS_PROFILE_FILE' is not supported)`)
ew.writeln(` - "AWS_REGION": Managed by the AWS client ('AWS_REGION_FILE' is not supported)`)
ew.writeln(` - "AWS_SDK_LOAD_CONFIG": Managed by the AWS client. Retrieve the region from the CLI config file ('AWS_SDK_LOAD_CONFIG_FILE' is not supported)`)
ew.writeln(` - "AWS_SECRET_ACCESS_KEY": Managed by the AWS client. Secret access key ('AWS_SECRET_ACCESS_KEY_FILE' is not supported, use 'AWS_SHARED_CREDENTIALS_FILE' instead)`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "AWS_MAX_RETRIES": The number of maximum returns the service will use to make an individual API request`)
ew.writeln(` - "AWS_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "AWS_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "AWS_SHARED_CREDENTIALS_FILE": Managed by the AWS client. Shared credentials file.`)
ew.writeln(` - "AWS_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/route53`)
case "safedns":
// generated from: providers/dns/safedns/safedns.toml
ew.writeln(`Configuration for UKFast SafeDNS.`)
ew.writeln(`Code: 'safedns'`)
ew.writeln(`Since: 'v4.6.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "SAFEDNS_AUTH_TOKEN": Authentication token`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "SAFEDNS_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "SAFEDNS_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "SAFEDNS_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "SAFEDNS_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/safedns`)
case "sakuracloud":
// generated from: providers/dns/sakuracloud/sakuracloud.toml
ew.writeln(`Configuration for Sakura Cloud.`)
ew.writeln(`Code: 'sakuracloud'`)
ew.writeln(`Since: 'v1.1.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "SAKURACLOUD_ACCESS_TOKEN": Access token`)
ew.writeln(` - "SAKURACLOUD_ACCESS_TOKEN_SECRET": Access token secret`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "SAKURACLOUD_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "SAKURACLOUD_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "SAKURACLOUD_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "SAKURACLOUD_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/sakuracloud`)
case "scaleway":
// generated from: providers/dns/scaleway/scaleway.toml
ew.writeln(`Configuration for Scaleway.`)
ew.writeln(`Code: 'scaleway'`)
ew.writeln(`Since: 'v3.4.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "SCALEWAY_API_TOKEN": API token`)
ew.writeln(` - "SCALEWAY_PROJECT_ID": Project to use (optional)`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "SCALEWAY_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "SCALEWAY_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "SCALEWAY_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/scaleway`)
case "selectel":
// generated from: providers/dns/selectel/selectel.toml
ew.writeln(`Configuration for Selectel.`)
ew.writeln(`Code: 'selectel'`)
ew.writeln(`Since: 'v1.2.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "SELECTEL_API_TOKEN": API token`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "SELECTEL_BASE_URL": API endpoint URL`)
ew.writeln(` - "SELECTEL_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "SELECTEL_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "SELECTEL_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "SELECTEL_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/selectel`)
case "servercow":
// generated from: providers/dns/servercow/servercow.toml
ew.writeln(`Configuration for Servercow.`)
ew.writeln(`Code: 'servercow'`)
ew.writeln(`Since: 'v3.4.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "SERVERCOW_PASSWORD": API password`)
ew.writeln(` - "SERVERCOW_USERNAME": API username`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "SERVERCOW_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "SERVERCOW_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "SERVERCOW_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "SERVERCOW_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/servercow`)
case "simply":
// generated from: providers/dns/simply/simply.toml
ew.writeln(`Configuration for Simply.com.`)
ew.writeln(`Code: 'simply'`)
ew.writeln(`Since: 'v4.4.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "SIMPLY_ACCOUNT_NAME": Account name`)
ew.writeln(` - "SIMPLY_API_KEY": API key`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "SIMPLY_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "SIMPLY_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "SIMPLY_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "SIMPLY_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/simply`)
case "sonic":
// generated from: providers/dns/sonic/sonic.toml
ew.writeln(`Configuration for Sonic.`)
ew.writeln(`Code: 'sonic'`)
ew.writeln(`Since: 'v4.4.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "SONIC_API_KEY": API Key`)
ew.writeln(` - "SONIC_USER_ID": User ID`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "SONIC_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "SONIC_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "SONIC_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "SONIC_SEQUENCE_INTERVAL": Time between sequential requests`)
ew.writeln(` - "SONIC_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/sonic`)
case "stackpath":
// generated from: providers/dns/stackpath/stackpath.toml
ew.writeln(`Configuration for Stackpath.`)
ew.writeln(`Code: 'stackpath'`)
ew.writeln(`Since: 'v1.1.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "STACKPATH_CLIENT_ID": Client ID`)
ew.writeln(` - "STACKPATH_CLIENT_SECRET": Client secret`)
ew.writeln(` - "STACKPATH_STACK_ID": Stack ID`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "STACKPATH_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "STACKPATH_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "STACKPATH_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/stackpath`)
case "tencentcloud":
// generated from: providers/dns/tencentcloud/tencentcloud.toml
ew.writeln(`Configuration for Tencent Cloud DNS.`)
ew.writeln(`Code: 'tencentcloud'`)
ew.writeln(`Since: 'v4.6.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "TENCENTCLOUD_SECRET_ID": Access key ID`)
ew.writeln(` - "TENCENTCLOUD_SECRET_KEY": Access Key secret`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "TENCENTCLOUD_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "TENCENTCLOUD_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "TENCENTCLOUD_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "TENCENTCLOUD_REGION": Region`)
ew.writeln(` - "TENCENTCLOUD_SESSION_TOKEN": Access Key token`)
ew.writeln(` - "TENCENTCLOUD_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/tencentcloud`)
case "transip":
// generated from: providers/dns/transip/transip.toml
ew.writeln(`Configuration for TransIP.`)
ew.writeln(`Code: 'transip'`)
ew.writeln(`Since: 'v2.0.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "TRANSIP_ACCOUNT_NAME": Account name`)
ew.writeln(` - "TRANSIP_PRIVATE_KEY_PATH": Private key path`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "TRANSIP_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "TRANSIP_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "TRANSIP_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/transip`)
case "variomedia":
// generated from: providers/dns/variomedia/variomedia.toml
ew.writeln(`Configuration for Variomedia.`)
ew.writeln(`Code: 'variomedia'`)
ew.writeln(`Since: 'v4.8.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "VARIOMEDIA_API_TOKEN": API token`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "DODE_SEQUENCE_INTERVAL": Time between sequential requests`)
ew.writeln(` - "VARIOMEDIA_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "VARIOMEDIA_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "VARIOMEDIA_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "VARIOMEDIA_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/variomedia`)
case "vegadns":
// generated from: providers/dns/vegadns/vegadns.toml
ew.writeln(`Configuration for VegaDNS.`)
ew.writeln(`Code: 'vegadns'`)
ew.writeln(`Since: 'v1.1.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "SECRET_VEGADNS_KEY": API key`)
ew.writeln(` - "SECRET_VEGADNS_SECRET": API secret`)
ew.writeln(` - "VEGADNS_URL": API endpoint URL`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "VEGADNS_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "VEGADNS_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "VEGADNS_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/vegadns`)
case "vercel":
// generated from: providers/dns/vercel/vercel.toml
ew.writeln(`Configuration for Vercel.`)
ew.writeln(`Code: 'vercel'`)
ew.writeln(`Since: 'v4.7.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "VERCEL_API_TOKEN": Authentication token`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "VERCEL_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "VERCEL_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "VERCEL_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "VERCEL_TEAM_ID": Team ID (ex: team_xxxxxxxxxxxxxxxxxxxxxxxx)`)
ew.writeln(` - "VERCEL_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/vercel`)
case "versio":
// generated from: providers/dns/versio/versio.toml
ew.writeln(`Configuration for Versio.[nl|eu|uk].`)
ew.writeln(`Code: 'versio'`)
ew.writeln(`Since: 'v2.7.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "VERSIO_PASSWORD": Basic authentication password`)
ew.writeln(` - "VERSIO_USERNAME": Basic authentication username`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "VERSIO_ENDPOINT": The endpoint URL of the API Server`)
ew.writeln(` - "VERSIO_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "VERSIO_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "VERSIO_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "VERSIO_SEQUENCE_INTERVAL": Time between sequential requests, default 60s`)
ew.writeln(` - "VERSIO_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/versio`)
case "vinyldns":
// generated from: providers/dns/vinyldns/vinyldns.toml
ew.writeln(`Configuration for VinylDNS.`)
ew.writeln(`Code: 'vinyldns'`)
ew.writeln(`Since: 'v4.4.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "VINYLDNS_ACCESS_KEY": The VinylDNS API key`)
ew.writeln(` - "VINYLDNS_HOST": The VinylDNS API URL`)
ew.writeln(` - "VINYLDNS_SECRET_KEY": The VinylDNS API Secret key`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "VINYLDNS_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "VINYLDNS_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "VINYLDNS_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/vinyldns`)
case "vkcloud":
// generated from: providers/dns/vkcloud/vkcloud.toml
ew.writeln(`Configuration for VK Cloud.`)
ew.writeln(`Code: 'vkcloud'`)
ew.writeln(`Since: 'v4.9.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "VK_CLOUD_PASSWORD": Password for VK Cloud account`)
ew.writeln(` - "VK_CLOUD_PROJECT_ID": String ID of project in VK Cloud`)
ew.writeln(` - "VK_CLOUD_USERNAME": Email of VK Cloud account`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "VK_CLOUD_DNS_ENDPOINT": URL of DNS API. Defaults to https://mcs.mail.ru/public-dns but can be changed for usage with private clouds`)
ew.writeln(` - "VK_CLOUD_DOMAIN_NAME": Openstack users domain name. Defaults to 'users' but can be changed for usage with private clouds`)
ew.writeln(` - "VK_CLOUD_IDENTITY_ENDPOINT": URL of OpenStack Auth API, Defaults to https://infra.mail.ru:35357/v3/ but can be changed for usage with private clouds`)
ew.writeln(` - "VK_CLOUD_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "VK_CLOUD_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "VK_CLOUD_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/vkcloud`)
case "vscale":
// generated from: providers/dns/vscale/vscale.toml
ew.writeln(`Configuration for Vscale.`)
ew.writeln(`Code: 'vscale'`)
ew.writeln(`Since: 'v2.0.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "VSCALE_API_TOKEN": API token`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "VSCALE_BASE_URL": API endpoint URL`)
ew.writeln(` - "VSCALE_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "VSCALE_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "VSCALE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "VSCALE_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/vscale`)
case "vultr":
// generated from: providers/dns/vultr/vultr.toml
ew.writeln(`Configuration for Vultr.`)
ew.writeln(`Code: 'vultr'`)
ew.writeln(`Since: 'v0.3.1'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "VULTR_API_KEY": API key`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "VULTR_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "VULTR_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "VULTR_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "VULTR_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/vultr`)
case "wedos":
// generated from: providers/dns/wedos/wedos.toml
ew.writeln(`Configuration for WEDOS.`)
ew.writeln(`Code: 'wedos'`)
ew.writeln(`Since: 'v4.4.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "WEDOS_USERNAME": Username is the same as for the admin account`)
ew.writeln(` - "WEDOS_WAPI_PASSWORD": Password needs to be generated and IP allowed in the admin interface`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "WEDOS_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "WEDOS_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "WEDOS_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "WEDOS_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/wedos`)
case "yandex":
// generated from: providers/dns/yandex/yandex.toml
ew.writeln(`Configuration for Yandex PDD.`)
ew.writeln(`Code: 'yandex'`)
ew.writeln(`Since: 'v3.7.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "YANDEX_PDD_TOKEN": Basic authentication username`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "YANDEX_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "YANDEX_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "YANDEX_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "YANDEX_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/yandex`)
case "yandexcloud":
// generated from: providers/dns/yandexcloud/yandexcloud.toml
ew.writeln(`Configuration for Yandex Cloud.`)
ew.writeln(`Code: 'yandexcloud'`)
ew.writeln(`Since: 'v4.9.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "YANDEX_CLOUD_FOLDER_ID": The string id of folder (aka project) in Yandex Cloud`)
ew.writeln(` - "YANDEX_CLOUD_IAM_TOKEN": The base64 encoded json which contains inforamtion about iam token of serivce account with 'dns.admin' permissions`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "YANDEX_CLOUD_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "YANDEX_CLOUD_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "YANDEX_CLOUD_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/yandexcloud`)
case "zoneee":
// generated from: providers/dns/zoneee/zoneee.toml
ew.writeln(`Configuration for Zone.ee.`)
ew.writeln(`Code: 'zoneee'`)
ew.writeln(`Since: 'v2.1.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "ZONEEE_API_KEY": API key`)
ew.writeln(` - "ZONEEE_API_USER": API user`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "ZONEEE_ENDPOINT": API endpoint URL`)
ew.writeln(` - "ZONEEE_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "ZONEEE_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "ZONEEE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "ZONEEE_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/zoneee`)
case "zonomi":
// generated from: providers/dns/zonomi/zonomi.toml
ew.writeln(`Configuration for Zonomi.`)
ew.writeln(`Code: 'zonomi'`)
ew.writeln(`Since: 'v3.5.0'`)
ew.writeln()
ew.writeln(`Credentials:`)
ew.writeln(` - "ZONOMI_API_KEY": User API key`)
ew.writeln()
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "ZONOMI_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "ZONOMI_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "ZONOMI_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "ZONOMI_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/zonomi`)
case "manual":
ew.writeln(`Solving the DNS-01 challenge using CLI prompt.`)
default:
return fmt.Errorf("%q is not yet supported", name)
}
if ew.err != nil {
return fmt.Errorf("error: %w", ew.err)
}
return w.Flush()
}
lego-4.9.1/docs/ 0000775 0000000 0000000 00000000000 14340204635 0013404 5 ustar 00root root 0000000 0000000 lego-4.9.1/docs/.gitignore 0000664 0000000 0000000 00000000041 14340204635 0015367 0 ustar 00root root 0000000 0000000 themes/
public/
.hugo_build.lock
lego-4.9.1/docs/Makefile 0000664 0000000 0000000 00000000632 14340204635 0015045 0 ustar 00root root 0000000 0000000 .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
lego-4.9.1/docs/archetypes/ 0000775 0000000 0000000 00000000000 14340204635 0015553 5 ustar 00root root 0000000 0000000 lego-4.9.1/docs/archetypes/default.md 0000664 0000000 0000000 00000000124 14340204635 0017516 0 ustar 00root root 0000000 0000000 ---
title: "{{ replace .Name "-" " " | title }}"
date: {{ .Date }}
draft: true
---
lego-4.9.1/docs/config.toml 0000664 0000000 0000000 00000005224 14340204635 0015551 0 ustar 00root root 0000000 0000000 baseURL = "https://go-acme.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"
custom_css = ["css/theme-custom.css"]
disableLandingPageButton = true
[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/go-acme/lego"
weight = 10
[[Languages.en.menu.shortcuts]]
name = " Issues"
url = "https://github.com/go-acme/lego/issues"
weight = 11
[[Languages.en.menu.shortcuts]]
name = " Discussions"
url = "https://github.com/go-acme/lego/discussions"
weight = 12
[outputs]
home = [ "HTML", "RSS", "JSON"]
lego-4.9.1/docs/content/ 0000775 0000000 0000000 00000000000 14340204635 0015056 5 ustar 00root root 0000000 0000000 lego-4.9.1/docs/content/_index.md 0000664 0000000 0000000 00000001251 14340204635 0016645 0 ustar 00root root 0000000 0000000 ---
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
- ACME v2 [RFC 8555](https://www.rfc-editor.org/rfc/rfc8555.html)
- 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]({{< ref "dns" >}})
- [Custom challenge solvers]({{< ref "usage/library/Writing-a-Challenge-Solver" >}})
- Certificate bundling
- OCSP helper function
lego-4.9.1/docs/content/dns/ 0000775 0000000 0000000 00000000000 14340204635 0015642 5 ustar 00root root 0000000 0000000 lego-4.9.1/docs/content/dns/_index.md 0000664 0000000 0000000 00000002323 14340204635 0017432 0 ustar 00root root 0000000 0000000 ---
title: "DNS Providers"
date: 2019-03-03T16:39:46+01:00
draft: false
weight: 3
---
## Configuration and Credentials
Credentials and DNS configuration for DNS providers must be passed through environment variables.
### Environment Variables: Value
The environment variables can reference a value.
Here is an example bash command using the Cloudflare DNS provider:
```console
$ CLOUDFLARE_EMAIL=you@example.com \
CLOUDFLARE_API_KEY=b9841238feb177a84330febba8a83208921177bffe733 \
lego --dns cloudflare --domains www.example.com --email you@example.com run
```
### Environment Variables: File
The environment variables can reference a path to file.
In this case the name of environment variable must be suffixed by `_FILE`.
{{% notice note %}}
The file must contain only the value.
{{% /notice %}}
Here is an example bash command using the CloudFlare DNS provider:
```console
$ cat /the/path/to/my/key
b9841238feb177a84330febba8a83208921177bffe733
$ cat /the/path/to/my/email
you@example.com
$ CLOUDFLARE_EMAIL_FILE=/the/path/to/my/email \
CLOUDFLARE_API_KEY_FILE=/the/path/to/my/key \
lego --dns cloudflare --domains www.example.com --email you@example.com run
```
## DNS Providers
{{% tableofdnsproviders %}}
lego-4.9.1/docs/content/dns/manual.md 0000664 0000000 0000000 00000005077 14340204635 0017452 0 ustar 00root root 0000000 0000000 ---
title: "Manual"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: manual
dnsprovider:
since: v0.3.0
code: manual
url:
---
Solving the DNS-01 challenge using CLI prompt.
## Example
To start using the CLI prompt "provider", start lego with `--dns manual`:
```console
$ lego --email "you@example.com" --domains="example.com" --dns "manual" run
```
What follows are a few log print outs, interspersed with some prompts, asking for you to do perform some actions:
```txt
No key found for account you@example.com. Generating a P256 key.
Saved key to ./.lego/accounts/acme-v02.api.letsencrypt.org/you@example.com/keys/you@example.com.key
Please review the TOS at https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf
Do you accept the TOS? Y/n
```
If you accept the linked Terms of Service, hit `Enter`.
```txt
[INFO] acme: Registering account for you@example.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] [example.com] acme: Obtaining bundled SAN certificate
[INFO] [example.com] AuthURL: https://acme-v02.api.letsencrypt.org/acme/authz-v3/2345678901
[INFO] [example.com] acme: Could not find solver for: tls-alpn-01
[INFO] [example.com] acme: Could not find solver for: http-01
[INFO] [example.com] acme: use dns-01 solver
[INFO] [example.com] acme: Preparing to solve DNS-01
lego: Please create the following TXT record in your example.com. zone:
_acme-challenge.example.com. 120 IN TXT "hX0dPkG6Gfs9hUvBAchQclkyyoEKbShbpvJ9mY5q2JQ"
lego: Press 'Enter' when you are done
```
Do as instructed, and create the TXT records, and hit `Enter`.
```txt
[INFO] [example.com] acme: Trying to solve DNS-01
[INFO] [example.com] acme: Checking DNS record propagation using [192.168.8.1:53]
[INFO] Wait for propagation [timeout: 1m0s, interval: 2s]
[INFO] [example.com] acme: Waiting for DNS record propagation.
[INFO] [example.com] The server validated our request
[INFO] [example.com] acme: Cleaning DNS-01 challenge
lego: You can now remove this TXT record from your example.com. zone:
_acme-challenge.example.com. 120 IN TXT "hX0dPkG6Gfs9hUvBAchQclkyyoEKbShbpvJ9mY5q2JQ"
[INFO] [example.com] acme: Validations succeeded; requesting certificates
[INFO] [example.com] Server responded with a certificate.
```
As mentioned, you can now remove the TXT record again.
lego-4.9.1/docs/content/dns/zz_gen_acme-dns.md 0000664 0000000 0000000 00000003035 14340204635 0021230 0 ustar 00root root 0000000 0000000 ---
title: "Joohoi's ACME-DNS"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: acme-dns
dnsprovider:
since: "v1.1.0"
code: "acme-dns"
url: "https://github.com/joohoi/acme-dns"
---
Configuration for [Joohoi's ACME-DNS](https://github.com/joohoi/acme-dns).
- Code: `acme-dns`
- Since: v1.1.0
Here is an example bash command using the Joohoi's ACME-DNS provider:
```bash
ACME_DNS_API_BASE=http://10.0.0.8:4443 \
ACME_DNS_STORAGE_PATH=/root/.lego-acme-dns-accounts.json \
lego --email you@example.com --dns acme-dns --domains my.example.org run
```
## 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. |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://github.com/joohoi/acme-dns#api)
- [Go client](https://github.com/cpu/goacmedns)
lego-4.9.1/docs/content/dns/zz_gen_alidns.md 0000664 0000000 0000000 00000004525 14340204635 0021020 0 ustar 00root root 0000000 0000000 ---
title: "Alibaba Cloud DNS"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: alidns
dnsprovider:
since: "v1.1.0"
code: "alidns"
url: "https://www.alibabacloud.com/product/dns"
---
Configuration for [Alibaba Cloud DNS](https://www.alibabacloud.com/product/dns).
- Code: `alidns`
- Since: v1.1.0
Here is an example bash command using the Alibaba Cloud DNS provider:
```bash
# Setup using instance RAM role
ALICLOUD_RAM_ROLE=lego \
lego --email you@example.com --dns alidns --domains my.example.org run
# Or, using credentials
ALICLOUD_ACCESS_KEY=abcdefghijklmnopqrstuvwx \
ALICLOUD_SECRET_KEY=your-secret-key \
ALICLOUD_SECURITY_TOKEN=your-sts-token \
lego --email you@example.com --dns alidns --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `ALICLOUD_ACCESS_KEY` | Access key ID |
| `ALICLOUD_RAM_ROLE` | Your instance RAM role (https://www.alibabacloud.com/help/doc-detail/54579.htm) |
| `ALICLOUD_SECRET_KEY` | Access Key secret |
| `ALICLOUD_SECURITY_TOKEN` | STS Security Token (optional) |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## 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 |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://www.alibabacloud.com/help/doc-detail/42875.htm)
- [Go client](https://github.com/aliyun/alibaba-cloud-sdk-go)
lego-4.9.1/docs/content/dns/zz_gen_allinkl.md 0000664 0000000 0000000 00000003444 14340204635 0021173 0 ustar 00root root 0000000 0000000 ---
title: "all-inkl"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: allinkl
dnsprovider:
since: "v4.5.0"
code: "allinkl"
url: "https://all-inkl.com"
---
Configuration for [all-inkl](https://all-inkl.com).
- Code: `allinkl`
- Since: v4.5.0
Here is an example bash command using the all-inkl provider:
```bash
ALL_INKL_LOGIN=xxxxxxxxxxxxxxxxxxxxxxxxxx \
ALL_INKL_PASSWORD=yyyyyyyyyyyyyyyyyyyyyyyyyy \
lego --email you@example.com --dns allinkl --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `ALL_INKL_LOGIN` | KAS login |
| `ALL_INKL_PASSWORD` | KAS password |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `ALL_INKL_HTTP_TIMEOUT` | API request timeout |
| `ALL_INKL_POLLING_INTERVAL` | Time between DNS propagation check |
| `ALL_INKL_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://kasapi.kasserver.com/dokumentation/phpdoc/index.html)
lego-4.9.1/docs/content/dns/zz_gen_arvancloud.md 0000664 0000000 0000000 00000003512 14340204635 0021677 0 ustar 00root root 0000000 0000000 ---
title: "ArvanCloud"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: arvancloud
dnsprovider:
since: "v3.8.0"
code: "arvancloud"
url: "https://arvancloud.com"
---
Configuration for [ArvanCloud](https://arvancloud.com).
- Code: `arvancloud`
- Since: v3.8.0
Here is an example bash command using the ArvanCloud provider:
```bash
ARVANCLOUD_API_KEY="Apikey xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" \
lego --email you@example.com --dns arvancloud --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `ARVANCLOUD_API_KEY` | API key |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `ARVANCLOUD_HTTP_TIMEOUT` | API request timeout |
| `ARVANCLOUD_POLLING_INTERVAL` | Time between DNS propagation check |
| `ARVANCLOUD_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `ARVANCLOUD_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://www.arvancloud.com/docs/api/cdn/4.0)
lego-4.9.1/docs/content/dns/zz_gen_auroradns.md 0000664 0000000 0000000 00000003725 14340204635 0021545 0 ustar 00root root 0000000 0000000 ---
title: "Aurora DNS"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: auroradns
dnsprovider:
since: "v0.4.0"
code: "auroradns"
url: "https://www.pcextreme.com/dns-health-checks"
---
Configuration for [Aurora DNS](https://www.pcextreme.com/dns-health-checks).
- Code: `auroradns`
- Since: v0.4.0
Here is an example bash command using the Aurora DNS provider:
```bash
AURORA_API_KEY=xxxxx \
AURORA_SECRET=yyyyyy \
lego --email you@example.com --dns auroradns --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `AURORA_API_KEY` | API key or username to used |
| `AURORA_SECRET` | Secret password to be used |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `AURORA_ENDPOINT` | API endpoint URL |
| `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 |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://libcloud.readthedocs.io/en/latest/dns/drivers/auroradns.html#api-docs)
- [Go client](https://github.com/nrdcg/auroradns)
lego-4.9.1/docs/content/dns/zz_gen_autodns.md 0000664 0000000 0000000 00000004064 14340204635 0021221 0 ustar 00root root 0000000 0000000 ---
title: "Autodns"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: autodns
dnsprovider:
since: "v3.2.0"
code: "autodns"
url: "https://www.internetx.com/domains/autodns/"
---
Configuration for [Autodns](https://www.internetx.com/domains/autodns/).
- Code: `autodns`
- Since: v3.2.0
Here is an example bash command using the Autodns provider:
```bash
AUTODNS_API_USER=username \
AUTODNS_API_PASSWORD=supersecretpassword \
lego --email you@example.com --dns autodns --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `AUTODNS_API_PASSWORD` | User Password |
| `AUTODNS_API_USER` | Username |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `AUTODNS_CONTEXT` | API context (4 for production, 1 for testing. Defaults to 4) |
| `AUTODNS_ENDPOINT` | API endpoint URL, defaults to https://api.autodns.com/v1/ |
| `AUTODNS_HTTP_TIMEOUT` | API request timeout, defaults to 30 seconds |
| `AUTODNS_POLLING_INTERVAL` | Time between DNS propagation check |
| `AUTODNS_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `AUTODNS_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://help.internetx.com/display/APIJSONEN)
lego-4.9.1/docs/content/dns/zz_gen_azure.md 0000664 0000000 0000000 00000004644 14340204635 0020676 0 ustar 00root root 0000000 0000000 ---
title: "Azure"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: azure
dnsprovider:
since: "v0.4.0"
code: "azure"
url: "https://azure.microsoft.com/services/dns/"
---
Configuration for [Azure](https://azure.microsoft.com/services/dns/).
- Code: `azure`
- Since: v0.4.0
{{% 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_ENVIRONMENT` | Azure environment, one of: public, usgovernment, german, and china |
| `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). |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `AZURE_METADATA_ENDPOINT` | Metadata Service endpoint URL |
| `AZURE_POLLING_INTERVAL` | Time between DNS propagation check |
| `AZURE_PRIVATE_ZONE` | Set to true to use Azure Private DNS Zones and not public |
| `AZURE_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `AZURE_TTL` | The TTL of the TXT record used for the DNS challenge |
| `AZURE_ZONE_NAME` | Zone name to use inside Azure DNS service to add the TXT record in |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://docs.microsoft.com/en-us/go/azure/)
- [Go client](https://github.com/Azure/azure-sdk-for-go)
lego-4.9.1/docs/content/dns/zz_gen_bindman.md 0000664 0000000 0000000 00000003635 14340204635 0021157 0 ustar 00root root 0000000 0000000 ---
title: "Bindman"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: bindman
dnsprovider:
since: "v2.6.0"
code: "bindman"
url: "https://github.com/labbsr0x/bindman-dns-webhook"
---
Configuration for [Bindman](https://github.com/labbsr0x/bindman-dns-webhook).
- Code: `bindman`
- Since: v2.6.0
Here is an example bash command using the Bindman provider:
```bash
BINDMAN_MANAGER_ADDRESS= \
lego --email you@example.com --dns bindman --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `BINDMAN_MANAGER_ADDRESS` | The server URL, should have scheme, hostname, and port (if required) of the Bindman-DNS Manager server |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `BINDMAN_HTTP_TIMEOUT` | API request timeout |
| `BINDMAN_POLLING_INTERVAL` | Time between DNS propagation check |
| `BINDMAN_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://gitlab.isc.org/isc-projects/bind9)
- [Go client](https://github.com/labbsr0x/bindman-dns-webhook)
lego-4.9.1/docs/content/dns/zz_gen_bluecat.md 0000664 0000000 0000000 00000004320 14340204635 0021156 0 ustar 00root root 0000000 0000000 ---
title: "Bluecat"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: bluecat
dnsprovider:
since: "v0.5.0"
code: "bluecat"
url: "https://www.bluecatnetworks.com"
---
Configuration for [Bluecat](https://www.bluecatnetworks.com).
- Code: `bluecat`
- Since: v0.5.0
Here is an example bash command using the Bluecat provider:
```bash
BLUECAT_PASSWORD=mypassword \
BLUECAT_DNS_VIEW=myview \
BLUECAT_USER_NAME=myusername \
BLUECAT_CONFIG_NAME=myconfig \
BLUECAT_SERVER_URL=https://bam.example.com \
BLUECAT_TTL=30 \
lego --email you@example.com --dns bluecat --domains my.example.org run
```
## 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 |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## 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 |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://docs.bluecatnetworks.com/r/Address-Manager-API-Guide/REST-API/9.1.0)
lego-4.9.1/docs/content/dns/zz_gen_checkdomain.md 0000664 0000000 0000000 00000003653 14340204635 0022014 0 ustar 00root root 0000000 0000000 ---
title: "Checkdomain"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: checkdomain
dnsprovider:
since: "v3.3.0"
code: "checkdomain"
url: "https://checkdomain.de/"
---
Configuration for [Checkdomain](https://checkdomain.de/).
- Code: `checkdomain`
- Since: v3.3.0
Here is an example bash command using the Checkdomain provider:
```bash
CHECKDOMAIN_TOKEN=yoursecrettoken \
lego --email you@example.com --dns checkdomain --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `CHECKDOMAIN_TOKEN` | API token |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `CHECKDOMAIN_ENDPOINT` | API endpoint URL, defaults to https://api.checkdomain.de |
| `CHECKDOMAIN_HTTP_TIMEOUT` | API request timeout, defaults to 30 seconds |
| `CHECKDOMAIN_POLLING_INTERVAL` | Time between DNS propagation check |
| `CHECKDOMAIN_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `CHECKDOMAIN_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://developer.checkdomain.de/reference/)
lego-4.9.1/docs/content/dns/zz_gen_civo.md 0000664 0000000 0000000 00000003175 14340204635 0020506 0 ustar 00root root 0000000 0000000 ---
title: "Civo"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: civo
dnsprovider:
since: "v4.9.0"
code: "civo"
url: "https://civo.com"
---
Configuration for [Civo](https://civo.com).
- Code: `civo`
- Since: v4.9.0
Here is an example bash command using the Civo provider:
```bash
CIVO_TOKEN=xxxxxx \
lego --email you@example.com --dns civo --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `CIVO_TOKEN` | Authentication token |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `CIVO_POLLING_INTERVAL` | Time between DNS propagation check |
| `CIVO_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `CIVO_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://www.civo.com/api/dns)
lego-4.9.1/docs/content/dns/zz_gen_clouddns.md 0000664 0000000 0000000 00000003675 14340204635 0021366 0 ustar 00root root 0000000 0000000 ---
title: "CloudDNS"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: clouddns
dnsprovider:
since: "v3.6.0"
code: "clouddns"
url: "https://vshosting.eu/"
---
Configuration for [CloudDNS](https://vshosting.eu/).
- Code: `clouddns`
- Since: v3.6.0
Here is an example bash command using the CloudDNS provider:
```bash
CLOUDDNS_CLIENT_ID=bLsdFAks23429841238feb177a572aX \
CLOUDDNS_EMAIL=you@example.com \
CLOUDDNS_PASSWORD=b9841238feb177a84330f \
lego --email you@example.com --dns clouddns --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `CLOUDDNS_CLIENT_ID` | Client ID |
| `CLOUDDNS_EMAIL` | Account email |
| `CLOUDDNS_PASSWORD` | Account password |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `CLOUDDNS_HTTP_TIMEOUT` | API request timeout |
| `CLOUDDNS_POLLING_INTERVAL` | Time between DNS propagation check |
| `CLOUDDNS_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `CLOUDDNS_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://admin.vshosting.cloud/clouddns/swagger/)
lego-4.9.1/docs/content/dns/zz_gen_cloudflare.md 0000664 0000000 0000000 00000010474 14340204635 0021666 0 ustar 00root root 0000000 0000000 ---
title: "Cloudflare"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: cloudflare
dnsprovider:
since: "v0.3.0"
code: "cloudflare"
url: "https://www.cloudflare.com/dns/"
---
Configuration for [Cloudflare](https://www.cloudflare.com/dns/).
- Code: `cloudflare`
- Since: v0.3.0
Here is an example bash command using the Cloudflare provider:
```bash
CLOUDFLARE_EMAIL=you@example.com \
CLOUDFLARE_API_KEY=b9841238feb177a84330febba8a83208921177bffe733 \
lego --email you@example.com --dns cloudflare --domains my.example.org run
# or
CLOUDFLARE_DNS_API_TOKEN=1234567890abcdefghijklmnopqrstuvwxyz \
lego --email you@example.com --dns cloudflare --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `CF_API_EMAIL` | Account email |
| `CF_API_KEY` | API key |
| `CF_DNS_API_TOKEN` | API token with DNS:Edit permission (since v3.1.0) |
| `CF_ZONE_API_TOKEN` | API token with Zone:Read permission (since v3.1.0) |
| `CLOUDFLARE_API_KEY` | Alias to CF_API_KEY |
| `CLOUDFLARE_DNS_API_TOKEN` | Alias to CF_DNS_API_TOKEN |
| `CLOUDFLARE_EMAIL` | Alias to CF_API_EMAIL |
| `CLOUDFLARE_ZONE_API_TOKEN` | Alias to CF_ZONE_API_TOKEN |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## 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 |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Description
You may use `CF_API_EMAIL` and `CF_API_KEY` to authenticate, or `CF_DNS_API_TOKEN`, or `CF_DNS_API_TOKEN` and `CF_ZONE_API_TOKEN`.
### API keys
If using API keys (`CF_API_EMAIL` and `CF_API_KEY`), the Global API Key needs to be used, not the Origin CA Key.
Please be aware, that this in principle allows Lego to read and change *everything* related to this account.
### API tokens
With API tokens (`CF_DNS_API_TOKEN`, and optionally `CF_ZONE_API_TOKEN`),
very specific access can be granted to your resources at Cloudflare.
See this [Cloudflare announcement](https://blog.cloudflare.com/api-tokens-general-availability/) for details.
The main resources Lego cares for are the DNS entries for your Zones.
It also need to resolve a domain name to an internal Zone ID in order to manipulate DNS entries.
Hence, you should create an API token with the following permissions:
* Zone / Zone / Read
* Zone / DNS / Edit
You also need to scope the access to all your domains for this to work.
Then pass the API token as `CF_DNS_API_TOKEN` to Lego.
**Alternatively,** if you prefer a more strict set of privileges,
you can split the access tokens:
* Create one with *Zone / Zone / Read* permissions and scope it to all your zones.
This is needed to resolve domain names to Zone IDs and can be shared among multiple Lego installations.
Pass this API token as `CF_ZONE_API_TOKEN` to Lego.
* Create another API token with *Zone / DNS / Edit* permissions and set the scope to the domains you want to manage with a single Lego installation.
Pass this token as `CF_DNS_API_TOKEN` to Lego.
* Repeat the previous step for each host you want to run Lego on.
This "paranoid" setup is mainly interesting for users who manage many zones/domains with a single Cloudflare account.
It follows the principle of least privilege and limits the possible damage, should one of the hosts become compromised.
## More information
- [API documentation](https://api.cloudflare.com/)
- [Go client](https://github.com/cloudflare/cloudflare-go)
lego-4.9.1/docs/content/dns/zz_gen_cloudns.md 0000664 0000000 0000000 00000003575 14340204635 0021221 0 ustar 00root root 0000000 0000000 ---
title: "ClouDNS"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: cloudns
dnsprovider:
since: "v2.3.0"
code: "cloudns"
url: "https://www.cloudns.net"
---
Configuration for [ClouDNS](https://www.cloudns.net).
- Code: `cloudns`
- Since: v2.3.0
Here is an example bash command using the ClouDNS provider:
```bash
CLOUDNS_AUTH_ID=xxxx \
CLOUDNS_AUTH_PASSWORD=yyyy \
lego --email you@example.com --dns cloudns --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `CLOUDNS_AUTH_ID` | The API user ID |
| `CLOUDNS_AUTH_PASSWORD` | The password for API user ID |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## 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_SUB_AUTH_ID` | The API sub user ID |
| `CLOUDNS_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://www.cloudns.net/wiki/article/42/)
lego-4.9.1/docs/content/dns/zz_gen_cloudxns.md 0000664 0000000 0000000 00000003551 14340204635 0021403 0 ustar 00root root 0000000 0000000 ---
title: "CloudXNS"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: cloudxns
dnsprovider:
since: "v0.5.0"
code: "cloudxns"
url: "https://www.cloudxns.net/"
---
Configuration for [CloudXNS](https://www.cloudxns.net/).
- Code: `cloudxns`
- Since: v0.5.0
Here is an example bash command using the CloudXNS provider:
```bash
CLOUDXNS_API_KEY=xxxx \
CLOUDXNS_SECRET_KEY=yyyy \
lego --email you@example.com --dns cloudxns --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `CLOUDXNS_API_KEY` | The API key |
| `CLOUDXNS_SECRET_KEY` | The API secret key |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## 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 |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://www.cloudxns.net/Public/Doc/CloudXNS_api2.0_doc_zh-cn.zip)
lego-4.9.1/docs/content/dns/zz_gen_conoha.md 0000664 0000000 0000000 00000003640 14340204635 0021012 0 ustar 00root root 0000000 0000000 ---
title: "ConoHa"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: conoha
dnsprovider:
since: "v1.2.0"
code: "conoha"
url: "https://www.conoha.jp/"
---
Configuration for [ConoHa](https://www.conoha.jp/).
- Code: `conoha`
- Since: v1.2.0
Here is an example bash command using the ConoHa provider:
```bash
CONOHA_TENANT_ID=487727e3921d44e3bfe7ebb337bf085e \
CONOHA_API_USERNAME=xxxx \
CONOHA_API_PASSWORD=yyyy \
lego --email you@example.com --dns conoha --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `CONOHA_API_PASSWORD` | The API password |
| `CONOHA_API_USERNAME` | The API username |
| `CONOHA_TENANT_ID` | Tenant ID |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## 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 |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://www.conoha.jp/docs/)
lego-4.9.1/docs/content/dns/zz_gen_constellix.md 0000664 0000000 0000000 00000003645 14340204635 0021734 0 ustar 00root root 0000000 0000000 ---
title: "Constellix"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: constellix
dnsprovider:
since: "v3.4.0"
code: "constellix"
url: "https://constellix.com"
---
Configuration for [Constellix](https://constellix.com).
- Code: `constellix`
- Since: v3.4.0
Here is an example bash command using the Constellix provider:
```bash
CONSTELLIX_API_KEY=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx \
CONSTELLIX_SECRET_KEY=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx \
lego --email you@example.com --dns constellix --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `CONSTELLIX_API_KEY` | User API key |
| `CONSTELLIX_SECRET_KEY` | User secret key |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `CONSTELLIX_HTTP_TIMEOUT` | API request timeout |
| `CONSTELLIX_POLLING_INTERVAL` | Time between DNS propagation check |
| `CONSTELLIX_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `CONSTELLIX_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://api-docs.constellix.com)
lego-4.9.1/docs/content/dns/zz_gen_desec.md 0000664 0000000 0000000 00000003336 14340204635 0020630 0 ustar 00root root 0000000 0000000 ---
title: "deSEC.io"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: desec
dnsprovider:
since: "v3.7.0"
code: "desec"
url: "https://desec.io"
---
Configuration for [deSEC.io](https://desec.io).
- Code: `desec`
- Since: v3.7.0
Here is an example bash command using the deSEC.io provider:
```bash
DESEC_TOKEN=x-xxxxxxxxxxxxxxxxxxxxxxxxxx \
lego --email you@example.com --dns desec --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `DESEC_TOKEN` | Domain token |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `DESEC_HTTP_TIMEOUT` | API request timeout |
| `DESEC_POLLING_INTERVAL` | Time between DNS propagation check |
| `DESEC_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `DESEC_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://desec.readthedocs.io/en/latest/)
lego-4.9.1/docs/content/dns/zz_gen_designate.md 0000664 0000000 0000000 00000007644 14340204635 0021516 0 ustar 00root root 0000000 0000000 ---
title: "Designate DNSaaS for Openstack"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: designate
dnsprovider:
since: "v2.2.0"
code: "designate"
url: "https://docs.openstack.org/designate/latest/"
---
Configuration for [Designate DNSaaS for Openstack](https://docs.openstack.org/designate/latest/).
- Code: `designate`
- Since: v2.2.0
Here is an example bash command using the Designate DNSaaS for Openstack provider:
```bash
# With a `clouds.yaml`
OS_CLOUD=my_openstack \
lego --email you@example.com --dns designate --domains my.example.org run
# or
OS_AUTH_URL=https://openstack.example.org \
OS_REGION_NAME=RegionOne \
OS_PROJECT_ID=23d4522a987d4ab529f722a007c27846
OS_USERNAME=myuser \
OS_PASSWORD=passw0rd \
lego --email you@example.com --dns designate --domains my.example.org run
# or
OS_AUTH_URL=https://openstack.example.org \
OS_REGION_NAME=RegionOne \
OS_AUTH_TYPE=v3applicationcredential \
OS_APPLICATION_CREDENTIAL_ID=imn74uq0or7dyzz20dwo1ytls4me8dry \
OS_APPLICATION_CREDENTIAL_SECRET=68FuSPSdQqkFQYH5X1OoriEIJOwyLtQ8QSqXZOc9XxFK1A9tzZT6He2PfPw0OMja \
lego --email you@example.com --dns designate --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `OS_APPLICATION_CREDENTIAL_ID` | Application credential ID |
| `OS_APPLICATION_CREDENTIAL_NAME` | Application credential name |
| `OS_APPLICATION_CREDENTIAL_SECRET` | Application credential secret |
| `OS_AUTH_URL` | Identity endpoint URL |
| `OS_PASSWORD` | Password |
| `OS_PROJECT_NAME` | Project name |
| `OS_REGION_NAME` | Region name |
| `OS_USERNAME` | Username |
| `OS_USER_ID` | User ID |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## 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 |
| `OS_PROJECT_ID` | Project ID |
| `OS_TENANT_NAME` | Tenant name (deprecated see OS_PROJECT_NAME and OS_PROJECT_ID) |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Description
There are three main ways of authenticating with Designate:
1. The first one is by using the `OS_CLOUD` environment variable and a `clouds.yaml` file.
2. The second one is using your username and password, via the `OS_USERNAME`, `OS_PASSWORD` and `OS_PROJECT_NAME` environment variables.
3. The third one is by using an application credential, via the `OS_APPLICATION_CREDENTIAL_*` and `OS_USER_ID` environment variables.
For the username/password and application methods, the `OS_AUTH_URL` and `OS_REGION_NAME` environment variables are required.
For more information, you can read about the different methods of authentication with OpenStack in the Keystone's documentation and the gophercloud documentation:
- [Keystone username/password](https://docs.openstack.org/keystone/latest/user/supported_clients.html)
- [Keystone application credentials](https://docs.openstack.org/keystone/latest/user/application_credentials.html)
## More information
- [API documentation](https://docs.openstack.org/designate/latest/)
- [Go client](https://godoc.org/github.com/gophercloud/gophercloud/openstack/dns/v2)
lego-4.9.1/docs/content/dns/zz_gen_digitalocean.md 0000664 0000000 0000000 00000003624 14340204635 0022170 0 ustar 00root root 0000000 0000000 ---
title: "Digital Ocean"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: digitalocean
dnsprovider:
since: "v0.3.0"
code: "digitalocean"
url: "https://www.digitalocean.com/docs/networking/dns/"
---
Configuration for [Digital Ocean](https://www.digitalocean.com/docs/networking/dns/).
- Code: `digitalocean`
- Since: v0.3.0
Here is an example bash command using the Digital Ocean provider:
```bash
DO_AUTH_TOKEN=xxxxxx \
lego --email you@example.com --dns digitalocean --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `DO_AUTH_TOKEN` | Authentication token |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `DO_API_URL` | The URL of the API |
| `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 |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://developers.digitalocean.com/documentation/v2/#domain-records)
lego-4.9.1/docs/content/dns/zz_gen_dnsimple.md 0000664 0000000 0000000 00000005101 14340204635 0021350 0 ustar 00root root 0000000 0000000 ---
title: "DNSimple"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: dnsimple
dnsprovider:
since: "v0.3.0"
code: "dnsimple"
url: "https://dnsimple.com/"
---
Configuration for [DNSimple](https://dnsimple.com/).
- Code: `dnsimple`
- Since: v0.3.0
Here is an example bash command using the DNSimple provider:
```bash
DNSIMPLE_OAUTH_TOKEN=1234567890abcdefghijklmnopqrstuvwxyz \
lego --email you@example.com --dns dnsimple --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `DNSIMPLE_OAUTH_TOKEN` | OAuth token |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `DNSIMPLE_BASE_URL` | API endpoint URL |
| `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 |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Description
`DNSIMPLE_BASE_URL` is optional and must be set to production (https://api.dnsimple.com).
if `DNSIMPLE_BASE_URL` is not defined or empty, the production URL is used by default.
While you can manage DNS records in the [DNSimple Sandbox environment](https://developer.dnsimple.com/sandbox/),
DNS records will not resolve and you will not be able to satisfy the ACME DNS challenge.
To authenticate you need to provide a valid API token.
HTTP Basic Authentication is intentionally not supported.
### API tokens
You can [generate a new API token](https://support.dnsimple.com/articles/api-access-token/) from your account page.
Only Account API tokens are supported, if you try to use an User API token you will receive an error message.
## More information
- [API documentation](https://developer.dnsimple.com/v2/)
- [Go client](https://github.com/dnsimple/dnsimple-go)
lego-4.9.1/docs/content/dns/zz_gen_dnsmadeeasy.md 0000664 0000000 0000000 00000003704 14340204635 0022041 0 ustar 00root root 0000000 0000000 ---
title: "DNS Made Easy"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: dnsmadeeasy
dnsprovider:
since: "v0.4.0"
code: "dnsmadeeasy"
url: "https://dnsmadeeasy.com/"
---
Configuration for [DNS Made Easy](https://dnsmadeeasy.com/).
- Code: `dnsmadeeasy`
- Since: v0.4.0
Here is an example bash command using the DNS Made Easy provider:
```bash
DNSMADEEASY_API_KEY=xxxxxx \
DNSMADEEASY_API_SECRET=yyyyy \
lego --email you@example.com --dns dnsmadeeasy --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `DNSMADEEASY_API_KEY` | The API key |
| `DNSMADEEASY_API_SECRET` | The API Secret key |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## 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 |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://api-docs.dnsmadeeasy.com/)
lego-4.9.1/docs/content/dns/zz_gen_dnspod.md 0000664 0000000 0000000 00000003430 14340204635 0021027 0 ustar 00root root 0000000 0000000 ---
title: "DNSPod (deprecated)"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: dnspod
dnsprovider:
since: "v0.4.0"
code: "dnspod"
url: "https://www.dnspod.com/"
---
Use the Tencent Cloud provider instead.
- Code: `dnspod`
- Since: v0.4.0
Here is an example bash command using the DNSPod (deprecated) provider:
```bash
DNSPOD_API_KEY=xxxxxx \
lego --email you@example.com --dns dnspod --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `DNSPOD_API_KEY` | The user token |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## 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 |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://docs.dnspod.com/api/)
- [Go client](https://github.com/nrdcg/dnspod-go)
lego-4.9.1/docs/content/dns/zz_gen_dode.md 0000664 0000000 0000000 00000003463 14340204635 0020461 0 ustar 00root root 0000000 0000000 ---
title: "Domain Offensive (do.de)"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: dode
dnsprovider:
since: "v2.4.0"
code: "dode"
url: "https://www.do.de/"
---
Configuration for [Domain Offensive (do.de)](https://www.do.de/).
- Code: `dode`
- Since: v2.4.0
Here is an example bash command using the Domain Offensive (do.de) provider:
```bash
DODE_TOKEN=xxxxxx \
lego --email you@example.com --dns dode --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `DODE_TOKEN` | API token |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `DODE_HTTP_TIMEOUT` | API request timeout |
| `DODE_POLLING_INTERVAL` | Time between DNS propagation check |
| `DODE_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `DODE_SEQUENCE_INTERVAL` | Time between sequential requests |
| `DODE_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://www.do.de/wiki/LetsEncrypt_-_Entwickler)
lego-4.9.1/docs/content/dns/zz_gen_domeneshop.md 0000664 0000000 0000000 00000003700 14340204635 0021701 0 ustar 00root root 0000000 0000000 ---
title: "Domeneshop"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: domeneshop
dnsprovider:
since: "v4.3.0"
code: "domeneshop"
url: "https://domene.shop"
---
Configuration for [Domeneshop](https://domene.shop).
- Code: `domeneshop`
- Since: v4.3.0
Here is an example bash command using the Domeneshop provider:
```bash
DOMENESHOP_API_TOKEN= \
DOMENESHOP_API_SECRET= \
lego --email example@example.com --dns domeneshop --domains example.com run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `DOMENESHOP_API_SECRET` | API secret |
| `DOMENESHOP_API_TOKEN` | API token |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `DOMENESHOP_HTTP_TIMEOUT` | API request timeout |
| `DOMENESHOP_POLLING_INTERVAL` | Time between DNS propagation check |
| `DOMENESHOP_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
### API credentials
Visit the following page for information on how to create API credentials with Domeneshop:
https://api.domeneshop.no/docs/#section/Authentication
## More information
- [API documentation](https://api.domeneshop.no/docs)
lego-4.9.1/docs/content/dns/zz_gen_dreamhost.md 0000664 0000000 0000000 00000003472 14340204635 0021534 0 ustar 00root root 0000000 0000000 ---
title: "DreamHost"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: dreamhost
dnsprovider:
since: "v1.1.0"
code: "dreamhost"
url: "https://www.dreamhost.com"
---
Configuration for [DreamHost](https://www.dreamhost.com).
- Code: `dreamhost`
- Since: v1.1.0
Here is an example bash command using the DreamHost provider:
```bash
DREAMHOST_API_KEY="YOURAPIKEY" \
lego --email you@example.com --dns dreamhost --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `DREAMHOST_API_KEY` | The API key |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## 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 |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://help.dreamhost.com/hc/en-us/articles/217560167-API_overview)
lego-4.9.1/docs/content/dns/zz_gen_duckdns.md 0000664 0000000 0000000 00000003461 14340204635 0021177 0 ustar 00root root 0000000 0000000 ---
title: "Duck DNS"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: duckdns
dnsprovider:
since: "v0.5.0"
code: "duckdns"
url: "https://www.duckdns.org/"
---
Configuration for [Duck DNS](https://www.duckdns.org/).
- Code: `duckdns`
- Since: v0.5.0
Here is an example bash command using the Duck DNS provider:
```bash
DUCKDNS_TOKEN=xxxxxx \
lego --email you@example.com --dns duckdns --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `DUCKDNS_TOKEN` | Account token |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## 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` | Time between sequential requests |
| `DUCKDNS_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://www.duckdns.org/spec.jsp)
lego-4.9.1/docs/content/dns/zz_gen_dyn.md 0000664 0000000 0000000 00000003411 14340204635 0020331 0 ustar 00root root 0000000 0000000 ---
title: "Dyn"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: dyn
dnsprovider:
since: "v0.3.0"
code: "dyn"
url: "https://dyn.com/"
---
Configuration for [Dyn](https://dyn.com/).
- Code: `dyn`
- Since: v0.3.0
Here is an example bash command using the Dyn provider:
```bash
DYN_CUSTOMER_NAME=xxxxxx \
DYN_USER_NAME=yyyyy \
DYN_PASSWORD=zzzz \
lego --email you@example.com --dns dyn --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `DYN_CUSTOMER_NAME` | Customer name |
| `DYN_PASSWORD` | Password |
| `DYN_USER_NAME` | User name |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## 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 |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://help.dyn.com/rest/)
lego-4.9.1/docs/content/dns/zz_gen_dynu.md 0000664 0000000 0000000 00000003324 14340204635 0020521 0 ustar 00root root 0000000 0000000 ---
title: "Dynu"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: dynu
dnsprovider:
since: "v3.5.0"
code: "dynu"
url: "https://www.dynu.com/"
---
Configuration for [Dynu](https://www.dynu.com/).
- Code: `dynu`
- Since: v3.5.0
Here is an example bash command using the Dynu provider:
```bash
DYNU_API_KEY=1234567890abcdefghijklmnopqrstuvwxyz \
lego --email you@example.com --dns dynu --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `DYNU_API_KEY` | API key |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `DYNU_HTTP_TIMEOUT` | API request timeout |
| `DYNU_POLLING_INTERVAL` | Time between DNS propagation check |
| `DYNU_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `DYNU_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://www.dynu.com/en-US/Support/API)
lego-4.9.1/docs/content/dns/zz_gen_easydns.md 0000664 0000000 0000000 00000003777 14340204635 0021224 0 ustar 00root root 0000000 0000000 ---
title: "EasyDNS"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: easydns
dnsprovider:
since: "v2.6.0"
code: "easydns"
url: "https://easydns.com/"
---
Configuration for [EasyDNS](https://easydns.com/).
- Code: `easydns`
- Since: v2.6.0
Here is an example bash command using the EasyDNS provider:
```bash
EASYDNS_TOKEN= \
EASYDNS_KEY= \
lego --email you@example.com --dns easydns --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `EASYDNS_KEY` | API Key |
| `EASYDNS_TOKEN` | API Token |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `EASYDNS_ENDPOINT` | The endpoint URL of the API Server |
| `EASYDNS_HTTP_TIMEOUT` | API request timeout |
| `EASYDNS_POLLING_INTERVAL` | Time between DNS propagation check |
| `EASYDNS_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `EASYDNS_SEQUENCE_INTERVAL` | Time between sequential requests |
| `EASYDNS_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
To test with the sandbox environment set ```EASYDNS_ENDPOINT=https://sandbox.rest.easydns.net```
## More information
- [API documentation](https://docs.sandbox.rest.easydns.net)
lego-4.9.1/docs/content/dns/zz_gen_edgedns.md 0000664 0000000 0000000 00000007457 14340204635 0021166 0 ustar 00root root 0000000 0000000 ---
title: "Akamai EdgeDNS"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: edgedns
dnsprovider:
since: "v3.9.0"
code: "edgedns"
url: "https://www.akamai.com/us/en/products/security/edge-dns.jsp"
---
Akamai edgedns supersedes FastDNS; implementing a DNS provider for solving the DNS-01 challenge using Akamai EdgeDNS
- Code: `edgedns`
- Since: v3.9.0
Here is an example bash command using the Akamai EdgeDNS provider:
```bash
AKAMAI_CLIENT_SECRET=abcdefghijklmnopqrstuvwxyz1234567890ABCDEFG= \
AKAMAI_CLIENT_TOKEN=akab-mnbvcxzlkjhgfdsapoiuytrewq1234567 \
AKAMAI_HOST=akab-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.luna.akamaiapis.net \
AKAMAI_ACCESS_TOKEN=akab-1234567890qwerty-asdfghjklzxcvtnu \
lego --email you@example.com --dns edgedns --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `AKAMAI_ACCESS_TOKEN` | Access token, managed by the Akamai EdgeGrid client |
| `AKAMAI_CLIENT_SECRET` | Client secret, managed by the Akamai EdgeGrid client |
| `AKAMAI_CLIENT_TOKEN` | Client token, managed by the Akamai EdgeGrid client |
| `AKAMAI_EDGERC` | Path to the .edgerc file, managed by the Akamai EdgeGrid client |
| `AKAMAI_EDGERC_SECTION` | Configuration section, managed by the Akamai EdgeGrid client |
| `AKAMAI_HOST` | API host, managed by the Akamai EdgeGrid client |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `AKAMAI_POLLING_INTERVAL` | Time between DNS propagation check. Default: 15 seconds |
| `AKAMAI_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation. Default: 3 minutes |
| `AKAMAI_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
Akamai credentials are automatically detected in the following locations and prioritized in the following order:
1. Section-specific environment variables (where `{SECTION}` is specified using `AKAMAI_EDGERC_SECTION`):
- `AKAMAI_{SECTION}_HOST`
- `AKAMAI_{SECTION}_ACCESS_TOKEN`
- `AKAMAI_{SECTION}_CLIENT_TOKEN`
- `AKAMAI_{SECTION}_CLIENT_SECRET`
2. If `AKAMAI_EDGERC_SECTION` is not defined or is set to `default`, environment variables:
- `AKAMAI_HOST`
- `AKAMAI_ACCESS_TOKEN`
- `AKAMAI_CLIENT_TOKEN`
- `AKAMAI_CLIENT_SECRET`
3. `.edgerc` file located at `AKAMAI_EDGERC`
- defaults to `~/.edgerc`, sections can be specified using `AKAMAI_EDGERC_SECTION`
4. Default environment variables:
- `AKAMAI_HOST`
- `AKAMAI_ACCESS_TOKEN`
- `AKAMAI_CLIENT_TOKEN`
- `AKAMAI_CLIENT_SECRET`
See also:
- [Setting up Akamai credentials](https://developer.akamai.com/api/getting-started)
- [.edgerc Format](https://developer.akamai.com/legacy/introduction/Conf_Client.html#edgercformat)
- [API Client Authentication](https://developer.akamai.com/legacy/introduction/Client_Auth.html)
- [Config from Env](https://github.com/akamai/AkamaiOPEN-edgegrid-golang/blob/master/edgegrid/config.go#L118)
## More information
- [API documentation](https://developer.akamai.com/api/cloud_security/edge_dns_zone_management/v2.html)
- [Go client](https://github.com/akamai/AkamaiOPEN-edgegrid-golang)
lego-4.9.1/docs/content/dns/zz_gen_epik.md 0000664 0000000 0000000 00000003411 14340204635 0020467 0 ustar 00root root 0000000 0000000 ---
title: "Epik"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: epik
dnsprovider:
since: "v4.5.0"
code: "epik"
url: "https://www.epik.com/"
---
Configuration for [Epik](https://www.epik.com/).
- Code: `epik`
- Since: v4.5.0
Here is an example bash command using the Epik provider:
```bash
EPIK_SIGNATURE=xxxxxxxxxxxxxxxxxxxxxxxxxx \
lego --email you@example.com --dns epik --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `EPIK_SIGNATURE` | Epik API signature (https://registrar.epik.com/account/api-settings/) |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `EPIK_HTTP_TIMEOUT` | API request timeout |
| `EPIK_POLLING_INTERVAL` | Time between DNS propagation check |
| `EPIK_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `EPIK_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://docs.userapi.epik.com/v2/#/)
lego-4.9.1/docs/content/dns/zz_gen_exec.md 0000664 0000000 0000000 00000010511 14340204635 0020462 0 ustar 00root root 0000000 0000000 ---
title: "External program"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: exec
dnsprovider:
since: "v0.5.0"
code: "exec"
url: "/dns/exec"
---
Solving the DNS-01 challenge using an external program.
- Code: `exec`
- Since: v0.5.0
Here is an example bash command using the External program provider:
```bash
EXEC_PATH=/the/path/to/myscript.sh \
lego --email you@example.com --dns exec --domains my.example.org run
```
## Base Configuration
| Environment Variable Name | Description |
|---------------------------|---------------------------------------|
| `EXEC_MODE` | `RAW`, none |
| `EXEC_PATH` | The path of the the external program. |
## Additional Configuration
| Environment Variable Name | Description |
|----------------------------|-------------------------------------------|
| `EXEC_POLLING_INTERVAL` | Time between DNS propagation check. |
| `EXEC_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation. |
| `EXEC_SEQUENCE_INTERVAL` | Time between sequential requests. |
## 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 'my.example.org' can be achieved by calling lego as follows:
```bash
EXEC_PATH=./update-dns.sh \
lego --email you@example.com \
--dns exec \
--domains my.example.org run
```
It will then call the program './update-dns.sh' with like this:
```bash
./update-dns.sh "present" "_acme-challenge.my.example.org." "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 --email you@example.com \
--dns exec \
--domains my.example.org run
```
It will then call the program `./update-dns.sh` like this:
```bash
./update-dns.sh "present" "my.example.org." "--" "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` |
lego-4.9.1/docs/content/dns/zz_gen_exoscale.md 0000664 0000000 0000000 00000003742 14340204635 0021351 0 ustar 00root root 0000000 0000000 ---
title: "Exoscale"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: exoscale
dnsprovider:
since: "v0.4.0"
code: "exoscale"
url: "https://www.exoscale.com/"
---
Configuration for [Exoscale](https://www.exoscale.com/).
- Code: `exoscale`
- Since: v0.4.0
Here is an example bash command using the Exoscale provider:
```bash
EXOSCALE_API_KEY=abcdefghijklmnopqrstuvwx \
EXOSCALE_API_SECRET=xxxxxxx \
lego --email you@example.com --dns exoscale --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `EXOSCALE_API_KEY` | API key |
| `EXOSCALE_API_SECRET` | API secret |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `EXOSCALE_API_ZONE` | API zone |
| `EXOSCALE_ENDPOINT` | API endpoint URL |
| `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 |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://openapi-v2.exoscale.com/#endpoint-dns)
- [Go client](https://github.com/exoscale/egoscale)
lego-4.9.1/docs/content/dns/zz_gen_freemyip.md 0000664 0000000 0000000 00000003477 14340204635 0021373 0 ustar 00root root 0000000 0000000 ---
title: "freemyip.com"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: freemyip
dnsprovider:
since: "v4.5.0"
code: "freemyip"
url: "https://freemyip.com/"
---
Configuration for [freemyip.com](https://freemyip.com/).
- Code: `freemyip`
- Since: v4.5.0
Here is an example bash command using the freemyip.com provider:
```bash
FREEMYIP_TOKEN=xxxxxx \
lego --email you@example.com --dns freemyip --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `FREEMYIP_TOKEN` | Account token |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `FREEMYIP_HTTP_TIMEOUT` | API request timeout |
| `FREEMYIP_POLLING_INTERVAL` | Time between DNS propagation check |
| `FREEMYIP_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `FREEMYIP_SEQUENCE_INTERVAL` | Time between sequential requests |
| `FREEMYIP_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://freemyip.com/help)
lego-4.9.1/docs/content/dns/zz_gen_gandi.md 0000664 0000000 0000000 00000003327 14340204635 0020627 0 ustar 00root root 0000000 0000000 ---
title: "Gandi"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: gandi
dnsprovider:
since: "v0.3.0"
code: "gandi"
url: "https://www.gandi.net"
---
Configuration for [Gandi](https://www.gandi.net).
- Code: `gandi`
- Since: v0.3.0
Here is an example bash command using the Gandi provider:
```bash
GANDI_API_KEY=abcdefghijklmnopqrstuvwx \
lego --email you@example.com --dns gandi --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `GANDI_API_KEY` | API key |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## 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 |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://doc.rpc.gandi.net/index.html)
lego-4.9.1/docs/content/dns/zz_gen_gandiv5.md 0000664 0000000 0000000 00000003434 14340204635 0021101 0 ustar 00root root 0000000 0000000 ---
title: "Gandi Live DNS (v5)"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: gandiv5
dnsprovider:
since: "v0.5.0"
code: "gandiv5"
url: "https://www.gandi.net"
---
Configuration for [Gandi Live DNS (v5)](https://www.gandi.net).
- Code: `gandiv5`
- Since: v0.5.0
Here is an example bash command using the Gandi Live DNS (v5) provider:
```bash
GANDIV5_API_KEY=abcdefghijklmnopqrstuvwx \
lego --email you@example.com --dns gandiv5 --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `GANDIV5_API_KEY` | API key |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## 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 |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://api.gandi.net/docs/livedns/)
lego-4.9.1/docs/content/dns/zz_gen_gcloud.md 0000664 0000000 0000000 00000004151 14340204635 0021016 0 ustar 00root root 0000000 0000000 ---
title: "Google Cloud"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: gcloud
dnsprovider:
since: "v0.3.0"
code: "gcloud"
url: "https://cloud.google.com"
---
Configuration for [Google Cloud](https://cloud.google.com).
- Code: `gcloud`
- Since: v0.3.0
{{% 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 (by default, the project name is auto-detected by using the metadata service) |
| `GCE_SERVICE_ACCOUNT` | Account |
| `GCE_SERVICE_ACCOUNT_FILE` | Account file path |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `GCE_ALLOW_PRIVATE_ZONE` | Allows requested domain to be in private DNS zone, works only with a private ACME server (by default: false) |
| `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 |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://cloud.google.com/dns/api/v1/)
- [Go client](https://github.com/googleapis/google-api-go-client)
lego-4.9.1/docs/content/dns/zz_gen_gcore.md 0000664 0000000 0000000 00000003512 14340204635 0020640 0 ustar 00root root 0000000 0000000 ---
title: "G-Core Labs"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: gcore
dnsprovider:
since: "v4.5.0"
code: "gcore"
url: "https://gcorelabs.com/dns/"
---
Configuration for [G-Core Labs](https://gcorelabs.com/dns/).
- Code: `gcore`
- Since: v4.5.0
Here is an example bash command using the G-Core Labs provider:
```bash
GCORE_PERMANENT_API_TOKEN=xxxxx \
lego --email you@example.com --dns gcore --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `GCORE_PERMANENT_API_TOKEN` | Permanent API tokene (https://gcorelabs.com/blog/permanent-api-token-explained/) |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `GCORE_HTTP_TIMEOUT` | API request timeout |
| `GCORE_POLLING_INTERVAL` | Time between DNS propagation check |
| `GCORE_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `GCORE_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://dnsapi.gcorelabs.com/docs#tag/zonesV2)
lego-4.9.1/docs/content/dns/zz_gen_glesys.md 0000664 0000000 0000000 00000003432 14340204635 0021050 0 ustar 00root root 0000000 0000000 ---
title: "Glesys"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: glesys
dnsprovider:
since: "v0.5.0"
code: "glesys"
url: "https://glesys.com/"
---
Configuration for [Glesys](https://glesys.com/).
- Code: `glesys`
- Since: v0.5.0
Here is an example bash command using the Glesys provider:
```bash
GLESYS_API_USER=xxxxx \
GLESYS_API_KEY=yyyyy \
lego --email you@example.com --dns glesys --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `GLESYS_API_KEY` | API key |
| `GLESYS_API_USER` | API user |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## 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 |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://github.com/GleSYS/API/wiki/API-Documentation)
lego-4.9.1/docs/content/dns/zz_gen_godaddy.md 0000664 0000000 0000000 00000003472 14340204635 0021161 0 ustar 00root root 0000000 0000000 ---
title: "Go Daddy"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: godaddy
dnsprovider:
since: "v0.5.0"
code: "godaddy"
url: "https://godaddy.com"
---
Configuration for [Go Daddy](https://godaddy.com).
- Code: `godaddy`
- Since: v0.5.0
Here is an example bash command using the Go Daddy provider:
```bash
GODADDY_API_KEY=xxxxxxxx \
GODADDY_API_SECRET=yyyyyyyy \
lego --email you@example.com --dns godaddy --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `GODADDY_API_KEY` | API key |
| `GODADDY_API_SECRET` | API secret |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## 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_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://developer.godaddy.com/doc/endpoint/domains)
lego-4.9.1/docs/content/dns/zz_gen_hetzner.md 0000664 0000000 0000000 00000003375 14340204635 0021227 0 ustar 00root root 0000000 0000000 ---
title: "Hetzner"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: hetzner
dnsprovider:
since: "v3.7.0"
code: "hetzner"
url: "https://hetzner.com"
---
Configuration for [Hetzner](https://hetzner.com).
- Code: `hetzner`
- Since: v3.7.0
Here is an example bash command using the Hetzner provider:
```bash
HETZNER_API_KEY=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx \
lego --email you@example.com --dns hetzner --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `HETZNER_API_KEY` | API key |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `HETZNER_HTTP_TIMEOUT` | API request timeout |
| `HETZNER_POLLING_INTERVAL` | Time between DNS propagation check |
| `HETZNER_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `HETZNER_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://dns.hetzner.com/api-docs)
lego-4.9.1/docs/content/dns/zz_gen_hostingde.md 0000664 0000000 0000000 00000003501 14340204635 0021523 0 ustar 00root root 0000000 0000000 ---
title: "Hosting.de"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: hostingde
dnsprovider:
since: "v1.1.0"
code: "hostingde"
url: "https://www.hosting.de/"
---
Configuration for [Hosting.de](https://www.hosting.de/).
- Code: `hostingde`
- Since: v1.1.0
Here is an example bash command using the Hosting.de provider:
```bash
HOSTINGDE_API_KEY=xxxxxxxx \
lego --email you@example.com --dns hostingde --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `HOSTINGDE_API_KEY` | API key |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## 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 |
| `HOSTINGDE_ZONE_NAME` | Zone name in ACE format |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://www.hosting.de/api/#dns)
lego-4.9.1/docs/content/dns/zz_gen_hosttech.md 0000664 0000000 0000000 00000003504 14340204635 0021363 0 ustar 00root root 0000000 0000000 ---
title: "Hosttech"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: hosttech
dnsprovider:
since: "v4.5.0"
code: "hosttech"
url: "https://www.hosttech.eu/"
---
Configuration for [Hosttech](https://www.hosttech.eu/).
- Code: `hosttech`
- Since: v4.5.0
Here is an example bash command using the Hosttech provider:
```bash
HOSTTECH_API_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxx \
lego --email you@example.com --dns hosttech --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `HOSTTECH_API_KEY` | API login |
| `HOSTTECH_PASSWORD` | API password |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `HOSTTECH_HTTP_TIMEOUT` | API request timeout |
| `HOSTTECH_POLLING_INTERVAL` | Time between DNS propagation check |
| `HOSTTECH_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `HOSTTECH_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://api.ns1.hosttech.eu/api/documentation)
lego-4.9.1/docs/content/dns/zz_gen_httpreq.md 0000664 0000000 0000000 00000004564 14340204635 0021240 0 ustar 00root root 0000000 0000000 ---
title: "HTTP request"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: httpreq
dnsprovider:
since: "v2.0.0"
code: "httpreq"
url: "/lego/dns/httpreq/"
---
Configuration for [HTTP request](/lego/dns/httpreq/).
- Code: `httpreq`
- Since: v2.0.0
Here is an example bash command using the HTTP request provider:
```bash
HTTPREQ_ENDPOINT=http://my.server.com:9090 \
lego --email you@example.com --dns httpreq --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `HTTPREQ_ENDPOINT` | The URL of the server |
| `HTTPREQ_MODE` | `RAW`, none |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `HTTPREQ_HTTP_TIMEOUT` | API request timeout |
| `HTTPREQ_PASSWORD` | Basic authentication password |
| `HTTPREQ_POLLING_INTERVAL` | Time between DNS propagation check |
| `HTTPREQ_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `HTTPREQ_USERNAME` | Basic authentication username |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## 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.
lego-4.9.1/docs/content/dns/zz_gen_hurricane.md 0000664 0000000 0000000 00000004652 14340204635 0021527 0 ustar 00root root 0000000 0000000 ---
title: "Hurricane Electric DNS"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: hurricane
dnsprovider:
since: "v4.3.0"
code: "hurricane"
url: "https://dns.he.net/"
---
Configuration for [Hurricane Electric DNS](https://dns.he.net/).
- Code: `hurricane`
- Since: v4.3.0
Here is an example bash command using the Hurricane Electric DNS provider:
```bash
HURRICANE_TOKENS=example.org:token \
lego --email you@example.com --dns hurricane --domains example.org --domains '*.example.org run'
HURRICANE_TOKENS=my.example.org:token1,demo.example.org:token2 \
lego --email you@example.com --dns hurricane --domains my.example.org --domains demo.example.org
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `HURRICANE_TOKENS` | TXT record names and tokens |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
Before using lego to request a certificate for a given domain or wildcard (such as `my.example.org` or `*.my.example.org`),
create a TXT record named `_acme-challenge.my.example.org`, and enable dynamic updates on it.
Generate a token for each URL with Hurricane Electric's UI, and copy it down.
Stick to alphanumeric tokens for greatest reliability.
To authenticate with the Hurricane Electric API,
add each record name/token pair you want to update to the `HURRICANE_TOKENS` environment variable, as shown in the examples.
Record names (without the `_acme-challenge.` component) and their tokens are separated with colons,
while the credential pairs are concatenated into a comma-separated list, like so:
```
HURRICANE_TOKENS=my.example.org:token1,demo.example.org:token2
```
If you are issuing both a wildcard certificate and a standard certificate for a given subdomain,
you should not have repeat entries for that name, as both will use the same credential.
```
HURRICANE_TOKENS=example.org:token
```
## More information
- [API documentation](https://dns.he.net/)
lego-4.9.1/docs/content/dns/zz_gen_hyperone.md 0000664 0000000 0000000 00000004666 14340204635 0021405 0 ustar 00root root 0000000 0000000 ---
title: "HyperOne"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: hyperone
dnsprovider:
since: "v3.9.0"
code: "hyperone"
url: "https://www.hyperone.com"
---
Configuration for [HyperOne](https://www.hyperone.com).
- Code: `hyperone`
- Since: v3.9.0
Here is an example bash command using the HyperOne provider:
```bash
lego --email you@example.com --dns hyperone --domains my.example.org run
```
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `HYPERONE_API_URL` | Allows to pass custom API Endpoint to be used in the challenge (default https://api.hyperone.com/v2) |
| `HYPERONE_LOCATION_ID` | Specifies location (region) to be used in API calls. (default pl-waw-1) |
| `HYPERONE_PASSPORT_LOCATION` | Allows to pass custom passport file location (default ~/.h1/passport.json) |
| `HYPERONE_POLLING_INTERVAL` | Time between DNS propagation check |
| `HYPERONE_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `HYPERONE_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Description
Default configuration does not require any additional environment variables,
just a passport file in `~/.h1/passport.json` location.
### Generating passport file using H1 CLI
To use this application you have to generate passport file for `sa`:
```
h1 iam project sa credential generate --name my-passport --project --sa --passport-output-file ~/.h1/passport.json
```
### Required permissions
The application requires following permissions:
- `dns/zone/list`
- `dns/zone.recordset/list`
- `dns/zone.recordset/create`
- `dns/zone.recordset/delete`
- `dns/zone.record/create`
- `dns/zone.record/list`
- `dns/zone.record/delete`
All required permissions are available via platform role `tool.lego`.
## More information
- [API documentation](https://api.hyperone.com/v2/docs)
lego-4.9.1/docs/content/dns/zz_gen_ibmcloud.md 0000664 0000000 0000000 00000004010 14340204635 0021331 0 ustar 00root root 0000000 0000000 ---
title: "IBM Cloud (SoftLayer)"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: ibmcloud
dnsprovider:
since: "v4.5.0"
code: "ibmcloud"
url: "https://www.ibm.com/cloud/"
---
Configuration for [IBM Cloud (SoftLayer)](https://www.ibm.com/cloud/).
- Code: `ibmcloud`
- Since: v4.5.0
Here is an example bash command using the IBM Cloud (SoftLayer) provider:
```bash
SOFTLAYER_USERNAME=xxxxx \
SOFTLAYER_API_KEY=yyyyy \
lego --email you@example.com --dns ibmcloud --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `SOFTLAYER_API_KEY` | Classic Infrastructure API key |
| `SOFTLAYER_USERNAME` | User name (IBM Cloud is _) |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `SOFTLAYER_POLLING_INTERVAL` | Time between DNS propagation check |
| `SOFTLAYER_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `SOFTLAYER_TIMEOUT` | API request timeout |
| `SOFTLAYER_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://cloud.ibm.com/docs/dns?topic=dns-getting-started-with-the-dns-api)
- [Go client](https://github.com/softlayer/softlayer-go)
lego-4.9.1/docs/content/dns/zz_gen_iij.md 0000664 0000000 0000000 00000003616 14340204635 0020321 0 ustar 00root root 0000000 0000000 ---
title: "Internet Initiative Japan"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: iij
dnsprovider:
since: "v1.1.0"
code: "iij"
url: "https://www.iij.ad.jp/en/"
---
Configuration for [Internet Initiative Japan](https://www.iij.ad.jp/en/).
- Code: `iij`
- Since: v1.1.0
Here is an example bash command using the Internet Initiative Japan provider:
```bash
IIJ_API_ACCESS_KEY=xxxxxxxx \
IIJ_API_SECRET_KEY=yyyyyy \
IIJ_DO_SERVICE_CODE=zzzzzz \
lego --email you@example.com --dns iij --domains my.example.org run
```
## 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 |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## 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 |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://manual.iij.jp/p2/pubapi/)
- [Go client](https://github.com/iij/doapi)
lego-4.9.1/docs/content/dns/zz_gen_iijdpf.md 0000664 0000000 0000000 00000004105 14340204635 0021005 0 ustar 00root root 0000000 0000000 ---
title: "IIJ DNS Platform Service"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: iijdpf
dnsprovider:
since: "v4.7.0"
code: "iijdpf"
url: "https://www.iij.ad.jp/en/biz/dns-pfm/"
---
Configuration for [IIJ DNS Platform Service](https://www.iij.ad.jp/en/biz/dns-pfm/).
- Code: `iijdpf`
- Since: v4.7.0
Here is an example bash command using the IIJ DNS Platform Service provider:
```bash
IIJ_DPF_API_TOKEN=xxxxxxxx \
IIJ_DPF_DPM_SERVICE_CODE=yyyyyy \
lego --email you@example.com --dns iijdpf --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `IIJ_DPF_API_TOKEN` | API token |
| `IIJ_DPF_DPM_SERVICE_CODE` | IIJ Managed DNS Service's service code |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `IIJ_DPF_API_ENDPOINT` | API endpoint URL, defaults to https://api.dns-platform.jp/dpf/v1 |
| `IIJ_DPF_POLLING_INTERVAL` | Time between DNS propagation check, defaults to 5 second |
| `IIJ_DPF_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation, defaults to 660 second |
| `IIJ_DPF_TTL` | The TTL of the TXT record used for the DNS challenge, default to 300 |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://manual.iij.jp/dpf/dpfapi/)
- [Go client](https://github.com/mimuret/golang-iij-dpf)
lego-4.9.1/docs/content/dns/zz_gen_infoblox.md 0000664 0000000 0000000 00000004641 14340204635 0021365 0 ustar 00root root 0000000 0000000 ---
title: "Infoblox"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: infoblox
dnsprovider:
since: "v4.4.0"
code: "infoblox"
url: "https://www.infoblox.com/"
---
Configuration for [Infoblox](https://www.infoblox.com/).
- Code: `infoblox`
- Since: v4.4.0
Here is an example bash command using the Infoblox provider:
```bash
INFOBLOX_USERNAME=api-user-529 \
INFOBLOX_PASSWORD=b9841238feb177a84330febba8a83208921177bffe733 \
INFOBLOX_HOST=infoblox.example.org
lego --email you@example.com --dns infoblox --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `INFOBLOX_HOST` | Host URI |
| `INFOBLOX_PASSWORD` | Account Password |
| `INFOBLOX_USERNAME` | Account Username |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `INFOBLOX_DNS_VIEW` | The view for the TXT records, default: External |
| `INFOBLOX_HTTP_TIMEOUT` | HTTP request timeout |
| `INFOBLOX_POLLING_INTERVAL` | Time between DNS propagation check |
| `INFOBLOX_PORT` | The port for the infoblox grid manager, default: 443 |
| `INFOBLOX_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `INFOBLOX_SSL_VERIFY` | Whether or not to verify the TLS certificate, default: true |
| `INFOBLOX_TTL` | The TTL of the TXT record used for the DNS challenge |
| `INFOBLOX_WAPI_VERSION` | The version of WAPI being used, default: 2.11 |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
When creating an API's user ensure it has the proper permissions for the view you are working with.
## More information
- [API documentation](https://your.infoblox.server/wapidoc/)
- [Go client](https://github.com/infobloxopen/infoblox-go-client)
lego-4.9.1/docs/content/dns/zz_gen_infomaniak.md 0000664 0000000 0000000 00000004001 14340204635 0021647 0 ustar 00root root 0000000 0000000 ---
title: "Infomaniak"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: infomaniak
dnsprovider:
since: "v4.1.0"
code: "infomaniak"
url: "https://www.infomaniak.com/"
---
Configuration for [Infomaniak](https://www.infomaniak.com/).
- Code: `infomaniak`
- Since: v4.1.0
Here is an example bash command using the Infomaniak provider:
```bash
INFOMANIAK_ACCESS_TOKEN=1234567898765432 \
lego --email you@example.com --dns infomaniak --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `INFOMANIAK_ACCESS_TOKEN` | Access token |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `INFOMANIAK_ENDPOINT` | https://api.infomaniak.com |
| `INFOMANIAK_HTTP_TIMEOUT` | API request timeout |
| `INFOMANIAK_POLLING_INTERVAL` | Time between DNS propagation check |
| `INFOMANIAK_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `INFOMANIAK_TTL` | The TTL of the TXT record used for the DNS challenge in seconds |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Access token
Access token can be created at the url https://manager.infomaniak.com/v3/infomaniak-api.
You will need domain scope.
## More information
- [API documentation](https://api.infomaniak.com/doc)
lego-4.9.1/docs/content/dns/zz_gen_internetbs.md 0000664 0000000 0000000 00000003633 14340204635 0021722 0 ustar 00root root 0000000 0000000 ---
title: "Internet.bs"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: internetbs
dnsprovider:
since: "v4.5.0"
code: "internetbs"
url: "https://internetbs.net"
---
Configuration for [Internet.bs](https://internetbs.net).
- Code: `internetbs`
- Since: v4.5.0
Here is an example bash command using the Internet.bs provider:
```bash
INTERNET_BS_API_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxx \
INTERNET_BS_PASSWORD=yyyyyyyyyyyyyyyyyyyyyyyyyy \
lego --email you@example.com --dns internetbs --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `INTERNET_BS_API_KEY` | API key |
| `INTERNET_BS_PASSWORD` | API password |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `INTERNET_BS_HTTP_TIMEOUT` | API request timeout |
| `INTERNET_BS_POLLING_INTERVAL` | Time between DNS propagation check |
| `INTERNET_BS_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `INTERNET_BS_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://internetbs.net/internet-bs-api.pdf)
lego-4.9.1/docs/content/dns/zz_gen_inwx.md 0000664 0000000 0000000 00000004023 14340204635 0020524 0 ustar 00root root 0000000 0000000 ---
title: "INWX"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: inwx
dnsprovider:
since: "v2.0.0"
code: "inwx"
url: "https://www.inwx.de/en"
---
Configuration for [INWX](https://www.inwx.de/en).
- Code: `inwx`
- Since: v2.0.0
Here is an example bash command using the INWX provider:
```bash
INWX_USERNAME=xxxxxxxxxx \
INWX_PASSWORD=yyyyyyyyyy \
lego --email you@example.com --dns inwx --domains my.example.org run
# 2FA
INWX_USERNAME=xxxxxxxxxx \
INWX_PASSWORD=yyyyyyyyyy \
INWX_SHARED_SECRET=zzzzzzzzzz \
lego --email you@example.com --dns inwx --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `INWX_PASSWORD` | Password |
| `INWX_USERNAME` | Username |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `INWX_POLLING_INTERVAL` | Time between DNS propagation check |
| `INWX_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation (default 360s) |
| `INWX_SANDBOX` | Activate the sandbox (boolean) |
| `INWX_SHARED_SECRET` | shared secret related to 2FA |
| `INWX_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://www.inwx.de/en/help/apidoc)
- [Go client](https://github.com/nrdcg/goinwx)
lego-4.9.1/docs/content/dns/zz_gen_ionos.md 0000664 0000000 0000000 00000003417 14340204635 0020674 0 ustar 00root root 0000000 0000000 ---
title: "Ionos"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: ionos
dnsprovider:
since: "v4.2.0"
code: "ionos"
url: "https://ionos.com"
---
Configuration for [Ionos](https://ionos.com).
- Code: `ionos`
- Since: v4.2.0
Here is an example bash command using the Ionos provider:
```bash
IONOS_API_KEY=xxxxxxxx \
lego --email you@example.com --dns ionos --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `IONOS_API_KEY` | API key `.` https://developer.hosting.ionos.com/docs/getstarted |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `IONOS_HTTP_TIMEOUT` | API request timeout |
| `IONOS_POLLING_INTERVAL` | Time between DNS propagation check |
| `IONOS_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `IONOS_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://developer.hosting.ionos.com/docs/dns)
lego-4.9.1/docs/content/dns/zz_gen_iwantmyname.md 0000664 0000000 0000000 00000003616 14340204635 0022077 0 ustar 00root root 0000000 0000000 ---
title: "iwantmyname"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: iwantmyname
dnsprovider:
since: "v4.7.0"
code: "iwantmyname"
url: "https://iwantmyname.com"
---
Configuration for [iwantmyname](https://iwantmyname.com).
- Code: `iwantmyname`
- Since: v4.7.0
Here is an example bash command using the iwantmyname provider:
```bash
IWANTMYNAME_USERNAME=xxxxxxxx \
IWANTMYNAME_PASSWORD=xxxxxxxx \
lego --email you@example.com --dns iwantmyname --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `IWANTMYNAME_PASSWORD` | API password |
| `IWANTMYNAME_USERNAME` | API username |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `IWANTMYNAME_HTTP_TIMEOUT` | API request timeout |
| `IWANTMYNAME_POLLING_INTERVAL` | Time between DNS propagation check |
| `IWANTMYNAME_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `IWANTMYNAME_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://iwantmyname.com/developer/domain-dns-api)
lego-4.9.1/docs/content/dns/zz_gen_joker.md 0000664 0000000 0000000 00000006217 14340204635 0020660 0 ustar 00root root 0000000 0000000 ---
title: "Joker"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: joker
dnsprovider:
since: "v2.6.0"
code: "joker"
url: "https://joker.com"
---
Configuration for [Joker](https://joker.com).
- Code: `joker`
- Since: v2.6.0
Here is an example bash command using the Joker provider:
```bash
# SVC
JOKER_API_MODE=SVC \
JOKER_USERNAME= \
JOKER_PASSWORD= \
lego --email you@example.com --dns joker --domains my.example.org run
# DMAPI
JOKER_API_MODE=DMAPI \
JOKER_USERNAME= \
JOKER_PASSWORD= \
lego --email you@example.com --dns joker --domains my.example.org run
## or
JOKER_API_MODE=DMAPI \
JOKER_API_KEY= \
lego --email you@example.com --dns joker --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `JOKER_API_KEY` | API key (only with DMAPI mode) |
| `JOKER_API_MODE` | 'DMAPI' or 'SVC'. DMAPI is for resellers accounts. (Default: DMAPI) |
| `JOKER_PASSWORD` | Joker.com password |
| `JOKER_USERNAME` | Joker.com username |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `JOKER_HTTP_TIMEOUT` | API request timeout |
| `JOKER_POLLING_INTERVAL` | Time between DNS propagation check |
| `JOKER_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `JOKER_SEQUENCE_INTERVAL` | Time between sequential requests (only with 'SVC' mode) |
| `JOKER_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## SVC mode
In the SVC mode, username and passsword are not your email and account passwords, but those displayed in Joker.com domain dashboard when enabling Dynamic DNS.
As per [Joker.com documentation](https://joker.com/faq/content/6/496/en/let_s-encrypt-support.html):
> 1. please login at Joker.com, visit 'My Domains',
> find the domain you want to add Let's Encrypt certificate for, and chose "DNS" in the menu
>
> 2. on the top right, you will find the setting for 'Dynamic DNS'.
> If not already active, please activate it.
> It will not affect any other already existing DNS records of this domain.
>
> 3. please take a note of the credentials which are now shown as 'Dynamic DNS Authentication', consisting of a 'username' and a 'password'.
>
> 4. this is all you have to do here - and only once per domain.
## More information
- [API documentation](https://joker.com/faq/category/39/22-dmapi.html)
lego-4.9.1/docs/content/dns/zz_gen_lightsail.md 0000664 0000000 0000000 00000006473 14340204635 0021532 0 ustar 00root root 0000000 0000000 ---
title: "Amazon Lightsail"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: lightsail
dnsprovider:
since: "v0.5.0"
code: "lightsail"
url: "https://aws.amazon.com/lightsail/"
---
Configuration for [Amazon Lightsail](https://aws.amazon.com/lightsail/).
- Code: `lightsail`
- Since: v0.5.0
{{% notice note %}}
_Please contribute by adding a CLI example._
{{% /notice %}}
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `AWS_ACCESS_KEY_ID` | Managed by the AWS client. Access key ID (`AWS_ACCESS_KEY_ID_FILE` is not supported, use `AWS_SHARED_CREDENTIALS_FILE` instead) |
| `AWS_SECRET_ACCESS_KEY` | Managed by the AWS client. Secret access key (`AWS_SECRET_ACCESS_KEY_FILE` is not supported, use `AWS_SHARED_CREDENTIALS_FILE` instead) |
| `DNS_ZONE` | Domain name of the DNS zone |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `AWS_SHARED_CREDENTIALS_FILE` | Managed by the AWS client. Shared credentials file. |
| `LIGHTSAIL_POLLING_INTERVAL` | Time between DNS propagation check |
| `LIGHTSAIL_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## 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_SESSION_TOKEN`]
2. Shared credentials file (defaults to `~/.aws/credentials`, profiles can be specified using `AWS_PROFILE`)
3. Amazon EC2 IAM role
AWS region is not required to set as the Lightsail DNS zone is in global (us-east-1) region.
## Policy
The following AWS IAM policy document describes the minimum permissions required for lego to complete the DNS challenge.
```json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"lightsail:DeleteDomainEntry",
"lightsail:CreateDomainEntry"
],
"Resource": ""
}
]
}
```
Replace the `Resource` value with your Lightsail DNS zone ARN.
You can retrieve the ARN using aws cli by running `aws lightsail get-domains --region us-east-1` (Lightsail web console does not show the ARN, unfortunately).
It should be in the format of `arn:aws:lightsail:global::Domain/`.
You also need to replace the region in the ARN to `us-east-1` (instead of `global`).
Alternatively, you can also set the `Resource` to `*` (wildcard), which allow to access all domain, but this is not recommended.
## More information
- [Go client](https://github.com/aws/aws-sdk-go/)
lego-4.9.1/docs/content/dns/zz_gen_linode.md 0000664 0000000 0000000 00000003430 14340204635 0021012 0 ustar 00root root 0000000 0000000 ---
title: "Linode (v4)"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: linode
dnsprovider:
since: "v1.1.0"
code: "linode"
url: "https://www.linode.com/"
---
Configuration for [Linode (v4)](https://www.linode.com/).
- Code: `linode`
- Since: v1.1.0
Here is an example bash command using the Linode (v4) provider:
```bash
LINODE_TOKEN=xxxxx \
lego --email you@example.com --dns linode --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `LINODE_TOKEN` | API token |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `LINODE_HTTP_TIMEOUT` | API request timeout |
| `LINODE_POLLING_INTERVAL` | Time between DNS propagation check |
| `LINODE_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `LINODE_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://developers.linode.com/api/v4)
- [Go client](https://github.com/linode/linodego)
lego-4.9.1/docs/content/dns/zz_gen_liquidweb.md 0000664 0000000 0000000 00000004120 14340204635 0021522 0 ustar 00root root 0000000 0000000 ---
title: "Liquid Web"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: liquidweb
dnsprovider:
since: "v3.1.0"
code: "liquidweb"
url: "https://liquidweb.com"
---
Configuration for [Liquid Web](https://liquidweb.com).
- Code: `liquidweb`
- Since: v3.1.0
Here is an example bash command using the Liquid Web provider:
```bash
LIQUID_WEB_USERNAME=someuser \
LIQUID_WEB_PASSWORD=somepass \
LIQUID_WEB_ZONE=tacoman.com.net \
lego --email you@example.com --dns liquidweb --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `LIQUID_WEB_PASSWORD` | Storm API Password |
| `LIQUID_WEB_USERNAME` | Storm API Username |
| `LIQUID_WEB_ZONE` | DNS Zone |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `LIQUID_WEB_HTTP_TIMEOUT` | Maximum waiting time for the DNS records to be created (not verified) |
| `LIQUID_WEB_POLLING_INTERVAL` | Time between DNS propagation check |
| `LIQUID_WEB_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `LIQUID_WEB_TTL` | The TTL of the TXT record used for the DNS challenge |
| `LIQUID_WEB_URL` | Storm API endpoint |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://cart.liquidweb.com/storm/api/docs/v1/)
- [Go client](https://github.com/liquidweb/liquidweb-go)
lego-4.9.1/docs/content/dns/zz_gen_loopia.md 0000664 0000000 0000000 00000004135 14340204635 0021026 0 ustar 00root root 0000000 0000000 ---
title: "Loopia"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: loopia
dnsprovider:
since: "v4.2.0"
code: "loopia"
url: "https://loopia.com"
---
Configuration for [Loopia](https://loopia.com).
- Code: `loopia`
- Since: v4.2.0
Here is an example bash command using the Loopia provider:
```bash
LOOPIA_API_USER=xxxxxxxx \
LOOPIA_API_PASSWORD=yyyyyyyy \
lego --email my@email.com --dns loopia --domains my.domain.com run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `LOOPIA_API_PASSWORD` | API password |
| `LOOPIA_API_USER` | API username |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `LOOPIA_API_URL` | API endpoint. Ex: https://api.loopia.se/RPCSERV or https://api.loopia.rs/RPCSERV |
| `LOOPIA_HTTP_TIMEOUT` | API request timeout |
| `LOOPIA_POLLING_INTERVAL` | Time between DNS propagation check |
| `LOOPIA_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `LOOPIA_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
### API user
You can [generate a new API user](https://customerzone.loopia.com/api/) from your account page.
It needs to have the following permissions:
* addZoneRecord
* getZoneRecords
* removeZoneRecord
* removeSubdomain
## More information
- [API documentation](https://www.loopia.com/api)
lego-4.9.1/docs/content/dns/zz_gen_luadns.md 0000664 0000000 0000000 00000003441 14340204635 0021030 0 ustar 00root root 0000000 0000000 ---
title: "LuaDNS"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: luadns
dnsprovider:
since: "v3.7.0"
code: "luadns"
url: "https://luadns.com"
---
Configuration for [LuaDNS](https://luadns.com).
- Code: `luadns`
- Since: v3.7.0
Here is an example bash command using the LuaDNS provider:
```bash
LUADNS_API_USERNAME=youremail \
LUADNS_API_TOKEN=xxxxxxxx \
lego --email you@example.com --dns luadns --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `LUADNS_API_TOKEN` | API token |
| `LUADNS_API_USERNAME` | Username (your email) |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `LUADNS_HTTP_TIMEOUT` | API request timeout |
| `LUADNS_POLLING_INTERVAL` | Time between DNS propagation check |
| `LUADNS_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `LUADNS_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://luadns.com/api.html)
lego-4.9.1/docs/content/dns/zz_gen_mydnsjp.md 0000664 0000000 0000000 00000003442 14340204635 0021227 0 ustar 00root root 0000000 0000000 ---
title: "MyDNS.jp"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: mydnsjp
dnsprovider:
since: "v1.2.0"
code: "mydnsjp"
url: "https://www.mydns.jp"
---
Configuration for [MyDNS.jp](https://www.mydns.jp).
- Code: `mydnsjp`
- Since: v1.2.0
Here is an example bash command using the MyDNS.jp provider:
```bash
MYDNSJP_MASTER_ID=xxxxx \
MYDNSJP_PASSWORD=xxxxx \
lego --email you@example.com --dns mydnsjp --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `MYDNSJP_MASTER_ID` | Master ID |
| `MYDNSJP_PASSWORD` | Password |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## 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 |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://www.mydns.jp/?MENU=030)
lego-4.9.1/docs/content/dns/zz_gen_mythicbeasts.md 0000664 0000000 0000000 00000004364 14340204635 0022246 0 ustar 00root root 0000000 0000000 ---
title: "MythicBeasts"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: mythicbeasts
dnsprovider:
since: "v0.3.7"
code: "mythicbeasts"
url: "https://www.mythic-beasts.com/"
---
Configuration for [MythicBeasts](https://www.mythic-beasts.com/).
- Code: `mythicbeasts`
- Since: v0.3.7
Here is an example bash command using the MythicBeasts provider:
```bash
MYTHICBEASTS_USERNAME=myuser \
MYTHICBEASTS_PASSWORD=mypass \
lego --email you@example.com --dns mythicbeasts --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `MYTHICBEASTS_PASSWORD` | Password |
| `MYTHICBEASTS_USERNAME` | User name |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `MYTHICBEASTS_API_ENDPOINT` | The endpoint for the API (must implement v2) |
| `MYTHICBEASTS_AUTH_API_ENDPOINT` | The endpoint for Mythic Beasts' Authentication |
| `MYTHICBEASTS_HTTP_TIMEOUT` | API request timeout |
| `MYTHICBEASTS_POLLING_INTERVAL` | Time between DNS propagation check |
| `MYTHICBEASTS_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `MYTHICBEASTS_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
If you are using specific API keys, then the username is the API ID for your API key, and the password is the API secret.
Your API key name is not needed to operate lego.
## More information
- [API documentation](https://www.mythic-beasts.com/support/api/dnsv2)
lego-4.9.1/docs/content/dns/zz_gen_namecheap.md 0000664 0000000 0000000 00000004366 14340204635 0021472 0 ustar 00root root 0000000 0000000 ---
title: "Namecheap"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: namecheap
dnsprovider:
since: "v0.3.0"
code: "namecheap"
url: "https://www.namecheap.com"
---
Configuration for [Namecheap](https://www.namecheap.com).
**To enable API access on the Namecheap production environment, some opaque requirements must be met.**
More information in the section [Enabling API Access](https://www.namecheap.com/support/api/intro/) of the Namecheap documentation.
(2020-08: Account balance of $50+, 20+ domains in your account, or purchases totaling $50+ within the last 2 years.)
- Code: `namecheap`
- Since: v0.3.0
Here is an example bash command using the Namecheap provider:
```bash
NAMECHEAP_API_USER=user \
NAMECHEAP_API_KEY=key \
lego --email you@example.com --dns namecheap --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `NAMECHEAP_API_KEY` | API key |
| `NAMECHEAP_API_USER` | API user |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## 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_SANDBOX` | Activate the sandbox (boolean) |
| `NAMECHEAP_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://www.namecheap.com/support/api/methods.aspx)
lego-4.9.1/docs/content/dns/zz_gen_namedotcom.md 0000664 0000000 0000000 00000003622 14340204635 0021671 0 ustar 00root root 0000000 0000000 ---
title: "Name.com"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: namedotcom
dnsprovider:
since: "v0.5.0"
code: "namedotcom"
url: "https://www.name.com"
---
Configuration for [Name.com](https://www.name.com).
- Code: `namedotcom`
- Since: v0.5.0
Here is an example bash command using the Name.com provider:
```bash
NAMECOM_USERNAME=foo.bar \
NAMECOM_API_TOKEN=a379a6f6eeafb9a55e378c118034e2751e682fab \
lego --email you@example.com --dns namedotcom --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `NAMECOM_API_TOKEN` | API token |
| `NAMECOM_USERNAME` | Username |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## 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 |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://www.name.com/api-docs/DNS)
- [Go client](https://github.com/namedotcom/go)
lego-4.9.1/docs/content/dns/zz_gen_namesilo.md 0000664 0000000 0000000 00000003561 14340204635 0021354 0 ustar 00root root 0000000 0000000 ---
title: "Namesilo"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: namesilo
dnsprovider:
since: "v2.7.0"
code: "namesilo"
url: "https://www.namesilo.com/"
---
Configuration for [Namesilo](https://www.namesilo.com/).
- Code: `namesilo`
- Since: v2.7.0
Here is an example bash command using the Namesilo provider:
```bash
NAMESILO_API_KEY=b9841238feb177a84330febba8a83208921177bffe733 \
lego --email you@example.com --dns namesilo --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `NAMESILO_API_KEY` | Client ID |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `NAMESILO_POLLING_INTERVAL` | Time between DNS propagation check |
| `NAMESILO_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation, it is better to set larger than 15m |
| `NAMESILO_TTL` | The TTL of the TXT record used for the DNS challenge, should be in [3600, 2592000] |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://www.namesilo.com/api_reference.php)
- [Go client](https://github.com/nrdcg/namesilo)
lego-4.9.1/docs/content/dns/zz_gen_nearlyfreespeech.md 0000664 0000000 0000000 00000004143 14340204635 0023066 0 ustar 00root root 0000000 0000000 ---
title: "NearlyFreeSpeech.NET"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: nearlyfreespeech
dnsprovider:
since: "v4.8.0"
code: "nearlyfreespeech"
url: "https://nearlyfreespeech.net/"
---
Configuration for [NearlyFreeSpeech.NET](https://nearlyfreespeech.net/).
- Code: `nearlyfreespeech`
- Since: v4.8.0
Here is an example bash command using the NearlyFreeSpeech.NET provider:
```bash
NEARLYFREESPEECH_API_KEY=xxxxxx \
NEARLYFREESPEECH_LOGIN=xxxx \
lego --email you@example.com --dns nearlyfreespeech --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `NEARLYFREESPEECH_API_KEY` | API Key for API requests |
| `NEARLYFREESPEECH_LOGIN` | Username for API requests |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `NEARLYFREESPEECH_HTTP_TIMEOUT` | API request timeout |
| `NEARLYFREESPEECH_POLLING_INTERVAL` | Time between DNS propagation check |
| `NEARLYFREESPEECH_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `NEARLYFREESPEECH_SEQUENCE_INTERVAL` | Time between sequential requests |
| `NEARLYFREESPEECH_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://members.nearlyfreespeech.net/wiki/API/Reference)
lego-4.9.1/docs/content/dns/zz_gen_netcup.md 0000664 0000000 0000000 00000003552 14340204635 0021043 0 ustar 00root root 0000000 0000000 ---
title: "Netcup"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: netcup
dnsprovider:
since: "v1.1.0"
code: "netcup"
url: "https://www.netcup.eu/"
---
Configuration for [Netcup](https://www.netcup.eu/).
- Code: `netcup`
- Since: v1.1.0
Here is an example bash command using the Netcup provider:
```bash
NETCUP_CUSTOMER_NUMBER=xxxx \
NETCUP_API_KEY=yyyy \
NETCUP_API_PASSWORD=zzzz \
lego --email you@example.com --dns netcup --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `NETCUP_API_KEY` | API key |
| `NETCUP_API_PASSWORD` | API password |
| `NETCUP_CUSTOMER_NUMBER` | Customer number |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## 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 |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://www.netcup-wiki.de/wiki/DNS_API)
lego-4.9.1/docs/content/dns/zz_gen_netlify.md 0000664 0000000 0000000 00000003430 14340204635 0021212 0 ustar 00root root 0000000 0000000 ---
title: "Netlify"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: netlify
dnsprovider:
since: "v3.7.0"
code: "netlify"
url: "https://www.netlify.com"
---
Configuration for [Netlify](https://www.netlify.com).
- Code: `netlify`
- Since: v3.7.0
Here is an example bash command using the Netlify provider:
```bash
NETLIFY_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx \
lego --email you@example.com --dns netlify --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `NETLIFY_TOKEN` | Token |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `NETLIFY_HTTP_TIMEOUT` | API request timeout |
| `NETLIFY_POLLING_INTERVAL` | Time between DNS propagation check |
| `NETLIFY_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `NETLIFY_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://open-api.netlify.com/)
lego-4.9.1/docs/content/dns/zz_gen_nicmanager.md 0000664 0000000 0000000 00000005444 14340204635 0021653 0 ustar 00root root 0000000 0000000 ---
title: "Nicmanager"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: nicmanager
dnsprovider:
since: "v4.5.0"
code: "nicmanager"
url: "https://www.nicmanager.com/"
---
Configuration for [Nicmanager](https://www.nicmanager.com/).
- Code: `nicmanager`
- Since: v4.5.0
Here is an example bash command using the Nicmanager provider:
```bash
## Login using email
NICMANAGER_API_EMAIL = "you@example.com" \
NICMANAGER_API_PASSWORD = "password" \
# Optionally, if your account has TOTP enabled, set the secret here
NICMANAGER_API_OTP = "long-secret" \
lego --email you@example.com --dns nicmanager --domains my.example.org run
## Login using account name + username
NICMANAGER_API_LOGIN = "myaccount" \
NICMANAGER_API_USERNAME = "myuser" \
NICMANAGER_API_PASSWORD = "password" \
# Optionally, if your account has TOTP enabled, set the secret here
NICMANAGER_API_OTP = "long-secret" \
lego --email you@example.com --dns nicmanager --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `NICMANAGER_API_EMAIL` | Email-based login |
| `NICMANAGER_API_LOGIN` | Login, used for Username-based login |
| `NICMANAGER_API_PASSWORD` | Password, always required |
| `NICMANAGER_API_USERNAME` | Username, used for Username-based login |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `NICMANAGER_API_MODE` | mode: 'anycast' or 'zone' (default: 'anycast') |
| `NICMANAGER_API_OTP` | TOTP Secret (optional) |
| `NICMANAGER_HTTP_TIMEOUT` | API request timeout |
| `NICMANAGER_POLLING_INTERVAL` | Time between DNS propagation check |
| `NICMANAGER_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `NICMANAGER_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Description
You can login using your account name + username or using your email address.
Optionally if TOTP is configured for your account, set `NICMANAGER_API_OTP`.
## More information
- [API documentation](https://api.nicmanager.com/docs/v1/)
lego-4.9.1/docs/content/dns/zz_gen_nifcloud.md 0000664 0000000 0000000 00000003576 14340204635 0021356 0 ustar 00root root 0000000 0000000 ---
title: "NIFCloud"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: nifcloud
dnsprovider:
since: "v1.1.0"
code: "nifcloud"
url: "https://www.nifcloud.com/"
---
Configuration for [NIFCloud](https://www.nifcloud.com/).
- Code: `nifcloud`
- Since: v1.1.0
Here is an example bash command using the NIFCloud provider:
```bash
NIFCLOUD_ACCESS_KEY_ID=xxxx \
NIFCLOUD_SECRET_ACCESS_KEY=yyyy \
lego --email you@example.com --dns nifcloud --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `NIFCLOUD_ACCESS_KEY_ID` | Access key |
| `NIFCLOUD_SECRET_ACCESS_KEY` | Secret access key |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## 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 |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://mbaas.nifcloud.com/doc/current/rest/common/format.html)
lego-4.9.1/docs/content/dns/zz_gen_njalla.md 0000664 0000000 0000000 00000003314 14340204635 0021002 0 ustar 00root root 0000000 0000000 ---
title: "Njalla"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: njalla
dnsprovider:
since: "v4.3.0"
code: "njalla"
url: "https://njal.la"
---
Configuration for [Njalla](https://njal.la).
- Code: `njalla`
- Since: v4.3.0
Here is an example bash command using the Njalla provider:
```bash
NJALLA_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxx \
lego --email you@example.com --dns njalla --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `NJALLA_TOKEN` | API token |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `NJALLA_HTTP_TIMEOUT` | API request timeout |
| `NJALLA_POLLING_INTERVAL` | Time between DNS propagation check |
| `NJALLA_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `NJALLA_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://njal.la/api/)
lego-4.9.1/docs/content/dns/zz_gen_ns1.md 0000664 0000000 0000000 00000003261 14340204635 0020243 0 ustar 00root root 0000000 0000000 ---
title: "NS1"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: ns1
dnsprovider:
since: "v0.4.0"
code: "ns1"
url: "https://ns1.com"
---
Configuration for [NS1](https://ns1.com).
- Code: `ns1`
- Since: v0.4.0
Here is an example bash command using the NS1 provider:
```bash
NS1_API_KEY=xxxx \
lego --email you@example.com --dns ns1 --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `NS1_API_KEY` | API key |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## 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 |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://ns1.com/api)
- [Go client](https://github.com/ns1/ns1-go)
lego-4.9.1/docs/content/dns/zz_gen_oraclecloud.md 0000664 0000000 0000000 00000004543 14340204635 0022042 0 ustar 00root root 0000000 0000000 ---
title: "Oracle Cloud"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: oraclecloud
dnsprovider:
since: "v2.3.0"
code: "oraclecloud"
url: "https://cloud.oracle.com/home"
---
Configuration for [Oracle Cloud](https://cloud.oracle.com/home).
- Code: `oraclecloud`
- Since: v2.3.0
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 --email you@example.com --dns oraclecloud --domains my.example.org 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` | Tenancy OCID |
| `OCI_USER_OCID` | User OCID |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## 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 |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://docs.cloud.oracle.com/iaas/Content/DNS/Concepts/dnszonemanagement.htm)
- [Go client](https://github.com/oracle/oci-go-sdk)
lego-4.9.1/docs/content/dns/zz_gen_otc.md 0000664 0000000 0000000 00000003457 14340204635 0020336 0 ustar 00root root 0000000 0000000 ---
title: "Open Telekom Cloud"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: otc
dnsprovider:
since: "v0.4.1"
code: "otc"
url: "https://cloud.telekom.de/en"
---
Configuration for [Open Telekom Cloud](https://cloud.telekom.de/en).
- Code: `otc`
- Since: v0.4.1
{{% 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 |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## 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 |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://docs.otc.t-systems.com/en-us/dns/index.html)
lego-4.9.1/docs/content/dns/zz_gen_ovh.md 0000664 0000000 0000000 00000004640 14340204635 0020340 0 ustar 00root root 0000000 0000000 ---
title: "OVH"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: ovh
dnsprovider:
since: "v0.4.0"
code: "ovh"
url: "https://www.ovh.com/"
---
Configuration for [OVH](https://www.ovh.com/).
- Code: `ovh`
- Since: v0.4.0
Here is an example bash command using the OVH provider:
```bash
OVH_APPLICATION_KEY=1234567898765432 \
OVH_APPLICATION_SECRET=b9841238feb177a84330febba8a832089 \
OVH_CONSUMER_KEY=256vfsd347245sdfg \
OVH_ENDPOINT=ovh-eu \
lego --email you@example.com --dns ovh --domains my.example.org run
```
## 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) |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## 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 |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Application Key and Secret
Application key and secret can be created by following the [OVH guide](https://docs.ovh.com/gb/en/customer/first-steps-with-ovh-api/).
When requesting the consumer key, the following configuration can be use to define access rights:
```json
{
"accessRules": [
{
"method": "POST",
"path": "/domain/zone/*"
},
{
"method": "DELETE",
"path": "/domain/zone/*"
}
]
}
```
## More information
- [API documentation](https://eu.api.ovh.com/)
- [Go client](https://github.com/ovh/go-ovh)
lego-4.9.1/docs/content/dns/zz_gen_pdns.md 0000664 0000000 0000000 00000004734 14340204635 0020514 0 ustar 00root root 0000000 0000000 ---
title: "PowerDNS"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: pdns
dnsprovider:
since: "v0.4.0"
code: "pdns"
url: "https://www.powerdns.com/"
---
Configuration for [PowerDNS](https://www.powerdns.com/).
- Code: `pdns`
- Since: v0.4.0
Here is an example bash command using the PowerDNS provider:
```bash
PDNS_API_URL=http://pdns-server:80/ \
PDNS_API_KEY=xxxx \
lego --email you@example.com --dns pdns --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `PDNS_API_KEY` | API key |
| `PDNS_API_URL` | API URL |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## 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_SERVER_NAME` | Name of the server in the URL, 'localhost' by default |
| `PDNS_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## 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/)
lego-4.9.1/docs/content/dns/zz_gen_porkbun.md 0000664 0000000 0000000 00000003474 14340204635 0021230 0 ustar 00root root 0000000 0000000 ---
title: "Porkbun"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: porkbun
dnsprovider:
since: "v4.4.0"
code: "porkbun"
url: "https://porkbun.com/"
---
Configuration for [Porkbun](https://porkbun.com/).
- Code: `porkbun`
- Since: v4.4.0
Here is an example bash command using the Porkbun provider:
```bash
PORKBUN_SECRET_API_KEY=xxxxxx \
PORKBUN_API_KEY=yyyyyy \
lego --email you@example.com --dns porkbun --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `PORKBUN_API_KEY` | API key |
| `PORKBUN_SECRET_API_KEY` | secret API key |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `PORKBUN_HTTP_TIMEOUT` | API request timeout |
| `PORKBUN_POLLING_INTERVAL` | Time between DNS propagation check |
| `PORKBUN_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `PORKBUN_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://porkbun.com/api/json/v3/documentation)
lego-4.9.1/docs/content/dns/zz_gen_rackspace.md 0000664 0000000 0000000 00000003525 14340204635 0021501 0 ustar 00root root 0000000 0000000 ---
title: "Rackspace"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: rackspace
dnsprovider:
since: "v0.4.0"
code: "rackspace"
url: "https://www.rackspace.com/"
---
Configuration for [Rackspace](https://www.rackspace.com/).
- Code: `rackspace`
- Since: v0.4.0
Here is an example bash command using the Rackspace provider:
```bash
RACKSPACE_USER=xxxx \
RACKSPACE_API_KEY=yyyy \
lego --email you@example.com --dns rackspace --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `RACKSPACE_API_KEY` | API key |
| `RACKSPACE_USER` | API user |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## 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 |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://developer.rackspace.com/docs/cloud-dns/v1/)
lego-4.9.1/docs/content/dns/zz_gen_regru.md 0000664 0000000 0000000 00000003407 14340204635 0020670 0 ustar 00root root 0000000 0000000 ---
title: "reg.ru"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: regru
dnsprovider:
since: "v3.5.0"
code: "regru"
url: "https://www.reg.ru/"
---
Configuration for [reg.ru](https://www.reg.ru/).
- Code: `regru`
- Since: v3.5.0
Here is an example bash command using the reg.ru provider:
```bash
REGRU_USERNAME=xxxxxx \
REGRU_PASSWORD=yyyyyy \
lego --email you@example.com --dns regru --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `REGRU_PASSWORD` | API password |
| `REGRU_USERNAME` | API username |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `REGRU_HTTP_TIMEOUT` | API request timeout |
| `REGRU_POLLING_INTERVAL` | Time between DNS propagation check |
| `REGRU_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `REGRU_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://www.reg.ru/support/help/api2)
lego-4.9.1/docs/content/dns/zz_gen_rfc2136.md 0000664 0000000 0000000 00000005566 14340204635 0020642 0 ustar 00root root 0000000 0000000 ---
title: "RFC2136"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: rfc2136
dnsprovider:
since: "v0.3.0"
code: "rfc2136"
url: "https://www.rfc-editor.org/rfc/rfc2136.html"
---
Configuration for [RFC2136](https://www.rfc-editor.org/rfc/rfc2136.html).
- Code: `rfc2136`
- Since: v0.3.0
Here is an example bash command using the RFC2136 provider:
```bash
RFC2136_NAMESERVER=127.0.0.1 \
RFC2136_TSIG_KEY=lego \
RFC2136_TSIG_ALGORITHM=hmac-sha256. \
RFC2136_TSIG_SECRET=YWJjZGVmZGdoaWprbG1ub3BxcnN0dXZ3eHl6MTIzNDU= \
lego --email you@example.com --dns rfc2136 --domains my.example.org run
## ---
keyname=lego; keyfile=lego.key; tsig-keygen $keyname > $keyfile
RFC2136_NAMESERVER=127.0.0.1 \
RFC2136_TSIG_KEY="$keyname" \
RFC2136_TSIG_ALGORITHM="$( awk -F'[ ";]' '/algorithm/ { print $2 }' $keyfile )." \
RFC2136_TSIG_SECRET="$( awk -F'[ ";]' '/secret/ { print $3 }' $keyfile )" \
lego --email you@example.com --dns rfc2136 --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `RFC2136_NAMESERVER` | Network address in the form "host" or "host:port" |
| `RFC2136_TSIG_ALGORITHM` | TSIG algorithm. 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. |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## 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` | Time between sequential requests |
| `RFC2136_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://www.rfc-editor.org/rfc/rfc2136.html)
lego-4.9.1/docs/content/dns/zz_gen_rimuhosting.md 0000664 0000000 0000000 00000003526 14340204635 0022116 0 ustar 00root root 0000000 0000000 ---
title: "RimuHosting"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: rimuhosting
dnsprovider:
since: "v0.3.5"
code: "rimuhosting"
url: "https://rimuhosting.com"
---
Configuration for [RimuHosting](https://rimuhosting.com).
- Code: `rimuhosting`
- Since: v0.3.5
Here is an example bash command using the RimuHosting provider:
```bash
RIMUHOSTING_API_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx \
lego --email you@example.com --dns rimuhosting --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `RIMUHOSTING_API_KEY` | User API key |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `RIMUHOSTING_HTTP_TIMEOUT` | API request timeout |
| `RIMUHOSTING_POLLING_INTERVAL` | Time between DNS propagation check |
| `RIMUHOSTING_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `RIMUHOSTING_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://rimuhosting.com/dns/dyndns.jsp)
lego-4.9.1/docs/content/dns/zz_gen_route53.md 0000664 0000000 0000000 00000013741 14340204635 0021054 0 ustar 00root root 0000000 0000000 ---
title: "Amazon Route 53"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: route53
dnsprovider:
since: "v0.3.0"
code: "route53"
url: "https://aws.amazon.com/route53/"
---
Configuration for [Amazon Route 53](https://aws.amazon.com/route53/).
- Code: `route53`
- Since: v0.3.0
{{% notice note %}}
_Please contribute by adding a CLI example._
{{% /notice %}}
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `AWS_ACCESS_KEY_ID` | Managed by the AWS client. Access key ID (`AWS_ACCESS_KEY_ID_FILE` is not supported, use `AWS_SHARED_CREDENTIALS_FILE` instead) |
| `AWS_ASSUME_ROLE_ARN` | Managed by the AWS Role ARN (`AWS_ASSUME_ROLE_ARN` is not supported) |
| `AWS_HOSTED_ZONE_ID` | Override the hosted zone ID. |
| `AWS_PROFILE` | Managed by the AWS client (`AWS_PROFILE_FILE` is not supported) |
| `AWS_REGION` | Managed by the AWS client (`AWS_REGION_FILE` is not supported) |
| `AWS_SDK_LOAD_CONFIG` | Managed by the AWS client. Retrieve the region from the CLI config file (`AWS_SDK_LOAD_CONFIG_FILE` is not supported) |
| `AWS_SECRET_ACCESS_KEY` | Managed by the AWS client. Secret access key (`AWS_SECRET_ACCESS_KEY_FILE` is not supported, use `AWS_SHARED_CREDENTIALS_FILE` instead) |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `AWS_MAX_RETRIES` | The number of maximum returns the service will use to make an individual API request |
| `AWS_POLLING_INTERVAL` | Time between DNS propagation check |
| `AWS_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `AWS_SHARED_CREDENTIALS_FILE` | Managed by the AWS client. Shared credentials file. |
| `AWS_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## 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_SESSION_TOKEN`]
2. Shared credentials file (defaults to `~/.aws/credentials`, profiles can be specified using `AWS_PROFILE`)
3. Amazon EC2 IAM role
The AWS Region is automatically detected in the following locations and prioritized in the following order:
1. Environment variables: `AWS_REGION`
2. Shared configuration file if `AWS_SDK_LOAD_CONFIG` is set (defaults to `~/.aws/config`, profiles can be specified using `AWS_PROFILE`)
If `AWS_HOSTED_ZONE_ID` is not set, Lego tries to determine the correct public hosted zone via the FQDN.
See also:
- [sessions](https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/sessions.html)
- [Setting AWS Credentials](https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials)
- [Setting AWS Region](https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-the-region)
## IAM Policy Examples
### Broad privileges for testing purposes
The following [IAM policy](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html) document grants access to the required APIs needed by lego to complete the DNS challenge.
A word of caution:
These permissions grant write access to any DNS record in any hosted zone,
so it is recommended to narrow them down as much as possible if you are using this policy in production.
```json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"route53:GetChange",
"route53:ChangeResourceRecordSets",
"route53:ListResourceRecordSets"
],
"Resource": [
"arn:aws:route53:::hostedzone/*",
"arn:aws:route53:::change/*"
]
},
{
"Effect": "Allow",
"Action": "route53:ListHostedZonesByName",
"Resource": "*"
}
]
}
```
### Least privilege policy for production purposes
The following AWS IAM policy document describes least privilege permissions required for lego to complete the DNS challenge.
Write access is limited to a specified hosted zone's DNS TXT records with a key of `_acme-challenge.example.com`.
Replace `Z11111112222222333333` with your hosted zone ID and `example.com` with your domain name to use this policy.
```json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "route53:GetChange",
"Resource": "arn:aws:route53:::change/*"
},
{
"Effect": "Allow",
"Action": "route53:ListHostedZonesByName",
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"route53:ListResourceRecordSets"
],
"Resource": [
"arn:aws:route53:::hostedzone/Z11111112222222333333"
]
},
{
"Effect": "Allow",
"Action": [
"route53:ChangeResourceRecordSets"
],
"Resource": [
"arn:aws:route53:::hostedzone/Z11111112222222333333"
],
"Condition": {
"ForAllValues:StringEquals": {
"route53:ChangeResourceRecordSetsNormalizedRecordNames": [
"_acme-challenge.example.com"
],
"route53:ChangeResourceRecordSetsRecordTypes": [
"TXT"
]
}
}
}
]
}
```
## 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)
lego-4.9.1/docs/content/dns/zz_gen_safedns.md 0000664 0000000 0000000 00000003505 14340204635 0021166 0 ustar 00root root 0000000 0000000 ---
title: "UKFast SafeDNS"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: safedns
dnsprovider:
since: "v4.6.0"
code: "safedns"
url: "https://www.ukfast.co.uk/dns-hosting.html"
---
Configuration for [UKFast SafeDNS](https://www.ukfast.co.uk/dns-hosting.html).
- Code: `safedns`
- Since: v4.6.0
Here is an example bash command using the UKFast SafeDNS provider:
```bash
SAFEDNS_AUTH_TOKEN=xxxxxx \
lego --email you@example.com --dns safedns --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `SAFEDNS_AUTH_TOKEN` | Authentication token |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `SAFEDNS_HTTP_TIMEOUT` | API request timeout |
| `SAFEDNS_POLLING_INTERVAL` | Time between DNS propagation check |
| `SAFEDNS_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `SAFEDNS_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://developers.ukfast.io/documentation/safedns)
lego-4.9.1/docs/content/dns/zz_gen_sakuracloud.md 0000664 0000000 0000000 00000003753 14340204635 0022065 0 ustar 00root root 0000000 0000000 ---
title: "Sakura Cloud"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: sakuracloud
dnsprovider:
since: "v1.1.0"
code: "sakuracloud"
url: "https://cloud.sakura.ad.jp/"
---
Configuration for [Sakura Cloud](https://cloud.sakura.ad.jp/).
- Code: `sakuracloud`
- Since: v1.1.0
Here is an example bash command using the Sakura Cloud provider:
```bash
SAKURACLOUD_ACCESS_TOKEN=xxxxx \
SAKURACLOUD_ACCESS_TOKEN_SECRET=yyyyy \
lego --email you@example.com --dns sakuracloud --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `SAKURACLOUD_ACCESS_TOKEN` | Access token |
| `SAKURACLOUD_ACCESS_TOKEN_SECRET` | Access token secret |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `SAKURACLOUD_HTTP_TIMEOUT` | API request timeout |
| `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 |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://developer.sakura.ad.jp/cloud/api/1.1/)
- [Go client](https://github.com/sacloud/iaas-api-go)
lego-4.9.1/docs/content/dns/zz_gen_scaleway.md 0000664 0000000 0000000 00000003506 14340204635 0021354 0 ustar 00root root 0000000 0000000 ---
title: "Scaleway"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: scaleway
dnsprovider:
since: "v3.4.0"
code: "scaleway"
url: "https://developers.scaleway.com/"
---
Configuration for [Scaleway](https://developers.scaleway.com/).
- Code: `scaleway`
- Since: v3.4.0
Here is an example bash command using the Scaleway provider:
```bash
SCALEWAY_API_TOKEN=xxxxxxx-xxxxx-xxxx-xxx-xxxxxx \
lego --email you@example.com --dns scaleway --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `SCALEWAY_API_TOKEN` | API token |
| `SCALEWAY_PROJECT_ID` | Project to use (optional) |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `SCALEWAY_POLLING_INTERVAL` | Time between DNS propagation check |
| `SCALEWAY_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `SCALEWAY_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://developers.scaleway.com/en/products/domain/dns/api/)
lego-4.9.1/docs/content/dns/zz_gen_selectel.md 0000664 0000000 0000000 00000003457 14340204635 0021351 0 ustar 00root root 0000000 0000000 ---
title: "Selectel"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: selectel
dnsprovider:
since: "v1.2.0"
code: "selectel"
url: "https://kb.selectel.com/"
---
Configuration for [Selectel](https://kb.selectel.com/).
- Code: `selectel`
- Since: v1.2.0
Here is an example bash command using the Selectel provider:
```bash
SELECTEL_API_TOKEN=xxxxx \
lego --email you@example.com --dns selectel --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `SELECTEL_API_TOKEN` | API token |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## 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 |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://kb.selectel.com/23136054.html)
lego-4.9.1/docs/content/dns/zz_gen_servercow.md 0000664 0000000 0000000 00000003615 14340204635 0021564 0 ustar 00root root 0000000 0000000 ---
title: "Servercow"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: servercow
dnsprovider:
since: "v3.4.0"
code: "servercow"
url: "https://servercow.de/"
---
Configuration for [Servercow](https://servercow.de/).
- Code: `servercow`
- Since: v3.4.0
Here is an example bash command using the Servercow provider:
```bash
SERVERCOW_USERNAME=xxxxxxxx \
SERVERCOW_PASSWORD=xxxxxxxx \
lego --email you@example.com --dns servercow --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `SERVERCOW_PASSWORD` | API password |
| `SERVERCOW_USERNAME` | API username |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `SERVERCOW_HTTP_TIMEOUT` | API request timeout |
| `SERVERCOW_POLLING_INTERVAL` | Time between DNS propagation check |
| `SERVERCOW_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `SERVERCOW_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://cp.servercow.de/client/plugin/support_manager/knowledgebase/view/34/dns-api-v1/7/)
lego-4.9.1/docs/content/dns/zz_gen_simply.md 0000664 0000000 0000000 00000003501 14340204635 0021054 0 ustar 00root root 0000000 0000000 ---
title: "Simply.com"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: simply
dnsprovider:
since: "v4.4.0"
code: "simply"
url: "https://www.simply.com/en/domains/"
---
Configuration for [Simply.com](https://www.simply.com/en/domains/).
- Code: `simply`
- Since: v4.4.0
Here is an example bash command using the Simply.com provider:
```bash
SIMPLY_ACCOUNT_NAME=xxxxxx \
SIMPLY_API_KEY=yyyyyy \
lego --email you@example.com --dns simply --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `SIMPLY_ACCOUNT_NAME` | Account name |
| `SIMPLY_API_KEY` | API key |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `SIMPLY_HTTP_TIMEOUT` | API request timeout |
| `SIMPLY_POLLING_INTERVAL` | Time between DNS propagation check |
| `SIMPLY_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `SIMPLY_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://www.simply.com/en/docs/api/)
lego-4.9.1/docs/content/dns/zz_gen_sonic.md 0000664 0000000 0000000 00000005017 14340204635 0020656 0 ustar 00root root 0000000 0000000 ---
title: "Sonic"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: sonic
dnsprovider:
since: "v4.4.0"
code: "sonic"
url: "https://www.sonic.com/"
---
Configuration for [Sonic](https://www.sonic.com/).
- Code: `sonic`
- Since: v4.4.0
Here is an example bash command using the Sonic provider:
```bash
SONIC_USER_ID=12345 \
SONIC_API_KEY=4d6fbf2f9ab0fa11697470918d37625851fc0c51 \
lego --email you@example.com --dns sonic --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `SONIC_API_KEY` | API Key |
| `SONIC_USER_ID` | User ID |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `SONIC_HTTP_TIMEOUT` | API request timeout |
| `SONIC_POLLING_INTERVAL` | Time between DNS propagation check |
| `SONIC_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `SONIC_SEQUENCE_INTERVAL` | Time between sequential requests |
| `SONIC_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## API keys
The API keys must be generated by calling the `dyndns/api_key` endpoint.
Example:
```bash
$ curl -X POST -H "Content-Type: application/json" --data '{"username":"notarealuser","password":"notarealpassword","hostname":"example.com"}' https://public-api.sonic.net/dyndns/api_key
{"userid":"12345","apikey":"4d6fbf2f9ab0fa11697470918d37625851fc0c51","result":200,"message":"OK"}
```
See https://public-api.sonic.net/dyndns/#requesting_an_api_key for additional details.
This `userid` and `apikey` combo allow modifications to any DNS entries connected to the managed domain (hostname).
Hostname should be the toplevel domain managed e.g `example.com` not `www.example.com`.
## More information
- [API documentation](https://public-api.sonic.net/dyndns/)
lego-4.9.1/docs/content/dns/zz_gen_stackpath.md 0000664 0000000 0000000 00000003602 14340204635 0021523 0 ustar 00root root 0000000 0000000 ---
title: "Stackpath"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: stackpath
dnsprovider:
since: "v1.1.0"
code: "stackpath"
url: "https://www.stackpath.com/"
---
Configuration for [Stackpath](https://www.stackpath.com/).
- Code: `stackpath`
- Since: v1.1.0
Here is an example bash command using the Stackpath provider:
```bash
STACKPATH_CLIENT_ID=xxxxx \
STACKPATH_CLIENT_SECRET=yyyyy \
STACKPATH_STACK_ID=zzzzz \
lego --email you@example.com --dns stackpath --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `STACKPATH_CLIENT_ID` | Client ID |
| `STACKPATH_CLIENT_SECRET` | Client secret |
| `STACKPATH_STACK_ID` | Stack ID |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## 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 |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://developer.stackpath.com/en/api/dns/#tag/Zone)
lego-4.9.1/docs/content/dns/zz_gen_tencentcloud.md 0000664 0000000 0000000 00000004216 14340204635 0022232 0 ustar 00root root 0000000 0000000 ---
title: "Tencent Cloud DNS"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: tencentcloud
dnsprovider:
since: "v4.6.0"
code: "tencentcloud"
url: "https://cloud.tencent.com/product/cns"
---
Configuration for [Tencent Cloud DNS](https://cloud.tencent.com/product/cns).
- Code: `tencentcloud`
- Since: v4.6.0
Here is an example bash command using the Tencent Cloud DNS provider:
```bash
TENCENTCLOUD_SECRET_ID=abcdefghijklmnopqrstuvwx \
TENCENTCLOUD_SECRET_KEY=your-secret-key \
lego --email you@example.com --dns tencentcloud --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `TENCENTCLOUD_SECRET_ID` | Access key ID |
| `TENCENTCLOUD_SECRET_KEY` | Access Key secret |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `TENCENTCLOUD_HTTP_TIMEOUT` | API request timeout |
| `TENCENTCLOUD_POLLING_INTERVAL` | Time between DNS propagation check |
| `TENCENTCLOUD_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `TENCENTCLOUD_REGION` | Region |
| `TENCENTCLOUD_SESSION_TOKEN` | Access Key token |
| `TENCENTCLOUD_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://cloud.tencent.com/document/product/1427/56153)
- [Go client](https://github.com/tencentcloud/tencentcloud-sdk-go)
lego-4.9.1/docs/content/dns/zz_gen_transip.md 0000664 0000000 0000000 00000003545 14340204635 0021227 0 ustar 00root root 0000000 0000000 ---
title: "TransIP"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: transip
dnsprovider:
since: "v2.0.0"
code: "transip"
url: "https://www.transip.nl/"
---
Configuration for [TransIP](https://www.transip.nl/).
- Code: `transip`
- Since: v2.0.0
Here is an example bash command using the TransIP provider:
```bash
TRANSIP_ACCOUNT_NAME = "Account name" \
TRANSIP_PRIVATE_KEY_PATH = "transip.key" \
lego --email you@example.com --dns transip --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `TRANSIP_ACCOUNT_NAME` | Account name |
| `TRANSIP_PRIVATE_KEY_PATH` | Private key path |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## 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 |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://api.transip.eu/rest/docs.html)
- [Go client](https://github.com/transip/gotransip)
lego-4.9.1/docs/content/dns/zz_gen_variomedia.md 0000664 0000000 0000000 00000003563 14340204635 0021667 0 ustar 00root root 0000000 0000000 ---
title: "Variomedia"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: variomedia
dnsprovider:
since: "v4.8.0"
code: "variomedia"
url: "https://www.variomedia.de/"
---
Configuration for [Variomedia](https://www.variomedia.de/).
- Code: `variomedia`
- Since: v4.8.0
Here is an example bash command using the Variomedia provider:
```bash
VARIOMEDIA_API_TOKEN=xxxx \
lego --email you@example.com --dns variomedia --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `VARIOMEDIA_API_TOKEN` | API token |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `DODE_SEQUENCE_INTERVAL` | Time between sequential requests |
| `VARIOMEDIA_HTTP_TIMEOUT` | API request timeout |
| `VARIOMEDIA_POLLING_INTERVAL` | Time between DNS propagation check |
| `VARIOMEDIA_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `VARIOMEDIA_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://api.variomedia.de/docs/dns-records.html)
lego-4.9.1/docs/content/dns/zz_gen_vegadns.md 0000664 0000000 0000000 00000003405 14340204635 0021171 0 ustar 00root root 0000000 0000000 ---
title: "VegaDNS"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: vegadns
dnsprovider:
since: "v1.1.0"
code: "vegadns"
url: "https://github.com/shupp/VegaDNS-API"
---
Configuration for [VegaDNS](https://github.com/shupp/VegaDNS-API).
- Code: `vegadns`
- Since: v1.1.0
{{% 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 |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## 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 |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://github.com/shupp/VegaDNS-API)
- [Go client](https://github.com/OpenDNS/vegadns2client)
lego-4.9.1/docs/content/dns/zz_gen_vercel.md 0000664 0000000 0000000 00000003456 14340204635 0021030 0 ustar 00root root 0000000 0000000 ---
title: "Vercel"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: vercel
dnsprovider:
since: "v4.7.0"
code: "vercel"
url: "https://vercel.com"
---
Configuration for [Vercel](https://vercel.com).
- Code: `vercel`
- Since: v4.7.0
Here is an example bash command using the Vercel provider:
```bash
VERCEL_API_TOKEN=xxxxxx \
lego --email you@example.com --dns vercel --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `VERCEL_API_TOKEN` | Authentication token |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `VERCEL_HTTP_TIMEOUT` | API request timeout |
| `VERCEL_POLLING_INTERVAL` | Time between DNS propagation check |
| `VERCEL_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `VERCEL_TEAM_ID` | Team ID (ex: team_xxxxxxxxxxxxxxxxxxxxxxxx) |
| `VERCEL_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://vercel.com/docs/rest-api#endpoints/dns)
lego-4.9.1/docs/content/dns/zz_gen_versio.md 0000664 0000000 0000000 00000004155 14340204635 0021054 0 ustar 00root root 0000000 0000000 ---
title: "Versio.[nl|eu|uk]"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: versio
dnsprovider:
since: "v2.7.0"
code: "versio"
url: "https://www.versio.nl/domeinnamen"
---
Configuration for [Versio.[nl|eu|uk]](https://www.versio.nl/domeinnamen).
- Code: `versio`
- Since: v2.7.0
Here is an example bash command using the Versio.[nl|eu|uk] provider:
```bash
VERSIO_USERNAME= \
VERSIO_PASSWORD= \
lego --email you@example.com --dns versio --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `VERSIO_PASSWORD` | Basic authentication password |
| `VERSIO_USERNAME` | Basic authentication username |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `VERSIO_ENDPOINT` | The endpoint URL of the API Server |
| `VERSIO_HTTP_TIMEOUT` | API request timeout |
| `VERSIO_POLLING_INTERVAL` | Time between DNS propagation check |
| `VERSIO_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `VERSIO_SEQUENCE_INTERVAL` | Time between sequential requests, default 60s |
| `VERSIO_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
To test with the sandbox environment set ```VERSIO_ENDPOINT=https://www.versio.nl/testapi/v1/```
## More information
- [API documentation](https://www.versio.nl/RESTapidoc/)
lego-4.9.1/docs/content/dns/zz_gen_vinyldns.md 0000664 0000000 0000000 00000004216 14340204635 0021411 0 ustar 00root root 0000000 0000000 ---
title: "VinylDNS"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: vinyldns
dnsprovider:
since: "v4.4.0"
code: "vinyldns"
url: "https://www.vinyldns.io"
---
Configuration for [VinylDNS](https://www.vinyldns.io).
- Code: `vinyldns`
- Since: v4.4.0
Here is an example bash command using the VinylDNS provider:
```bash
VINYLDNS_ACCESS_KEY=xxxxxx \
VINYLDNS_SECRET_KEY=yyyyy \
VINYLDNS_HOST=https://api.vinyldns.example.org:9443 \
lego --email you@example.com --dns vinyldns --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `VINYLDNS_ACCESS_KEY` | The VinylDNS API key |
| `VINYLDNS_HOST` | The VinylDNS API URL |
| `VINYLDNS_SECRET_KEY` | The VinylDNS API Secret key |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `VINYLDNS_POLLING_INTERVAL` | Time between DNS propagation check |
| `VINYLDNS_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `VINYLDNS_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
The vinyldns integration makes use of dotted hostnames to ease permission management.
Users are required to have DELETE ACL level or zone admin permissions on the VinylDNS zone containing the target host.
## More information
- [API documentation](https://www.vinyldns.io/api/)
- [Go client](https://github.com/vinyldns/go-vinyldns)
lego-4.9.1/docs/content/dns/zz_gen_vkcloud.md 0000664 0000000 0000000 00000005523 14340204635 0021214 0 ustar 00root root 0000000 0000000 ---
title: "VK Cloud"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: vkcloud
dnsprovider:
since: "v4.9.0"
code: "vkcloud"
url: "https://mcs.mail.ru/"
---
Configuration for [VK Cloud](https://mcs.mail.ru/).
- Code: `vkcloud`
- Since: v4.9.0
Here is an example bash command using the VK Cloud provider:
```bash
VK_CLOUD_PROJECT_ID="" \
VK_CLOUD_USERNAME="" \
VK_CLOUD_PASSWORD="" \
lego --email you@example.com --dns vkcloud --domains "example.org" --domains "*.example.org" run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `VK_CLOUD_PASSWORD` | Password for VK Cloud account |
| `VK_CLOUD_PROJECT_ID` | String ID of project in VK Cloud |
| `VK_CLOUD_USERNAME` | Email of VK Cloud account |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `VK_CLOUD_DNS_ENDPOINT` | URL of DNS API. Defaults to https://mcs.mail.ru/public-dns but can be changed for usage with private clouds |
| `VK_CLOUD_DOMAIN_NAME` | Openstack users domain name. Defaults to `users` but can be changed for usage with private clouds |
| `VK_CLOUD_IDENTITY_ENDPOINT` | URL of OpenStack Auth API, Defaults to https://infra.mail.ru:35357/v3/ but can be changed for usage with private clouds |
| `VK_CLOUD_POLLING_INTERVAL` | Time between DNS propagation check |
| `VK_CLOUD_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `VK_CLOUD_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Credential inforamtion
You can find all required and additional information on ["Project/Keys" page](https://mcs.mail.ru/app/en/project/keys) of your cloud.
| ENV Variable | Parameter from page |
|----------------------------|---------------------|
| VK_CLOUD_PROJECT_ID | Project ID |
| VK_CLOUD_USERNAME | Username |
| VK_CLOUD_DOMAIN_NAME | User Domain Name |
| VK_CLOUD_IDENTITY_ENDPOINT | Identity endpoint |
## More information
- [API documentation](https://mcs.mail.ru/docs/networks/vnet/networks/publicdns/api)
lego-4.9.1/docs/content/dns/zz_gen_vscale.md 0000664 0000000 0000000 00000003440 14340204635 0021016 0 ustar 00root root 0000000 0000000 ---
title: "Vscale"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: vscale
dnsprovider:
since: "v2.0.0"
code: "vscale"
url: "https://vscale.io/"
---
Configuration for [Vscale](https://vscale.io/).
- Code: `vscale`
- Since: v2.0.0
Here is an example bash command using the Vscale provider:
```bash
VSCALE_API_TOKEN=xxxxx \
lego --email you@example.com --dns vscale --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `VSCALE_API_TOKEN` | API token |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `VSCALE_BASE_URL` | API endpoint 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 |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://developers.vscale.io/documentation/api/v1/#api-Domains_Records)
lego-4.9.1/docs/content/dns/zz_gen_vultr.md 0000664 0000000 0000000 00000003360 14340204635 0020716 0 ustar 00root root 0000000 0000000 ---
title: "Vultr"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: vultr
dnsprovider:
since: "v0.3.1"
code: "vultr"
url: "https://www.vultr.com/"
---
Configuration for [Vultr](https://www.vultr.com/).
- Code: `vultr`
- Since: v0.3.1
Here is an example bash command using the Vultr provider:
```bash
VULTR_API_KEY=xxxxx \
lego --email you@example.com --dns vultr --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `VULTR_API_KEY` | API key |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## 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 |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://www.vultr.com/api/#dns)
- [Go client](https://github.com/vultr/govultr)
lego-4.9.1/docs/content/dns/zz_gen_wedos.md 0000664 0000000 0000000 00000003610 14340204635 0020661 0 ustar 00root root 0000000 0000000 ---
title: "WEDOS"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: wedos
dnsprovider:
since: "v4.4.0"
code: "wedos"
url: "https://www.wedos.com"
---
Configuration for [WEDOS](https://www.wedos.com).
- Code: `wedos`
- Since: v4.4.0
Here is an example bash command using the WEDOS provider:
```bash
WEDOS_USERNAME=xxxxxxxx \
WEDOS_WAPI_PASSWORD=xxxxxxxx \
lego --email you@example.com --dns wedos --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `WEDOS_USERNAME` | Username is the same as for the admin account |
| `WEDOS_WAPI_PASSWORD` | Password needs to be generated and IP allowed in the admin interface |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `WEDOS_HTTP_TIMEOUT` | API request timeout |
| `WEDOS_POLLING_INTERVAL` | Time between DNS propagation check |
| `WEDOS_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `WEDOS_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://kb.wedos.com/en/kategorie/wapi-api-interface/wdns-en/)
lego-4.9.1/docs/content/dns/zz_gen_yandex.md 0000664 0000000 0000000 00000003433 14340204635 0021033 0 ustar 00root root 0000000 0000000 ---
title: "Yandex PDD"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: yandex
dnsprovider:
since: "v3.7.0"
code: "yandex"
url: "https://pdd.yandex.com"
---
Configuration for [Yandex PDD](https://pdd.yandex.com).
- Code: `yandex`
- Since: v3.7.0
Here is an example bash command using the Yandex PDD provider:
```bash
YANDEX_PDD_TOKEN= \
lego --email you@example.com --dns yandex --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `YANDEX_PDD_TOKEN` | Basic authentication username |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `YANDEX_HTTP_TIMEOUT` | API request timeout |
| `YANDEX_POLLING_INTERVAL` | Time between DNS propagation check |
| `YANDEX_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `YANDEX_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://yandex.com/dev/domain/doc/concepts/api-dns.html)
lego-4.9.1/docs/content/dns/zz_gen_yandexcloud.md 0000664 0000000 0000000 00000005511 14340204635 0022061 0 ustar 00root root 0000000 0000000 ---
title: "Yandex Cloud"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: yandexcloud
dnsprovider:
since: "v4.9.0"
code: "yandexcloud"
url: "https://cloud.yandex.com"
---
Configuration for [Yandex Cloud](https://cloud.yandex.com).
- Code: `yandexcloud`
- Since: v4.9.0
Here is an example bash command using the Yandex Cloud provider:
```bash
YANDEX_CLOUD_IAM_TOKEN= \
YANDEX_CLOUD_FOLDER_ID= \
lego --email you@example.com --dns yandexcloud --domains "example.org" --domains "*.example.org" run
# ---
YANDEX_CLOUD_IAM_TOKEN=$(echo '{ \
"id": "", \
"service_account_id": "", \
"created_at": "", \
"key_algorithm": "RSA_2048", \
"public_key": "-----BEGIN PUBLIC KEY----------END PUBLIC KEY-----", \
"private_key": "-----BEGIN PRIVATE KEY----------END PRIVATE KEY-----" \
}' | base64) \
YANDEX_CLOUD_FOLDER_ID= \
lego --email you@example.com --dns yandexcloud --domains "example.org" --domains "*.example.org" run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `YANDEX_CLOUD_FOLDER_ID` | The string id of folder (aka project) in Yandex Cloud |
| `YANDEX_CLOUD_IAM_TOKEN` | The base64 encoded json which contains inforamtion about iam token of serivce account with `dns.admin` permissions |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `YANDEX_CLOUD_POLLING_INTERVAL` | Time between DNS propagation check |
| `YANDEX_CLOUD_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `YANDEX_CLOUD_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## IAM Token
The simplest way to retrieve IAM access token is usage of yc-cli,
follow [docs](https://cloud.yandex.ru/docs/iam/operations/iam-token/create-for-sa) to get it
```bash
yc iam key create --service-account-name my-robot --output key.json
cat key.json | base64
```
## More information
- [API documentation](https://cloud.yandex.com/en/docs/dns/quickstart)
lego-4.9.1/docs/content/dns/zz_gen_zoneee.md 0000664 0000000 0000000 00000003452 14340204635 0021031 0 ustar 00root root 0000000 0000000 ---
title: "Zone.ee"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: zoneee
dnsprovider:
since: "v2.1.0"
code: "zoneee"
url: "https://www.zone.ee/"
---
Configuration for [Zone.ee](https://www.zone.ee/).
- Code: `zoneee`
- Since: v2.1.0
Here is an example bash command using the Zone.ee provider:
```bash
ZONEEE_API_USER=xxxxx \
ZONEEE_API_KEY=yyyyy \
lego --email you@example.com --dns zoneee --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `ZONEEE_API_KEY` | API key |
| `ZONEEE_API_USER` | API user |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## 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 |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://api.zone.eu/v2)
lego-4.9.1/docs/content/dns/zz_gen_zonomi.md 0000664 0000000 0000000 00000003366 14340204635 0021063 0 ustar 00root root 0000000 0000000 ---
title: "Zonomi"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: zonomi
dnsprovider:
since: "v3.5.0"
code: "zonomi"
url: "https://zonomi.com"
---
Configuration for [Zonomi](https://zonomi.com).
- Code: `zonomi`
- Since: v3.5.0
Here is an example bash command using the Zonomi provider:
```bash
ZONOMI_API_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx \
lego --email you@example.com --dns zonomi --domains my.example.org run
```
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `ZONOMI_API_KEY` | User API key |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `ZONOMI_HTTP_TIMEOUT` | API request timeout |
| `ZONOMI_POLLING_INTERVAL` | Time between DNS propagation check |
| `ZONOMI_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `ZONOMI_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
## More information
- [API documentation](https://zonomi.com/app/dns/dyndns.jsp)
lego-4.9.1/docs/content/installation/ 0000775 0000000 0000000 00000000000 14340204635 0017557 5 ustar 00root root 0000000 0000000 lego-4.9.1/docs/content/installation/_index.md 0000664 0000000 0000000 00000002026 14340204635 0021347 0 ustar 00root root 0000000 0000000 ---
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/go-acme/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 goacme/lego -h
```
## From package managers
- [ArchLinux (AUR)](https://aur.archlinux.org/packages/lego) (official):
```bash
yay -S lego
```
- [FreeBSD (Ports)](https://www.freshports.org/security/lego) (unofficial):
```bash
cd /usr/ports/security/lego && make install clean
```
or
```bash
pkg install lego
```
## From sources
Requirements:
- go1.17+
- environment variable: `GO111MODULE=on`
To install the latest version from sources, just run:
```bash
go install github.com/go-acme/lego/v4/cmd/lego@latest
```
or
```bash
git clone git@github.com:go-acme/lego.git
cd lego
make # tests + doc + build
make build # only build
```
lego-4.9.1/docs/content/usage/ 0000775 0000000 0000000 00000000000 14340204635 0016162 5 ustar 00root root 0000000 0000000 lego-4.9.1/docs/content/usage/_index.md 0000664 0000000 0000000 00000000176 14340204635 0017756 0 ustar 00root root 0000000 0000000 ---
title: "Usage"
date: 2019-03-03T16:39:46+01:00
draft: false
weight: 2
---
{{% children style="h2" description="true" %}}
lego-4.9.1/docs/content/usage/cli/ 0000775 0000000 0000000 00000000000 14340204635 0016731 5 ustar 00root root 0000000 0000000 lego-4.9.1/docs/content/usage/cli/General-Instructions.md 0000664 0000000 0000000 00000002111 14340204635 0023325 0 ustar 00root root 0000000 0000000 ---
title: General Instructions
date: 2019-03-03T16:39:46+01:00
draft: false
summary: Read this first to clarify some assumptions made by the following guides.
weight: 1
---
These examples assume you have [lego installed]({{< ref "installation" >}}).
You can get a pre-built binary from the [releases](https://github.com/go-acme/lego/releases) page.
The web server examples require that the `lego` binary has permission to bind to ports 80 and 443.
If your environment does not allow you to bind to these ports, please read [Running without root privileges]({{< ref "usage/cli/Options#running-without-root-privileges" >}}) and [Port Usage]({{< ref "usage/cli/Options#port-usage" >}}).
Unless otherwise instructed with the `--path` command line flag, lego will look for a directory named `.lego` in the *current working directory*.
If you run `cd /dir/a && lego ... run`, lego will create a directory `/dir/a/.lego` where it will save account registration and certificate files into.
If you later try to renew a certificate with `cd /dir/b && lego ... renew`, lego will likely produce an error.
lego-4.9.1/docs/content/usage/cli/Obtain-a-Certificate.md 0000664 0000000 0000000 00000011340 14340204635 0023124 0 ustar 00root root 0000000 0000000 ---
title: Obtain a Certificate
date: 2019-03-03T16:39:46+01:00
draft: false
weight: 2
---
This guide explains various ways to obtain a new certificate.
## Using the built-in web server
Open a terminal, and execute the following command (insert your own email address and domain):
```bash
lego --email="you@example.com" --domains="example.com" --http run
```
You will find your certificate in the `.lego` folder of the current working directory:
```console
$ ls -1 ./.lego/certificates
example.com.crt
example.com.issuer.crt
example.com.json
example.com.key
[maybe more files for different domains...]
```
where
- `example.com.crt` is the server certificate (including the CA certificate),
- `example.com.key` is the private key needed for the server certificate,
- `example.com.issuer.crt` is the CA certificate, and
- `example.com.json` contains some JSON encoded meta information.
For each domain, you will have a set of these four files.
For wildcard certificates (`*.example.com`), the filenames will look like `_.example.com.crt`.
The `.crt` and `.key` files are PEM-encoded x509 certificates and private keys.
If you're looking for a `cert.pem` and `privkey.pem`, you can just use `example.com.crt` and `example.com.key`.
## Using a DNS provider
If you can't or don't want to start a web server, you need to use a DNS provider.
lego comes with [support for many]({{< ref "dns#dns-providers" >}}) providers,
and you need to pick the one where your domain's DNS settings are set up.
Typically, this is the registrar where you bought the domain, but in some cases this can be another third-party provider.
For this example, let's assume you have setup CloudFlare for your domain.
Execute this command:
```bash
CLOUDFLARE_EMAIL="you@example.com" \
CLOUDFLARE_API_KEY="yourprivatecloudflareapikey" \
lego --email "you@example.com" --dns cloudflare --domains "example.org" run
```
## Using a custom certificate signing request (CSR)
The first step in the process of obtaining certificates involves creating a signing request.
This CSR bundles various information, including the domain name(s) and a public key.
By default, lego will hide this step from you, but if you already have a CSR, you can easily reuse it:
```bash
lego --email="you@example.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.
## Using an existing, running web server
If you have an existing server running on port 80, the `--http` option also requires the `--http.webroot` option.
This just writes the http-01 challenge token to the given directory in the folder `.well-known/acme-challenge` and does not start a server.
The given directory **should** be publicly served as `/` on the domain(s) for the validation to complete.
If the given directory is not publicly served you will have to support rewriting the request to the directory;
You could also implement a rewrite to rewrite `.well-known/acme-challenge` to the given directory `.well-known/acme-challenge`.
You should be able to run an existing webserver on port 80 and have lego write the token file with the HTTP-01 challenge key authorization to `/.well-known/acme-challenge/` by running something like:
```bash
lego --accept-tos --email you@example.com --http --http.webroot /path/to/webroot --domains example.com run
```
## Running a script afterward
You can easily hook into the certificate-obtaining process by providing the path to a script:
```bash
lego --email="you@example.com" --domains="example.com" --http run --run-hook="./myscript.sh"
```
Some information is provided through environment variables:
- `LEGO_ACCOUNT_EMAIL`: the email of the account.
- `LEGO_CERT_DOMAIN`: the main domain of the certificate.
- `LEGO_CERT_PATH`: the path of the certificate.
- `LEGO_CERT_KEY_PATH`: the path of the certificate key.
### Use case
A typical use case is distribute the certificate for other services and reload them if necessary.
Since PEM-formatted TLS certificates are understood by many programs, it is relatively simple to use certificates for more than a web server.
This example script installs the new certificate for a mail server, and reloads it.
Beware: this is just a starting point, error checking is omitted for brevity.
```bash
#!/bin/bash
# copy certificates to a directory controlled by Postfix
postfix_cert_dir="/etc/postfix/certificates"
# our Postfix server only handles mail for @example.com domain
if [ "$LEGO_CERT_DOMAIN" = "example.com" ]; then
install -u postfix -g postfix -m 0644 "$LEGO_CERT_PATH" "$postfix_cert_dir"
install -u postfix -g postfix -m 0640 "$LEGO_KEY_PATH" "$postfix_cert_dir"
systemctl reload postfix@-service
fi
```
lego-4.9.1/docs/content/usage/cli/Options.md 0000664 0000000 0000000 00000022251 14340204635 0020710 0 ustar 00root root 0000000 0000000 ---
title: "Options"
date: 2019-03-03T16:39:46+01:00
draft: false
summary: This page describes various command line options.
weight: 4
---
## Usage
{{< tabs >}}
{{% tab name="lego --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. (default: false)
--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. (default: false)
--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: "ec256")
--filename value (deprecated) Filename of the generated certificate.
--path value Directory to use for storing the data. (default: "./.lego") [$LEGO_PATH]
--http Use the HTTP challenge to solve challenges. Can be mixed with other types of challenges. (default: false)
--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.proxy-header value Validate against this HTTP header when solving HTTP based challenges behind a reverse proxy. (default: "Host")
--http.webroot value Set the webroot folder to use for HTTP based challenges to write directly in a file in .well-known/acme-challenge. This disables the built-in server and expects the given directory to be publicly served with access to .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. (default: false)
--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. (default: false)
--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. (default: false)
--pfx Generate a .pfx (PKCS#12) file by with the .key and .crt and issuer .crt files together. (default: false)
--pfx.pass value The password used to encrypt the .pfx (PCKS#12) file. (default: "changeit")
--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 (default: false)
--version, -v print the version (default: false)
```
{{% /tab %}}
{{% tab name="lego run --help" %}}
```slim
NAME:
lego run - Register an account, then create and install a certificate
USAGE:
lego run [command options] [arguments...]
OPTIONS:
--no-bundle Do not create a certificate bundle by adding the issuers certificate to the new certificate. (default: false)
--must-staple Include the OCSP must staple TLS extension in the CSR and generated certificate. Only works if the CSR is generated by lego. (default: false)
--run-hook value Define a hook. The hook is executed when the certificates are effectively created.
--preferred-chain value If the CA offers multiple certificate chains, prefer the chain with an issuer matching this Subject Common Name. If no match, the default offered chain will be used.
--always-deactivate-authorizations value Force the authorizations to be relinquished even if the certificate request was successful.
```
{{% /tab %}}
{{% tab name="lego renew --help" %}}
```slim
NAME:
lego renew - Renew a certificate
USAGE:
lego renew [command options] [arguments...]
OPTIONS:
--days value The number of days left on a certificate to renew it. (default: 30)
--reuse-key Used to indicate you want to reuse your current private key for the new certificate. (default: false)
--no-bundle Do not create a certificate bundle by adding the issuers certificate to the new certificate. (default: false)
--must-staple Include the OCSP must staple TLS extension in the CSR and generated certificate. Only works if the CSR is generated by lego. (default: false)
--renew-hook value Define a hook. The hook is executed only when the certificates are effectively renewed.
--preferred-chain value If the CA offers multiple certificate chains, prefer the chain with an issuer matching this Subject Common Name. If no match, the default offered chain will be used.
--always-deactivate-authorizations value Force the authorizations to be relinquished even if the certificate request was successful.
--no-random-sleep Do not add a random sleep before the renewal. We do not recommend using this flag if you are doing your renewals in an automated way. (default: false)
```
{{% /tab %}}
{{< /tabs >}}
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 …
```
## Running without root privileges
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/lego` (Linux only)
- 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.[^header]
**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.
[^header]: You must ensure that incoming validation requests contains the correct value for the HTTP `Host` header. If you operate lego behind a non-transparent reverse proxy (such as Apache or NGINX), you might need to alter the header field using `--http.proxy-header X-Forwarded-Host`.
lego-4.9.1/docs/content/usage/cli/Renew-a-Certificate.md 0000664 0000000 0000000 00000007251 14340204635 0022776 0 ustar 00root root 0000000 0000000 ---
title: Renew a Certificate
date: 2019-03-03T16:39:46+01:00
draft: false
weight: 3
---
This guide describes how to renew existing certificates.
Certificates issues by Let's Encrypt are valid for a period of 90 days.
To avoid certificate errors, you need to ensure that you renew your certificate *before* it expires.
In order to renew a certificate, follow the general instructions laid out under [Obtain a Certificate]({{< ref "usage/cli/Obtain-a-Certificate" >}}), and replace `lego ... run` with `lego ... renew`.
Note that the `renew` sub-command supports a slightly different set of some command line flags.
## Using the built-in web server
By default, and following best practices, a certificate is only renewed if its expiry date is less than 30 days in the future.
```bash
lego --email="you@example.com" --domains="example.com" --http renew
```
If the certificate needs to renewed earlier, you can specify the number of remaining days:
```bash
lego --email="you@example.com" --domains="example.com" --http renew --days 45
```
## Using a DNS provider
If you can't or don't want to start a web server, you need to use a DNS provider.
lego comes with [support for many]({{< ref "dns#dns-providers" >}}) providers,
and you need to pick the one where your domain's DNS settings are set up.
Typically, this is the registrar where you bought the domain, but in some cases this can be another third-party provider.
For this example, let's assume you have setup CloudFlare for your domain.
Execute this command:
```bash
CLOUDFLARE_EMAIL="you@example.com" \
CLOUDFLARE_API_KEY="yourprivatecloudflareapikey" \
lego --email "you@example.com" --dns cloudflare --domains "example.org" renew
```
## Running a script afterward
You can easily hook into the certificate-obtaining process by providing the path to a script.
The hook is executed only when the certificates are effectively renewed.
```bash
lego --email="you@example.com" --domains="example.com" --http renew --renew-hook="./myscript.sh"
```
Some information is provided through environment variables:
- `LEGO_ACCOUNT_EMAIL`: the email of the account.
- `LEGO_CERT_DOMAIN`: the main domain of the certificate.
- `LEGO_CERT_PATH`: the path of the certificate.
- `LEGO_CERT_KEY_PATH`: the path of the certificate key.
See [Obtain a Certificate → Use case]({{< ref "usage/cli/Obtain-a-Certificate#use-case" >}}) for an example script.
## Automatic renewal
It is tempting to create a cron job (or systemd timer) to automatically renew all you certificates.
When doing so, please note that some cron defaults will cause measurable load on the ACME provider's infrastructure.
Notably `@daily` jobs run at midnight.
To both counteract load spikes (caused by all lego users) and reduce subsequent renewal failures, we were asked to implement a small random delay for non-interactive renewals.[^loadspikes]
Since v4.8.0, lego will pause for up to 8 minutes to help spread the load.
You can help further, by adjusting your crontab entry, like so:
```ruby
# avoid:
#@daily /usr/bin/lego ... renew
#@midnight /usr/bin/lego ... renew
#0 0 * * * /usr/bin/lego ... renew
# instead, use a randomly chosen time:
3 35 * * * /usr/bin/lego ... renew
```
If you use systemd timers, consider doing something similar, and/or introduce a `RandomizedDelaySec`:
```ini
[Unit]
Description=Renew certificates
[Timer]
Persistent=true
# avoid:
#OnCalendar=*-*-* 00:00:00
#OnCalendar=daily
# instead, use a randomly chosen time:
OnCalendar=*-*-* 3:35
# add extra delay, here up to 1 hour:
RandomizedDelaySec=1h
[Install]
WantedBy=timers.target
```
[^loadspikes]: See [Github issue #1656](https://github.com/go-acme/lego/issues/1656) for an excellent problem description.
lego-4.9.1/docs/content/usage/cli/_index.md 0000664 0000000 0000000 00000000232 14340204635 0020516 0 ustar 00root root 0000000 0000000 ---
title: "CLI"
date: 2019-03-03T16:39:46+01:00
draft: false
---
Lego can be use as a CLI.
{{% children style="h2" description="true" %}}
lego-4.9.1/docs/content/usage/cli/examples.md 0000664 0000000 0000000 00000001552 14340204635 0021074 0 ustar 00root root 0000000 0000000 ---
title: Examples
date: 2019-03-03T16:39:46+01:00
draft: false
hidden: true
---
{{% notice note %}}
**Heads up!** We've restructured the content a bit.
{{% /notice %}}
You'll find the content now at one of these pages:
- Guide: [**How to obtain a certificate**]({{< ref "usage/cli/Obtain-a-Certificate" >}})
- Using the built-in web server
- Using a DNS provider
- Using a custom certificate signing request (CSR)
- Using an existing, running web server
- Running a script afterward
- Use case
- Guide: [**How to renew a certificate**]({{< ref "usage/cli/Renew-a-Certificate" >}})
- Using the built-in web server
- Using a DNS provider
- Running a script afterward
- Automatic renewal
- Reference: [**Command line options**]({{< ref "usage/cli/Options" >}})
- Usage
- Let's Encrypt ACME server
- Running without root privileges
- Port Usage
lego-4.9.1/docs/content/usage/library/ 0000775 0000000 0000000 00000000000 14340204635 0017626 5 ustar 00root root 0000000 0000000 lego-4.9.1/docs/content/usage/library/Writing-a-Challenge-Solver.md 0000664 0000000 0000000 00000010236 14340204635 0025143 0 ustar 00root root 0000000 0000000 ---
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://pkg.go.dev/github.com/go-acme/lego/v4/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://pkg.go.dev/github.com/go-acme/lego/v4/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!
lego-4.9.1/docs/content/usage/library/_index.md 0000664 0000000 0000000 00000005367 14340204635 0021431 0 ustar 00root root 0000000 0000000 ---
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://pkg.go.dev/mod/github.com/go-acme/lego/v4).
## 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/go-acme/lego/v4/certcrypto"
"github.com/go-acme/lego/v4/certificate"
"github.com/go-acme/lego/v4/challenge/http01"
"github.com/go-acme/lego/v4/challenge/tlsalpn01"
"github.com/go-acme/lego/v4/lego"
"github.com/go-acme/lego/v4/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.
}
```
lego-4.9.1/docs/layouts/ 0000775 0000000 0000000 00000000000 14340204635 0015104 5 ustar 00root root 0000000 0000000 lego-4.9.1/docs/layouts/partials/ 0000775 0000000 0000000 00000000000 14340204635 0016723 5 ustar 00root root 0000000 0000000 lego-4.9.1/docs/layouts/partials/logo.html 0000664 0000000 0000000 00000000137 14340204635 0020552 0 ustar 00root root 0000000 0000000
lego-4.9.1/docs/layouts/shortcodes/ 0000775 0000000 0000000 00000000000 14340204635 0017261 5 ustar 00root root 0000000 0000000 lego-4.9.1/docs/layouts/shortcodes/tableofdnsproviders.html 0000664 0000000 0000000 00000001405 14340204635 0024226 0 ustar 00root root 0000000 0000000 {{ $_hugo_config := `{ "version": 1 }` }}
Provider name
CLI flag name
Required lego version
{{- range .Site.AllPages.ByWeight -}}
{{- if .Params.dnsprovider -}}
{{- $params := .Params.dnsprovider -}}