Support custom DNS resolvers for Let's Encrypt.
This commit is contained in:
parent
ac11323fdd
commit
74dc5b1c58
5 changed files with 113 additions and 4 deletions
|
@ -451,6 +451,9 @@ func (a *ACME) buildACMEClient(account *Account) (*acme.Client, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
acmeprovider.SetRecursiveNameServers(a.DNSChallenge.Resolvers)
|
||||||
|
acmeprovider.SetPropagationCheck(a.DNSChallenge.DisablePropagationCheck)
|
||||||
|
|
||||||
var provider acme.ChallengeProvider
|
var provider acme.ChallengeProvider
|
||||||
provider, err = dns.NewDNSChallengeProviderByName(a.DNSChallenge.Provider)
|
provider, err = dns.NewDNSChallengeProviderByName(a.DNSChallenge.Provider)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -71,6 +71,7 @@ Complete documentation is available at https://traefik.io`,
|
||||||
f.AddParser(reflect.TypeOf(kubernetes.Namespaces{}), &kubernetes.Namespaces{})
|
f.AddParser(reflect.TypeOf(kubernetes.Namespaces{}), &kubernetes.Namespaces{})
|
||||||
f.AddParser(reflect.TypeOf(ecs.Clusters{}), &ecs.Clusters{})
|
f.AddParser(reflect.TypeOf(ecs.Clusters{}), &ecs.Clusters{})
|
||||||
f.AddParser(reflect.TypeOf([]types.Domain{}), &types.Domains{})
|
f.AddParser(reflect.TypeOf([]types.Domain{}), &types.Domains{})
|
||||||
|
f.AddParser(reflect.TypeOf(types.DNSResolvers{}), &types.DNSResolvers{})
|
||||||
f.AddParser(reflect.TypeOf(types.Buckets{}), &types.Buckets{})
|
f.AddParser(reflect.TypeOf(types.Buckets{}), &types.Buckets{})
|
||||||
f.AddParser(reflect.TypeOf(types.StatusCodes{}), &types.StatusCodes{})
|
f.AddParser(reflect.TypeOf(types.StatusCodes{}), &types.StatusCodes{})
|
||||||
f.AddParser(reflect.TypeOf(types.FieldNames{}), &types.FieldNames{})
|
f.AddParser(reflect.TypeOf(types.FieldNames{}), &types.FieldNames{})
|
||||||
|
|
|
@ -142,6 +142,23 @@ entryPoint = "https"
|
||||||
#
|
#
|
||||||
# delayBeforeCheck = 0
|
# delayBeforeCheck = 0
|
||||||
|
|
||||||
|
# Use following DNS servers to resolve the FQDN authority.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: empty
|
||||||
|
#
|
||||||
|
# resolvers = ["1.1.1.1:53", "8.8.8.8:53"]
|
||||||
|
|
||||||
|
# Disable the DNS propagation checks before notifying ACME that the DNS challenge is ready.
|
||||||
|
#
|
||||||
|
# NOT RECOMMENDED:
|
||||||
|
# Increase the risk of reaching Let's Encrypt's rate limits.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: false
|
||||||
|
#
|
||||||
|
# disablePropagationCheck = true
|
||||||
|
|
||||||
# Domains list.
|
# Domains list.
|
||||||
# Only domains defined here can generate wildcard certificates.
|
# Only domains defined here can generate wildcard certificates.
|
||||||
# The certificates for these domains are negotiated at traefik startup only.
|
# The certificates for these domains are negotiated at traefik startup only.
|
||||||
|
@ -302,6 +319,10 @@ Here is a list of supported `provider`s, that can automate the DNS verification,
|
||||||
| [VegaDNS](https://github.com/shupp/VegaDNS-API) | `vegadns` | `SECRET_VEGADNS_KEY`, `SECRET_VEGADNS_SECRET`, `VEGADNS_URL` | Not tested yet |
|
| [VegaDNS](https://github.com/shupp/VegaDNS-API) | `vegadns` | `SECRET_VEGADNS_KEY`, `SECRET_VEGADNS_SECRET`, `VEGADNS_URL` | Not tested yet |
|
||||||
| [VULTR](https://www.vultr.com) | `vultr` | `VULTR_API_KEY` | Not tested yet |
|
| [VULTR](https://www.vultr.com) | `vultr` | `VULTR_API_KEY` | Not tested yet |
|
||||||
|
|
||||||
|
#### `resolvers`
|
||||||
|
|
||||||
|
Use custom DNS servers to resolve the FQDN authority.
|
||||||
|
|
||||||
### `domains`
|
### `domains`
|
||||||
|
|
||||||
You can provide SANs (alternative domains) to each main domain.
|
You can provide SANs (alternative domains) to each main domain.
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
fmtlog "log"
|
fmtlog "log"
|
||||||
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -74,10 +75,12 @@ type Certificate struct {
|
||||||
|
|
||||||
// DNSChallenge contains DNS challenge Configuration
|
// DNSChallenge contains DNS challenge Configuration
|
||||||
type DNSChallenge struct {
|
type DNSChallenge struct {
|
||||||
Provider string `description:"Use a DNS-01 based challenge provider rather than HTTPS."`
|
Provider string `description:"Use a DNS-01 based challenge provider rather than HTTPS."`
|
||||||
DelayBeforeCheck flaeg.Duration `description:"Assume DNS propagates after a delay in seconds rather than finding and querying nameservers."`
|
DelayBeforeCheck flaeg.Duration `description:"Assume DNS propagates after a delay in seconds rather than finding and querying nameservers."`
|
||||||
preCheckTimeout time.Duration
|
Resolvers types.DNSResolvers `description:"Use following DNS servers to resolve the FQDN authority."`
|
||||||
preCheckInterval time.Duration
|
DisablePropagationCheck bool `description:"Disable the DNS propagation checks before notifying ACME that the DNS challenge is ready. [not recommended]"`
|
||||||
|
preCheckTimeout time.Duration
|
||||||
|
preCheckInterval time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTTPChallenge contains HTTP challenge Configuration
|
// HTTPChallenge contains HTTP challenge Configuration
|
||||||
|
@ -252,6 +255,9 @@ func (p *Provider) getClient() (*acme.Client, error) {
|
||||||
if p.DNSChallenge != nil && len(p.DNSChallenge.Provider) > 0 {
|
if p.DNSChallenge != nil && len(p.DNSChallenge.Provider) > 0 {
|
||||||
log.Debugf("Using DNS Challenge provider: %s", p.DNSChallenge.Provider)
|
log.Debugf("Using DNS Challenge provider: %s", p.DNSChallenge.Provider)
|
||||||
|
|
||||||
|
SetRecursiveNameServers(p.DNSChallenge.Resolvers)
|
||||||
|
SetPropagationCheck(p.DNSChallenge.DisablePropagationCheck)
|
||||||
|
|
||||||
err = dnsOverrideDelay(p.DNSChallenge.DelayBeforeCheck)
|
err = dnsOverrideDelay(p.DNSChallenge.DelayBeforeCheck)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -784,3 +790,37 @@ func isDomainAlreadyChecked(domainToCheck string, existentDomains []string) bool
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetPropagationCheck to disable the Lego PreCheck.
|
||||||
|
func SetPropagationCheck(disable bool) {
|
||||||
|
if disable {
|
||||||
|
acme.PreCheckDNS = func(_, _ string) (bool, error) {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetRecursiveNameServers to provide a custom DNS resolver.
|
||||||
|
func SetRecursiveNameServers(dnsResolvers []string) {
|
||||||
|
resolvers := normaliseDNSResolvers(dnsResolvers)
|
||||||
|
if len(resolvers) > 0 {
|
||||||
|
acme.RecursiveNameservers = resolvers
|
||||||
|
log.Infof("Validating FQDN authority with DNS using %+v", resolvers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure all servers have a port number
|
||||||
|
func normaliseDNSResolvers(dnsResolvers []string) []string {
|
||||||
|
var normalisedResolvers []string
|
||||||
|
for _, server := range dnsResolvers {
|
||||||
|
srv := strings.TrimSpace(server)
|
||||||
|
if len(srv) > 0 {
|
||||||
|
if host, port, err := net.SplitHostPort(srv); err != nil {
|
||||||
|
normalisedResolvers = append(normalisedResolvers, net.JoinHostPort(srv, "53"))
|
||||||
|
} else {
|
||||||
|
normalisedResolvers = append(normalisedResolvers, net.JoinHostPort(host, port))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return normalisedResolvers
|
||||||
|
}
|
||||||
|
|
44
types/dns_resolvers.go
Normal file
44
types/dns_resolvers.go
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DNSResolvers is a list of DNSes that we will try to resolve the challenged FQDN against
|
||||||
|
type DNSResolvers []string
|
||||||
|
|
||||||
|
// String is the method to format the flag's value, part of the flag.Value interface.
|
||||||
|
// The String method's output will be used in diagnostics.
|
||||||
|
func (r *DNSResolvers) String() string {
|
||||||
|
return strings.Join(*r, ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set is the method to set the flag value, part of the flag.Value interface.
|
||||||
|
// Set's argument is a string to be parsed to set the flag.
|
||||||
|
// It's a comma-separated list, so we split it.
|
||||||
|
func (r *DNSResolvers) Set(value string) error {
|
||||||
|
entryPoints := strings.Split(value, ",")
|
||||||
|
if len(entryPoints) == 0 {
|
||||||
|
return fmt.Errorf("wrong DNSResolvers format: %s", value)
|
||||||
|
}
|
||||||
|
for _, entryPoint := range entryPoints {
|
||||||
|
*r = append(*r, entryPoint)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get return the DNSResolvers list
|
||||||
|
func (r *DNSResolvers) Get() interface{} {
|
||||||
|
return *r
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetValue sets the DNSResolvers list
|
||||||
|
func (r *DNSResolvers) SetValue(val interface{}) {
|
||||||
|
*r = val.(DNSResolvers)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type is type of the struct
|
||||||
|
func (r *DNSResolvers) Type() string {
|
||||||
|
return "dnsresolvers"
|
||||||
|
}
|
Loading…
Reference in a new issue