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

@ -33,6 +33,7 @@ func generateFeatureMatrix() error {
{"SRV", "Driver has explicitly implemented SRV record management"},
{"PTR", "Provider supports adding PTR records for reverse lookup zones"},
{"CAA", "Provider can manage CAA records"},
{"TLSA", "Provider can manage TLSA records"},
{"dual host", "This provider is recommended for use in 'dual hosting' scenarios. Usually this means the provider allows full control over the apex NS records"},
{"create-domains", "This means the provider can automatically create domains that do not currently exist on your account. The 'dnscontrol create-domains' command will initialize any missing domains"},
@ -67,6 +68,7 @@ func generateFeatureMatrix() error {
setCap("SRV", providers.CanUseSRV)
setCap("PTR", providers.CanUsePTR)
setCap("CAA", providers.CanUseCAA)
setCap("TLSA", providers.CanUseTLSA)
setDoc("dual host", providers.DocDualHost)
setDoc("create-domains", providers.DocCreateDomains)

View file

@ -0,0 +1,27 @@
---
name: TLSA
parameters:
- name
- usage
- selector
- type
- certificate
- modifiers...
---
TLSA adds a TLSA record to a domain. The name should be the relative label for the record.
Usage, selector, and type are ints.
Certificate is a hex string.
{% include startExample.html %}
{% highlight js %}
D("example.com", REGISTRAR, DnsProvider("GCLOUD"),
// Create TLSA record for certificate used on TCP port 443
TLSA("_443._tcp", 3, 1, 1, "abcdef0"),
);
{%endhighlight%}
{% include endExample.html %}

View file

@ -228,6 +228,22 @@
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
</tr>
<tr>
<th class="row-header" style="text-decoration: underline;" data-toggle="tooltip" data-container="body" data-placement="top" title="Provider can manage TLSA records">TLSA</th>
<td><i class="fa fa-minus dim"></i></td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td>
</tr>
<tr>
<th class="row-header" style="text-decoration: underline;" data-toggle="tooltip" data-container="body" data-placement="top" title="This provider is recommended for use in &#39;dual hosting&#39; scenarios. Usually this means the provider allows full control over the apex NS records">dual host</th>
<td class="danger" data-toggle="tooltip" data-container="body" data-placement="top" title="This driver does not manage NS records, so should not be used for dual-host scenarios">

View file

@ -242,6 +242,15 @@ func caa(name string, tag string, flag uint8, target string) *rec {
return r
}
func tlsa(name string, usage, selector, matchingtype uint8, target string) *rec {
r := makeRec(name, target, "TLSA")
r.TlsaUsage = usage
r.TlsaSelector = selector
r.TlsaMatchingType = matchingtype
r.Target = target
return r
}
func makeRec(name, target, typ string) *rec {
return &rec{
Name: name,
@ -370,6 +379,19 @@ func makeTests(t *testing.T) []*TestCase {
)
}
//TLSA
if !providers.ProviderHasCabability(*providerToRun, providers.CanUseTLSA) {
t.Log("Skipping TLSA Tests because provider does not support them")
} else {
tests = append(tests, tc("Empty"),
tc("TLSA record", tlsa("_443._tcp", 3, 1, 1, "abcdef0123456789==")),
tc("TLSA change usage", tlsa("_443._tcp", 2, 1, 1, "abcdef0123456789==")),
tc("TLSA change selector", tlsa("_443._tcp", 2, 0, 1, "abcdef0123456789==")),
tc("TLSA change matchingtype", tlsa("_443._tcp", 2, 0, 0, "abcdef0123456789==")),
tc("TLSA change certificate", tlsa("_443._tcp", 2, 0, 0, "0123456789abcdef==")),
)
}
// Test large zonefiles.
// Mostly to test paging. Many providers page at 100
// Known page sizes:

View file

@ -61,18 +61,21 @@ type DNSProviderConfig struct {
// This is the FQDN version of Name.
// It should never have a trailiing ".".
type RecordConfig struct {
Type string `json:"type"`
Name string `json:"name"` // The short name. See below.
Target string `json:"target"` // If a name, must end with "."
TTL uint32 `json:"ttl,omitempty"`
Metadata map[string]string `json:"meta,omitempty"`
NameFQDN string `json:"-"` // Must end with ".$origin". See below.
MxPreference uint16 `json:"mxpreference,omitempty"` // FIXME(tlim): Rename to MxPreference
SrvPriority uint16 `json:"srvpriority,omitempty"`
SrvWeight uint16 `json:"srvweight,omitempty"`
SrvPort uint16 `json:"srvport,omitempty"`
CaaTag string `json:"caatag,omitempty"`
CaaFlag uint8 `json:"caaflag,omitempty"`
Type string `json:"type"`
Name string `json:"name"` // The short name. See below.
Target string `json:"target"` // If a name, must end with "."
TTL uint32 `json:"ttl,omitempty"`
Metadata map[string]string `json:"meta,omitempty"`
NameFQDN string `json:"-"` // Must end with ".$origin". See below.
MxPreference uint16 `json:"mxpreference,omitempty"` // FIXME(tlim): Rename to MxPreference
SrvPriority uint16 `json:"srvpriority,omitempty"`
SrvWeight uint16 `json:"srvweight,omitempty"`
SrvPort uint16 `json:"srvport,omitempty"`
CaaTag string `json:"caatag,omitempty"`
CaaFlag uint8 `json:"caaflag,omitempty"`
TlsaUsage uint8 `json:"tlsausage,omitempty"`
TlsaSelector uint8 `json:"tlsaselector,omitempty"`
TlsaMatchingType uint8 `json:"tlsamatchingtype,omitempty"`
CombinedTarget bool `json:"-"`
@ -147,6 +150,9 @@ func (r *RecordConfig) MergeToTarget() {
r.SrvPort = 0
r.CaaFlag = 0
r.CaaTag = ""
r.TlsaUsage = 0
r.TlsaMatchingType = 0
r.TlsaSelector = 0
r.CombinedTarget = true
}
@ -206,6 +212,11 @@ func (rc *RecordConfig) ToRR() dns.RR {
rr.(*dns.CAA).Flag = rc.CaaFlag
rr.(*dns.CAA).Tag = rc.CaaTag
rr.(*dns.CAA).Value = rc.Target
case dns.TypeTLSA:
rr.(*dns.TLSA).Usage = rc.TlsaUsage
rr.(*dns.TLSA).MatchingType = rc.TlsaMatchingType
rr.(*dns.TLSA).Selector = rc.TlsaSelector
rr.(*dns.TLSA).Certificate = rc.Target
case dns.TypeTXT:
rr.(*dns.TXT).Txt = []string{rc.Target}
default:
@ -301,7 +312,7 @@ func (dc *DomainConfig) Punycode() error {
if err != nil {
return err
}
case "A", "AAAA", "CAA", "TXT":
case "A", "AAAA", "CAA", "TXT", "TLSA":
// Nothing to do.
default:
msg := fmt.Sprintf("Punycode rtype %v unimplemented", rec.Type)

View file

@ -51,4 +51,20 @@ func TestRR(t *testing.T) {
if found != expected {
t.Errorf("RR expected (%#v) got (%#v)\n", expected, found)
}
experiment = RecordConfig{
Type: "TLSA",
Name: "@",
Target: "abcdef0123456789",
TTL: 300,
NameFQDN: "_443._tcp.example.com",
TlsaUsage: 0,
TlsaSelector: 0,
TlsaMatchingType: 1,
}
expected = "_443._tcp.example.com.\t300\tIN\tTLSA\t0 0 1 abcdef0123456789"
found = experiment.ToRR().String()
if found != expected {
t.Errorf("RR expected (%#v) got (%#v)\n", expected, found)
}
}

View file

@ -191,6 +191,24 @@ var SRV = recordBuilder('SRV', {
},
});
// name, usage, selector, matchingtype, certificate
var TLSA = recordBuilder('TLSA', {
args: [
['name', _.isString],
['usage', _.isNumber],
['selector', _.isNumber],
['matchingtype', _.isNumber],
['target', _.isString], //recordBuilder needs a "target" argument
],
transform: function(record, args, modifiers){
record.name = args.name;
record.tlsausage = args.usage;
record.tlsaselector = args.selector;
record.tlsamatchingtype = args.matchingtype;
record.target = args.target;
},
});
// TXT(name,target, recordModifiers...)
var TXT = recordBuilder('TXT');

View file

@ -0,0 +1,3 @@
D("foo.com","none",
TLSA("_443._tcp",3,1,1,"MDFiYTQ3MTljODBiNmZlOTExYjA5MWE3YzA1MTI0YjY0ZWVlY2U5NjRlMDljMDU4ZWY4Zjk4MDVkYWNhNTQ2YiAgLQo=")
);

View file

@ -0,0 +1,22 @@
{
"registrars":[],
"dns_providers":[],
"domains":[
{
"name":"foo.com",
"registrar":"none",
"dnsProviders":{},
"records":[
{
"type":"TLSA",
"name":"_443._tcp",
"target":"MDFiYTQ3MTljODBiNmZlOTExYjA5MWE3YzA1MTI0YjY0ZWVlY2U5NjRlMDljMDU4ZWY4Zjk4MDVkYWNhNTQ2YiAgLQo=",
"tlsausage":3,
"tlsaselector":1,
"tlsamatchingtype":1
}
]
}
]
}

View file

@ -190,74 +190,75 @@ var _escData = map[string]*_escFile{
"/helpers.js": {
local: "pkg/js/helpers.js",
size: 13505,
size: 14087,
modtime: 0,
compressed: `
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
AAA=
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
`,
},

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")
}
}

View file

@ -57,7 +57,7 @@ func initBind(config map[string]string, providermeta json.RawMessage) (providers
func init() {
providers.RegisterDomainServiceProviderType("BIND", initBind, providers.CanUsePTR,
providers.CanUseSRV, providers.CanUseCAA, providers.CantUseNOPURGE, docNotes)
providers.CanUseSRV, providers.CanUseCAA, providers.CanUseTLSA, providers.CantUseNOPURGE, docNotes)
}
type SoaInfo struct {
@ -132,6 +132,11 @@ func rrToRecord(rr dns.RR, origin string, replaceSerial uint32) (models.RecordCo
rc.SrvPort = v.Port
rc.SrvWeight = v.Weight
rc.SrvPriority = v.Priority
case *dns.TLSA:
rc.TlsaUsage = v.Usage
rc.TlsaSelector = v.Selector
rc.TlsaMatchingType = v.MatchingType
rc.Target = v.Certificate
case *dns.TXT:
rc.Target = strings.Join(v.Txt, " ")
default:

View file

@ -40,7 +40,7 @@ func (z *zoneGenData) Less(i, j int) bool {
return zoneRrtypeLess(rrtypeA, rrtypeB)
}
switch rrtypeA { // #rtype_variations
case dns.TypeNS, dns.TypeTXT:
case dns.TypeNS, dns.TypeTXT, dns.TypeTLSA:
// pass through.
case dns.TypeA:
ta2, tb2 := a.(*dns.A), b.(*dns.A)

View file

@ -17,6 +17,8 @@ const (
CanUseSRV
// CanUseCAA indicates the provider can handle CAA records
CanUseCAA
// CanUseTLSA indicates the provider can handle TLSA records
CanUseTLSA
// CantUseNOPURGE indicates NO_PURGE is broken for this provider. To make it
// work would require complex emulation of an incremental update mechanism,
// so it is easier to simply mark this feature as not working for this