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
|
||||
}
|
||||
|
||||
acmeprovider.SetRecursiveNameServers(a.DNSChallenge.Resolvers)
|
||||
acmeprovider.SetPropagationCheck(a.DNSChallenge.DisablePropagationCheck)
|
||||
|
||||
var provider acme.ChallengeProvider
|
||||
provider, err = dns.NewDNSChallengeProviderByName(a.DNSChallenge.Provider)
|
||||
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(ecs.Clusters{}), &ecs.Clusters{})
|
||||
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.StatusCodes{}), &types.StatusCodes{})
|
||||
f.AddParser(reflect.TypeOf(types.FieldNames{}), &types.FieldNames{})
|
||||
|
|
|
@ -142,6 +142,23 @@ entryPoint = "https"
|
|||
#
|
||||
# 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.
|
||||
# Only domains defined here can generate wildcard certificates.
|
||||
# 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 |
|
||||
| [VULTR](https://www.vultr.com) | `vultr` | `VULTR_API_KEY` | Not tested yet |
|
||||
|
||||
#### `resolvers`
|
||||
|
||||
Use custom DNS servers to resolve the FQDN authority.
|
||||
|
||||
### `domains`
|
||||
|
||||
You can provide SANs (alternative domains) to each main domain.
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"fmt"
|
||||
"io/ioutil"
|
||||
fmtlog "log"
|
||||
"net"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
@ -74,10 +75,12 @@ type Certificate struct {
|
|||
|
||||
// DNSChallenge contains DNS challenge Configuration
|
||||
type DNSChallenge struct {
|
||||
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."`
|
||||
preCheckTimeout time.Duration
|
||||
preCheckInterval time.Duration
|
||||
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."`
|
||||
Resolvers types.DNSResolvers `description:"Use following DNS servers to resolve the FQDN authority."`
|
||||
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
|
||||
|
@ -252,6 +255,9 @@ func (p *Provider) getClient() (*acme.Client, error) {
|
|||
if p.DNSChallenge != nil && len(p.DNSChallenge.Provider) > 0 {
|
||||
log.Debugf("Using DNS Challenge provider: %s", p.DNSChallenge.Provider)
|
||||
|
||||
SetRecursiveNameServers(p.DNSChallenge.Resolvers)
|
||||
SetPropagationCheck(p.DNSChallenge.DisablePropagationCheck)
|
||||
|
||||
err = dnsOverrideDelay(p.DNSChallenge.DelayBeforeCheck)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -784,3 +790,37 @@ func isDomainAlreadyChecked(domainToCheck string, existentDomains []string) bool
|
|||
}
|
||||
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