cpanel: remove custom DNS call (#2102)

This commit is contained in:
Ludovic Fernandez 2024-02-06 17:17:59 +01:00 committed by GitHub
parent 83ff393131
commit c5a95c4cd0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 74 additions and 188 deletions

View file

@ -621,7 +621,6 @@ func displayDNSHelp(w io.Writer, name string) error {
ew.writeln(`Credentials:`)
ew.writeln(` - "CPANEL_BASE_URL": API server URL`)
ew.writeln(` - "CPANEL_NAMESERVER": Nameserver`)
ew.writeln(` - "CPANEL_TOKEN": API token`)
ew.writeln(` - "CPANEL_USERNAME": username`)
ew.writeln()

View file

@ -31,7 +31,6 @@ Here is an example bash command using the CPanel/WHM provider:
CPANEL_USERNAME = "yyyy"
CPANEL_TOKEN = "xxxx"
CPANEL_BASE_URL = "https://example.com:2083" \
CPANEL_NAMESERVER = "ns1.example.com:53" \
lego --email you@example.com --dns cpanel --domains my.example.org run
## WHM
@ -40,7 +39,6 @@ CPANEL_MODE = whm
CPANEL_USERNAME = "yyyy"
CPANEL_TOKEN = "xxxx"
CPANEL_BASE_URL = "https://example.com:2087" \
CPANEL_NAMESERVER = "ns1.example.com:53" \
lego --email you@example.com --dns cpanel --domains my.example.org run
```
@ -52,7 +50,6 @@ lego --email you@example.com --dns cpanel --domains my.example.org run
| Environment Variable Name | Description |
|-----------------------|-------------|
| `CPANEL_BASE_URL` | API server URL |
| `CPANEL_NAMESERVER` | Nameserver |
| `CPANEL_TOKEN` | API token |
| `CPANEL_USERNAME` | username |

View file

@ -21,11 +21,10 @@ import (
const (
envNamespace = "CPANEL_"
EnvMode = envNamespace + "MODE"
EnvUsername = envNamespace + "USERNAME"
EnvToken = envNamespace + "TOKEN"
EnvBaseURL = envNamespace + "BASE_URL"
EnvNameserver = envNamespace + "NAMESERVER"
EnvMode = envNamespace + "MODE"
EnvUsername = envNamespace + "USERNAME"
EnvToken = envNamespace + "TOKEN"
EnvBaseURL = envNamespace + "BASE_URL"
EnvTTL = envNamespace + "TTL"
EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
@ -46,7 +45,6 @@ type Config struct {
Username string
Token string
BaseURL string
Nameserver string
TTL int
PropagationTimeout time.Duration
PollingInterval time.Duration
@ -58,7 +56,7 @@ func NewDefaultConfig() *Config {
return &Config{
Mode: env.GetOrDefaultString(EnvMode, "cpanel"),
TTL: env.GetOrDefaultInt(EnvTTL, 300),
PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, dns01.DefaultPropagationTimeout),
PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 2*time.Minute),
PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval),
HTTPClient: &http.Client{
Timeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second),
@ -68,16 +66,15 @@ func NewDefaultConfig() *Config {
// DNSProvider implements the challenge.Provider interface.
type DNSProvider struct {
config *Config
client apiClient
dnsClient *shared.DNSClient
config *Config
client apiClient
}
// NewDNSProvider returns a DNSProvider instance configured for CPanel.
// Credentials must be passed in the environment variables:
// CPANEL_USERNAME, CPANEL_TOKEN, CPANEL_BASE_URL, CPANEL_NAMESERVER.
func NewDNSProvider() (*DNSProvider, error) {
values, err := env.Get(EnvUsername, EnvToken, EnvBaseURL, EnvNameserver)
values, err := env.Get(EnvUsername, EnvToken, EnvBaseURL)
if err != nil {
return nil, fmt.Errorf("cpanel: %w", err)
}
@ -86,7 +83,6 @@ func NewDNSProvider() (*DNSProvider, error) {
config.Username = values[EnvUsername]
config.Token = values[EnvToken]
config.BaseURL = values[EnvBaseURL]
config.Nameserver = values[EnvNameserver]
return NewDNSProviderConfig(config)
}
@ -101,7 +97,7 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
return nil, errors.New("cpanel: some credentials information are missing")
}
if config.BaseURL == "" || config.Nameserver == "" {
if config.BaseURL == "" {
return nil, errors.New("cpanel: server information are missing")
}
@ -111,9 +107,8 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
}
return &DNSProvider{
config: config,
client: client,
dnsClient: shared.NewDNSClient(10 * time.Second),
config: config,
client: client,
}, nil
}
@ -128,21 +123,19 @@ func (d *DNSProvider) Present(domain, _, keyAuth string) error {
ctx := context.Background()
info := dns01.GetChallengeInfo(domain, keyAuth)
effectiveDomain := strings.TrimPrefix(info.EffectiveFQDN, "_acme-challenge.")
soa, err := d.dnsClient.SOACall(effectiveDomain, d.config.Nameserver)
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
if err != nil {
return fmt.Errorf("cpanel[mode=%s]: could not find SOA for domain %q (%s) in %s: %w", d.config.Mode, domain, info.EffectiveFQDN, d.config.Nameserver, err)
return fmt.Errorf("arvancloud: could not find zone for domain %q (%s): %w", domain, info.EffectiveFQDN, err)
}
zone := dns01.UnFqdn(soa.Hdr.Name)
zone := dns01.UnFqdn(authZone)
zoneInfo, err := d.client.FetchZoneInformation(ctx, zone)
if err != nil {
return fmt.Errorf("cpanel[mode=%s]: fetch zone information: %w", d.config.Mode, err)
}
serial, err := getZoneSerial(soa.Hdr.Name, zoneInfo)
serial, err := getZoneSerial(authZone, zoneInfo)
if err != nil {
return fmt.Errorf("cpanel[mode=%s]: get zone serial: %w", d.config.Mode, err)
}
@ -204,19 +197,19 @@ func (d *DNSProvider) CleanUp(domain, _, keyAuth string) error {
ctx := context.Background()
info := dns01.GetChallengeInfo(domain, keyAuth)
soa, err := d.dnsClient.SOACall(strings.TrimPrefix(info.EffectiveFQDN, "_acme-challenge."), d.config.Nameserver)
authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
if err != nil {
return fmt.Errorf("cpanel[mode=%s]: could not find SOA for domain %q (%s) in %s: %w", d.config.Mode, domain, info.EffectiveFQDN, d.config.Nameserver, err)
return fmt.Errorf("arvancloud: could not find zone for domain %q (%s): %w", domain, info.EffectiveFQDN, err)
}
zone := dns01.UnFqdn(soa.Hdr.Name)
zone := dns01.UnFqdn(authZone)
zoneInfo, err := d.client.FetchZoneInformation(ctx, zone)
if err != nil {
return fmt.Errorf("cpanel[mode=%s]: fetch zone information: %w", d.config.Mode, err)
}
serial, err := getZoneSerial(soa.Hdr.Name, zoneInfo)
serial, err := getZoneSerial(authZone, zoneInfo)
if err != nil {
return fmt.Errorf("cpanel[mode=%s]: get zone serial: %w", d.config.Mode, err)
}

View file

@ -10,7 +10,6 @@ Example = '''
CPANEL_USERNAME = "yyyy"
CPANEL_TOKEN = "xxxx"
CPANEL_BASE_URL = "https://example.com:2083" \
CPANEL_NAMESERVER = "ns1.example.com:53" \
lego --email you@example.com --dns cpanel --domains my.example.org run
## WHM
@ -19,7 +18,6 @@ CPANEL_MODE = whm
CPANEL_USERNAME = "yyyy"
CPANEL_TOKEN = "xxxx"
CPANEL_BASE_URL = "https://example.com:2087" \
CPANEL_NAMESERVER = "ns1.example.com:53" \
lego --email you@example.com --dns cpanel --domains my.example.org run
'''
@ -28,7 +26,6 @@ lego --email you@example.com --dns cpanel --domains my.example.org run
CPANEL_USERNAME = "username"
CPANEL_TOKEN = "API token"
CPANEL_BASE_URL = "API server URL"
CPANEL_NAMESERVER = "Nameserver"
[Configuration.Additional]
CPANEL_MODE = "use cpanel API or WHM API (Default: cpanel)"
CPANEL_POLLING_INTERVAL = "Time between DNS propagation check"

View file

@ -16,8 +16,7 @@ var envTest = tester.NewEnvTest(
EnvMode,
EnvUsername,
EnvToken,
EnvBaseURL,
EnvNameserver).
EnvBaseURL).
WithDomain(envDomain)
func TestNewDNSProvider(t *testing.T) {
@ -30,62 +29,47 @@ func TestNewDNSProvider(t *testing.T) {
{
desc: "success cpanel mode (default)",
envVars: map[string]string{
EnvUsername: "user",
EnvToken: "secret",
EnvBaseURL: "https://example.com",
EnvNameserver: "ns.example.com:53",
EnvUsername: "user",
EnvToken: "secret",
EnvBaseURL: "https://example.com",
},
expectedMode: "cpanel",
},
{
desc: "success whm mode",
envVars: map[string]string{
EnvMode: "whm",
EnvUsername: "user",
EnvToken: "secret",
EnvBaseURL: "https://example.com",
EnvNameserver: "ns.example.com:53",
EnvMode: "whm",
EnvUsername: "user",
EnvToken: "secret",
EnvBaseURL: "https://example.com",
},
expectedMode: "whm",
},
{
desc: "missing user",
envVars: map[string]string{
EnvToken: "secret",
EnvBaseURL: "https://example.com",
EnvNameserver: "ns.example.com:53",
EnvToken: "secret",
EnvBaseURL: "https://example.com",
},
expected: "cpanel: some credentials information are missing: CPANEL_USERNAME",
},
{
desc: "missing token",
envVars: map[string]string{
EnvUsername: "user",
EnvBaseURL: "https://example.com",
EnvNameserver: "ns.example.com:53",
EnvUsername: "user",
EnvBaseURL: "https://example.com",
},
expected: "cpanel: some credentials information are missing: CPANEL_TOKEN",
},
{
desc: "missing base URL",
envVars: map[string]string{
EnvUsername: "user",
EnvToken: "secret",
EnvBaseURL: "",
EnvNameserver: "ns.example.com:53",
EnvUsername: "user",
EnvToken: "secret",
EnvBaseURL: "",
},
expected: "cpanel: some credentials information are missing: CPANEL_BASE_URL",
},
{
desc: "missing nameserver",
envVars: map[string]string{
EnvUsername: "user",
EnvToken: "secret",
EnvBaseURL: "https://example.com",
EnvNameserver: "",
},
expected: "cpanel: some credentials information are missing: CPANEL_NAMESERVER",
},
}
for _, test := range testCases {
@ -111,74 +95,58 @@ func TestNewDNSProvider(t *testing.T) {
func TestNewDNSProviderConfig(t *testing.T) {
testCases := []struct {
desc string
mode string
username string
token string
baseURL string
nameserver string
expected string
desc string
mode string
username string
token string
baseURL string
expected string
}{
{
desc: "success",
mode: "whm",
username: "user",
token: "secret",
baseURL: "https://example.com",
nameserver: "ns.example.com:53",
desc: "success",
mode: "whm",
username: "user",
token: "secret",
baseURL: "https://example.com",
},
{
desc: "missing mode",
username: "user",
token: "secret",
baseURL: "https://example.com",
nameserver: "ns.example.com:53",
expected: `cpanel: create client error: unsupported mode: ""`,
desc: "missing mode",
username: "user",
token: "secret",
baseURL: "https://example.com",
expected: `cpanel: create client error: unsupported mode: ""`,
},
{
desc: "invalid mode",
mode: "test",
username: "user",
token: "secret",
baseURL: "https://example.com",
nameserver: "ns.example.com:53",
expected: `cpanel: create client error: unsupported mode: "test"`,
desc: "invalid mode",
mode: "test",
username: "user",
token: "secret",
baseURL: "https://example.com",
expected: `cpanel: create client error: unsupported mode: "test"`,
},
{
desc: "missing username",
mode: "whm",
username: "",
token: "secret",
baseURL: "https://example.com",
nameserver: "ns.example.com:53",
expected: "cpanel: some credentials information are missing",
desc: "missing username",
mode: "whm",
username: "",
token: "secret",
baseURL: "https://example.com",
expected: "cpanel: some credentials information are missing",
},
{
desc: "missing token",
mode: "whm",
username: "user",
token: "",
baseURL: "https://example.com",
nameserver: "ns.example.com:53",
expected: "cpanel: some credentials information are missing",
desc: "missing token",
mode: "whm",
username: "user",
token: "",
baseURL: "https://example.com",
expected: "cpanel: some credentials information are missing",
},
{
desc: "missing base URL",
mode: "whm",
username: "user",
token: "secret",
baseURL: "",
nameserver: "ns.example.com:53",
expected: "cpanel: server information are missing",
},
{
desc: "missing nameserver",
mode: "whm",
username: "user",
token: "secret",
baseURL: "https://example.com",
nameserver: "",
expected: "cpanel: server information are missing",
desc: "missing base URL",
mode: "whm",
username: "user",
token: "secret",
baseURL: "",
expected: "cpanel: server information are missing",
},
}
@ -189,7 +157,6 @@ func TestNewDNSProviderConfig(t *testing.T) {
config.Username = test.username
config.Token = test.token
config.BaseURL = test.baseURL
config.Nameserver = test.nameserver
p, err := NewDNSProviderConfig(config)

View file

@ -1,67 +0,0 @@
package shared
import (
"fmt"
"os"
"strconv"
"time"
"github.com/miekg/dns"
)
type DNSClient struct {
timeout time.Duration
}
func NewDNSClient(timeout time.Duration) *DNSClient {
return &DNSClient{timeout: timeout}
}
func (d DNSClient) SOACall(fqdn, nameserver string) (*dns.SOA, error) {
m := new(dns.Msg)
m.SetQuestion(fqdn, dns.TypeSOA)
m.SetEdns0(4096, false)
in, err := d.sendDNSQuery(m, nameserver)
if err != nil {
return nil, err
}
if len(in.Answer) == 0 {
if len(in.Ns) > 0 {
if soa, ok := in.Ns[0].(*dns.SOA); ok && fqdn != soa.Hdr.Name {
return d.SOACall(soa.Hdr.Name, nameserver)
}
}
return nil, fmt.Errorf("empty answer for %s in %s", fqdn, nameserver)
}
for _, rr := range in.Answer {
if soa, ok := rr.(*dns.SOA); ok {
return soa, nil
}
}
return nil, fmt.Errorf("SOA not found for %s in %s", fqdn, nameserver)
}
func (d DNSClient) sendDNSQuery(m *dns.Msg, ns string) (*dns.Msg, error) {
if ok, _ := strconv.ParseBool(os.Getenv("LEGO_EXPERIMENTAL_DNS_TCP_ONLY")); ok {
tcp := &dns.Client{Net: "tcp", Timeout: d.timeout}
in, _, err := tcp.Exchange(m, ns)
return in, err
}
udp := &dns.Client{Net: "udp", Timeout: d.timeout}
in, _, err := udp.Exchange(m, ns)
if in != nil && in.Truncated {
tcp := &dns.Client{Net: "tcp", Timeout: d.timeout}
// If the TCP request succeeds, the err will reset to nil
in, _, err = tcp.Exchange(m, ns)
}
return in, err
}