From 9c1a856b73a1e85b0328bd645fae42abe0c81054 Mon Sep 17 00:00:00 2001 From: m0t1x <80338020+m0t1x@users.noreply.github.com> Date: Sat, 27 Jan 2024 23:23:10 +0100 Subject: [PATCH] pdns: optional custom API version (#2019) Co-authored-by: Fernandez Ludovic --- cmd/zz_gen_cmd_dnshelp.go | 1 + docs/content/dns/zz_gen_pdns.md | 2 ++ docs/data/zz_cli_help.toml | 2 +- providers/dns/pdns/internal/client.go | 3 +- providers/dns/pdns/internal/client_test.go | 5 ++- providers/dns/pdns/pdns.go | 15 ++++++--- providers/dns/pdns/pdns.toml | 4 ++- providers/dns/pdns/pdns_test.go | 38 ++++++++++++++-------- 8 files changed, 45 insertions(+), 25 deletions(-) diff --git a/cmd/zz_gen_cmd_dnshelp.go b/cmd/zz_gen_cmd_dnshelp.go index 24245e02..f28ef2e6 100644 --- a/cmd/zz_gen_cmd_dnshelp.go +++ b/cmd/zz_gen_cmd_dnshelp.go @@ -2078,6 +2078,7 @@ func displayDNSHelp(w io.Writer, name string) error { ew.writeln() ew.writeln(`Additional Configuration:`) + ew.writeln(` - "PDNS_API_VERSION": Skip API version autodetection and use the provided version number.`) 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`) diff --git a/docs/content/dns/zz_gen_pdns.md b/docs/content/dns/zz_gen_pdns.md index c6febaaf..22a730b0 100644 --- a/docs/content/dns/zz_gen_pdns.md +++ b/docs/content/dns/zz_gen_pdns.md @@ -49,6 +49,7 @@ More information [here]({{< ref "dns#configuration-and-credentials" >}}). | Environment Variable Name | Description | |--------------------------------|-------------| +| `PDNS_API_VERSION` | Skip API version autodetection and use the provided version number. | | `PDNS_HTTP_TIMEOUT` | API request timeout | | `PDNS_POLLING_INTERVAL` | Time between DNS propagation check | | `PDNS_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | @@ -65,6 +66,7 @@ Tested and confirmed to work with PowerDNS authoritative server 3.4.8 and 4.0.1. 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 +- Some PowerDNS servers doesn't have root API endpoints enabled and API version autodetection will not work. In that case version number can be defined using `PDNS_API_VERSION`. diff --git a/docs/data/zz_cli_help.toml b/docs/data/zz_cli_help.toml index e3b598b0..a877121b 100644 --- a/docs/data/zz_cli_help.toml +++ b/docs/data/zz_cli_help.toml @@ -82,7 +82,7 @@ USAGE: OPTIONS: --days value The number of days left on a certificate to renew it. (default: 0) - --ari-enable Use the renewalInfo endpoint (draft-ietf-acme-ari-02) to check if a certificate should be renewed. (default: false) + --ari-enable Use the renewalInfo endpoint (draft-ietf-acme-ari) to check if a certificate should be renewed. (default: false) --ari-wait-to-renew-duration value The maximum duration you're willing to sleep for a renewal time returned by the renewalInfo endpoint. (default: 0s) --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) diff --git a/providers/dns/pdns/internal/client.go b/providers/dns/pdns/internal/client.go index d7b247a4..7eed57e3 100644 --- a/providers/dns/pdns/internal/client.go +++ b/providers/dns/pdns/internal/client.go @@ -30,10 +30,11 @@ type Client struct { } // NewClient creates a new Client. -func NewClient(host *url.URL, serverName string, apiKey string) *Client { +func NewClient(host *url.URL, serverName string, apiVersion int, apiKey string) *Client { return &Client{ serverName: serverName, apiKey: apiKey, + apiVersion: apiVersion, Host: host, HTTPClient: &http.Client{Timeout: 5 * time.Second}, } diff --git a/providers/dns/pdns/internal/client_test.go b/providers/dns/pdns/internal/client_test.go index d102a5ef..4e17a4fd 100644 --- a/providers/dns/pdns/internal/client_test.go +++ b/providers/dns/pdns/internal/client_test.go @@ -57,7 +57,7 @@ func setupTest(t *testing.T, method, pattern string, status int, file string) *C serverURL, _ := url.Parse(server.URL) - client := NewClient(serverURL, "server", "secret") + client := NewClient(serverURL, "server", 0, "secret") client.HTTPClient = server.Client() return client @@ -151,8 +151,7 @@ func TestClient_joinPath(t *testing.T) { host, err := url.Parse(test.baseURL) require.NoError(t, err) - client := NewClient(host, "test", "secret") - client.apiVersion = test.apiVersion + client := NewClient(host, "test", test.apiVersion, "secret") endpoint := client.joinPath(test.uri) diff --git a/providers/dns/pdns/pdns.go b/providers/dns/pdns/pdns.go index 5962fddc..f3d3e2b8 100644 --- a/providers/dns/pdns/pdns.go +++ b/providers/dns/pdns/pdns.go @@ -23,6 +23,7 @@ const ( EnvAPIURL = envNamespace + "API_URL" EnvTTL = envNamespace + "TTL" + EnvAPIVersion = envNamespace + "API_VERSION" EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT" EnvPollingInterval = envNamespace + "POLLING_INTERVAL" EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT" @@ -34,6 +35,7 @@ type Config struct { APIKey string Host *url.URL ServerName string + APIVersion int PropagationTimeout time.Duration PollingInterval time.Duration TTL int @@ -43,10 +45,11 @@ type Config struct { // NewDefaultConfig returns a default configuration for the DNSProvider. func NewDefaultConfig() *Config { return &Config{ + ServerName: env.GetOrDefaultString(EnvServerName, "localhost"), + APIVersion: env.GetOrDefaultInt(EnvAPIVersion, 0), TTL: env.GetOrDefaultInt(EnvTTL, dns01.DefaultTTL), PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 120*time.Second), PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, 2*time.Second), - ServerName: env.GetOrDefaultString(EnvServerName, "localhost"), HTTPClient: &http.Client{ Timeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second), }, @@ -94,11 +97,13 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { return nil, errors.New("pdns: API URL missing") } - client := internal.NewClient(config.Host, config.ServerName, config.APIKey) + client := internal.NewClient(config.Host, config.ServerName, config.APIVersion, config.APIKey) - err := client.SetAPIVersion(context.Background()) - if err != nil { - log.Warnf("pdns: failed to get API version %v", err) + if config.APIVersion <= 0 { + err := client.SetAPIVersion(context.Background()) + if err != nil { + log.Warnf("pdns: failed to get API version %v", err) + } } return &DNSProvider{config: config, client: client}, nil diff --git a/providers/dns/pdns/pdns.toml b/providers/dns/pdns/pdns.toml index f1209e4a..a59c02cd 100644 --- a/providers/dns/pdns/pdns.toml +++ b/providers/dns/pdns/pdns.toml @@ -18,6 +18,7 @@ Tested and confirmed to work with PowerDNS authoritative server 3.4.8 and 4.0.1. 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 +- Some PowerDNS servers doesn't have root API endpoints enabled and API version autodetection will not work. In that case version number can be defined using `PDNS_API_VERSION`. ''' [Configuration] @@ -25,11 +26,12 @@ PowerDNS Notes: PDNS_API_KEY = "API key" PDNS_API_URL = "API URL" [Configuration.Additional] + PDNS_SERVER_NAME = "Name of the server in the URL, 'localhost' by default" + PDNS_API_VERSION = "Skip API version autodetection and use the provided version number." PDNS_POLLING_INTERVAL = "Time between DNS propagation check" PDNS_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" PDNS_TTL = "The TTL of the TXT record used for the DNS challenge" PDNS_HTTP_TIMEOUT = "API request timeout" - PDNS_SERVER_NAME = "Name of the server in the URL, 'localhost' by default" [Links] API = "https://doc.powerdns.com/md/httpapi/README/" diff --git a/providers/dns/pdns/pdns_test.go b/providers/dns/pdns/pdns_test.go index 854b159d..70b386b8 100644 --- a/providers/dns/pdns/pdns_test.go +++ b/providers/dns/pdns/pdns_test.go @@ -76,30 +76,31 @@ func TestNewDNSProvider(t *testing.T) { func TestNewDNSProviderConfig(t *testing.T) { testCases := []struct { - desc string - apiKey string - host *url.URL - expected string + desc string + apiKey string + customAPIVersion int + host *url.URL + expected string }{ { desc: "success", apiKey: "123", - host: func() *url.URL { - u, _ := url.Parse("http://example.com") - return u - }(), + host: mustParse("http://example.com"), + }, + { + desc: "success custom API version", + apiKey: "123", + customAPIVersion: 1, + host: mustParse("http://example.com"), }, { desc: "missing credentials", expected: "pdns: API key missing", }, { - desc: "missing API key", - apiKey: "", - host: func() *url.URL { - u, _ := url.Parse("http://example.com") - return u - }(), + desc: "missing API key", + apiKey: "", + host: mustParse("http://example.com"), expected: "pdns: API key missing", }, { @@ -114,6 +115,7 @@ func TestNewDNSProviderConfig(t *testing.T) { config := NewDefaultConfig() config.APIKey = test.apiKey config.Host = test.host + config.APIVersion = test.customAPIVersion p, err := NewDNSProviderConfig(config) @@ -143,3 +145,11 @@ func TestLivePresentAndCleanup(t *testing.T) { err = provider.CleanUp(envTest.GetDomain(), "", "123d==") require.NoError(t, err) } + +func mustParse(rawURL string) *url.URL { + u, err := url.Parse(rawURL) + if err != nil { + panic(err) + } + return u +}