diff --git a/build/generate/featureMatrix.go b/build/generate/featureMatrix.go index db1e45970..48c593b84 100644 --- a/build/generate/featureMatrix.go +++ b/build/generate/featureMatrix.go @@ -90,6 +90,7 @@ func matrixData() *FeatureMatrix { DomainModifierDs = "[`DS`](language-reference/domain-modifiers/DS.md)" DomainModifierDhcid = "[`DHCID`](language-reference/domain-modifiers/DHCID.md)" DomainModifierDname = "[`DNAME`](language-reference/domain-modifiers/DNAME.md)" + DomainModifierDnskey = "[`DNSKEY`](language-reference/domain-modifiers/DNSKEY.md)" DualHost = "dual host" CreateDomains = "create-domains" GetZones = "get-zones" @@ -115,6 +116,7 @@ func matrixData() *FeatureMatrix { DomainModifierDs, DomainModifierDhcid, DomainModifierDname, + DomainModifierDnskey, DualHost, CreateDomains, //NoPurge, @@ -201,6 +203,10 @@ func matrixData() *FeatureMatrix { DomainModifierDs, providers.CanUseDS, ) + setCapability( + DomainModifierDnskey, + providers.CanUseDNSKEY, + ) setCapability( DomainModifierLoc, providers.CanUseLOC, diff --git a/commands/getZones.go b/commands/getZones.go index 0bec3cbb6..0b5860193 100644 --- a/commands/getZones.go +++ b/commands/getZones.go @@ -337,6 +337,8 @@ func formatDsl(rec *models.RecordConfig, defaultTTL uint32) string { return makeCaa(rec, ttlop) case "DS": target = fmt.Sprintf(`%d, %d, %d, "%s"`, rec.DsKeyTag, rec.DsAlgorithm, rec.DsDigestType, rec.DsDigest) + case "DNSKEY": + target = fmt.Sprintf(`%d, %d, %d, "%s"`, rec.DnskeyFlags, rec.DnskeyProtocol, rec.DnskeyAlgorithm, rec.DnskeyPublicKey) case "MX": target = fmt.Sprintf(`%d, "%s"`, rec.MxPreference, rec.GetTargetField()) case "NAPTR": diff --git a/commands/types/dnscontrol.d.ts b/commands/types/dnscontrol.d.ts index 01d2246dd..d23a59938 100644 --- a/commands/types/dnscontrol.d.ts +++ b/commands/types/dnscontrol.d.ts @@ -835,6 +835,27 @@ declare function DMARC_BUILDER(opts: { label?: string; version?: string; policy: */ declare function DNAME(name: string, target: string, ...modifiers: RecordModifier[]): DomainModifier; +/** + * DNSKEY adds a DNSKEY record to the domain. + * + * Flags should be a number. + * + * Protocol should be a number. + * + * Algorithm must be a number. + * + * Public key must be a string. + * + * ```javascript + * D("example.com", REG_MY_PROVIDER, DnsProvider(DSP_MY_PROVIDER), + * DNSKEY("@", 257, 3, 13, "AABBCCDD") + * ); + * ``` + * + * @see https://docs.dnscontrol.org/language-reference/domain-modifiers/dnskey + */ +declare function DNSKEY(name: string, flags: number, protocol: number, algorithm: number, publicKey: string, ...modifiers: RecordModifier[]): DomainModifier; + /** * `DOMAIN_ELSEWHERE()` is a helper macro that lets you easily indicate that * a domain's zones are managed elsewhere. That is, it permits you easily delegate diff --git a/documentation/SUMMARY.md b/documentation/SUMMARY.md index 2e2eec426..653ce865e 100644 --- a/documentation/SUMMARY.md +++ b/documentation/SUMMARY.md @@ -39,6 +39,7 @@ * [CNAME](language-reference/domain-modifiers/CNAME.md) * [DHCID](language-reference/domain-modifiers/DHCID.md) * [DNAME](language-reference/domain-modifiers/DNAME.md) + * [DNSKEY](language-reference/domain-modifiers/DNSKEY.md) * [DISABLE_IGNORE_SAFETY_CHECK](language-reference/domain-modifiers/DISABLE_IGNORE_SAFETY_CHECK.md) * [DMARC_BUILDER](language-reference/domain-modifiers/DMARC_BUILDER.md) * [DS](language-reference/domain-modifiers/DS.md) diff --git a/documentation/adding-new-rtypes.md b/documentation/adding-new-rtypes.md index 943aaa32a..b3677eb7d 100644 --- a/documentation/adding-new-rtypes.md +++ b/documentation/adding-new-rtypes.md @@ -60,14 +60,14 @@ a minimum. - Run stringer to auto-update the file `dnscontrol/providers/capability_string.go` ```shell -pushd; cd provider/; +pushd; cd providers/; stringer -type=Capability popd ``` alternatively ```shell -pushd; cd provider/; +pushd; cd providers/; go generate popd ``` diff --git a/documentation/language-reference/domain-modifiers/DNSKEY.md b/documentation/language-reference/domain-modifiers/DNSKEY.md new file mode 100644 index 000000000..2f6cf8587 --- /dev/null +++ b/documentation/language-reference/domain-modifiers/DNSKEY.md @@ -0,0 +1,35 @@ +--- +name: DNSKEY +parameters: + - name + - flags + - protocol + - algorithm + - publicKey + - modifiers... +parameter_types: + name: string + flags: number + protocol: number + algorithm: number + publicKey: string + "modifiers...": RecordModifier[] +--- + +DNSKEY adds a DNSKEY record to the domain. + +Flags should be a number. + +Protocol should be a number. + +Algorithm must be a number. + +Public key must be a string. + +{% code title="dnsconfig.js" %} +```javascript +D("example.com", REG_MY_PROVIDER, DnsProvider(DSP_MY_PROVIDER), + DNSKEY("@", 257, 3, 13, "AABBCCDD") +); +``` +{% endcode %} diff --git a/documentation/providers.md b/documentation/providers.md index bbaff36d6..4264657b8 100644 --- a/documentation/providers.md +++ b/documentation/providers.md @@ -12,58 +12,58 @@ a provider that supports it, we'd love your contribution to ensure it works corr If a feature is definitively not supported for whatever reason, we would also like a PR to clarify why it is not supported, and fill in this entire matrix. -| Provider name | Official Support | DNS Provider | Registrar | Concurrency Verified | [`ALIAS`](language-reference/domain-modifiers/ALIAS.md) | [`CAA`](language-reference/domain-modifiers/CAA.md) | [`AUTODNSSEC`](language-reference/domain-modifiers/AUTODNSSEC_ON.md) | [`LOC`](language-reference/domain-modifiers/LOC.md) | [`NAPTR`](language-reference/domain-modifiers/NAPTR.md) | [`PTR`](language-reference/domain-modifiers/PTR.md) | [`SOA`](language-reference/domain-modifiers/SOA.md) | [`SRV`](language-reference/domain-modifiers/SRV.md) | [`SSHFP`](language-reference/domain-modifiers/SSHFP.md) | [`TLSA`](language-reference/domain-modifiers/TLSA.md) | [`DS`](language-reference/domain-modifiers/DS.md) | [`DHCID`](language-reference/domain-modifiers/DHCID.md) | [`DNAME`](language-reference/domain-modifiers/DNAME.md) | dual host | create-domains | get-zones | -| ------------- | ---------------- | ------------ | --------- | -------------------- | ------------------------------------------------------- | --------------------------------------------------- | -------------------------------------------------------------------- | --------------------------------------------------- | ------------------------------------------------------- | --------------------------------------------------- | --------------------------------------------------- | --------------------------------------------------- | ------------------------------------------------------- | ----------------------------------------------------- | ------------------------------------------------- | ------------------------------------------------------- | ------------------------------------------------------- | --------- | -------------- | --------- | -| [`AKAMAIEDGEDNS`](provider/akamaiedgedns.md) | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ❌ | ❔ | ❔ | ✅ | ✅ | ✅ | -| [`AUTODNS`](provider/autodns.md) | ❌ | ✅ | ❌ | ❌ | ✅ | ✅ | ❔ | ❔ | ❔ | ❌ | ❔ | ✅ | ❌ | ❌ | ❌ | ❔ | ❔ | ❌ | ❌ | ✅ | -| [`AXFRDDNS`](provider/axfrddns.md) | ❌ | ✅ | ❌ | ❌ | ❔ | ✅ | ✅ | ❔ | ✅ | ✅ | ❔ | ✅ | ✅ | ✅ | ❔ | ✅ | ❔ | ❌ | ❌ | ❌ | -| [`AZURE_DNS`](provider/azure_dns.md) | ✅ | ✅ | ❌ | ✅ | ❌ | ✅ | ❔ | ❌ | ❌ | ✅ | ❔ | ✅ | ❌ | ❌ | ❔ | ❔ | ❔ | ✅ | ✅ | ✅ | -| [`AZURE_PRIVATE_DNS`](provider/azure_private_dns.md) | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❔ | ❌ | ❌ | ✅ | ❔ | ✅ | ❌ | ❌ | ❔ | ❔ | ❔ | ✅ | ✅ | ✅ | -| [`BIND`](provider/bind.md) | ✅ | ✅ | ❌ | ❌ | ❔ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [`BUNNY_DNS`](provider/bunny_dns.md) | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ❔ | ❌ | ✅ | ✅ | -| [`CLOUDFLAREAPI`](provider/cloudflareapi.md) | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ❔ | ❌ | ✅ | ✅ | ❔ | ✅ | ✅ | ✅ | ❔ | ❔ | ❔ | ❌ | ✅ | ✅ | -| [`CLOUDNS`](provider/cloudns.md) | ❌ | ✅ | ❌ | ❌ | ✅ | ✅ | ❔ | ❌ | ❔ | ✅ | ❔ | ✅ | ✅ | ✅ | ❔ | ❔ | ✅ | ❔ | ✅ | ✅ | -| [`CSCGLOBAL`](provider/cscglobal.md) | ✅ | ✅ | ✅ | ✅ | ❔ | ✅ | ❔ | ❔ | ❔ | ❔ | ❔ | ✅ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❌ | ✅ | -| [`DESEC`](provider/desec.md) | ❌ | ✅ | ❌ | ❌ | ❔ | ✅ | ✅ | ❔ | ✅ | ✅ | ❔ | ✅ | ✅ | ✅ | ✅ | ❔ | ❔ | ❔ | ✅ | ✅ | -| [`DIGITALOCEAN`](provider/digitalocean.md) | ❌ | ✅ | ❌ | ❌ | ❔ | ✅ | ❔ | ❌ | ❔ | ❔ | ❔ | ✅ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ✅ | ✅ | -| [`DNSIMPLE`](provider/dnsimple.md) | ❌ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ❔ | ✅ | ✅ | ❌ | ❌ | ❔ | ❔ | ❌ | ❌ | ✅ | -| [`DNSMADEEASY`](provider/dnsmadeeasy.md) | ❌ | ✅ | ❌ | ❌ | ✅ | ✅ | ❔ | ❌ | ❔ | ✅ | ❔ | ✅ | ❌ | ❌ | ❌ | ❔ | ❔ | ✅ | ✅ | ✅ | -| [`DNSOVERHTTPS`](provider/dnsoverhttps.md) | ❌ | ❌ | ✅ | ❌ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❌ | ❔ | -| [`DOMAINNAMESHOP`](provider/domainnameshop.md) | ❌ | ✅ | ❌ | ❌ | ❔ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | -| [`DYNADOT`](provider/dynadot.md) | ❌ | ❌ | ✅ | ❌ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❌ | ❔ | -| [`EASYNAME`](provider/easyname.md) | ❌ | ❌ | ✅ | ❌ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❌ | ❔ | -| [`EXOSCALE`](provider/exoscale.md) | ❌ | ✅ | ❌ | ❌ | ✅ | ✅ | ❔ | ❌ | ❔ | ✅ | ❔ | ✅ | ❔ | ❌ | ❔ | ❔ | ❔ | ❌ | ❌ | ❔ | -| [`GANDI_V5`](provider/gandi_v5.md) | ❌ | ✅ | ✅ | ❌ | ✅ | ✅ | ❔ | ❌ | ❔ | ✅ | ❔ | ✅ | ✅ | ✅ | ❌ | ❔ | ❔ | ❔ | ❌ | ✅ | -| [`GCLOUD`](provider/gcloud.md) | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ❔ | ❌ | ❔ | ✅ | ❔ | ✅ | ✅ | ✅ | ❔ | ❔ | ❔ | ✅ | ✅ | ✅ | -| [`GCORE`](provider/gcore.md) | ❌ | ✅ | ❌ | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ | ❔ | ✅ | ❌ | ❌ | ❌ | ❔ | ❔ | ✅ | ✅ | ✅ | -| [`HEDNS`](provider/hedns.md) | ❌ | ✅ | ❌ | ❌ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ❔ | ❔ | ✅ | ✅ | ✅ | -| [`HETZNER`](provider/hetzner.md) | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ✅ | ✅ | ❔ | ❔ | ✅ | ✅ | ✅ | -| [`HEXONET`](provider/hexonet.md) | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ | ❔ | ❔ | ❔ | ✅ | ❔ | ✅ | ❔ | ✅ | ❔ | ❔ | ❔ | ✅ | ✅ | ❔ | -| [`HOSTINGDE`](provider/hostingde.md) | ❌ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❔ | ❔ | ✅ | ✅ | ✅ | -| [`INTERNETBS`](provider/internetbs.md) | ❌ | ❌ | ✅ | ❌ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❌ | ❔ | -| [`INWX`](provider/inwx.md) | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ | ❔ | ❔ | ✅ | ✅ | ❔ | ✅ | ✅ | ✅ | ❔ | ❔ | ❔ | ✅ | ✅ | ✅ | -| [`LINODE`](provider/linode.md) | ❌ | ✅ | ❌ | ❌ | ❔ | ✅ | ❔ | ❌ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❌ | ❌ | ✅ | -| [`LOOPIA`](provider/loopia.md) | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ | ✅ | ✅ | ❌ | ❔ | ❔ | ✅ | ❌ | ✅ | -| [`LUADNS`](provider/luadns.md) | ❌ | ✅ | ❌ | ❌ | ✅ | ✅ | ❔ | ❌ | ❔ | ✅ | ❔ | ✅ | ✅ | ✅ | ❔ | ❔ | ❔ | ✅ | ✅ | ✅ | -| [`MSDNS`](provider/msdns.md) | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❔ | ❌ | ✅ | ✅ | ❔ | ✅ | ❔ | ❔ | ❔ | ❔ | ❔ | ❌ | ❌ | ✅ | -| [`MYTHICBEASTS`](provider/mythicbeasts.md) | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ❔ | ❌ | ❔ | ✅ | ❔ | ✅ | ✅ | ✅ | ❔ | ❔ | ❔ | ✅ | ❌ | ✅ | -| [`NAMECHEAP`](provider/namecheap.md) | ❌ | ✅ | ✅ | ❌ | ✅ | ✅ | ❔ | ❌ | ❔ | ❌ | ❔ | ❌ | ❔ | ❌ | ❔ | ❔ | ❔ | ❌ | ❌ | ✅ | -| [`NAMEDOTCOM`](provider/namedotcom.md) | ❌ | ✅ | ✅ | ❌ | ✅ | ❔ | ❔ | ❌ | ❔ | ❌ | ❔ | ✅ | ❔ | ❔ | ❔ | ❔ | ❔ | ✅ | ❌ | ✅ | -| [`NETCUP`](provider/netcup.md) | ❌ | ✅ | ❌ | ❌ | ❔ | ✅ | ❔ | ❌ | ❔ | ❌ | ❔ | ✅ | ❔ | ❔ | ❔ | ❔ | ❔ | ❌ | ❌ | ❌ | -| [`NETLIFY`](provider/netlify.md) | ❌ | ✅ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❔ | ✅ | ❌ | ❌ | ❌ | ❔ | ❔ | ❌ | ❌ | ✅ | -| [`NS1`](provider/ns1.md) | ❌ | ✅ | ❌ | ❌ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ❔ | ✅ | ❔ | ❔ | ✅ | ❔ | ❔ | ✅ | ✅ | ✅ | -| [`OPENSRS`](provider/opensrs.md) | ❌ | ❌ | ✅ | ❌ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❌ | ❔ | -| [`ORACLE`](provider/oracle.md) | ❌ | ✅ | ❌ | ❌ | ✅ | ✅ | ❔ | ❔ | ✅ | ✅ | ❔ | ✅ | ✅ | ✅ | ❌ | ❔ | ❔ | ✅ | ✅ | ✅ | -| [`OVH`](provider/ovh.md) | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ | ❔ | ❔ | ❔ | ❌ | ❔ | ✅ | ✅ | ✅ | ❔ | ❔ | ❔ | ✅ | ❌ | ✅ | -| [`PACKETFRAME`](provider/packetframe.md) | ❌ | ✅ | ❌ | ❌ | ❔ | ❔ | ❔ | ❔ | ❔ | ✅ | ❔ | ✅ | ❔ | ❔ | ❔ | ❔ | ❔ | ❌ | ❌ | ❔ | -| [`PORKBUN`](provider/porkbun.md) | ❌ | ✅ | ✅ | ❌ | ✅ | ❔ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ✅ | ❌ | ❔ | ❔ | ❌ | ❌ | ✅ | -| [`POWERDNS`](provider/powerdns.md) | ❌ | ✅ | ❌ | ❌ | ✅ | ✅ | ✅ | ❔ | ✅ | ✅ | ❔ | ✅ | ✅ | ✅ | ✅ | ✅ | ❔ | ✅ | ✅ | ✅ | -| [`REALTIMEREGISTER`](provider/realtimeregister.md) | ❌ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ | ❔ | ❌ | ✅ | ✅ | -| [`ROUTE53`](provider/route53.md) | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ❔ | ❌ | ❔ | ✅ | ❔ | ✅ | ❔ | ❔ | ❔ | ❔ | ❔ | ✅ | ✅ | ✅ | -| [`RWTH`](provider/rwth.md) | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ❔ | ❌ | ❌ | ✅ | ❔ | ✅ | ✅ | ❌ | ❔ | ❔ | ❔ | ❌ | ❌ | ✅ | -| [`SOFTLAYER`](provider/softlayer.md) | ❌ | ✅ | ❌ | ❌ | ❔ | ❔ | ❔ | ❌ | ❔ | ❔ | ❔ | ✅ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❌ | ❔ | -| [`TRANSIP`](provider/transip.md) | ❌ | ✅ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ | ❔ | ❔ | ✅ | ✅ | ✅ | ❌ | ❔ | ❔ | ❔ | ❌ | ✅ | -| [`VULTR`](provider/vultr.md) | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ❔ | ❌ | ❔ | ❌ | ❔ | ✅ | ✅ | ❌ | ❔ | ❔ | ❔ | ❔ | ✅ | ✅ | +| Provider name | Official Support | DNS Provider | Registrar | Concurrency Verified | [`ALIAS`](language-reference/domain-modifiers/ALIAS.md) | [`CAA`](language-reference/domain-modifiers/CAA.md) | [`AUTODNSSEC`](language-reference/domain-modifiers/AUTODNSSEC_ON.md) | [`LOC`](language-reference/domain-modifiers/LOC.md) | [`NAPTR`](language-reference/domain-modifiers/NAPTR.md) | [`PTR`](language-reference/domain-modifiers/PTR.md) | [`SOA`](language-reference/domain-modifiers/SOA.md) | [`SRV`](language-reference/domain-modifiers/SRV.md) | [`SSHFP`](language-reference/domain-modifiers/SSHFP.md) | [`TLSA`](language-reference/domain-modifiers/TLSA.md) | [`DS`](language-reference/domain-modifiers/DS.md) | [`DHCID`](language-reference/domain-modifiers/DHCID.md) | [`DNAME`](language-reference/domain-modifiers/DNAME.md) | [`DNSKEY`](language-reference/domain-modifiers/DNSKEY.md) | dual host | create-domains | get-zones | +| ------------- | ---------------- | ------------ | --------- | -------------------- | ------------------------------------------------------- | --------------------------------------------------- | -------------------------------------------------------------------- | --------------------------------------------------- | ------------------------------------------------------- | --------------------------------------------------- | --------------------------------------------------- | --------------------------------------------------- | ------------------------------------------------------- | ----------------------------------------------------- | ------------------------------------------------- | ------------------------------------------------------- | ------------------------------------------------------- | --------------------------------------------------------- | --------- | -------------- | --------- | +| [`AKAMAIEDGEDNS`](provider/akamaiedgedns.md) | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ❌ | ❔ | ❔ | ❔ | ✅ | ✅ | ✅ | +| [`AUTODNS`](provider/autodns.md) | ❌ | ✅ | ❌ | ❌ | ✅ | ✅ | ❔ | ❔ | ❔ | ❌ | ❔ | ✅ | ❌ | ❌ | ❌ | ❔ | ❔ | ❔ | ❌ | ❌ | ✅ | +| [`AXFRDDNS`](provider/axfrddns.md) | ❌ | ✅ | ❌ | ❌ | ❔ | ✅ | ✅ | ❔ | ✅ | ✅ | ❔ | ✅ | ✅ | ✅ | ❔ | ✅ | ❔ | ❔ | ❌ | ❌ | ❌ | +| [`AZURE_DNS`](provider/azure_dns.md) | ✅ | ✅ | ❌ | ✅ | ❌ | ✅ | ❔ | ❌ | ❌ | ✅ | ❔ | ✅ | ❌ | ❌ | ❔ | ❔ | ❔ | ❔ | ✅ | ✅ | ✅ | +| [`AZURE_PRIVATE_DNS`](provider/azure_private_dns.md) | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❔ | ❌ | ❌ | ✅ | ❔ | ✅ | ❌ | ❌ | ❔ | ❔ | ❔ | ❔ | ✅ | ✅ | ✅ | +| [`BIND`](provider/bind.md) | ✅ | ✅ | ❌ | ❌ | ❔ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [`BUNNY_DNS`](provider/bunny_dns.md) | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ❔ | ❔ | ❌ | ✅ | ✅ | +| [`CLOUDFLAREAPI`](provider/cloudflareapi.md) | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ❔ | ❌ | ✅ | ✅ | ❔ | ✅ | ✅ | ✅ | ❔ | ❔ | ❔ | ❔ | ❌ | ✅ | ✅ | +| [`CLOUDNS`](provider/cloudns.md) | ❌ | ✅ | ❌ | ❌ | ✅ | ✅ | ❔ | ❌ | ❔ | ✅ | ❔ | ✅ | ✅ | ✅ | ❔ | ❔ | ✅ | ❔ | ❔ | ✅ | ✅ | +| [`CSCGLOBAL`](provider/cscglobal.md) | ✅ | ✅ | ✅ | ✅ | ❔ | ✅ | ❔ | ❔ | ❔ | ❔ | ❔ | ✅ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❌ | ✅ | +| [`DESEC`](provider/desec.md) | ❌ | ✅ | ❌ | ❌ | ❔ | ✅ | ✅ | ❔ | ✅ | ✅ | ❔ | ✅ | ✅ | ✅ | ✅ | ❔ | ❔ | ✅ | ❔ | ✅ | ✅ | +| [`DIGITALOCEAN`](provider/digitalocean.md) | ❌ | ✅ | ❌ | ❌ | ❔ | ✅ | ❔ | ❌ | ❔ | ❔ | ❔ | ✅ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ✅ | ✅ | +| [`DNSIMPLE`](provider/dnsimple.md) | ❌ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ❔ | ✅ | ✅ | ❌ | ❌ | ❔ | ❔ | ❔ | ❌ | ❌ | ✅ | +| [`DNSMADEEASY`](provider/dnsmadeeasy.md) | ❌ | ✅ | ❌ | ❌ | ✅ | ✅ | ❔ | ❌ | ❔ | ✅ | ❔ | ✅ | ❌ | ❌ | ❌ | ❔ | ❔ | ❔ | ✅ | ✅ | ✅ | +| [`DNSOVERHTTPS`](provider/dnsoverhttps.md) | ❌ | ❌ | ✅ | ❌ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❌ | ❔ | +| [`DOMAINNAMESHOP`](provider/domainnameshop.md) | ❌ | ✅ | ❌ | ❌ | ❔ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | +| [`DYNADOT`](provider/dynadot.md) | ❌ | ❌ | ✅ | ❌ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❌ | ❔ | +| [`EASYNAME`](provider/easyname.md) | ❌ | ❌ | ✅ | ❌ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❌ | ❔ | +| [`EXOSCALE`](provider/exoscale.md) | ❌ | ✅ | ❌ | ❌ | ✅ | ✅ | ❔ | ❌ | ❔ | ✅ | ❔ | ✅ | ❔ | ❌ | ❔ | ❔ | ❔ | ❔ | ❌ | ❌ | ❔ | +| [`GANDI_V5`](provider/gandi_v5.md) | ❌ | ✅ | ✅ | ❌ | ✅ | ✅ | ❔ | ❌ | ❔ | ✅ | ❔ | ✅ | ✅ | ✅ | ❌ | ❔ | ❔ | ❔ | ❔ | ❌ | ✅ | +| [`GCLOUD`](provider/gcloud.md) | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ❔ | ❌ | ❔ | ✅ | ❔ | ✅ | ✅ | ✅ | ❔ | ❔ | ❔ | ❔ | ✅ | ✅ | ✅ | +| [`GCORE`](provider/gcore.md) | ❌ | ✅ | ❌ | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ | ❔ | ✅ | ❌ | ❌ | ❌ | ❔ | ❔ | ❔ | ✅ | ✅ | ✅ | +| [`HEDNS`](provider/hedns.md) | ❌ | ✅ | ❌ | ❌ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ❔ | ❔ | ❔ | ✅ | ✅ | ✅ | +| [`HETZNER`](provider/hetzner.md) | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ✅ | ✅ | ❔ | ❔ | ❔ | ✅ | ✅ | ✅ | +| [`HEXONET`](provider/hexonet.md) | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ | ❔ | ❔ | ❔ | ✅ | ❔ | ✅ | ❔ | ✅ | ❔ | ❔ | ❔ | ❔ | ✅ | ✅ | ❔ | +| [`HOSTINGDE`](provider/hostingde.md) | ❌ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❔ | ❔ | ❔ | ✅ | ✅ | ✅ | +| [`INTERNETBS`](provider/internetbs.md) | ❌ | ❌ | ✅ | ❌ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❌ | ❔ | +| [`INWX`](provider/inwx.md) | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ | ❔ | ❔ | ✅ | ✅ | ❔ | ✅ | ✅ | ✅ | ❔ | ❔ | ❔ | ❔ | ✅ | ✅ | ✅ | +| [`LINODE`](provider/linode.md) | ❌ | ✅ | ❌ | ❌ | ❔ | ✅ | ❔ | ❌ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❌ | ❌ | ✅ | +| [`LOOPIA`](provider/loopia.md) | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ | ✅ | ✅ | ❌ | ❔ | ❔ | ❔ | ✅ | ❌ | ✅ | +| [`LUADNS`](provider/luadns.md) | ❌ | ✅ | ❌ | ❌ | ✅ | ✅ | ❔ | ❌ | ❔ | ✅ | ❔ | ✅ | ✅ | ✅ | ❔ | ❔ | ❔ | ❔ | ✅ | ✅ | ✅ | +| [`MSDNS`](provider/msdns.md) | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❔ | ❌ | ✅ | ✅ | ❔ | ✅ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❌ | ❌ | ✅ | +| [`MYTHICBEASTS`](provider/mythicbeasts.md) | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ❔ | ❌ | ❔ | ✅ | ❔ | ✅ | ✅ | ✅ | ❔ | ❔ | ❔ | ❔ | ✅ | ❌ | ✅ | +| [`NAMECHEAP`](provider/namecheap.md) | ❌ | ✅ | ✅ | ❌ | ✅ | ✅ | ❔ | ❌ | ❔ | ❌ | ❔ | ❌ | ❔ | ❌ | ❔ | ❔ | ❔ | ❔ | ❌ | ❌ | ✅ | +| [`NAMEDOTCOM`](provider/namedotcom.md) | ❌ | ✅ | ✅ | ❌ | ✅ | ❔ | ❔ | ❌ | ❔ | ❌ | ❔ | ✅ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ✅ | ❌ | ✅ | +| [`NETCUP`](provider/netcup.md) | ❌ | ✅ | ❌ | ❌ | ❔ | ✅ | ❔ | ❌ | ❔ | ❌ | ❔ | ✅ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❌ | ❌ | ❌ | +| [`NETLIFY`](provider/netlify.md) | ❌ | ✅ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❔ | ✅ | ❌ | ❌ | ❌ | ❔ | ❔ | ❔ | ❌ | ❌ | ✅ | +| [`NS1`](provider/ns1.md) | ❌ | ✅ | ❌ | ❌ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ❔ | ✅ | ❔ | ❔ | ✅ | ❔ | ❔ | ❔ | ✅ | ✅ | ✅ | +| [`OPENSRS`](provider/opensrs.md) | ❌ | ❌ | ✅ | ❌ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❌ | ❔ | +| [`ORACLE`](provider/oracle.md) | ❌ | ✅ | ❌ | ❌ | ✅ | ✅ | ❔ | ❔ | ✅ | ✅ | ❔ | ✅ | ✅ | ✅ | ❌ | ❔ | ❔ | ❔ | ✅ | ✅ | ✅ | +| [`OVH`](provider/ovh.md) | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ | ❔ | ❔ | ❔ | ❌ | ❔ | ✅ | ✅ | ✅ | ❔ | ❔ | ❔ | ❔ | ✅ | ❌ | ✅ | +| [`PACKETFRAME`](provider/packetframe.md) | ❌ | ✅ | ❌ | ❌ | ❔ | ❔ | ❔ | ❔ | ❔ | ✅ | ❔ | ✅ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❌ | ❌ | ❔ | +| [`PORKBUN`](provider/porkbun.md) | ❌ | ✅ | ✅ | ❌ | ✅ | ❔ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ✅ | ❌ | ❔ | ❔ | ❔ | ❌ | ❌ | ✅ | +| [`POWERDNS`](provider/powerdns.md) | ❌ | ✅ | ❌ | ❌ | ✅ | ✅ | ✅ | ❔ | ✅ | ✅ | ❔ | ✅ | ✅ | ✅ | ✅ | ✅ | ❔ | ❔ | ✅ | ✅ | ✅ | +| [`REALTIMEREGISTER`](provider/realtimeregister.md) | ❌ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ | ❔ | ❔ | ❌ | ✅ | ✅ | +| [`ROUTE53`](provider/route53.md) | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ❔ | ❌ | ❔ | ✅ | ❔ | ✅ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ✅ | ✅ | ✅ | +| [`RWTH`](provider/rwth.md) | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ❔ | ❌ | ❌ | ✅ | ❔ | ✅ | ✅ | ❌ | ❔ | ❔ | ❔ | ❔ | ❌ | ❌ | ✅ | +| [`SOFTLAYER`](provider/softlayer.md) | ❌ | ✅ | ❌ | ❌ | ❔ | ❔ | ❔ | ❌ | ❔ | ❔ | ❔ | ✅ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❌ | ❔ | +| [`TRANSIP`](provider/transip.md) | ❌ | ✅ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ | ❔ | ❔ | ✅ | ✅ | ✅ | ❌ | ❔ | ❔ | ❔ | ❔ | ❌ | ✅ | +| [`VULTR`](provider/vultr.md) | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ❔ | ❌ | ❔ | ❌ | ❔ | ✅ | ✅ | ❌ | ❔ | ❔ | ❔ | ❔ | ❔ | ✅ | ✅ | ### Providers with "official support" diff --git a/integrationTest/integration_test.go b/integrationTest/integration_test.go index 3f1833472..14f3f8fca 100644 --- a/integrationTest/integration_test.go +++ b/integrationTest/integration_test.go @@ -522,6 +522,12 @@ func ds(name string, keyTag uint16, algorithm, digestType uint8, digest string) return r } +func dnskey(name string, flags uint16, protocol, algorithm uint8, publicKey string) *models.RecordConfig { + r := makeRec(name, "", "DNSKEY") + r.SetTargetDNSKEY(flags, protocol, algorithm, publicKey) + return r +} + func ignoreName(labelSpec string) *models.RecordConfig { return ignore(labelSpec, "*", "*") } @@ -1612,6 +1618,14 @@ func makeTests() []*TestGroup { tc("Create DNAME record in non-FQDN", dname("a", "b")), ), + testgroup("DNSKEY", + requires(providers.CanUseDNSKEY), + tc("Create DNSKEY record", dnskey("test", 257, 3, 13, "fRnjbeUVyKvz1bDx2lPmu3KY1k64T358t8kP6Hjveos=")), + tc("Modify DNSKEY record 1", dnskey("test", 256, 3, 13, "fRnjbeUVyKvz1bDx2lPmu3KY1k64T358t8kP6Hjveos=")), + tc("Modify DNSKEY record 2", dnskey("test", 256, 3, 13, "whjtMiJP9C86l0oTJUxemuYtQ0RIZePWt6QETC2kkKM=")), + tc("Modify DNSKEY record 3", dnskey("test", 256, 3, 15, "whjtMiJP9C86l0oTJUxemuYtQ0RIZePWt6QETC2kkKM=")), + ), + //// Vendor-specific record types // Narrative: DNSControl supports DNS records that don't exist! diff --git a/models/dnsrr.go b/models/dnsrr.go index e142fad08..e41128031 100644 --- a/models/dnsrr.go +++ b/models/dnsrr.go @@ -52,6 +52,8 @@ func helperRRtoRC(rr dns.RR, origin string, fixBug bool) (RecordConfig, error) { err = rc.SetTarget(v.Target) case *dns.DS: err = rc.SetTargetDS(v.KeyTag, v.Algorithm, v.DigestType, v.Digest) + case *dns.DNSKEY: + err = rc.SetTargetDNSKEY(v.Flags, v.Protocol, v.Algorithm, v.PublicKey) case *dns.LOC: err = rc.SetTargetLOC(v.Version, v.Latitude, v.Longitude, v.Altitude, v.Size, v.HorizPre, v.VertPre) case *dns.MX: diff --git a/models/domain.go b/models/domain.go index 4a8fb4f4b..6e29f5728 100644 --- a/models/domain.go +++ b/models/domain.go @@ -141,7 +141,7 @@ func (dc *DomainConfig) Punycode() error { rec.SetTarget(t) case "CF_REDIRECT", "CF_TEMP_REDIRECT", "CF_WORKER_ROUTE": rec.SetTarget(rec.GetTargetField()) - case "A", "AAAA", "CAA", "DHCID", "DS", "LOC", "NAPTR", "SOA", "SSHFP", "TXT", "TLSA", "AZURE_ALIAS": + case "A", "AAAA", "CAA", "DHCID", "DNSKEY", "DS", "LOC", "NAPTR", "SOA", "SSHFP", "TXT", "TLSA", "AZURE_ALIAS": // Nothing to do. default: return fmt.Errorf("Punycode rtype %v unimplemented", rec.Type) diff --git a/models/record.go b/models/record.go index 47809f001..46c19b031 100644 --- a/models/record.go +++ b/models/record.go @@ -104,6 +104,10 @@ type RecordConfig struct { DsAlgorithm uint8 `json:"dsalgorithm,omitempty"` DsDigestType uint8 `json:"dsdigesttype,omitempty"` DsDigest string `json:"dsdigest,omitempty"` + DnskeyFlags uint16 `json:"dnskeyflags,omitempty"` + DnskeyProtocol uint8 `json:"dnskeyprotocol,omitempty"` + DnskeyAlgorithm uint8 `json:"dnskeyalgorithm,omitempty"` + DnskeyPublicKey string `json:"dnskeypublickey,omitempty"` LocVersion uint8 `json:"locversion,omitempty"` LocSize uint8 `json:"locsize,omitempty"` LocHorizPre uint8 `json:"lochorizpre,omitempty"` @@ -172,6 +176,10 @@ func (rc *RecordConfig) UnmarshalJSON(b []byte) error { DsAlgorithm uint8 `json:"dsalgorithm,omitempty"` DsDigestType uint8 `json:"dsdigesttype,omitempty"` DsDigest string `json:"dsdigest,omitempty"` + DnskeyFlags uint16 `json:"dnskeyflags,omitempty"` + DnskeyProtocol uint8 `json:"dnskeyprotocol,omitempty"` + DnskeyAlgorithm uint8 `json:"dnskeyalgorithm,omitempty"` + DnskeyPublicKey string `json:"dnskeypublickey,omitempty"` LocVersion uint8 `json:"locversion,omitempty"` LocSize uint8 `json:"locsize,omitempty"` LocHorizPre uint8 `json:"lochorizpre,omitempty"` @@ -368,6 +376,11 @@ func (rc *RecordConfig) ToRR() dns.RR { rr.(*dns.DS).DigestType = rc.DsDigestType rr.(*dns.DS).Digest = rc.DsDigest rr.(*dns.DS).KeyTag = rc.DsKeyTag + case dns.TypeDNSKEY: + rr.(*dns.DNSKEY).Flags = rc.DnskeyFlags + rr.(*dns.DNSKEY).Protocol = rc.DnskeyProtocol + rr.(*dns.DNSKEY).Algorithm = rc.DnskeyAlgorithm + rr.(*dns.DNSKEY).PublicKey = rc.DnskeyPublicKey case dns.TypeLOC: // fmt.Printf("ToRR long: %d, lat:%d, sz: %d, hz:%d, vt:%d\n", rc.LocLongitude, rc.LocLatitude, rc.LocSize, rc.LocHorizPre, rc.LocVertPre) // fmt.Printf("ToRR rc: %+v\n", *rc) @@ -539,7 +552,7 @@ func Downcase(recs []*RecordConfig) { r.Name = strings.ToLower(r.Name) r.NameFQDN = strings.ToLower(r.NameFQDN) switch r.Type { // #rtype_variations - case "AKAMAICDN", "ALIAS", "AAAA", "ANAME", "CNAME", "DNAME", "DS", "MX", "NS", "NAPTR", "PTR", "SRV", "TLSA": + case "AKAMAICDN", "ALIAS", "AAAA", "ANAME", "CNAME", "DNAME", "DS", "DNSKEY", "MX", "NS", "NAPTR", "PTR", "SRV", "TLSA": // Target is case insensitive. Downcase it. r.target = strings.ToLower(r.target) // BUGFIX(tlim): isn't ALIAS in the wrong case statement? @@ -564,7 +577,7 @@ func CanonicalizeTargets(recs []*RecordConfig, origin string) { for _, r := range recs { switch r.Type { // #rtype_variations - case "ALIAS", "ANAME", "CNAME", "DNAME", "DS", "MX", "NS", "NAPTR", "PTR", "SRV": + case "ALIAS", "ANAME", "CNAME", "DNAME", "DS", "DNSKEY", "MX", "NS", "NAPTR", "PTR", "SRV": // Target is a hostname that might be a shortname. Turn it into a FQDN. r.target = dnsutil.AddOrigin(r.target, originFQDN) case "A", "AKAMAICDN", "CAA", "DHCID", "CF_REDIRECT", "CF_TEMP_REDIRECT", "CF_WORKER_ROUTE", "IMPORT_TRANSFORM", "LOC", "SSHFP", "TLSA", "TXT": diff --git a/models/t_dnskey.go b/models/t_dnskey.go new file mode 100644 index 000000000..4f2be60ff --- /dev/null +++ b/models/t_dnskey.go @@ -0,0 +1,52 @@ +package models + +import ( + "strconv" + "strings" + + "github.com/pkg/errors" +) + +// SetTargetDNSKEY sets the DNSKEY fields. +func (rc *RecordConfig) SetTargetDNSKEY(flags uint16, protocol, algorithm uint8, publicKey string) error { + rc.DnskeyFlags = flags + rc.DnskeyProtocol = protocol + rc.DnskeyAlgorithm = algorithm + rc.DnskeyPublicKey = publicKey + + if rc.Type == "" { + rc.Type = "DNSKEY" + } + if rc.Type != "DNSKEY" { + panic("assertion failed: SetTargetDNSKEY called when .Type is not DNSKEY") + } + + return nil +} + +// SetTargetDNSKEYStrings is like SetTargetDNSKEY but accepts strings. +func (rc *RecordConfig) SetTargetDNSKEYStrings(flags, protocol, algorithm, publicKey string) error { + u16flags, err := strconv.ParseUint(flags, 10, 16) + if err != nil { + return errors.Wrap(err, "DNSKEY Flags can't fit in 16 bits") + } + u8protocol, err := strconv.ParseUint(protocol, 10, 8) + if err != nil { + return errors.Wrap(err, "DNSKEY Protocol can't fit in 8 bits") + } + u8algorithm, err := strconv.ParseUint(algorithm, 10, 8) + if err != nil { + return errors.Wrap(err, "DNSKEY Algorithm can't fit in 8 bits") + } + + return rc.SetTargetDNSKEY(uint16(u16flags), uint8(u8protocol), uint8(u8algorithm), publicKey) +} + +// SetTargetDNSKEYString is like SetTargetDNSKEY but accepts one big string. +func (rc *RecordConfig) SetTargetDNSKEYString(s string) error { + part := strings.Fields(s) + if len(part) != 4 { + return errors.Errorf("DNSKEY value does not contain 4 fieldnskey: (%#v)", s) + } + return rc.SetTargetDNSKEYStrings(part[0], part[1], part[2], part[3]) +} diff --git a/models/t_parse.go b/models/t_parse.go index a0aa4f374..c7f50f62b 100644 --- a/models/t_parse.go +++ b/models/t_parse.go @@ -78,6 +78,8 @@ func (rc *RecordConfig) PopulateFromStringFunc(rtype, contents, origin string, t return rc.SetTargetCAAString(contents) case "DS": return rc.SetTargetDSString(contents) + case "DNSKEY": + return rc.SetTargetDNSKEYString(contents) case "DHCID": return rc.SetTarget(contents) case "DNAME": @@ -164,6 +166,8 @@ func (rc *RecordConfig) PopulateFromString(rtype, contents, origin string) error return rc.SetTargetCAAString(contents) case "DS": return rc.SetTargetDSString(contents) + case "DNSKEY": + return rc.SetTargetDNSKEYString(contents) case "DHCID": return rc.SetTarget(contents) case "DNAME": diff --git a/models/target.go b/models/target.go index 4194b0334..ecb82237f 100644 --- a/models/target.go +++ b/models/target.go @@ -117,6 +117,8 @@ func (rc *RecordConfig) GetTargetDebug() string { content += fmt.Sprintf(" caatag=%s caaflag=%d", rc.CaaTag, rc.CaaFlag) case "DS": content += fmt.Sprintf(" ds_algorithm=%d ds_keytag=%d ds_digesttype=%d ds_digest=%s", rc.DsAlgorithm, rc.DsKeyTag, rc.DsDigestType, rc.DsDigest) + case "DNSKEY": + content += fmt.Sprintf(" dnskey_flags=%d dnskey_protocol=%d dnskey_algorithm=%d dnskey_publickey=%s", rc.DnskeyFlags, rc.DnskeyProtocol, rc.DnskeyAlgorithm, rc.DnskeyPublicKey) case "MX": content += fmt.Sprintf(" pref=%d", rc.MxPreference) case "NAPTR": diff --git a/pkg/js/helpers.js b/pkg/js/helpers.js index c4b8ed2d2..bebaf4d34 100644 --- a/pkg/js/helpers.js +++ b/pkg/js/helpers.js @@ -430,6 +430,25 @@ var DHCID = recordBuilder('DHCID'); // DNAME(name,target, recordModifiers...) var DNAME = recordBuilder('DNAME'); +// DNSKEY(name, flags, protocol, algorithm, publickey) +var DNSKEY = recordBuilder('DNSKEY', { + args: [ + ['name', _.isString], + ['flags', _.isNumber], + ['protocol', _.isNumber], + ['algorithm', _.isNumber], + ['publickey', _.isString], + ], + transform: function (record, args, modifiers) { + record.name = args.name; + record.dnskeyflags = args.flags; + record.dnskeyprotocol = args.protocol; + record.dnskeyalgorithm = args.algorithm; + record.dnskeypublickey = args.publickey; + record.target = args.target; + }, +}); + // PTR(name,target, recordModifiers...) var PTR = recordBuilder('PTR'); @@ -871,11 +890,11 @@ function IGNORE(labelPattern, rtypePattern, targetPattern) { // IGNORE_NAME(name, rTypes) function IGNORE_NAME(name, rTypes) { - return IGNORE(name, rTypes) + return IGNORE(name, rTypes); } function IGNORE_TARGET(target, rType) { - return IGNORE("*", rType, target) + return IGNORE('*', rType, target); } // IMPORT_TRANSFORM(translation_table, domain) @@ -1482,18 +1501,18 @@ function CAA_BUILDER(value) { } if (value.issue) { - var flag = function() {}; + var flag = function () {}; if (value.issue_critical) { - flag = CAA_CRITICAL; + flag = CAA_CRITICAL; } for (var i = 0, len = value.issue.length; i < len; i++) r.push(CAA(value.label, 'issue', value.issue[i], flag)); } if (value.issuewild) { - var flag = function() {}; + var flag = function () {}; if (value.issuewild_critical) { - flag = CAA_CRITICAL; + flag = CAA_CRITICAL; } for (var i = 0, len = value.issuewild.length; i < len; i++) r.push(CAA(value.label, 'issuewild', value.issuewild[i], flag)); diff --git a/pkg/js/parse_tests/048-DNSKEY.js b/pkg/js/parse_tests/048-DNSKEY.js new file mode 100644 index 000000000..0f066011a --- /dev/null +++ b/pkg/js/parse_tests/048-DNSKEY.js @@ -0,0 +1,3 @@ +D("foo.com","none", + DNSKEY("@", 257, 3, 13, "AABBCCDD") +); diff --git a/pkg/js/parse_tests/048-DNSKEY.json b/pkg/js/parse_tests/048-DNSKEY.json new file mode 100644 index 000000000..52ebf43b9 --- /dev/null +++ b/pkg/js/parse_tests/048-DNSKEY.json @@ -0,0 +1,22 @@ +{ + "registrars": [], + "dns_providers": [], + "domains": [ + { + "name": "foo.com", + "registrar": "none", + "dnsProviders": {}, + "records": [ + { + "type": "DNSKEY", + "name": "@", + "dnskeyflags": 257, + "dnskeyprotocol": 3, + "dnskeyalgorithm": 13, + "dnskeypublickey": "AABBCCDD", + "target": "" + } + ] + } + ] +} diff --git a/pkg/normalize/validate.go b/pkg/normalize/validate.go index 6cc9cf7bd..cbb120642 100644 --- a/pkg/normalize/validate.go +++ b/pkg/normalize/validate.go @@ -62,6 +62,7 @@ func validateRecordTypes(rec *models.RecordConfig, domain string, pTypes []strin "DHCID": true, "DNAME": true, "DS": true, + "DNSKEY": true, "IMPORT_TRANSFORM": false, "LOC": true, "MX": true, @@ -223,7 +224,7 @@ func checkTargets(rec *models.RecordConfig, domain string) (errs []error) { } case "SRV": check(checkTarget(target)) - case "CAA", "DHCID", "DS", "IMPORT_TRANSFORM", "SSHFP", "TLSA", "TXT": + case "CAA", "DHCID", "DNSKEY", "DS", "IMPORT_TRANSFORM", "SSHFP", "TLSA", "TXT": default: if rec.Metadata["orig_custom_type"] != "" { // it is a valid custom type. We perform no validation on target @@ -683,6 +684,7 @@ var providerCapabilityChecks = []pairTypeCapability{ capabilityCheck("CAA", providers.CanUseCAA), capabilityCheck("DHCID", providers.CanUseDHCID), capabilityCheck("DNAME", providers.CanUseDNAME), + capabilityCheck("DNSKEY", providers.CanUseDNSKEY), capabilityCheck("LOC", providers.CanUseLOC), capabilityCheck("NAPTR", providers.CanUseNAPTR), capabilityCheck("PTR", providers.CanUsePTR), diff --git a/pkg/prettyzone/prettyzone_test.go b/pkg/prettyzone/prettyzone_test.go index c3283e29c..1e27c757d 100644 --- a/pkg/prettyzone/prettyzone_test.go +++ b/pkg/prettyzone/prettyzone_test.go @@ -339,6 +339,8 @@ func TestWriteZoneFileEach(t *testing.T) { d = append(d, mustNewRR(`x.bosun.org. 300 IN CNAME bosun.org.`)) // Must be a label with no other records. d = append(d, mustNewRR(`bosun.org. 300 IN DHCID AAIBY2/AuCccgoJbsaxcQc9TUapptP69lOjxfNuVAA2kjEA=`)) d = append(d, mustNewRR(`dname.bosun.org. 300 IN DNAME example.com.`)) + d = append(d, mustNewRR(`dnssec.bosun.org. 300 IN DS 31334 13 2 94cc505ebc36b1f4e051268b820efb230f1572d445e833bb5bf7380d6c2cbc0a`)) + d = append(d, mustNewRR(`dnssec.bosun.org. 300 IN DNSKEY 257 3 13 rNR701yiOPHfqDP53GnsHZdlsRqI7O1ksk60rnFILZVk7Z4eTBd1U49oSkTNVNox9tb7N15N2hboXoMEyFFzcw==`)) buf := &bytes.Buffer{} writeZoneFileRR(buf, d, "bosun.org") if buf.String() != testdataZFEach { @@ -360,6 +362,8 @@ var testdataZFEach = `$TTL 300 4.5 IN PTR y.bosun.org. _443._tcp IN TLSA 3 1 1 abcdef0 dname IN DNAME example.com. +dnssec IN DNSKEY 257 3 13 rNR701yiOPHfqDP53GnsHZdlsRqI7O1ksk60rnFILZVk7Z4eTBd1U49oSkTNVNox9tb7N15N2hboXoMEyFFzcw== + IN DS 31334 13 2 94CC505EBC36B1F4E051268B820EFB230F1572D445E833BB5BF7380D6C2CBC0A sub IN NS bosun.org. x IN CNAME bosun.org. ` diff --git a/pkg/prettyzone/sorting.go b/pkg/prettyzone/sorting.go index c5c373cb8..57bda8983 100644 --- a/pkg/prettyzone/sorting.go +++ b/pkg/prettyzone/sorting.go @@ -99,6 +99,20 @@ func (z *ZoneGenData) Less(i, j int) bool { // flag set goes before ones without flag set return fa > fb } + case "DS": + pa, pb := a.DsKeyTag, b.DsKeyTag + if pa != pb { + return pa < pb + } + case "DNSKEY": + pa, pb := a.DnskeyFlags, b.DnskeyFlags + if pa != pb { + return pa < pb + } + fa, fb := a.DnskeyProtocol, b.DnskeyProtocol + if fa != fb { + return fa < fb + } default: // pass through. String comparison is sufficient. } diff --git a/providers/bind/bindProvider.go b/providers/bind/bindProvider.go index 763c58d4d..a1d31cb8d 100644 --- a/providers/bind/bindProvider.go +++ b/providers/bind/bindProvider.go @@ -39,6 +39,7 @@ var features = providers.DocumentationNotes{ providers.CanUseDHCID: providers.Can(), providers.CanUseDNAME: providers.Can(), providers.CanUseDS: providers.Can(), + providers.CanUseDNSKEY: providers.Can(), providers.CanUseLOC: providers.Can(), providers.CanUseNAPTR: providers.Can(), providers.CanUsePTR: providers.Can(), diff --git a/providers/capabilities.go b/providers/capabilities.go index a15fcba16..83c6af69f 100644 --- a/providers/capabilities.go +++ b/providers/capabilities.go @@ -78,6 +78,9 @@ const ( // CanUseTLSA indicates the provider can handle TLSA records CanUseTLSA + // CanUseDNSKEY indicates that the provider can handle DNSKEY records + CanUseDNSKEY + // DocCreateDomains means provider can add domains with the `dnscontrol create-domains` command DocCreateDomains diff --git a/providers/capability_string.go b/providers/capability_string.go index 6e458e625..3c7612a97 100644 --- a/providers/capability_string.go +++ b/providers/capability_string.go @@ -27,14 +27,15 @@ func _() { _ = x[CanUseSRV-16] _ = x[CanUseSSHFP-17] _ = x[CanUseTLSA-18] - _ = x[DocCreateDomains-19] - _ = x[DocDualHost-20] - _ = x[DocOfficiallySupported-21] + _ = x[CanUseDNSKEY-19] + _ = x[DocCreateDomains-20] + _ = x[DocDualHost-21] + _ = x[DocOfficiallySupported-22] } -const _Capability_name = "CanAutoDNSSECCanConcurCanGetZonesCanUseAKAMAICDNCanUseAliasCanUseAzureAliasCanUseCAACanUseDHCIDCanUseDNAMECanUseDSCanUseDSForChildrenCanUseLOCCanUseNAPTRCanUsePTRCanUseRoute53AliasCanUseSOACanUseSRVCanUseSSHFPCanUseTLSADocCreateDomainsDocDualHostDocOfficiallySupported" +const _Capability_name = "CanAutoDNSSECCanConcurCanGetZonesCanUseAKAMAICDNCanUseAliasCanUseAzureAliasCanUseCAACanUseDHCIDCanUseDNAMECanUseDSCanUseDSForChildrenCanUseLOCCanUseNAPTRCanUsePTRCanUseRoute53AliasCanUseSOACanUseSRVCanUseSSHFPCanUseTLSACanUseDNSKEYDocCreateDomainsDocDualHostDocOfficiallySupported" -var _Capability_index = [...]uint16{0, 13, 22, 33, 48, 59, 75, 84, 95, 106, 114, 133, 142, 153, 162, 180, 189, 198, 209, 219, 235, 246, 268} +var _Capability_index = [...]uint16{0, 13, 22, 33, 48, 59, 75, 84, 95, 106, 114, 133, 142, 153, 162, 180, 189, 198, 209, 219, 231, 247, 258, 280} func (i Capability) String() string { if i >= Capability(len(_Capability_index)-1) { diff --git a/providers/desec/desecProvider.go b/providers/desec/desecProvider.go index d9aae6fde..dd2374a5f 100644 --- a/providers/desec/desecProvider.go +++ b/providers/desec/desecProvider.go @@ -45,6 +45,7 @@ var features = providers.DocumentationNotes{ providers.CanUseAlias: providers.Unimplemented("Apex aliasing is supported via new SVCB and HTTPS record types. For details, check the deSEC docs."), providers.CanUseCAA: providers.Can(), providers.CanUseDS: providers.Can(), + providers.CanUseDNSKEY: providers.Can(), providers.CanUseLOC: providers.Unimplemented(), providers.CanUseNAPTR: providers.Can(), providers.CanUsePTR: providers.Can(),