Add CAA support (#161)

* Added CAA support

* Fixed bind parsing of CAA records

* Added CAA parsing test

* Renamed CAA json fields

* Added CAA tag validation

* Updated CAA docs to clarify on the value field

* parse_tests: Fixed typo in caaflags

* Added integration test

* Small cleanups
This commit is contained in:
Tom Limoncelli 2017-07-25 14:59:40 -04:00 committed by GitHub
parent 1a84edbe9c
commit 2f0f5330fc
16 changed files with 283 additions and 58 deletions

View file

@ -0,0 +1,36 @@
---
name: CAA
parameters:
- name
- tag
- value
- modifiers...
---
CAA adds a CAA record to a domain. The name should be the relative label for the record. Use `@` for the domain apex.
Tag can be one of "issue", "issuewild" or "iodef".
Value is a string. The format of the contents is different depending on the tag. DNSControl will handle any escaping or quoting required, similer to TXT records. For example use `CAA("@", "issue", "letsencrypt.org")` rather than `CAA("@", "issue", "\"letsencrypt.org\"")`.
Flags are controlled by modifier.:
- CAA_CRITICAL: Issuer critical flag. CA that does not understand this tag will refuse to issue certificate for this domain.
CAA record is supported only by BIND and Google Cloud DNS. Some certificate authorities may not support this record until the mandatory date of September 2017.
{% include startExample.html %}
{% highlight js %}
D("example.com", REGISTRAR, DnsProvider("GCLOUD"),
// Allow letsencrypt to issue certificate for this domain
CAA("@", "issue", "letsencrypt.org"),
// Allow no CA to issue wildcard certificate for this domain
CAA("@", "issuewild", ";"),
// Report all violation to test@example.com. If CA does not support
// this record then refuse to issue any certificate
CAA("@", "iodef", "mailto:test@example.com", CAA_CRITICAL)
);
{%endhighlight%}
{% include endExample.html %}

View file

@ -236,6 +236,13 @@ func srv(name string, priority, weight, port uint16, target string) *rec {
return r
}
func caa(name string, tag string, flag uint8, target string) *rec {
r := makeRec(name, target, "CAA")
r.CaaFlag = flag
r.CaaTag = tag
return r
}
func makeRec(name, target, typ string) *rec {
return &rec{
Name: name,
@ -327,6 +334,15 @@ var tests = []*TestCase{
tc("Change Weight", srv("@", 52, 62, 7, "foo.com."), srv("@", 15, 65, 75, "foo4.com.")).IfHasCapability(providers.CanUseSRV),
tc("Change Port", srv("@", 52, 62, 72, "foo.com."), srv("@", 15, 65, 75, "foo4.com.")).IfHasCapability(providers.CanUseSRV),
//CAA
tc("Empty").IfHasCapability(providers.CanUseCAA),
tc("CAA record", caa("@", "issue", 0, "letsencrypt.org")).IfHasCapability(providers.CanUseCAA),
tc("CAA change tag", caa("@", "issuewild", 0, "letsencrypt.org")).IfHasCapability(providers.CanUseCAA),
tc("CAA change target", caa("@", "issuewild", 0, "example.com")).IfHasCapability(providers.CanUseCAA),
tc("CAA change flag", caa("@", "issuewild", 1, "example.com")).IfHasCapability(providers.CanUseCAA),
tc("CAA many records", caa("@", "issue", 0, "letsencrypt.org"), caa("@", "issuewild", 0, ";"), caa("@", "iodef", 1, "mailto:test@example.com")).IfHasCapability(providers.CanUseCAA),
tc("CAA delete", caa("@", "issue", 0, "letsencrypt.org")).IfHasCapability(providers.CanUseCAA),
//TODO: in validation, check that everything is given in unicode. This case hurts too much.
//tc("IDN pre-punycoded", cname("xn--o-0gab", "xn--o-0gab.xn--o-0gab.")),
}

View file

@ -1,4 +1,4 @@
$TTL 300
@ IN SOA DEFAULT_NOT_SET. DEFAULT_NOT_SET. 2017071990 3600 600 604800 1440
@ IN SOA DEFAULT_NOT_SET. DEFAULT_NOT_SET. 2017072603 3600 600 604800 1440
IN NS ns1.otherdomain.tld.
IN NS ns2.otherdomain.tld.

View file

@ -71,8 +71,10 @@ type RecordConfig struct {
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"`
CombinedTarget bool `json:"omit"`
CombinedTarget bool `json:"-"`
Original interface{} `json:"-"` // Store pointer to provider-specific record object. Used in diffing.
}
@ -90,6 +92,8 @@ func (r *RecordConfig) String() (content string) {
content += fmt.Sprintf(" priority=%d", r.MxPreference)
case "SOA":
content = fmt.Sprintf("%s %s %s %d", r.Type, r.Name, r.Target, r.TTL)
case "CAA":
content += fmt.Sprintf(" caatag=%s caaflag=%d", r.CaaTag, r.CaaFlag)
default:
panic(fmt.Sprintf("rc.String rtype %v unimplemented", r.Type))
}
@ -138,6 +142,8 @@ func (r *RecordConfig) MergeToTarget() {
r.SrvPriority = 0
r.SrvWeight = 0
r.SrvPort = 0
r.CaaFlag = 0
r.CaaTag = ""
r.CombinedTarget = true
}
@ -193,6 +199,10 @@ func (rc *RecordConfig) ToRR() dns.RR {
rr.(*dns.SRV).Weight = rc.SrvWeight
rr.(*dns.SRV).Port = rc.SrvPort
rr.(*dns.SRV).Target = rc.Target
case dns.TypeCAA:
rr.(*dns.CAA).Flag = rc.CaaFlag
rr.(*dns.CAA).Tag = rc.CaaTag
rr.(*dns.CAA).Value = rc.Target
case dns.TypeTXT:
rr.(*dns.TXT).Txt = []string{rc.Target}
default:

View file

@ -36,4 +36,19 @@ func TestRR(t *testing.T) {
if found != expected {
t.Errorf("RR expected (%#v) got (%#v)\n", expected, found)
}
experiment = RecordConfig{
Type: "CAA",
Name: "@",
Target: "mailto:test@example.com",
TTL: 300,
NameFQDN: "example.com",
CaaTag: "iodef",
CaaFlags: 1,
}
expected = "example.com.\t300\tIN\tCAA\t1 iodef \"mailto:test@example.com\""
found = experiment.ToRR().String()
if found != expected {
t.Errorf("RR expected (%#v) got (%#v)\n", expected, found)
}
}

View file

@ -118,6 +118,8 @@ function DefaultTTL(v) {
}
}
// CAA_CRITICAL: Critical CAA flag
var CAA_CRITICAL = 1<<0;
// DnsProvider("providerName", 0)
@ -156,6 +158,18 @@ function ALIAS(name, target) {
}
}
// CAA(name,tag,value, recordModifiers...)
function CAA(name, tag, value){
checkArgs([_.isString, _.isString, _.isString], arguments, "CAA expects (name, tag, value) plus optional flag as a meta argument")
var mods = getModifiers(arguments,3)
mods.push({caatag: tag});
return function(d) {
addRecord(d,"CAA",name,value,mods)
}
}
// CNAME(name,target, recordModifiers...)
function CNAME(name, target) {
@ -303,6 +317,9 @@ function addRecord(d,type,name,target,mods) {
var m = mods[i]
if (_.isFunction(m)) {
m(rec);
} else if (_.isObject(m) && m.caatag) {
// caatag is a top level object, not in meta
rec.caatag = m.caatag;
} else if (_.isObject(m)) {
//convert transforms to strings
if (m.transform && _.isArray(m.transform)){
@ -312,6 +329,8 @@ function addRecord(d,type,name,target,mods) {
_.extend(rec.meta,m);
} else if (_.isNumber(m) && type == "MX") {
rec.mxpreference = m;
} else if (_.isNumber(m) && type == "CAA") {
rec.caaflags |= m;
} else {
console.log("WARNING: Modifier type unsupported:", typeof m, "(Skipping!)");
}

View file

@ -0,0 +1,13 @@
D("foo.com","none",
// Allow letsencrypt to issue certificate for this domain
CAA("@","issue","letsencrypt.org"),
// Allow no CA to issue wildcard certificate for this domain
CAA("@","issuewild",";"),
// Report all violation to test@example.com. If CA does not support
// this record then refuse to issue any certificate
CAA("@", "iodef", "mailto:test@example.com", CAA_CRITICAL),
// Optionally report violation to http://example.com
CAA("@", "iodef", "http://example.com"),
// Report violation to https://example.com
CAA("@", "iodef", "https://example.com", CAA_CRITICAL)
);

View file

@ -0,0 +1,43 @@
{
"registrars":[],
"dns_providers":[],
"domains":[
{
"name":"foo.com",
"registrar":"none",
"dnsProviders":{},
"records":[
{
"type":"CAA",
"name":"@",
"target":"letsencrypt.org",
"caatag":"issue"
},
{
"type":"CAA",
"name":"@",
"target":";",
"caatag":"issuewild"
},
{
"type":"CAA",
"name":"@",
"target":"mailto:test@example.com",
"caatag":"iodef"
},
{
"type":"CAA",
"name":"@",
"target":"http://example.com",
"caatag":"iodef"
},
{
"type":"CAA",
"name":"@",
"target":"https://example.com",
"caatag":"iodef"
}
]
}
]
}

View file

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

View file

@ -53,6 +53,7 @@ func validateRecordTypes(rec *models.RecordConfig, domain string, pTypes []strin
"A": true,
"AAAA": true,
"CNAME": true,
"CAA": true,
"IMPORT_TRANSFORM": false,
"MX": true,
"SRV": true,
@ -149,7 +150,7 @@ func checkTargets(rec *models.RecordConfig, domain string) (errs []error) {
check(checkTarget(target))
case "SRV":
check(checkTarget(target))
case "TXT", "IMPORT_TRANSFORM":
case "TXT", "IMPORT_TRANSFORM", "CAA":
default:
if rec.Metadata["orig_custom_type"] != "" {
//it is a valid custom type. We perform no validation on target
@ -206,7 +207,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":
case "MX", "NS", "SRV", "TXT", "CAA":
// Not imported.
continue
default:
@ -281,6 +282,10 @@ func NormalizeAndValidateConfig(config *models.DNSConfig) (errs []error) {
if rec.Name, err = transform.PtrNameMagic(rec.Name, domain.Name); err != nil {
errs = append(errs, err)
}
} else if rec.Type == "CAA" {
if rec.CaaTag != "issue" && rec.CaaTag != "issuewild" && rec.CaaTag != "iodef" {
errs = append(errs, fmt.Errorf("CAA tag %s is invalid", rec.CaaTag))
}
}
// Populate FQDN:
rec.NameFQDN = dnsutil.AddOrigin(rec.Name, domain.Name)
@ -357,6 +362,7 @@ func checkProviderCapabilities(dc *models.DomainConfig, pList []*models.DNSProvi
{"ALIAS", providers.CanUseAlias},
{"PTR", providers.CanUsePTR},
{"SRV", providers.CanUseSRV},
{"CAA", providers.CanUseCAA},
}
for _, ty := range types {
hasAny := false

View file

@ -173,3 +173,21 @@ func TestCNAMEMutex(t *testing.T) {
})
}
}
func TestCAAValidation(t *testing.T) {
config := &models.DNSConfig{
Domains: []*models.DomainConfig{
{
Name: "example.com",
Registrar: "BIND",
Records: []*models.RecordConfig{
{Name: "@", Type: "CAA", CaaTag: "invalid", Target: "example.com"},
},
},
},
}
errs := NormalizeAndValidateConfig(config)
if len(errs) != 1 {
t.Error("Expect error on invalid CAA but got none")
}
}

View file

@ -47,7 +47,7 @@ func initBind(config map[string]string, providermeta json.RawMessage) (providers
}
func init() {
providers.RegisterDomainServiceProviderType("BIND", initBind, providers.CanUsePTR, providers.CanUseSRV)
providers.RegisterDomainServiceProviderType("BIND", initBind, providers.CanUsePTR, providers.CanUseSRV, providers.CanUseCAA)
}
type SoaInfo struct {
@ -93,6 +93,10 @@ func rrToRecord(rr dns.RR, origin string, replaceSerial uint32) (models.RecordCo
rc.Target = v.A.String()
case *dns.AAAA:
rc.Target = v.AAAA.String()
case *dns.CAA:
rc.CaaTag = v.Tag
rc.CaaFlag = v.Flag
rc.Target = v.Value
case *dns.CNAME:
rc.Target = v.Target
case *dns.MX:

View file

@ -67,6 +67,19 @@ func (z *zoneGenData) Less(i, j int) bool {
if pa != pb {
return pa < pb
}
case dns.TypeCAA:
ta2, tb2 := a.(*dns.CAA), b.(*dns.CAA)
// sort by tag
pa, pb := ta2.Tag, tb2.Tag
if pa != pb {
return pa < pb
}
// then flag
fa, fb := ta2.Flag, tb2.Flag
if fa != fb {
// flag set goes before ones without flag set
return fa > fb
}
default:
panic(fmt.Sprintf("zoneGenData Less: unimplemented rtype %v", dns.TypeToString[rrtypeA]))
}

View file

@ -181,6 +181,33 @@ var testdataZFSRV = `$TTL 300
IN SRV 10 10 9999 foo.com.
`
func TestWriteZoneFileCaa(t *testing.T) {
//exhibits explicit ttls and long name
r1, _ := dns.NewRR(`bosun.org. 300 IN CAA 0 issuewild ";"`)
r2, _ := dns.NewRR(`bosun.org. 300 IN CAA 0 issue "letsencrypt.org"`)
r3, _ := dns.NewRR(`bosun.org. 300 IN CAA 1 iodef "http://example.com"`)
r4, _ := dns.NewRR(`bosun.org. 300 IN CAA 0 iodef "https://example.com"`)
r5, _ := dns.NewRR(`bosun.org. 300 IN CAA 0 iodef "https://example.net"`)
r6, _ := dns.NewRR(`bosun.org. 300 IN CAA 1 iodef "mailto:example.com"`)
buf := &bytes.Buffer{}
WriteZoneFile(buf, []dns.RR{r1, r2, r3, r4, r5, r6}, "bosun.org")
if buf.String() != testdataZFCAA {
t.Log(buf.String())
t.Log(testdataZFCAA)
t.Fatalf("Zone file does not match.")
}
parseAndRegen(t, buf, testdataZFCAA)
}
var testdataZFCAA = `$TTL 300
@ IN CAA 1 iodef "http://example.com"
IN CAA 1 iodef "mailto:example.com"
IN CAA 0 iodef "https://example.com"
IN CAA 0 iodef "https://example.net"
IN CAA 0 issue "letsencrypt.org"
IN CAA 0 issuewild ";"
`
func TestWriteZoneFileOrder(t *testing.T) {
var records []dns.RR
for i, td := range []string{

View file

@ -15,7 +15,7 @@ import (
)
func init() {
providers.RegisterDomainServiceProviderType("GCLOUD", New, providers.CanUsePTR, providers.CanUseSRV)
providers.RegisterDomainServiceProviderType("GCLOUD", New, providers.CanUsePTR, providers.CanUseSRV, providers.CanUseCAA)
}
type gcloud struct {

View file

@ -47,6 +47,8 @@ const (
CanUsePTR
// CanUseSRV indicates the provider can handle SRV records
CanUseSRV
// CanUseCAA indicates the provider can handle CAA records
CanUseCAA
)
func ProviderHasCabability(pType string, cap Capability) bool {