diff --git a/pkg/normalize/validate.go b/pkg/normalize/validate.go index c4ca289dc..51e6bea24 100644 --- a/pkg/normalize/validate.go +++ b/pkg/normalize/validate.go @@ -90,7 +90,7 @@ var labelUnderscores = []string{"_domainkey", "_dmarc", "_amazonses", "_acme-cha //these record types may contain underscores var rTypeUnderscores = []string{"SRV", "TLSA", "TXT"} -func checkLabel(label string, rType string, domain string) error { +func checkLabel(label string, rType string, domain string, meta map[string]string) error { if label == "@" { return nil } @@ -100,6 +100,12 @@ func checkLabel(label string, rType string, domain string) error { if label[len(label)-1] == '.' { return fmt.Errorf("label %s.%s ends with a (.)", label, domain) } + if strings.HasSuffix(label, domain) { + if m := meta["skip_fqdn_check"]; m != "true" { + return fmt.Errorf(`label %s ends with domain name %s. Record names should not be fully qualified. Add {skip_fqdn_check:"true"} to this record if you really want to make %s.%s`, label, domain, label, domain) + } + } + // check for underscores last for _, ex := range rTypeUnderscores { if rType == ex { return nil @@ -114,6 +120,7 @@ func checkLabel(label string, rType string, domain string) error { if strings.ContainsRune(label, '_') { return Warning{fmt.Errorf("label %s.%s contains an underscore", label, domain)} } + return nil } @@ -274,7 +281,7 @@ func NormalizeAndValidateConfig(config *models.DNSConfig) (errs []error) { if err := validateRecordTypes(rec, domain.Name, pTypes); err != nil { errs = append(errs, err) } - if err := checkLabel(rec.Name, rec.Type, domain.Name); err != nil { + if err := checkLabel(rec.Name, rec.Type, domain.Name, rec.Metadata); err != nil { errs = append(errs, err) } if errs2 := checkTargets(rec, domain.Name); errs2 != nil { diff --git a/pkg/normalize/validate_test.go b/pkg/normalize/validate_test.go index fb5aadb39..9c92153aa 100644 --- a/pkg/normalize/validate_test.go +++ b/pkg/normalize/validate_test.go @@ -10,24 +10,36 @@ import ( func TestCheckLabel(t *testing.T) { var tests = []struct { - label string - rType string - target string - isError bool + label string + rType string + isError bool + hasSkipMeta bool }{ - {"@", "A", "0.0.0.0", false}, - {"@", "A", "foo.tld", true}, - {"foo.bar", "A", "0.0.0.0", false}, - {"_foo", "SRV", "foo.tld", false}, - {"_foo", "TLSA", "foo.tld", false}, - {"_foo", "TXT", "foo.tld", false}, + {"@", "A", false, false}, + {"foo.bar", "A", false, false}, + {"_foo", "A", true, false}, + {"_foo", "SRV", false, false}, + {"_foo", "TLSA", false, false}, + {"_foo", "TXT", false, false}, + {"test.foo.tld", "A", true, false}, + {"test.foo.tld", "A", false, true}, } for _, test := range tests { - err := checkLabel(test.label, test.rType, test.target) - if err != nil && test.isError { - t.Errorf("%v: Expected error but got none \n", "TestCheckLabel") - } + t.Run(fmt.Sprintf("%s %s", test.label, test.rType), func(t *testing.T) { + meta := map[string]string{} + if test.hasSkipMeta { + meta["skip_fqdn_check"] = "true" + } + err := checkLabel(test.label, test.rType, "foo.tld", meta) + if err != nil && !test.isError { + t.Errorf(" Expected no error but got %s", err) + } + if err == nil && test.isError { + t.Errorf(" Expected error but got none") + } + }) + } }