From 6db8efaf6c54a98b9801af9c6146461da4f435ae Mon Sep 17 00:00:00 2001 From: Fernandez Ludovic Date: Fri, 8 Nov 2024 15:30:41 +0100 Subject: [PATCH 01/12] rfc2136: add support for tsig-keygen generated file --- .../internal/fixtures/invalid_key.conf | 4 + .../internal/fixtures/mising_algo.conf | 3 + .../internal/fixtures/missing_secret.conf | 3 + .../dns/rfc2136/internal/fixtures/sample.conf | 4 + .../rfc2136/internal/fixtures/text_after.conf | 4 + .../internal/fixtures/text_before.conf | 4 + providers/dns/rfc2136/internal/readme.md | 10 ++ providers/dns/rfc2136/internal/tsigkey.go | 87 +++++++++++ .../dns/rfc2136/internal/tsigkey_test.go | 59 ++++++++ providers/dns/rfc2136/rfc2136.go | 41 +++++- providers/dns/rfc2136/rfc2136.toml | 7 +- providers/dns/rfc2136/rfc2136_test.go | 137 ++++++++++++++++++ 12 files changed, 353 insertions(+), 10 deletions(-) create mode 100644 providers/dns/rfc2136/internal/fixtures/invalid_key.conf create mode 100644 providers/dns/rfc2136/internal/fixtures/mising_algo.conf create mode 100644 providers/dns/rfc2136/internal/fixtures/missing_secret.conf create mode 100644 providers/dns/rfc2136/internal/fixtures/sample.conf create mode 100644 providers/dns/rfc2136/internal/fixtures/text_after.conf create mode 100644 providers/dns/rfc2136/internal/fixtures/text_before.conf create mode 100644 providers/dns/rfc2136/internal/readme.md create mode 100644 providers/dns/rfc2136/internal/tsigkey.go create mode 100644 providers/dns/rfc2136/internal/tsigkey_test.go diff --git a/providers/dns/rfc2136/internal/fixtures/invalid_key.conf b/providers/dns/rfc2136/internal/fixtures/invalid_key.conf new file mode 100644 index 0000000000..965888eaeb --- /dev/null +++ b/providers/dns/rfc2136/internal/fixtures/invalid_key.conf @@ -0,0 +1,4 @@ +key { + algorithm hmac-sha256; + secret "TCG5A6/lOHUGbW0e/9RYYbzWDFMlj1pIxCvybLBayBg="; +}; diff --git a/providers/dns/rfc2136/internal/fixtures/mising_algo.conf b/providers/dns/rfc2136/internal/fixtures/mising_algo.conf new file mode 100644 index 0000000000..e4959f6918 --- /dev/null +++ b/providers/dns/rfc2136/internal/fixtures/mising_algo.conf @@ -0,0 +1,3 @@ +key "lego" { + secret "TCG5A6/lOHUGbW0e/9RYYbzWDFMlj1pIxCvybLBayBg="; +}; diff --git a/providers/dns/rfc2136/internal/fixtures/missing_secret.conf b/providers/dns/rfc2136/internal/fixtures/missing_secret.conf new file mode 100644 index 0000000000..03fdba4b5a --- /dev/null +++ b/providers/dns/rfc2136/internal/fixtures/missing_secret.conf @@ -0,0 +1,3 @@ +key "lego" { + algorithm hmac-sha256; +}; diff --git a/providers/dns/rfc2136/internal/fixtures/sample.conf b/providers/dns/rfc2136/internal/fixtures/sample.conf new file mode 100644 index 0000000000..5ab8a37f85 --- /dev/null +++ b/providers/dns/rfc2136/internal/fixtures/sample.conf @@ -0,0 +1,4 @@ +key "lego" { + algorithm hmac-sha256; + secret "TCG5A6/lOHUGbW0e/9RYYbzWDFMlj1pIxCvybLBayBg="; +}; diff --git a/providers/dns/rfc2136/internal/fixtures/text_after.conf b/providers/dns/rfc2136/internal/fixtures/text_after.conf new file mode 100644 index 0000000000..5ab8a37f85 --- /dev/null +++ b/providers/dns/rfc2136/internal/fixtures/text_after.conf @@ -0,0 +1,4 @@ +key "lego" { + algorithm hmac-sha256; + secret "TCG5A6/lOHUGbW0e/9RYYbzWDFMlj1pIxCvybLBayBg="; +}; diff --git a/providers/dns/rfc2136/internal/fixtures/text_before.conf b/providers/dns/rfc2136/internal/fixtures/text_before.conf new file mode 100644 index 0000000000..5ab8a37f85 --- /dev/null +++ b/providers/dns/rfc2136/internal/fixtures/text_before.conf @@ -0,0 +1,4 @@ +key "lego" { + algorithm hmac-sha256; + secret "TCG5A6/lOHUGbW0e/9RYYbzWDFMlj1pIxCvybLBayBg="; +}; diff --git a/providers/dns/rfc2136/internal/readme.md b/providers/dns/rfc2136/internal/readme.md new file mode 100644 index 0000000000..fb4dd6bdef --- /dev/null +++ b/providers/dns/rfc2136/internal/readme.md @@ -0,0 +1,10 @@ +# TSIG Key File + +How to generate example: + +```console +$ docker run --rm -it -v $(pwd):/app -w /app alpine sh +/app # apk add bind +/app # tsig-keygen lego > sample1.conf +/app # tsig-keygen -a hmac-sha512 lego > sample2.conf +``` diff --git a/providers/dns/rfc2136/internal/tsigkey.go b/providers/dns/rfc2136/internal/tsigkey.go new file mode 100644 index 0000000000..499f5345f6 --- /dev/null +++ b/providers/dns/rfc2136/internal/tsigkey.go @@ -0,0 +1,87 @@ +package internal + +import ( + "bufio" + "bytes" + "fmt" + "os" + "strings" + + "github.com/go-viper/mapstructure/v2" +) + +type Key struct { + Name string `mapstructure:"name"` + Algorithm string `mapstructure:"algorithm"` + Secret string `mapstructure:"secret"` +} + +// ReadTSIGFile reads TSIG key file generated with `tsig-keygen`. +func ReadTSIGFile(filename string) (*Key, error) { + raw, err := os.ReadFile(filename) + if err != nil { + return nil, err + } + + data := make(map[string]string) + + var read bool + + scanner := bufio.NewScanner(bytes.NewReader(raw)) + for scanner.Scan() { + line := strings.TrimSuffix(scanner.Text(), ";") + + if line == "}" { + break + } + + switch { + case strings.HasPrefix(line, "key "): + read = true + fields := strings.Fields(line) + + if len(fields) != 3 { + return nil, fmt.Errorf("invalid key line: %s", line) + } + + data["name"] = safeUnquote(fields[1]) + + case !read: + continue + + default: + fields := strings.Fields(line) + + if len(fields) != 2 { + continue + } + + data[safeUnquote(fields[0])] = safeUnquote(fields[1]) + } + } + + key := &Key{} + err = mapstructure.Decode(data, key) + if err != nil { + return nil, err + } + + if key.Algorithm != "" { + // to be compatible with https://github.com/miekg/dns/blob/master/tsig.go + key.Algorithm += "." + } + + return key, nil +} + +func safeUnquote(v string) string { + if v == "" { + return v + } + + if len(v)-1 != 0 && v[0] == '"' && v[len(v)-1] == '"' { + return v[1 : len(v)-1] + } + + return v +} diff --git a/providers/dns/rfc2136/internal/tsigkey_test.go b/providers/dns/rfc2136/internal/tsigkey_test.go new file mode 100644 index 0000000000..3a1b89c5ad --- /dev/null +++ b/providers/dns/rfc2136/internal/tsigkey_test.go @@ -0,0 +1,59 @@ +package internal + +import ( + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestReadTSIGFile(t *testing.T) { + testCases := []struct { + desc string + filename string + expected *Key + }{ + { + desc: "basic", + filename: "sample.conf", + expected: &Key{Name: "lego", Algorithm: "hmac-sha256.", Secret: "TCG5A6/lOHUGbW0e/9RYYbzWDFMlj1pIxCvybLBayBg="}, + }, + { + desc: "data before the key", + filename: "text_before.conf", + expected: &Key{Name: "lego", Algorithm: "hmac-sha256.", Secret: "TCG5A6/lOHUGbW0e/9RYYbzWDFMlj1pIxCvybLBayBg="}, + }, + { + desc: "data after the key", + filename: "text_after.conf", + expected: &Key{Name: "lego", Algorithm: "hmac-sha256.", Secret: "TCG5A6/lOHUGbW0e/9RYYbzWDFMlj1pIxCvybLBayBg="}, + }, + { + desc: "missing secret", + filename: "missing_secret.conf", + expected: &Key{Name: "lego", Algorithm: "hmac-sha256."}, + }, + { + desc: "missing algorithm", + filename: "mising_algo.conf", + expected: &Key{Name: "lego", Secret: "TCG5A6/lOHUGbW0e/9RYYbzWDFMlj1pIxCvybLBayBg="}, + }, + } + + for _, test := range testCases { + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + key, err := ReadTSIGFile(filepath.Join("fixtures", test.filename)) + require.NoError(t, err) + + assert.Equal(t, test.expected, key) + }) + } +} + +func TestReadTSIGFile_error(t *testing.T) { + _, err := ReadTSIGFile(filepath.Join("fixtures", "invalid_key.conf")) + require.Error(t, err) +} diff --git a/providers/dns/rfc2136/rfc2136.go b/providers/dns/rfc2136/rfc2136.go index 8a7dedc803..33ea7557ad 100644 --- a/providers/dns/rfc2136/rfc2136.go +++ b/providers/dns/rfc2136/rfc2136.go @@ -10,6 +10,7 @@ import ( "github.com/go-acme/lego/v4/challenge/dns01" "github.com/go-acme/lego/v4/platform/config/env" + "github.com/go-acme/lego/v4/providers/dns/rfc2136/internal" "github.com/miekg/dns" ) @@ -17,11 +18,14 @@ import ( const ( envNamespace = "RFC2136_" + EnvTSIGFile = envNamespace + "TSIG_FILE" + EnvTSIGKey = envNamespace + "TSIG_KEY" EnvTSIGSecret = envNamespace + "TSIG_SECRET" EnvTSIGAlgorithm = envNamespace + "TSIG_ALGORITHM" - EnvNameserver = envNamespace + "NAMESERVER" - EnvDNSTimeout = envNamespace + "DNS_TIMEOUT" + + EnvNameserver = envNamespace + "NAMESERVER" + EnvDNSTimeout = envNamespace + "DNS_TIMEOUT" EnvTTL = envNamespace + "TTL" EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT" @@ -31,10 +35,14 @@ const ( // Config is used to configure the creation of the DNSProvider. type Config struct { - Nameserver string - TSIGAlgorithm string - TSIGKey string - TSIGSecret string + Nameserver string + + TSIGFile string + + TSIGAlgorithm string + TSIGKey string + TSIGSecret string + PropagationTimeout time.Duration PollingInterval time.Duration TTL int @@ -76,6 +84,9 @@ func NewDNSProvider() (*DNSProvider, error) { config := NewDefaultConfig() config.Nameserver = values[EnvNameserver] + + config.TSIGFile = env.GetOrDefaultString(EnvTSIGFile, "") + config.TSIGKey = env.GetOrFile(EnvTSIGKey) config.TSIGSecret = env.GetOrFile(EnvTSIGSecret) @@ -92,10 +103,28 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { return nil, errors.New("rfc2136: nameserver missing") } + if config.TSIGFile != "" { + key, err := internal.ReadTSIGFile(config.TSIGFile) + if err != nil { + return nil, fmt.Errorf("rfc2136: read TSIG file: %w", err) + } + + config.TSIGAlgorithm = key.Algorithm + config.TSIGKey = key.Name + config.TSIGSecret = key.Secret + } + if config.TSIGAlgorithm == "" { config.TSIGAlgorithm = dns.HmacSHA1 } + switch config.TSIGAlgorithm { + case dns.HmacSHA1, dns.HmacSHA224, dns.HmacSHA256, dns.HmacSHA384, dns.HmacSHA512: + // valid algorithm + default: + return nil, fmt.Errorf("rfc2136: unsupported TSIG algorithm: %s", config.TSIGAlgorithm) + } + // Append the default DNS port if none is specified. if _, _, err := net.SplitHostPort(config.Nameserver); err != nil { if strings.Contains(err.Error(), "missing port") { diff --git a/providers/dns/rfc2136/rfc2136.toml b/providers/dns/rfc2136/rfc2136.toml index 621acc8026..74ad120806 100644 --- a/providers/dns/rfc2136/rfc2136.toml +++ b/providers/dns/rfc2136/rfc2136.toml @@ -16,10 +16,8 @@ lego --email you@example.com --dns rfc2136 -d '*.example.com' -d example.com 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 d "*.example.com" -d example.com run +RFC2136_TSIG_FILE="$keyfile" \ +lego --email you@example.com --dns rfc2136 d '*.example.com' -d example.com run ''' [Configuration] @@ -29,6 +27,7 @@ lego --email you@example.com --dns rfc2136 d "*.example.com" -d example.com run 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_NAMESERVER = 'Network address in the form "host" or "host:port"' [Configuration.Additional] + RFC2136_TSIG_FILE = "Path to a key file generated by tsig-keygen" RFC2136_POLLING_INTERVAL = "Time between DNS propagation check" RFC2136_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" RFC2136_TTL = "The TTL of the TXT record used for the DNS challenge" diff --git a/providers/dns/rfc2136/rfc2136_test.go b/providers/dns/rfc2136/rfc2136_test.go index 235ce4e4e5..5cc78c1e74 100644 --- a/providers/dns/rfc2136/rfc2136_test.go +++ b/providers/dns/rfc2136/rfc2136_test.go @@ -10,6 +10,7 @@ import ( "time" "github.com/go-acme/lego/v4/challenge/dns01" + "github.com/go-acme/lego/v4/platform/tester" "github.com/miekg/dns" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -26,6 +27,142 @@ const ( fakeTsigSecret = "IwBTJx9wrDp4Y1RyC3H0gA==" ) +const envDomain = envNamespace + "DOMAIN" + +var envTest = tester.NewEnvTest( + EnvTSIGFile, + EnvTSIGKey, + EnvTSIGSecret, + EnvTSIGAlgorithm, + EnvNameserver, + EnvDNSTimeout, +).WithDomain(envDomain) + +func TestNewDNSProvider(t *testing.T) { + testCases := []struct { + desc string + envVars map[string]string + expected string + }{ + { + desc: "success", + envVars: map[string]string{ + EnvNameserver: "example.com", + }, + }, + { + desc: "missing nameserver", + envVars: map[string]string{ + EnvNameserver: "", + }, + expected: "rfc2136: some credentials information are missing: RFC2136_NAMESERVER", + }, + { + desc: "invalid algorithm", + envVars: map[string]string{ + EnvNameserver: "example.com", + EnvTSIGKey: "", + EnvTSIGSecret: "", + EnvTSIGAlgorithm: "foo", + }, + expected: "rfc2136: unsupported TSIG algorithm: foo", + }, + { + desc: "valid TSIG file", + envVars: map[string]string{ + EnvNameserver: "example.com", + EnvTSIGFile: "./internal/fixtures/sample.conf", + }, + }, + { + desc: "invalid TSIG file", + envVars: map[string]string{ + EnvNameserver: "example.com", + EnvTSIGFile: "./internal/fixtures/invalid_key.conf", + }, + expected: "rfc2136: read TSIG file: invalid key line: key {", + }, + } + + for _, test := range testCases { + t.Run(test.desc, func(t *testing.T) { + defer envTest.RestoreEnv() + envTest.ClearEnv() + + envTest.Apply(test.envVars) + + p, err := NewDNSProvider() + + if test.expected == "" { + require.NoError(t, err) + require.NotNil(t, p) + require.NotNil(t, p.config) + } else { + require.EqualError(t, err, test.expected) + } + }) + } +} + +func TestNewDNSProviderConfig(t *testing.T) { + testCases := []struct { + desc string + expected string + nameserver string + tsigFile string + tsigAlgorithm string + tsigKey string + tsigSecret string + }{ + { + desc: "success", + nameserver: "example.com", + }, + { + desc: "missing nameserver", + expected: "rfc2136: nameserver missing", + }, + { + desc: "invalid algorithm", + nameserver: "example.com", + tsigAlgorithm: "foo", + expected: "rfc2136: unsupported TSIG algorithm: foo", + }, + { + desc: "valid TSIG file", + nameserver: "example.com", + tsigFile: "./internal/fixtures/sample.conf", + }, + { + desc: "invalid TSIG file", + nameserver: "example.com", + tsigFile: "./internal/fixtures/invalid_key.conf", + expected: "rfc2136: read TSIG file: invalid key line: key {", + }, + } + + for _, test := range testCases { + t.Run(test.desc, func(t *testing.T) { + config := NewDefaultConfig() + config.Nameserver = test.nameserver + config.TSIGFile = test.tsigFile + config.TSIGAlgorithm = test.tsigAlgorithm + config.TSIGKey = test.tsigKey + config.TSIGSecret = test.tsigSecret + + p, err := NewDNSProviderConfig(config) + + if test.expected == "" { + require.NoError(t, err) + require.NotNil(t, p) + require.NotNil(t, p.config) + } else { + require.EqualError(t, err, test.expected) + } + }) + } +} + func TestCanaryLocalTestServer(t *testing.T) { dns01.ClearFqdnCache() dns.HandleFunc("example.com.", serverHandlerHello) From b5abda292fdf05f78ce17494401c5227e6fae5ae Mon Sep 17 00:00:00 2001 From: Fernandez Ludovic Date: Fri, 8 Nov 2024 15:33:28 +0100 Subject: [PATCH 02/12] chore: generate --- cmd/zz_gen_cmd_dnshelp.go | 1 + docs/content/dns/zz_gen_rfc2136.md | 7 +++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/zz_gen_cmd_dnshelp.go b/cmd/zz_gen_cmd_dnshelp.go index 936a99ec4b..66ec72a185 100644 --- a/cmd/zz_gen_cmd_dnshelp.go +++ b/cmd/zz_gen_cmd_dnshelp.go @@ -2438,6 +2438,7 @@ func displayDNSHelp(w io.Writer, name string) error { 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_TSIG_FILE": Path to a key file generated by tsig-keygen`) ew.writeln(` - "RFC2136_TTL": The TTL of the TXT record used for the DNS challenge`) ew.writeln() diff --git a/docs/content/dns/zz_gen_rfc2136.md b/docs/content/dns/zz_gen_rfc2136.md index d203992658..0afc0211b2 100644 --- a/docs/content/dns/zz_gen_rfc2136.md +++ b/docs/content/dns/zz_gen_rfc2136.md @@ -37,10 +37,8 @@ lego --email you@example.com --dns rfc2136 -d '*.example.com' -d example.com 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 d "*.example.com" -d example.com run +RFC2136_TSIG_FILE="$keyfile" \ +lego --email you@example.com --dns rfc2136 d '*.example.com' -d example.com run ``` @@ -67,6 +65,7 @@ More information [here]({{% ref "dns#configuration-and-credentials" %}}). | `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_TSIG_FILE` | Path to a key file generated by tsig-keygen | | `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. From 3ec6afc6b9361e3a21b2a9a06dcc0d25bb553517 Mon Sep 17 00:00:00 2001 From: Fernandez Ludovic Date: Fri, 8 Nov 2024 15:57:27 +0100 Subject: [PATCH 03/12] chore: open file instead of Readfile --- providers/dns/rfc2136/internal/tsigkey.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/providers/dns/rfc2136/internal/tsigkey.go b/providers/dns/rfc2136/internal/tsigkey.go index 499f5345f6..8d0e7107e2 100644 --- a/providers/dns/rfc2136/internal/tsigkey.go +++ b/providers/dns/rfc2136/internal/tsigkey.go @@ -2,7 +2,6 @@ package internal import ( "bufio" - "bytes" "fmt" "os" "strings" @@ -18,16 +17,18 @@ type Key struct { // ReadTSIGFile reads TSIG key file generated with `tsig-keygen`. func ReadTSIGFile(filename string) (*Key, error) { - raw, err := os.ReadFile(filename) + file, err := os.Open(filename) if err != nil { return nil, err } + defer func() { _ = file.Close() }() + data := make(map[string]string) var read bool - scanner := bufio.NewScanner(bytes.NewReader(raw)) + scanner := bufio.NewScanner(file) for scanner.Scan() { line := strings.TrimSuffix(scanner.Text(), ";") From f22e1471e7898870974f3e123bcc25176db84c59 Mon Sep 17 00:00:00 2001 From: Fernandez Ludovic Date: Fri, 8 Nov 2024 21:51:33 +0100 Subject: [PATCH 04/12] fix: parsing --- providers/dns/rfc2136/internal/fixtures/text_after.conf | 5 +++++ providers/dns/rfc2136/internal/fixtures/text_before.conf | 4 ++++ providers/dns/rfc2136/internal/tsigkey.go | 2 +- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/providers/dns/rfc2136/internal/fixtures/text_after.conf b/providers/dns/rfc2136/internal/fixtures/text_after.conf index 5ab8a37f85..30e32fdc2e 100644 --- a/providers/dns/rfc2136/internal/fixtures/text_after.conf +++ b/providers/dns/rfc2136/internal/fixtures/text_after.conf @@ -2,3 +2,8 @@ key "lego" { algorithm hmac-sha256; secret "TCG5A6/lOHUGbW0e/9RYYbzWDFMlj1pIxCvybLBayBg="; }; + +key "test" { + algorithm hmac-sha512; + secret "v6CkK3gop6HXj4+dcWiLXLGSYKVY5J1cTMjDsdl/Ah9B8aWfTgjwFBoHHyiHWSyvwWPDuEIRs2Pqm8nedca4+g=="; +}; diff --git a/providers/dns/rfc2136/internal/fixtures/text_before.conf b/providers/dns/rfc2136/internal/fixtures/text_before.conf index 5ab8a37f85..3babf22151 100644 --- a/providers/dns/rfc2136/internal/fixtures/text_before.conf +++ b/providers/dns/rfc2136/internal/fixtures/text_before.conf @@ -1,3 +1,7 @@ +foo { + bar example; +}; + key "lego" { algorithm hmac-sha256; secret "TCG5A6/lOHUGbW0e/9RYYbzWDFMlj1pIxCvybLBayBg="; diff --git a/providers/dns/rfc2136/internal/tsigkey.go b/providers/dns/rfc2136/internal/tsigkey.go index 8d0e7107e2..8261f8809a 100644 --- a/providers/dns/rfc2136/internal/tsigkey.go +++ b/providers/dns/rfc2136/internal/tsigkey.go @@ -32,7 +32,7 @@ func ReadTSIGFile(filename string) (*Key, error) { for scanner.Scan() { line := strings.TrimSuffix(scanner.Text(), ";") - if line == "}" { + if read && line == "}" { break } From 542c49f596c342a24a32e0a784c9364d725880b8 Mon Sep 17 00:00:00 2001 From: Fernandez Ludovic Date: Fri, 8 Nov 2024 22:38:52 +0100 Subject: [PATCH 05/12] chore: simplify --- .../internal/fixtures/invalid_field.conf | 4 ++ providers/dns/rfc2136/internal/tsigkey.go | 17 +++++---- .../dns/rfc2136/internal/tsigkey_test.go | 38 +++++++++++++++++-- providers/dns/rfc2136/rfc2136.go | 2 +- providers/dns/rfc2136/rfc2136_test.go | 4 +- 5 files changed, 51 insertions(+), 14 deletions(-) create mode 100644 providers/dns/rfc2136/internal/fixtures/invalid_field.conf diff --git a/providers/dns/rfc2136/internal/fixtures/invalid_field.conf b/providers/dns/rfc2136/internal/fixtures/invalid_field.conf new file mode 100644 index 0000000000..15309f366f --- /dev/null +++ b/providers/dns/rfc2136/internal/fixtures/invalid_field.conf @@ -0,0 +1,4 @@ +key "lego" { + algorithm; + secret "TCG5A6/lOHUGbW0e/9RYYbzWDFMlj1pIxCvybLBayBg="; +}; diff --git a/providers/dns/rfc2136/internal/tsigkey.go b/providers/dns/rfc2136/internal/tsigkey.go index 8261f8809a..9227079c38 100644 --- a/providers/dns/rfc2136/internal/tsigkey.go +++ b/providers/dns/rfc2136/internal/tsigkey.go @@ -19,7 +19,7 @@ type Key struct { func ReadTSIGFile(filename string) (*Key, error) { file, err := os.Open(filename) if err != nil { - return nil, err + return nil, fmt.Errorf("open file: %w", err) } defer func() { _ = file.Close() }() @@ -30,16 +30,21 @@ func ReadTSIGFile(filename string) (*Key, error) { scanner := bufio.NewScanner(file) for scanner.Scan() { - line := strings.TrimSuffix(scanner.Text(), ";") + line := strings.TrimSpace(strings.TrimSuffix(scanner.Text(), ";")) + + if line == "" { + continue + } if read && line == "}" { break } + fields := strings.Fields(line) + switch { - case strings.HasPrefix(line, "key "): + case fields[0] == "key": read = true - fields := strings.Fields(line) if len(fields) != 3 { return nil, fmt.Errorf("invalid key line: %s", line) @@ -51,8 +56,6 @@ func ReadTSIGFile(filename string) (*Key, error) { continue default: - fields := strings.Fields(line) - if len(fields) != 2 { continue } @@ -64,7 +67,7 @@ func ReadTSIGFile(filename string) (*Key, error) { key := &Key{} err = mapstructure.Decode(data, key) if err != nil { - return nil, err + return nil, fmt.Errorf("decode key: %w", err) } if key.Algorithm != "" { diff --git a/providers/dns/rfc2136/internal/tsigkey_test.go b/providers/dns/rfc2136/internal/tsigkey_test.go index 3a1b89c5ad..8493a428fd 100644 --- a/providers/dns/rfc2136/internal/tsigkey_test.go +++ b/providers/dns/rfc2136/internal/tsigkey_test.go @@ -30,15 +30,20 @@ func TestReadTSIGFile(t *testing.T) { expected: &Key{Name: "lego", Algorithm: "hmac-sha256.", Secret: "TCG5A6/lOHUGbW0e/9RYYbzWDFMlj1pIxCvybLBayBg="}, }, { - desc: "missing secret", + desc: "ignore missing secret", filename: "missing_secret.conf", expected: &Key{Name: "lego", Algorithm: "hmac-sha256."}, }, { - desc: "missing algorithm", + desc: "ignore missing algorithm", filename: "mising_algo.conf", expected: &Key{Name: "lego", Secret: "TCG5A6/lOHUGbW0e/9RYYbzWDFMlj1pIxCvybLBayBg="}, }, + { + desc: "ignore invalid field format", + filename: "invalid_field.conf", + expected: &Key{Name: "lego", Secret: "TCG5A6/lOHUGbW0e/9RYYbzWDFMlj1pIxCvybLBayBg="}, + }, } for _, test := range testCases { @@ -54,6 +59,31 @@ func TestReadTSIGFile(t *testing.T) { } func TestReadTSIGFile_error(t *testing.T) { - _, err := ReadTSIGFile(filepath.Join("fixtures", "invalid_key.conf")) - require.Error(t, err) + testCases := []struct { + desc string + filename string + expected string + }{ + { + desc: "missing file", + filename: "missing.conf", + expected: "open file: open fixtures/missing.conf: no such file or directory", + }, + { + desc: "invalid key format", + filename: "invalid_key.conf", + expected: "invalid key line: key {", + }, + } + + for _, test := range testCases { + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + _, err := ReadTSIGFile(filepath.Join("fixtures", test.filename)) + require.Error(t, err) + + require.EqualError(t, err, test.expected) + }) + } } diff --git a/providers/dns/rfc2136/rfc2136.go b/providers/dns/rfc2136/rfc2136.go index 33ea7557ad..275903caec 100644 --- a/providers/dns/rfc2136/rfc2136.go +++ b/providers/dns/rfc2136/rfc2136.go @@ -106,7 +106,7 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { if config.TSIGFile != "" { key, err := internal.ReadTSIGFile(config.TSIGFile) if err != nil { - return nil, fmt.Errorf("rfc2136: read TSIG file: %w", err) + return nil, fmt.Errorf("rfc2136: read TSIG file %s: %w", config.TSIGFile, err) } config.TSIGAlgorithm = key.Algorithm diff --git a/providers/dns/rfc2136/rfc2136_test.go b/providers/dns/rfc2136/rfc2136_test.go index 5cc78c1e74..a284893cbb 100644 --- a/providers/dns/rfc2136/rfc2136_test.go +++ b/providers/dns/rfc2136/rfc2136_test.go @@ -80,7 +80,7 @@ func TestNewDNSProvider(t *testing.T) { EnvNameserver: "example.com", EnvTSIGFile: "./internal/fixtures/invalid_key.conf", }, - expected: "rfc2136: read TSIG file: invalid key line: key {", + expected: "rfc2136: read TSIG file ./internal/fixtures/invalid_key.conf: invalid key line: key {", }, } @@ -137,7 +137,7 @@ func TestNewDNSProviderConfig(t *testing.T) { desc: "invalid TSIG file", nameserver: "example.com", tsigFile: "./internal/fixtures/invalid_key.conf", - expected: "rfc2136: read TSIG file: invalid key line: key {", + expected: "rfc2136: read TSIG file ./internal/fixtures/invalid_key.conf: invalid key line: key {", }, } From f063de346ab60b7bb7b2fbc4c5c742aeb9cba1a5 Mon Sep 17 00:00:00 2001 From: Fernandez Ludovic Date: Fri, 8 Nov 2024 22:55:11 +0100 Subject: [PATCH 06/12] chore: simplify --- providers/dns/rfc2136/internal/tsigkey.go | 5 --- .../dns/rfc2136/internal/tsigkey_test.go | 8 ++-- providers/dns/rfc2136/rfc2136.go | 37 ++++++++++--------- providers/dns/rfc2136/rfc2136_test.go | 4 +- 4 files changed, 26 insertions(+), 28 deletions(-) diff --git a/providers/dns/rfc2136/internal/tsigkey.go b/providers/dns/rfc2136/internal/tsigkey.go index 9227079c38..2c871bee76 100644 --- a/providers/dns/rfc2136/internal/tsigkey.go +++ b/providers/dns/rfc2136/internal/tsigkey.go @@ -70,11 +70,6 @@ func ReadTSIGFile(filename string) (*Key, error) { return nil, fmt.Errorf("decode key: %w", err) } - if key.Algorithm != "" { - // to be compatible with https://github.com/miekg/dns/blob/master/tsig.go - key.Algorithm += "." - } - return key, nil } diff --git a/providers/dns/rfc2136/internal/tsigkey_test.go b/providers/dns/rfc2136/internal/tsigkey_test.go index 8493a428fd..c76137fa33 100644 --- a/providers/dns/rfc2136/internal/tsigkey_test.go +++ b/providers/dns/rfc2136/internal/tsigkey_test.go @@ -17,22 +17,22 @@ func TestReadTSIGFile(t *testing.T) { { desc: "basic", filename: "sample.conf", - expected: &Key{Name: "lego", Algorithm: "hmac-sha256.", Secret: "TCG5A6/lOHUGbW0e/9RYYbzWDFMlj1pIxCvybLBayBg="}, + expected: &Key{Name: "lego", Algorithm: "hmac-sha256", Secret: "TCG5A6/lOHUGbW0e/9RYYbzWDFMlj1pIxCvybLBayBg="}, }, { desc: "data before the key", filename: "text_before.conf", - expected: &Key{Name: "lego", Algorithm: "hmac-sha256.", Secret: "TCG5A6/lOHUGbW0e/9RYYbzWDFMlj1pIxCvybLBayBg="}, + expected: &Key{Name: "lego", Algorithm: "hmac-sha256", Secret: "TCG5A6/lOHUGbW0e/9RYYbzWDFMlj1pIxCvybLBayBg="}, }, { desc: "data after the key", filename: "text_after.conf", - expected: &Key{Name: "lego", Algorithm: "hmac-sha256.", Secret: "TCG5A6/lOHUGbW0e/9RYYbzWDFMlj1pIxCvybLBayBg="}, + expected: &Key{Name: "lego", Algorithm: "hmac-sha256", Secret: "TCG5A6/lOHUGbW0e/9RYYbzWDFMlj1pIxCvybLBayBg="}, }, { desc: "ignore missing secret", filename: "missing_secret.conf", - expected: &Key{Name: "lego", Algorithm: "hmac-sha256."}, + expected: &Key{Name: "lego", Algorithm: "hmac-sha256"}, }, { desc: "ignore missing algorithm", diff --git a/providers/dns/rfc2136/rfc2136.go b/providers/dns/rfc2136/rfc2136.go index 275903caec..bd1d58a0cf 100644 --- a/providers/dns/rfc2136/rfc2136.go +++ b/providers/dns/rfc2136/rfc2136.go @@ -114,17 +114,6 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { config.TSIGSecret = key.Secret } - if config.TSIGAlgorithm == "" { - config.TSIGAlgorithm = dns.HmacSHA1 - } - - switch config.TSIGAlgorithm { - case dns.HmacSHA1, dns.HmacSHA224, dns.HmacSHA256, dns.HmacSHA384, dns.HmacSHA512: - // valid algorithm - default: - return nil, fmt.Errorf("rfc2136: unsupported TSIG algorithm: %s", config.TSIGAlgorithm) - } - // Append the default DNS port if none is specified. if _, _, err := net.SplitHostPort(config.Nameserver); err != nil { if strings.Contains(err.Error(), "missing port") { @@ -137,6 +126,23 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { if config.TSIGKey == "" || config.TSIGSecret == "" { config.TSIGKey = "" config.TSIGSecret = "" + } else { + // zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2) + config.TSIGKey = strings.ToLower(dns.Fqdn(config.TSIGKey)) + } + + if config.TSIGAlgorithm == "" { + config.TSIGAlgorithm = dns.HmacSHA1 + } else { + // To be compatible with https://github.com/miekg/dns/blob/master/tsig.go + config.TSIGAlgorithm = dns.Fqdn(config.TSIGAlgorithm) + } + + switch config.TSIGAlgorithm { + case dns.HmacSHA1, dns.HmacSHA224, dns.HmacSHA256, dns.HmacSHA384, dns.HmacSHA512: + // valid algorithm + default: + return nil, fmt.Errorf("rfc2136: unsupported TSIG algorithm: %s", config.TSIGAlgorithm) } return &DNSProvider{config: config}, nil @@ -208,13 +214,10 @@ func (d *DNSProvider) changeRecord(action, fqdn, value string, ttl int) error { // TSIG authentication / msg signing if d.config.TSIGKey != "" && d.config.TSIGSecret != "" { - key := strings.ToLower(dns.Fqdn(d.config.TSIGKey)) - alg := dns.Fqdn(d.config.TSIGAlgorithm) - m.SetTsig(key, alg, 300, time.Now().Unix()) + m.SetTsig(d.config.TSIGKey, d.config.TSIGAlgorithm, 300, time.Now().Unix()) - // secret(s) for Tsig map[], - // zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2) - c.TsigSecret = map[string]string{key: d.config.TSIGSecret} + // Secret(s) for TSIG map[]. + c.TsigSecret = map[string]string{d.config.TSIGKey: d.config.TSIGSecret} } // Send the query diff --git a/providers/dns/rfc2136/rfc2136_test.go b/providers/dns/rfc2136/rfc2136_test.go index a284893cbb..80fdc69cbd 100644 --- a/providers/dns/rfc2136/rfc2136_test.go +++ b/providers/dns/rfc2136/rfc2136_test.go @@ -65,7 +65,7 @@ func TestNewDNSProvider(t *testing.T) { EnvTSIGSecret: "", EnvTSIGAlgorithm: "foo", }, - expected: "rfc2136: unsupported TSIG algorithm: foo", + expected: "rfc2136: unsupported TSIG algorithm: foo.", }, { desc: "valid TSIG file", @@ -126,7 +126,7 @@ func TestNewDNSProviderConfig(t *testing.T) { desc: "invalid algorithm", nameserver: "example.com", tsigAlgorithm: "foo", - expected: "rfc2136: unsupported TSIG algorithm: foo", + expected: "rfc2136: unsupported TSIG algorithm: foo.", }, { desc: "valid TSIG file", From 3a1b0a90df8dcff9d240638fe496e8f5fd7131a9 Mon Sep 17 00:00:00 2001 From: Fernandez Ludovic Date: Fri, 8 Nov 2024 22:59:47 +0100 Subject: [PATCH 07/12] docs: more realistic example --- .../dns/rfc2136/internal/fixtures/invalid_field.conf | 2 +- .../dns/rfc2136/internal/fixtures/mising_algo.conf | 2 +- .../rfc2136/internal/fixtures/missing_secret.conf | 2 +- providers/dns/rfc2136/internal/fixtures/sample.conf | 2 +- .../dns/rfc2136/internal/fixtures/text_after.conf | 4 ++-- .../dns/rfc2136/internal/fixtures/text_before.conf | 2 +- providers/dns/rfc2136/internal/readme.md | 4 ++-- providers/dns/rfc2136/internal/tsigkey_test.go | 12 ++++++------ providers/dns/rfc2136/rfc2136.toml | 12 ++++++------ 9 files changed, 21 insertions(+), 21 deletions(-) diff --git a/providers/dns/rfc2136/internal/fixtures/invalid_field.conf b/providers/dns/rfc2136/internal/fixtures/invalid_field.conf index 15309f366f..07c6a7be24 100644 --- a/providers/dns/rfc2136/internal/fixtures/invalid_field.conf +++ b/providers/dns/rfc2136/internal/fixtures/invalid_field.conf @@ -1,4 +1,4 @@ -key "lego" { +key "example.com" { algorithm; secret "TCG5A6/lOHUGbW0e/9RYYbzWDFMlj1pIxCvybLBayBg="; }; diff --git a/providers/dns/rfc2136/internal/fixtures/mising_algo.conf b/providers/dns/rfc2136/internal/fixtures/mising_algo.conf index e4959f6918..530323172d 100644 --- a/providers/dns/rfc2136/internal/fixtures/mising_algo.conf +++ b/providers/dns/rfc2136/internal/fixtures/mising_algo.conf @@ -1,3 +1,3 @@ -key "lego" { +key "example.com" { secret "TCG5A6/lOHUGbW0e/9RYYbzWDFMlj1pIxCvybLBayBg="; }; diff --git a/providers/dns/rfc2136/internal/fixtures/missing_secret.conf b/providers/dns/rfc2136/internal/fixtures/missing_secret.conf index 03fdba4b5a..f45eeac300 100644 --- a/providers/dns/rfc2136/internal/fixtures/missing_secret.conf +++ b/providers/dns/rfc2136/internal/fixtures/missing_secret.conf @@ -1,3 +1,3 @@ -key "lego" { +key "example.com" { algorithm hmac-sha256; }; diff --git a/providers/dns/rfc2136/internal/fixtures/sample.conf b/providers/dns/rfc2136/internal/fixtures/sample.conf index 5ab8a37f85..6e249e8a58 100644 --- a/providers/dns/rfc2136/internal/fixtures/sample.conf +++ b/providers/dns/rfc2136/internal/fixtures/sample.conf @@ -1,4 +1,4 @@ -key "lego" { +key "example.com" { algorithm hmac-sha256; secret "TCG5A6/lOHUGbW0e/9RYYbzWDFMlj1pIxCvybLBayBg="; }; diff --git a/providers/dns/rfc2136/internal/fixtures/text_after.conf b/providers/dns/rfc2136/internal/fixtures/text_after.conf index 30e32fdc2e..9b1cf8e587 100644 --- a/providers/dns/rfc2136/internal/fixtures/text_after.conf +++ b/providers/dns/rfc2136/internal/fixtures/text_after.conf @@ -1,9 +1,9 @@ -key "lego" { +key "example.com" { algorithm hmac-sha256; secret "TCG5A6/lOHUGbW0e/9RYYbzWDFMlj1pIxCvybLBayBg="; }; -key "test" { +key "example.org" { algorithm hmac-sha512; secret "v6CkK3gop6HXj4+dcWiLXLGSYKVY5J1cTMjDsdl/Ah9B8aWfTgjwFBoHHyiHWSyvwWPDuEIRs2Pqm8nedca4+g=="; }; diff --git a/providers/dns/rfc2136/internal/fixtures/text_before.conf b/providers/dns/rfc2136/internal/fixtures/text_before.conf index 3babf22151..0a8415b21e 100644 --- a/providers/dns/rfc2136/internal/fixtures/text_before.conf +++ b/providers/dns/rfc2136/internal/fixtures/text_before.conf @@ -2,7 +2,7 @@ foo { bar example; }; -key "lego" { +key "example.com" { algorithm hmac-sha256; secret "TCG5A6/lOHUGbW0e/9RYYbzWDFMlj1pIxCvybLBayBg="; }; diff --git a/providers/dns/rfc2136/internal/readme.md b/providers/dns/rfc2136/internal/readme.md index fb4dd6bdef..d0ecae7f49 100644 --- a/providers/dns/rfc2136/internal/readme.md +++ b/providers/dns/rfc2136/internal/readme.md @@ -5,6 +5,6 @@ How to generate example: ```console $ docker run --rm -it -v $(pwd):/app -w /app alpine sh /app # apk add bind -/app # tsig-keygen lego > sample1.conf -/app # tsig-keygen -a hmac-sha512 lego > sample2.conf +/app # tsig-keygen example.com > sample1.conf +/app # tsig-keygen -a hmac-sha512 example.com > sample2.conf ``` diff --git a/providers/dns/rfc2136/internal/tsigkey_test.go b/providers/dns/rfc2136/internal/tsigkey_test.go index c76137fa33..062a4d7043 100644 --- a/providers/dns/rfc2136/internal/tsigkey_test.go +++ b/providers/dns/rfc2136/internal/tsigkey_test.go @@ -17,32 +17,32 @@ func TestReadTSIGFile(t *testing.T) { { desc: "basic", filename: "sample.conf", - expected: &Key{Name: "lego", Algorithm: "hmac-sha256", Secret: "TCG5A6/lOHUGbW0e/9RYYbzWDFMlj1pIxCvybLBayBg="}, + expected: &Key{Name: "example.com", Algorithm: "hmac-sha256", Secret: "TCG5A6/lOHUGbW0e/9RYYbzWDFMlj1pIxCvybLBayBg="}, }, { desc: "data before the key", filename: "text_before.conf", - expected: &Key{Name: "lego", Algorithm: "hmac-sha256", Secret: "TCG5A6/lOHUGbW0e/9RYYbzWDFMlj1pIxCvybLBayBg="}, + expected: &Key{Name: "example.com", Algorithm: "hmac-sha256", Secret: "TCG5A6/lOHUGbW0e/9RYYbzWDFMlj1pIxCvybLBayBg="}, }, { desc: "data after the key", filename: "text_after.conf", - expected: &Key{Name: "lego", Algorithm: "hmac-sha256", Secret: "TCG5A6/lOHUGbW0e/9RYYbzWDFMlj1pIxCvybLBayBg="}, + expected: &Key{Name: "example.com", Algorithm: "hmac-sha256", Secret: "TCG5A6/lOHUGbW0e/9RYYbzWDFMlj1pIxCvybLBayBg="}, }, { desc: "ignore missing secret", filename: "missing_secret.conf", - expected: &Key{Name: "lego", Algorithm: "hmac-sha256"}, + expected: &Key{Name: "example.com", Algorithm: "hmac-sha256"}, }, { desc: "ignore missing algorithm", filename: "mising_algo.conf", - expected: &Key{Name: "lego", Secret: "TCG5A6/lOHUGbW0e/9RYYbzWDFMlj1pIxCvybLBayBg="}, + expected: &Key{Name: "example.com", Secret: "TCG5A6/lOHUGbW0e/9RYYbzWDFMlj1pIxCvybLBayBg="}, }, { desc: "ignore invalid field format", filename: "invalid_field.conf", - expected: &Key{Name: "lego", Secret: "TCG5A6/lOHUGbW0e/9RYYbzWDFMlj1pIxCvybLBayBg="}, + expected: &Key{Name: "example.com", Secret: "TCG5A6/lOHUGbW0e/9RYYbzWDFMlj1pIxCvybLBayBg="}, }, } diff --git a/providers/dns/rfc2136/rfc2136.toml b/providers/dns/rfc2136/rfc2136.toml index 74ad120806..df313fde77 100644 --- a/providers/dns/rfc2136/rfc2136.toml +++ b/providers/dns/rfc2136/rfc2136.toml @@ -6,25 +6,25 @@ Since = "v0.3.0" Example = ''' RFC2136_NAMESERVER=127.0.0.1 \ -RFC2136_TSIG_KEY=lego \ +RFC2136_TSIG_KEY=example.com \ RFC2136_TSIG_ALGORITHM=hmac-sha256. \ RFC2136_TSIG_SECRET=YWJjZGVmZGdoaWprbG1ub3BxcnN0dXZ3eHl6MTIzNDU= \ lego --email you@example.com --dns rfc2136 -d '*.example.com' -d example.com run ## --- -keyname=lego; keyfile=lego.key; tsig-keygen $keyname > $keyfile +keyname=example.com; keyfile=example.com.key; tsig-keygen $keyname > $keyfile RFC2136_NAMESERVER=127.0.0.1 \ RFC2136_TSIG_FILE="$keyfile" \ -lego --email you@example.com --dns rfc2136 d '*.example.com' -d example.com run +lego --email you@example.com --dns rfc2136 -d '*.example.com' -d example.com run ''' [Configuration] [Configuration.Credentials] - 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." - 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_KEY` variable unset." + RFC2136_TSIG_SECRET = "Secret key payload. To disable TSIG authentication, leave the `RFC2136_TSIG_SECRET` variable unset." + 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_KEY` or `RFC2136_TSIG_SECRET` variables unset." RFC2136_NAMESERVER = 'Network address in the form "host" or "host:port"' [Configuration.Additional] RFC2136_TSIG_FILE = "Path to a key file generated by tsig-keygen" From cbd1922df7a48da0aa05c3f74a240577f0283116 Mon Sep 17 00:00:00 2001 From: Fernandez Ludovic Date: Fri, 8 Nov 2024 23:06:43 +0100 Subject: [PATCH 08/12] chore: generate --- cmd/zz_gen_cmd_dnshelp.go | 6 +++--- docs/content/dns/zz_gen_rfc2136.md | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cmd/zz_gen_cmd_dnshelp.go b/cmd/zz_gen_cmd_dnshelp.go index 66ec72a185..dd217c7170 100644 --- a/cmd/zz_gen_cmd_dnshelp.go +++ b/cmd/zz_gen_cmd_dnshelp.go @@ -2428,9 +2428,9 @@ func displayDNSHelp(w io.Writer, name string) error { 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(` - "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_KEY' or 'RFC2136_TSIG_SECRET' 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_KEY' variable unset.`) + ew.writeln(` - "RFC2136_TSIG_SECRET": Secret key payload. To disable TSIG authentication, leave the 'RFC2136_TSIG_SECRET' variable unset.`) ew.writeln() ew.writeln(`Additional Configuration:`) diff --git a/docs/content/dns/zz_gen_rfc2136.md b/docs/content/dns/zz_gen_rfc2136.md index 0afc0211b2..ad52005d4e 100644 --- a/docs/content/dns/zz_gen_rfc2136.md +++ b/docs/content/dns/zz_gen_rfc2136.md @@ -27,18 +27,18 @@ Here is an example bash command using the RFC2136 provider: ```bash RFC2136_NAMESERVER=127.0.0.1 \ -RFC2136_TSIG_KEY=lego \ +RFC2136_TSIG_KEY=example.com \ RFC2136_TSIG_ALGORITHM=hmac-sha256. \ RFC2136_TSIG_SECRET=YWJjZGVmZGdoaWprbG1ub3BxcnN0dXZ3eHl6MTIzNDU= \ lego --email you@example.com --dns rfc2136 -d '*.example.com' -d example.com run ## --- -keyname=lego; keyfile=lego.key; tsig-keygen $keyname > $keyfile +keyname=example.com; keyfile=example.com.key; tsig-keygen $keyname > $keyfile RFC2136_NAMESERVER=127.0.0.1 \ RFC2136_TSIG_FILE="$keyfile" \ -lego --email you@example.com --dns rfc2136 d '*.example.com' -d example.com run +lego --email you@example.com --dns rfc2136 -d '*.example.com' -d example.com run ``` @@ -49,9 +49,9 @@ lego --email you@example.com --dns rfc2136 d '*.example.com' -d example.com run | 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. | +| `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_KEY` or `RFC2136_TSIG_SECRET` variables unset. | +| `RFC2136_TSIG_KEY` | Name of the secret key as defined in DNS server configuration. To disable TSIG authentication, leave the `RFC2136_TSIG_KEY` variable unset. | +| `RFC2136_TSIG_SECRET` | Secret key payload. To disable TSIG authentication, leave the `RFC2136_TSIG_SECRET` variable 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" %}}). From 02065e52a2d2b89a834c2b77bd8af7d5c2cca3ed Mon Sep 17 00:00:00 2001 From: Fernandez Ludovic Date: Fri, 8 Nov 2024 23:37:17 +0100 Subject: [PATCH 09/12] tests: windows... --- providers/dns/rfc2136/internal/tsigkey_test.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/providers/dns/rfc2136/internal/tsigkey_test.go b/providers/dns/rfc2136/internal/tsigkey_test.go index 062a4d7043..4ed7f66168 100644 --- a/providers/dns/rfc2136/internal/tsigkey_test.go +++ b/providers/dns/rfc2136/internal/tsigkey_test.go @@ -2,6 +2,7 @@ package internal import ( "path/filepath" + "runtime" "testing" "github.com/stretchr/testify/assert" @@ -59,6 +60,11 @@ func TestReadTSIGFile(t *testing.T) { } func TestReadTSIGFile_error(t *testing.T) { + if runtime.GOOS != "linux" { + // Because error messages are different on Windows. + t.Skip("only for UNIX systems") + } + testCases := []struct { desc string filename string From ed3792abb9fc4cf3af618f892add31a62cab824f Mon Sep 17 00:00:00 2001 From: Fernandez Ludovic Date: Sat, 9 Nov 2024 00:35:39 +0100 Subject: [PATCH 10/12] review: simplify --- providers/dns/rfc2136/internal/tsigkey.go | 29 ++++++++++++----------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/providers/dns/rfc2136/internal/tsigkey.go b/providers/dns/rfc2136/internal/tsigkey.go index 2c871bee76..20ac15c3c7 100644 --- a/providers/dns/rfc2136/internal/tsigkey.go +++ b/providers/dns/rfc2136/internal/tsigkey.go @@ -5,14 +5,12 @@ import ( "fmt" "os" "strings" - - "github.com/go-viper/mapstructure/v2" ) type Key struct { - Name string `mapstructure:"name"` - Algorithm string `mapstructure:"algorithm"` - Secret string `mapstructure:"secret"` + Name string + Algorithm string + Secret string } // ReadTSIGFile reads TSIG key file generated with `tsig-keygen`. @@ -24,7 +22,7 @@ func ReadTSIGFile(filename string) (*Key, error) { defer func() { _ = file.Close() }() - data := make(map[string]string) + key := &Key{} var read bool @@ -50,7 +48,7 @@ func ReadTSIGFile(filename string) (*Key, error) { return nil, fmt.Errorf("invalid key line: %s", line) } - data["name"] = safeUnquote(fields[1]) + key.Name = safeUnquote(fields[1]) case !read: continue @@ -60,14 +58,17 @@ func ReadTSIGFile(filename string) (*Key, error) { continue } - data[safeUnquote(fields[0])] = safeUnquote(fields[1]) - } - } + v := safeUnquote(fields[1]) - key := &Key{} - err = mapstructure.Decode(data, key) - if err != nil { - return nil, fmt.Errorf("decode key: %w", err) + switch strings.ToLower(safeUnquote(fields[0])) { + case "algorithm": + key.Algorithm = v + case "secret": + key.Secret = v + default: + continue + } + } } return key, nil From 7d7cd86de94b7c6b2b4d600c759bf09db415a5f5 Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Sat, 9 Nov 2024 00:39:04 +0100 Subject: [PATCH 11/12] review: apply Co-authored-by: Dominik Menke --- providers/dns/rfc2136/internal/tsigkey.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/providers/dns/rfc2136/internal/tsigkey.go b/providers/dns/rfc2136/internal/tsigkey.go index 20ac15c3c7..24058fc9a3 100644 --- a/providers/dns/rfc2136/internal/tsigkey.go +++ b/providers/dns/rfc2136/internal/tsigkey.go @@ -75,11 +75,13 @@ func ReadTSIGFile(filename string) (*Key, error) { } func safeUnquote(v string) string { - if v == "" { + if len(v) < 2 { + // empty or single character string return v } - - if len(v)-1 != 0 && v[0] == '"' && v[len(v)-1] == '"' { + + if v[0] == '"' && v[len(v)-1] == '"' { + // string wrapped in quotes return v[1 : len(v)-1] } From 1355f7594f24d9b5e993fb57c3ad4ccfed703af8 Mon Sep 17 00:00:00 2001 From: Fernandez Ludovic Date: Sat, 9 Nov 2024 00:42:19 +0100 Subject: [PATCH 12/12] review: simplify --- providers/dns/rfc2136/internal/tsigkey.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/providers/dns/rfc2136/internal/tsigkey.go b/providers/dns/rfc2136/internal/tsigkey.go index 24058fc9a3..b4672f44dd 100644 --- a/providers/dns/rfc2136/internal/tsigkey.go +++ b/providers/dns/rfc2136/internal/tsigkey.go @@ -60,7 +60,7 @@ func ReadTSIGFile(filename string) (*Key, error) { v := safeUnquote(fields[1]) - switch strings.ToLower(safeUnquote(fields[0])) { + switch safeUnquote(fields[0]) { case "algorithm": key.Algorithm = v case "secret": @@ -79,7 +79,7 @@ func safeUnquote(v string) string { // empty or single character string return v } - + if v[0] == '"' && v[len(v)-1] == '"' { // string wrapped in quotes return v[1 : len(v)-1]