1
0
mirror of https://github.com/StackExchange/dnscontrol.git synced 2024-05-11 05:55:12 +00:00

Add TLSA record support (#165) (#203)

This commit is contained in:
eliheady
2017-09-15 09:03:29 -04:00
committed by Tom Limoncelli
parent a342aa7e90
commit 4aac517d62
15 changed files with 292 additions and 108 deletions

View File

@@ -54,6 +54,7 @@ func validateRecordTypes(rec *models.RecordConfig, domain string, pTypes []strin
"AAAA": true,
"CNAME": true,
"CAA": true,
"TLSA": true,
"IMPORT_TRANSFORM": false,
"MX": true,
"SRV": true,
@@ -84,7 +85,10 @@ func validateRecordTypes(rec *models.RecordConfig, domain string, pTypes []strin
// underscores in names are often used erroneously. They are valid for dns records, but invalid for urls.
// here we list common records expected to have underscores. Anything else containing an underscore will print a warning.
var expectedUnderscores = []string{"_domainkey", "_dmarc", "_amazonses"}
var labelUnderscores = []string{"_domainkey", "_dmarc", "_amazonses"}
//these record types may contain underscores
var rTypeUnderscores = []string{"SRV", "TLSA", "TXT"}
func checkLabel(label string, rType string, domain string) error {
if label == "@" {
@@ -96,20 +100,19 @@ 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)
}
for _, ex := range rTypeUnderscores {
if rType == ex {
return nil
}
}
for _, ex := range labelUnderscores {
if strings.Contains(label, ex) {
return nil
}
}
//underscores are warnings
if rType != "SRV" && strings.ContainsRune(label, '_') {
//unless it is in our exclusion list
ok := false
for _, ex := range expectedUnderscores {
if strings.Contains(label, ex) {
ok = true
break
}
}
if !ok {
return Warning{fmt.Errorf("label %s.%s contains an underscore", label, domain)}
}
if strings.ContainsRune(label, '_') {
return Warning{fmt.Errorf("label %s.%s contains an underscore", label, domain)}
}
return nil
}
@@ -150,7 +153,7 @@ func checkTargets(rec *models.RecordConfig, domain string) (errs []error) {
check(checkTarget(target))
case "SRV":
check(checkTarget(target))
case "TXT", "IMPORT_TRANSFORM", "CAA":
case "TXT", "IMPORT_TRANSFORM", "CAA", "TLSA":
default:
if rec.Metadata["orig_custom_type"] != "" {
//it is a valid custom type. We perform no validation on target
@@ -207,7 +210,7 @@ func importTransform(srcDomain, dstDomain *models.DomainConfig, transforms []tra
r := newRec()
r.Target = transformCNAME(r.Target, srcDomain.Name, dstDomain.Name)
dstDomain.Records = append(dstDomain.Records, r)
case "MX", "NS", "SRV", "TXT", "CAA":
case "MX", "NS", "SRV", "TXT", "CAA", "TLSA":
// Not imported.
continue
default:
@@ -291,7 +294,21 @@ func NormalizeAndValidateConfig(config *models.DNSConfig) (errs []error) {
if rec.CaaTag != "issue" && rec.CaaTag != "issuewild" && rec.CaaTag != "iodef" {
errs = append(errs, fmt.Errorf("CAA tag %s is invalid", rec.CaaTag))
}
} else if rec.Type == "TLSA" {
if rec.TlsaUsage < 0 || rec.TlsaUsage > 3 {
errs = append(errs, fmt.Errorf("TLSA Usage %d is invalid in record %s (domain %s)",
rec.TlsaUsage, rec.Name, domain.Name))
}
if rec.TlsaSelector < 0 || rec.TlsaSelector > 1 {
errs = append(errs, fmt.Errorf("TLSA Selector %d is invalid in record %s (domain %s)",
rec.TlsaSelector, rec.Name, domain.Name))
}
if rec.TlsaMatchingType < 0 || rec.TlsaMatchingType > 2 {
errs = append(errs, fmt.Errorf("TLSA MatchingType %d is invalid in record %s (domain %s)",
rec.TlsaMatchingType, rec.Name, domain.Name))
}
}
// Populate FQDN:
rec.NameFQDN = dnsutil.AddOrigin(rec.Name, domain.Name)
}
@@ -368,6 +385,7 @@ func checkProviderCapabilities(dc *models.DomainConfig, pList []*models.DNSProvi
{"PTR", providers.CanUsePTR},
{"SRV", providers.CanUseSRV},
{"CAA", providers.CanUseCAA},
{"TLSA", providers.CanUseTLSA},
}
for _, ty := range types {
hasAny := false

View File

@@ -10,21 +10,24 @@ import (
func TestCheckLabel(t *testing.T) {
var tests = []struct {
experiment string
isError bool
label string
rType string
target string
isError bool
}{
{"@", false},
{"foo", false},
{"foo.bar", false},
{"foo.", true},
{"foo.bar.", true},
{"foo_bar", true},
{"_domainkey", false},
{"@", "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},
}
for _, test := range tests {
err := checkLabel(test.experiment, "A", "foo.com")
checkError(t, err, test.isError, test.experiment)
err := checkLabel(test.label, test.rType, test.target)
if err != nil && test.isError {
t.Errorf("%v: Expected error but got none \n", "TestCheckLabel")
}
}
}
@@ -191,3 +194,21 @@ func TestCAAValidation(t *testing.T) {
t.Error("Expect error on invalid CAA but got none")
}
}
func TestTLSAValidation(t *testing.T) {
config := &models.DNSConfig{
Domains: []*models.DomainConfig{
{
Name: "_443._tcp.example.com",
Registrar: "BIND",
Records: []*models.RecordConfig{
{Name: "_443._tcp", Type: "TLSA", TlsaUsage: 4, TlsaSelector: 1, TlsaMatchingType: 1, Target: "abcdef0"},
},
},
},
}
errs := NormalizeAndValidateConfig(config)
if len(errs) != 1 {
t.Error("Expect error on invalid TLSA but got none")
}
}