config: watch a target listed twice only once (closes #207) #211
@@ -330,7 +330,8 @@ following precedence (highest to lowest):
|
|||||||
monitoring targets are configured. A monitoring daemon with nothing to monitor
|
monitoring targets are configured. A monitoring daemon with nothing to monitor
|
||||||
is a misconfiguration, so dnswatcher fails fast with a clear error message
|
is a misconfiguration, so dnswatcher fails fast with a clear error message
|
||||||
rather than running silently. Set `DNSWATCHER_TARGETS` to a comma-separated list
|
rather than running silently. Set `DNSWATCHER_TARGETS` to a comma-separated list
|
||||||
of DNS names before starting.
|
of DNS names before starting. A name listed more than once, in any letter case
|
||||||
|
or with a trailing dot, is watched once.
|
||||||
|
|
||||||
**`/metrics` is rate limited.** Each client address may send it 30 requests a
|
**`/metrics` is rate limited.** Each client address may send it 30 requests a
|
||||||
minute, failed logins included; beyond that it answers `429 Too Many Requests`
|
minute, failed logins included; beyond that it answers `429 Too Many Requests`
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ trial run of the finished image: https://git.eeqj.de/sneak/dnswatcher/issues/149
|
|||||||
|
|
||||||
# Completed Steps
|
# Completed Steps
|
||||||
|
|
||||||
|
- 2026-10-02: a name listed more than once in `DNSWATCHER_TARGETS`, in any
|
||||||
|
letter case or with a trailing dot, is watched once (closes #207).
|
||||||
- 2026-10-01: a certificate within the expiry warning period is warned about on
|
- 2026-10-01: a certificate within the expiry warning period is warned about on
|
||||||
every TLS check, where some checks used to skip it at random (closes #204).
|
every TLS check, where some checks used to skip it at random (closes #204).
|
||||||
- 2026-10-01: a domain's NS set is its delegation from the parent zone's
|
- 2026-10-01: a domain's NS set is its delegation from the parent zone's
|
||||||
|
|||||||
@@ -57,17 +57,22 @@ func ClassifyDNSName(name string) (DNSNameType, error) {
|
|||||||
|
|
||||||
// ClassifyTargets splits a list of DNS names into apex domains and
|
// ClassifyTargets splits a list of DNS names into apex domains and
|
||||||
// hostnames using the Public Suffix List. It returns an error if any
|
// hostnames using the Public Suffix List. It returns an error if any
|
||||||
// name cannot be classified.
|
// name cannot be classified. A name given more than once, in any letter
|
||||||
|
// case or with a trailing dot, is kept once.
|
||||||
func ClassifyTargets(targets []string) ([]string, []string, error) {
|
func ClassifyTargets(targets []string) ([]string, []string, error) {
|
||||||
var domains, hostnames []string
|
var domains, hostnames []string
|
||||||
|
|
||||||
|
seen := make(map[string]bool)
|
||||||
|
|
||||||
for _, t := range targets {
|
for _, t := range targets {
|
||||||
normalized := strings.ToLower(strings.TrimSuffix(strings.TrimSpace(t), "."))
|
normalized := strings.ToLower(strings.TrimSuffix(strings.TrimSpace(t), "."))
|
||||||
|
|
||||||
if normalized == "" {
|
if normalized == "" || seen[normalized] {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
seen[normalized] = true
|
||||||
|
|
||||||
typ, classErr := ClassifyDNSName(normalized)
|
typ, classErr := ClassifyDNSName(normalized)
|
||||||
if classErr != nil {
|
if classErr != nil {
|
||||||
return nil, nil, classErr
|
return nil, nil, classErr
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package config_test
|
package config_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"slices"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"sneak.berlin/go/dnswatcher/internal/config"
|
"sneak.berlin/go/dnswatcher/internal/config"
|
||||||
@@ -93,6 +94,29 @@ func TestClassifyTargets(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestClassifyTargetsKeepsEachNameOnce(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
domains, hostnames, err := config.ClassifyTargets([]string{
|
||||||
|
"example.org",
|
||||||
|
"Example.org.",
|
||||||
|
"www.example.org",
|
||||||
|
"EXAMPLE.ORG",
|
||||||
|
"WWW.Example.org.",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !slices.Equal(domains, []string{"example.org"}) {
|
||||||
|
t.Errorf("domains = %v, want [example.org]", domains)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !slices.Equal(hostnames, []string{"www.example.org"}) {
|
||||||
|
t.Errorf("hostnames = %v, want [www.example.org]", hostnames)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestClassifyTargetsRejectsPublicSuffix(t *testing.T) {
|
func TestClassifyTargetsRejectsPublicSuffix(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user