Repository: StackExchange/dnscontrol
Pull request #863 (2020-09-20 12:54:05)

AUTODNSSEC -> AUTODNSSEC_ON
CC @hnrgrgr @aeden @jpbede

I'm changing how AutoDNSSEC works.  In https://github.com/StackExchange/dnscontrol/pull/856 we discussed that there is now way to specify "leave AutoDNSSEC alone".  This means someone that has previously enabled AutoDNSSEC on either axfrddns, dnsimple, or powerdns would get a big surprise when they upgrade to the newest DNSControl.  If they didn't know to add AUTODNSSEC to their dnsconfig.js file, DNSControl would disable AutoDNSSEC.  That would be bad.

I changed AutoDNSSEC from being a bool (true/false) to being either "on", "off", or "" (ignore). 

Please check your provider to make sure I didn't break anything.  If you'd like to discuss this change please join the discussion on https://github.com/StackExchange/dnscontrol/pull/856.

patschi: Might also be relevant: #847
patschi: This removes the existing `AUTODNSSEC` switch, which might break existing setups - regarding backwards compatibility. What about using `AUTODNSSEC` automatically for "ignore" to not break existing setups?
tlimoncelli: How about AUTODNSSEC being a macro that fails with an error like, "AUTODNSSEC is replaced with AUTODNSSEC_ON, please see http://link for more details".  At that link we'll explain how to transition.
patschi: Maybe ignoring & output a warning about deprecation? This won't break existing setups then, when e.g. people use the dnscontrol docker image:latest in their CI. (Beside the fact blindly using latest-tag in CI is an awful idea anyway).

Until a specific release in the future users should have migrated away from `AUTODNSSEC` and it can be removed completely. My 2 cents.

Patch:
diff --git a/docs/_functions/domain/AUTODNSSEC.md b/docs/_functions/domain/AUTODNSSEC.md
deleted file mode 100644
index 152f8830..00000000
--- a/docs/_functions/domain/AUTODNSSEC.md
+++ /dev/null
@@ -1,19 +0,0 @@
----
-name: AUTODNSSEC
----
-
-AUTODNSSEC indicates that the DNS provider can automatically manage
-DNSSEC for a domain and we should ask it to do so.
-
-At this time, AUTODNSSEC takes no parameters.
-There is no ability to tune what the DNS provider sets, no algorithm choice.
-We simply ask that they follow their defaults when enabling a no-fuss DNSSEC
-data model.
-
-{% include startExample.html %}
-{% highlight js %}
-D("example.com", .... ,
-  AUTODNSSEC,
-);
-{%endhighlight%}
-{% include endExample.html %}
diff --git a/docs/_functions/domain/AUTODNSSEC_OFF.md b/docs/_functions/domain/AUTODNSSEC_OFF.md
new file mode 100644
index 00000000..c2e91fe5
--- /dev/null
+++ b/docs/_functions/domain/AUTODNSSEC_OFF.md
@@ -0,0 +1,8 @@
+---
+name: AUTODNSSEC_OFF
+---
+
+AUTODNSSEC_OFF tells the provider to disable AutoDNSSEC. It takes no
+parameters.
+
+See `AUTODNSSEC_ON` for further details.
diff --git a/docs/_functions/domain/AUTODNSSEC_ON.md b/docs/_functions/domain/AUTODNSSEC_ON.md
new file mode 100644
index 00000000..a1f77f19
--- /dev/null
+++ b/docs/_functions/domain/AUTODNSSEC_ON.md
@@ -0,0 +1,35 @@
+---
+name: AUTODNSSEC_ON
+---
+
+AUTODNSSEC_ON tells the provider to enable AutoDNSSEC.
+
+AUTODNSSEC_OFF tells the provider to disable AutoDNSSEC.
+
+AutoDNSSEC is a feature where a DNS provider can automatically manage
+DNSSEC for a domain. Not all providers support this.
+
+At this time, AUTODNSSEC_ON takes no parameters.  There is no ability
+to tune what the DNS provider sets, no algorithm choice.  We simply
+ask that they follow their defaults when enabling a no-fuss DNSSEC
+data model.
+
+NOTE: No parenthesis should follow these keywords.  That is, the
+correct syntax is `AUTODNSSEC_ON` not `AUTODNSSEC_ON()`
+
+{% include startExample.html %}
+{% highlight js %}
+D("example.com", .... ,
+  AUTODNSSEC_ON,  // Enable AutoDNSSEC.
+  A("@", "10.1.1.1")
+);
+
+D("insecure.com", .... ,
+  AUTODNSSEC_OFF,  // Disable AutoDNSSEC.
+  A("@", "10.2.2.2")
+);
+{%endhighlight%}
+{% include endExample.html %}
+
+If neither `AUTODNSSEC_ON` or `AUTODNSSEC_OFF` is specified for a
+domain no changes will be requested.
diff --git a/docs/_includes/matrix.html b/docs/_includes/matrix.html
index 86ea10cd..39f4ad1c 100644
--- a/docs/_includes/matrix.html
+++ b/docs/_includes/matrix.html
@@ -427,8 +427,8 @@
 		<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 class="info" data-toggle="tooltip" data-container="body" data-placement="top" title="Need support in library first">
-			<i class="fa fa-circle-o text-info" aria-hidden="true"></i>
+		<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>
diff --git a/docs/_providers/axfrddns.md b/docs/_providers/axfrddns.md
index 54338f55..219ea81c 100644
--- a/docs/_providers/axfrddns.md
+++ b/docs/_providers/axfrddns.md
@@ -191,6 +191,8 @@ The AXFR+DDNS provider is not able to create domain.
 
 The AXFR+DDNS provider is not able to ask the DNS server to sign the zone. But, it is able to check whether the server seems to do so or not.
 
-When AutoDNSSEC is set, the AXFR+DDNS provider will emit a warning when no RRSIG, DNSKEY or NSEC records are found in the zone.
+When AutoDNSSEC is enabled, the AXFR+DDNS provider will emit a warning when no RRSIG, DNSKEY or NSEC records are found in the zone.
 
-When AutoDNSSEC is not set, the AXFR+DDNS provider will emit a warning when RRSIG, DNSKEY or NSEC records are found in the zone.
+When AutoDNSSEC is disabled, the AXFR+DDNS provider will emit a warning when RRSIG, DNSKEY or NSEC records are found in the zone.
+
+When AutoDNSSEC is not enabled or disabled, no checking is done.
diff --git a/models/domain.go b/models/domain.go
index 2e008469..eb547bfc 100644
--- a/models/domain.go
+++ b/models/domain.go
@@ -18,7 +18,7 @@ type DomainConfig struct {
 	KeepUnknown    bool              `json:"keepunknown,omitempty"`
 	IgnoredNames   []string          `json:"ignored_names,omitempty"`
 	IgnoredTargets []*IgnoreTarget   `json:"ignored_targets,omitempty"`
-	AutoDNSSEC     bool              `json:"auto_dnssec,omitempty"`
+	AutoDNSSEC     string            `json:"auto_dnssec,omitempty"` // "", "on", "off"
 	//DNSSEC        bool              `json:"dnssec,omitempty"`
 
 	// These fields contain instantiated provider instances once everything is linked up.
diff --git a/models/record.go b/models/record.go
index 2bd1165a..ed5d2be3 100644
--- a/models/record.go
+++ b/models/record.go
@@ -28,7 +28,6 @@ import (
 //     TXT
 //   Pseudo-Types:
 //     ALIAS
-//     AUTODNSSEC
 //     CF_REDIRECT
 //     CF_TEMP_REDIRECT
 //     FRAME
diff --git a/pkg/js/helpers.js b/pkg/js/helpers.js
index 76cb95ed..2053bb62 100644
--- a/pkg/js/helpers.js
+++ b/pkg/js/helpers.js
@@ -2,6 +2,7 @@
 
 // If you edit this file, you must run `go generate` to embed this
 // file in the source code.
+
 // If you are heavily debugging this code, the "-dev" flag will
 // read this file directly instead of using the output of
 // `go generate`. You'll still need to run `go generate` before
@@ -535,9 +536,21 @@ function NO_PURGE(d) {
     d.KeepUnknown = true;
 }
 
-// AUTODNSSEC()
+// AUTODNSSEC
+// Permitted values are:
+// ""  Do not modify the setting (the default)
+// "on"   Enable AUTODNSSEC for this domain
+// "off"  Disable AUTODNSSEC for this domain
+function AUTODNSSEC_ON(d) {
+  d.auto_dnssec = "on";
+}
+function AUTODNSSEC_OFF(d) {
+  d.auto_dnssec = "off";
+}
 function AUTODNSSEC(d) {
-    d.auto_dnssec = true;
+  console.log(
+    "WARNING: AUTODNSSEC is deprecated. It is now a no-op.  Please use AUTODNSSEC_ON or AUTODNSSEC_OFF. The default is to make no modifications. This message will disappear in a future release."
+  );
 }
 
 /**
diff --git a/pkg/js/static.go b/pkg/js/static.go
index b9be4028..a099616a 100644
--- a/pkg/js/static.go
+++ b/pkg/js/static.go
@@ -212,122 +212,124 @@ var _escData = map[string]*_escFile{
 	"/helpers.js": {
 		name:    "helpers.js",
 		local:   "pkg/js/helpers.js",
-		size:    26104,
+		size:    26563,
 		modtime: 0,
 		compressed: `
-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
-/3My09H4ZQAA
+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
 `,
 	},
 }
diff --git a/pkg/normalize/validate.go b/pkg/normalize/validate.go
index 86b49552..0707d3e9 100644
--- a/pkg/normalize/validate.go
+++ b/pkg/normalize/validate.go
@@ -392,11 +392,20 @@ func ValidateAndNormalizeConfig(config *models.DNSConfig) (errs []error) {
 				errs = append(errs, fmt.Errorf("record named '%s' does not have correct FQDN for domain '%s'. FQDN: %s", r.Name, d.Name, r.NameFQDN))
 			}
 		}
+		// Verify AutoDNSSEC is valid.
+		errs = append(errs, checkAutoDNSSEC(d)...)
 	}
 
 	return errs
 }
 
+func checkAutoDNSSEC(dc *models.DomainConfig) (errs []error) {
+	if dc.AutoDNSSEC != "" && dc.AutoDNSSEC != "on" && dc.AutoDNSSEC != "off" {
+		errs = append(errs, fmt.Errorf("Domain %q AutoDNSSEC=%q is invalid (expecting \"\", \"off\", or \"on\")", dc.Name, dc.AutoDNSSEC))
+	}
+	return
+}
+
 func checkCNAMEs(dc *models.DomainConfig) (errs []error) {
 	cnames := map[string]bool{}
 	for _, r := range dc.Records {
@@ -510,7 +519,7 @@ func checkProviderCapabilities(dc *models.DomainConfig) error {
 		hasAny := false
 		switch ty.rType {
 		case "AUTODNSSEC":
-			if dc.AutoDNSSEC {
+			if dc.AutoDNSSEC != "" {
 				hasAny = true
 			}
 		default:
diff --git a/providers/axfrddns/axfrddnsProvider.go b/providers/axfrddns/axfrddnsProvider.go
index bde48396..ecdbc288 100644
--- a/providers/axfrddns/axfrddnsProvider.go
+++ b/providers/axfrddns/axfrddnsProvider.go
@@ -194,7 +194,7 @@ func (c *AxfrDdns) FetchZoneRecords(domain string) ([]dns.RR, error) {
 			if err == "dns: bad xfr rcode: 9" {
 				err = "NOT AUTH (9)"
 			}
-			return nil, fmt.Errorf("[Error] AXFRDDNS: nameserver refused to transfer the zone: %s", msg)
+			return nil, fmt.Errorf("[Error] AXFRDDNS: nameserver refused to transfer the zone: %s", err)
 		}
 		rawRecords = append(rawRecords, msg.RR...)
 	}
@@ -279,10 +279,12 @@ func (c *AxfrDdns) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Corr
 		}
 	}
 
-	if dc.AutoDNSSEC && !hasDnssecRecords {
-		fmt.Printf("Warning: AUTODNSSEC is set, but no DNSKEY or RRSIG record was found in the AXFR answer!\n")
-	} else if !dc.AutoDNSSEC && hasDnssecRecords {
-		fmt.Printf("Warning: AUTODNSSEC is not set, but DNSKEY or RRSIG records were found in the AXFR answer!\n")
+	// TODO(tlim): This check should be done on all providers. Move to the global validation code.
+	if dc.AutoDNSSEC == "on" && !hasDnssecRecords {
+		fmt.Printf("Warning: AUTODNSSEC is enabled, but no DNSKEY or RRSIG record was found in the AXFR answer!\n")
+	}
+	if dc.AutoDNSSEC == "off" && hasDnssecRecords {
+		fmt.Printf("Warning: AUTODNSSEC is disabled, but DNSKEY or RRSIG records were found in the AXFR answer!\n")
 	}
 
 	// Normalize
diff --git a/providers/bind/bindProvider.go b/providers/bind/bindProvider.go
index dbd23e1b..4cdab993 100644
--- a/providers/bind/bindProvider.go
+++ b/providers/bind/bindProvider.go
@@ -177,7 +177,11 @@ func (c *Bind) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correcti
 	comments = append(comments,
 		fmt.Sprintf("generated with dnscontrol %s", time.Now().Format(time.RFC3339)),
 	)
-	if dc.AutoDNSSEC {
+	if dc.AutoDNSSEC == "on" {
+		// This does nothing but reminds the user to add the correct
+		// auto-dnssecc zone statement to named.conf.
+		// While it is a no-op, it is useful for situations where a zone
+		// has multiple providers.
 		comments = append(comments, "Automatic DNSSEC signing requested")
 	}
 
diff --git a/providers/dnsimple/dnsimpleProvider.go b/providers/dnsimple/dnsimpleProvider.go
index 190d2594..e972e48d 100644
--- a/providers/dnsimple/dnsimpleProvider.go
+++ b/providers/dnsimple/dnsimpleProvider.go
@@ -228,7 +228,7 @@ func (c *DnsimpleAPI) getDNSSECCorrections(dc *models.DomainConfig) ([]*models.C
 		return nil, err
 	}
 
-	if enabled && !dc.AutoDNSSEC {
+	if enabled && dc.AutoDNSSEC == "off" {
 		return []*models.Correction{
 			{
 				Msg: "Disable DNSSEC",
@@ -237,7 +237,7 @@ func (c *DnsimpleAPI) getDNSSECCorrections(dc *models.DomainConfig) ([]*models.C
 		}, nil
 	}
 
-	if !enabled && dc.AutoDNSSEC {
+	if !enabled && dc.AutoDNSSEC == "on" {
 		return []*models.Correction{
 			{
 				Msg: "Enable DNSSEC",
diff --git a/providers/powerdns/dnssec.go b/providers/powerdns/dnssec.go
index bd1a4187..d3c5ff7f 100644
--- a/providers/powerdns/dnssec.go
+++ b/providers/powerdns/dnssec.go
@@ -2,6 +2,7 @@ package powerdns
 
 import (
 	"context"
+
 	"github.com/StackExchange/dnscontrol/v3/models"
 	"github.com/mittwald/go-powerdns/apis/cryptokeys"
 )
@@ -28,7 +29,7 @@ func (api *PowerDNS) getDNSSECCorrections(dc *models.DomainConfig) ([]*models.Co
 	}
 
 	// dnssec is enabled, we want it to be disabled
-	if hasEnabledKey && !dc.AutoDNSSEC {
+	if hasEnabledKey && dc.AutoDNSSEC == "off" {
 		return []*models.Correction{
 			{
 				Msg: "Disable DNSSEC",
@@ -38,7 +39,7 @@ func (api *PowerDNS) getDNSSECCorrections(dc *models.DomainConfig) ([]*models.Co
 	}
 
 	// dnssec is disabled, we want it to be enabled
-	if !hasEnabledKey && dc.AutoDNSSEC {
+	if !hasEnabledKey && dc.AutoDNSSEC == "on" {
 		return []*models.Correction{
 			{
 				Msg: "Enable DNSSEC",


Test patch:
diff --git a/pkg/js/parse_tests/025-autodnssec.js b/pkg/js/parse_tests/025-autodnssec.js
index 3b2cb528..68184a51 100644
--- a/pkg/js/parse_tests/025-autodnssec.js
+++ b/pkg/js/parse_tests/025-autodnssec.js
@@ -1,3 +1,8 @@
-D("foo.com","none",
-  AUTODNSSEC
+D("nothing.com","none"
+);
+D("with.com","none",
+  AUTODNSSEC_ON
+);
+D("without.com","none",
+  AUTODNSSEC_OFF
 );
diff --git a/pkg/js/parse_tests/025-autodnssec.json b/pkg/js/parse_tests/025-autodnssec.json
index dd8f5a2d..07706e0d 100644
--- a/pkg/js/parse_tests/025-autodnssec.json
+++ b/pkg/js/parse_tests/025-autodnssec.json
@@ -3,11 +3,24 @@
   "dns_providers": [],
   "domains": [
     {
-      "name": "foo.com",
+      "name": "nothing.com",
       "registrar": "none",
       "dnsProviders": {},
-      "records":[],
-      "auto_dnssec": true
+      "records": []
+    },
+    {
+      "name": "with.com",
+      "registrar": "none",
+      "dnsProviders": {},
+      "records": [],
+      "auto_dnssec": "on"
+    },
+    {
+      "name": "without.com",
+      "registrar": "none",
+      "dnsProviders": {},
+      "records": [],
+      "auto_dnssec": "off"
     }
   ]
 }