mirror of
https://github.com/StackExchange/dnscontrol.git
synced 2024-12-28 19:23:07 +08:00
parent
a342aa7e90
commit
4aac517d62
15 changed files with 292 additions and 108 deletions
|
@ -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)
|
||||
|
||||
|
|
27
docs/_functions/domain/TLSA.md
Normal file
27
docs/_functions/domain/TLSA.md
Normal 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 %}
|
|
@ -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 'dual hosting' 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">
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
|
||||
|
|
3
pkg/js/parse_tests/015-tlsa.js
Normal file
3
pkg/js/parse_tests/015-tlsa.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
D("foo.com","none",
|
||||
TLSA("_443._tcp",3,1,1,"MDFiYTQ3MTljODBiNmZlOTExYjA5MWE3YzA1MTI0YjY0ZWVlY2U5NjRlMDljMDU4ZWY4Zjk4MDVkYWNhNTQ2YiAgLQo=")
|
||||
);
|
22
pkg/js/parse_tests/015-tlsa.json
Normal file
22
pkg/js/parse_tests/015-tlsa.json
Normal 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
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
133
pkg/js/static.go
133
pkg/js/static.go
|
@ -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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
|
||||
`,
|
||||
},
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue