Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .github/workflows/pr_integration_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ jobs:
Write-Host "Integration test providers: $Providers"
echo "integration_test_providers=$(ConvertTo-Json -InputObject $Providers -Compress)" >> $env:GITHUB_OUTPUT
env:
PROVIDERS: "['ALIDNS', 'AXFRDDNS', 'AXFRDDNS_DNSSEC', 'AZURE_DNS','BIND','BUNNY_DNS','CLOUDFLAREAPI','CLOUDNS','CNR','DIGITALOCEAN','FORTIGATE','GANDI_V5','GCLOUD','GIDINET','HEDNS','HETZNER_V2','HUAWEICLOUD','INWX','JOKER','MIKROTIK','MYTHICBEASTS', 'NAMEDOTCOM','NS1','POWERDNS','ROUTE53','SAKURACLOUD','TRANSIP','UNIFI']"
PROVIDERS: "['ALIDNS', 'AXFRDDNS', 'AXFRDDNS_DNSSEC', 'AZURE_DNS','BIND','BUNNY_DNS','CLOUDFLAREAPI','CLOUDNS','CNR','DIGITALOCEAN','FORTIGATE','GANDI_V5','GCLOUD','GIDINET','HEDNS','HETZNER_V2','HUAWEICLOUD','INWX','JOKER','MIKROTIK','MYTHICBEASTS', 'NAMEDOTCOM','NETNOD','NS1','POWERDNS','ROUTE53','SAKURACLOUD','TRANSIP','UNIFI']"
ENV_CONTEXT: ${{ toJson(env) }}
VARS_CONTEXT: ${{ toJson(vars) }}
SECRETS_CONTEXT: ${{ toJson(secrets) }}
Expand Down Expand Up @@ -108,6 +108,7 @@ jobs:
MIKROTIK_DOMAIN: ${{ vars.MIKROTIK_DOMAIN }}
MYTHICBEASTS_DOMAIN: ${{ vars.MYTHICBEASTS_DOMAIN }}
NAMEDOTCOM_DOMAIN: ${{ vars.NAMEDOTCOM_DOMAIN }}
NETNOD_DOMAIN: ${{ vars.NETNOD_DOMAIN }}
NS1_DOMAIN: ${{ vars.NS1_DOMAIN }}
POWERDNS_DOMAIN: ${{ vars.POWERDNS_DOMAIN }}
ROUTE53_DOMAIN: ${{ vars.ROUTE53_DOMAIN }}
Expand Down Expand Up @@ -197,6 +198,9 @@ jobs:
NAMEDOTCOM_URL: ${{ secrets.NAMEDOTCOM_URL }}
NAMEDOTCOM_USER: ${{ secrets.NAMEDOTCOM_USER }}
#
NETNOD_APIKEY: ${{ secrets.NETNOD_APIKEY }}
NETNOD_APIURL: ${{ secrets.NETNOD_APIURL }}
#
NS1_TOKEN: ${{ secrets.NS1_TOKEN }}
#
POWERDNS_APIKEY: ${{ secrets.POWERDNS_APIKEY }}
Expand Down
2 changes: 1 addition & 1 deletion .goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ changelog:
regexp: "(?i)^.*(major|new provider|feature)[(\\w)]*:+.*$"
order: 1
- title: 'Provider-specific changes:'
regexp: "(?i)((adguardhome|akamaiedgedns|alidns|autodns|axfrddns|azure_dns|azure_private_dns|bind|bunny_dns|cloudflare|cloudflareapi|cloudns|cnr|cscglobal|desec|digitalocean|dnscale|dnsimple|dnsmadeeasy|dnsoverhttps|domainnameshop|dynadot|easyname|exoscale|fortigate|gandi_v5|gcloud|gcore|gidinet|hedns|hetzner|hetzner_v2|hostingde|huaweicloud|infomaniak|internetbs|inwx|joker|linode|loopia|luadns|mikrotik|mythicbeasts|namecheap|namedotcom|netcup|netlify|ns1|opensrs|oracle|ovh|packetframe|porkbun|powerdns|realtimeregister|route53|rwth|sakuracloud|softlayer|transip|unifi|vercel|vultr).*:)+.*"
regexp: "(?i)((adguardhome|akamaiedgedns|alidns|autodns|axfrddns|azure_dns|azure_private_dns|bind|bunny_dns|cloudflare|cloudflareapi|cloudns|cnr|cscglobal|desec|digitalocean|dnscale|dnsimple|dnsmadeeasy|dnsoverhttps|domainnameshop|dynadot|easyname|exoscale|fortigate|gandi_v5|gcloud|gcore|gidinet|hedns|hetzner|hetzner_v2|hostingde|huaweicloud|infomaniak|internetbs|inwx|joker|linode|loopia|luadns|mikrotik|mythicbeasts|namecheap|namedotcom|netcup|netlify|netnod|ns1|opensrs|oracle|ovh|packetframe|porkbun|powerdns|realtimeregister|route53|rwth|sakuracloud|softlayer|transip|unifi|vercel|vultr).*:)+.*"
order: 2
- title: 'Documentation:'
regexp: "(?i)^.*(docs)[(\\w)]*:+.*$"
Expand Down
1 change: 1 addition & 0 deletions OWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ providers/namecheap @willpower232
# providers/namedotcom NEEDS VOLUNTEER
providers/netcup @kordianbruck
providers/netlify @SphericalKat
providers/netnod @Netnod
providers/ns1 @costasd
# providers/opensrs NEEDS VOLUNTEER
providers/oracle @kallsyms
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ Currently supported DNS providers:
- Namecheap
- Netcup
- Netlify
- Netnod
- NS1
- Oracle Cloud
- OVH
Expand Down
1 change: 1 addition & 0 deletions documentation/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@
* [Name.com](provider/namedotcom.md)
* [Netcup](provider/netcup.md)
* [Netlify](provider/netlify.md)
* [Netnod](provider/netnod.md)
* [NS1](provider/ns1.md)
* [OpenSRS](provider/opensrs.md)
* [Oracle Cloud](provider/oracle.md)
Expand Down
7 changes: 7 additions & 0 deletions documentation/provider/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ Jump to a table:
| [`NAMEDOTCOM`](namedotcom.md) | ❌ | ✅ | ✅ |
| [`NETCUP`](netcup.md) | ❌ | ✅ | ❌ |
| [`NETLIFY`](netlify.md) | ❌ | ✅ | ❌ |
| [`NETNOD`](netnod.md) | ❌ | ✅ | ❌ |
| [`NS1`](ns1.md) | ❌ | ✅ | ❌ |
| [`OPENSRS`](opensrs.md) | ❌ | ❌ | ✅ |
| [`ORACLE`](oracle.md) | ❌ | ✅ | ❌ |
Expand Down Expand Up @@ -138,6 +139,7 @@ Jump to a table:
| [`NAMEDOTCOM`](namedotcom.md) | ❔ | ✅ | ❌ | ✅ |
| [`NETCUP`](netcup.md) | ❔ | ❌ | ❌ | ❌ |
| [`NETLIFY`](netlify.md) | ✅ | ❌ | ❌ | ✅ |
| [`NETNOD`](netnod.md) | ❔ | ✅ | ✅ | ✅ |
| [`NS1`](ns1.md) | ✅ | ✅ | ✅ | ✅ |
| [`OPENSRS`](opensrs.md) | ❔ | ❔ | ❌ | ❔ |
| [`ORACLE`](oracle.md) | ❔ | ✅ | ✅ | ✅ |
Expand Down Expand Up @@ -201,6 +203,7 @@ Jump to a table:
| [`NAMEDOTCOM`](namedotcom.md) | ✅ | ❔ | ❌ | ❌ | ❔ |
| [`NETCUP`](netcup.md) | ❔ | ❔ | ❌ | ❌ | ❔ |
| [`NETLIFY`](netlify.md) | ✅ | ❔ | ❌ | ❌ | ❔ |
| [`NETNOD`](netnod.md) | ✅ | ❌ | ❌ | ✅ | ❌ |
| [`NS1`](ns1.md) | ✅ | ✅ | ❌ | ✅ | ❔ |
| [`ORACLE`](oracle.md) | ✅ | ❔ | ❔ | ✅ | ❔ |
| [`OVH`](ovh.md) | ❌ | ❔ | ❔ | ❌ | ❔ |
Expand Down Expand Up @@ -262,6 +265,7 @@ Jump to a table:
| [`NAMEDOTCOM`](namedotcom.md) | ❔ | ❔ | ✅ | ❔ |
| [`NETCUP`](netcup.md) | ❔ | ❔ | ✅ | ❔ |
| [`NETLIFY`](netlify.md) | ❔ | ❌ | ✅ | ❔ |
| [`NETNOD`](netnod.md) | ❌ | ❌ | ✅ | ❌ |
| [`NS1`](ns1.md) | ✅ | ✅ | ✅ | ✅ |
| [`ORACLE`](oracle.md) | ❔ | ✅ | ✅ | ❔ |
| [`OVH`](ovh.md) | ❔ | ❔ | ✅ | ❔ |
Expand Down Expand Up @@ -322,6 +326,7 @@ Jump to a table:
| [`NAMECHEAP`](namecheap.md) | ✅ | ❔ | ❔ | ❔ | ❌ |
| [`NETCUP`](netcup.md) | ✅ | ❔ | ❔ | ❔ | ✅ |
| [`NETLIFY`](netlify.md) | ✅ | ❔ | ❔ | ❌ | ❌ |
| [`NETNOD`](netnod.md) | ✅ | ✅ | ❔ | ❌ | ✅ |
| [`NS1`](ns1.md) | ✅ | ✅ | ❔ | ❔ | ✅ |
| [`ORACLE`](oracle.md) | ✅ | ❔ | ❔ | ✅ | ✅ |
| [`OVH`](ovh.md) | ✅ | ❔ | ❔ | ✅ | ✅ |
Expand Down Expand Up @@ -369,6 +374,7 @@ Jump to a table:
| [`LOOPIA`](loopia.md) | ❌ | ❌ | ❌ |
| [`MIKROTIK`](mikrotik.md) | ❌ | ❔ | ❌ |
| [`NETLIFY`](netlify.md) | ❌ | ❔ | ❌ |
| [`NETNOD`](netnod.md) | ❌ | ❌ | ❌ |
| [`NS1`](ns1.md) | ✅ | ❔ | ✅ |
| [`ORACLE`](oracle.md) | ❔ | ❔ | ❌ |
| [`PORKBUN`](porkbun.md) | ❌ | ❔ | ❌ |
Expand Down Expand Up @@ -458,6 +464,7 @@ Providers in this category and their maintainers are:
|[`NAMECHEAP`](namecheap.md)|@willpower232|
|[`NETCUP`](netcup.md)|@kordianbruck|
|[`NETLIFY`](netlify.md)|@SphericalKat|
|[`NETNOD`](netnod.md)|@Netnod|
|[`NS1`](ns1.md)|@costasd|
|[`OPENSRS`](opensrs.md)|@philhug|
|[`ORACLE`](oracle.md)|@kallsyms|
Expand Down
59 changes: 59 additions & 0 deletions documentation/provider/netnod.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
## Configuration

To use this provider, add an entry to `creds.json` with `TYPE` set to `NETNOD` along with your API URL and API Key. The API URL can be omitted to use the default value `https://primarydnsapi.netnod.se`.

Example:

{% code title="creds.json" %}

```json
{
"netnod": {
"TYPE": "NETNOD",
"apiKey": "your-key",
"apiUrl": "https://primarydnsapi.netnod.se"
}
}
```

{% endcode %}

## Metadata

The following provider metadata is available:

{% code title="dnsconfig.js" %}

```javascript
var DSP_NETNOD = NewDnsProvider('netnod', {
default_ns: ['a.example.com.', 'b.example.com.'],
also_notify: ['192.36.148.17', '2001:7fe::53'],
allow_transfer_keys: ['netnod-key1.'],
});
```

{% endcode %}

- `default_ns` sets the nameservers used when creating zones.
- `also_notify` sets a list of IP addresses that will receive DNS NOTIFY messages when a zone is created. This is the provider-level default and applies to all zones unless overridden per zone (see below).
- `allow_transfer_keys` sets the TSIG key IDs permitted to perform zone transfers from the distribution servers when a zone is created.
This should include all keys used for DNS secondary replication, including those used by the Netnod secondary DNS service. This is the provider-level default and applies to all zones unless overridden per zone.

## Usage

An example configuration:

{% code title="dnsconfig.js" %}

```javascript
var REG_NONE = NewRegistrar('none');
var DSP_NETNOD = NewDnsProvider('netnod');

D('example.com', REG_NONE, DnsProvider(DSP_NETNOD), A('test', '1.2.3.4'));
```

{% endcode %}

## Activation

See the [Netnod DNS](https://www.netnod.se/dns/dns-enterprise-services).
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/StackExchange/dnscontrol/v4

go 1.25.0
go 1.26

retract v4.8.0

Expand Down Expand Up @@ -77,6 +77,7 @@ require (
github.com/kylelemons/godebug v1.1.0
github.com/luadns/luadns-go v0.3.0
github.com/mattn/go-isatty v0.0.20
github.com/netnod/netnod-primary-dns-client v1.0.0
github.com/nicholas-fedor/shoutrrr v0.13.2
github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481
github.com/oracle/oci-go-sdk/v65 v65.109.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 h1:o6uBwrhM5C8Ll3MAAxrQxRHEu7FkapwTuI2WmL1rw4g=
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04/go.mod h1:5sN+Lt1CaY4wsPvgQH/jsuJi4XO2ssZbdsIizr4CVC8=
github.com/netnod/netnod-primary-dns-client v1.0.0 h1:ISyr7eOsQtUAZ4c1ov0sFV22I4yB09Du1kf9zy/q85U=
github.com/netnod/netnod-primary-dns-client v1.0.0/go.mod h1:MLvJf4UhbAB8WQdypLqYv9rwtOGD+3cZlAVXMwgad3c=
github.com/nicholas-fedor/shoutrrr v0.13.2 h1:hfsYBIqSFYGg92pZP5CXk/g7/OJIkLYmiUnRl+AD1IA=
github.com/nicholas-fedor/shoutrrr v0.13.2/go.mod h1:ZqzV3gY/Wj6AvWs1etlO7+yKbh4iptSbeL8avBpMQbA=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
Expand Down
6 changes: 6 additions & 0 deletions integrationTest/profiles.json
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,12 @@
"slug": "$NETLIFY_ACCOUNT_SLUG",
"token": "$NETLIFY_TOKEN"
},
"NETNOD": {
"TYPE": "NETNOD",
"apiKey": "$NETNOD_APIKEY",
"apiUrl": "$NETNOD_APIURL",
"domain": "$NETNOD_DOMAIN"
},
"NS1": {
"TYPE": "NS1",
"api_token": "$NS1_TOKEN",
Expand Down
1 change: 1 addition & 0 deletions pkg/providers/_all/all.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import (
_ "github.com/StackExchange/dnscontrol/v4/providers/namedotcom"
_ "github.com/StackExchange/dnscontrol/v4/providers/netcup"
_ "github.com/StackExchange/dnscontrol/v4/providers/netlify"
_ "github.com/StackExchange/dnscontrol/v4/providers/netnod"
_ "github.com/StackExchange/dnscontrol/v4/providers/ns1"
_ "github.com/StackExchange/dnscontrol/v4/providers/opensrs"
_ "github.com/StackExchange/dnscontrol/v4/providers/oracle"
Expand Down
18 changes: 18 additions & 0 deletions providers/netnod/auditrecords.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package netnod

import (
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/rejectif"
)

// AuditRecords returns a list of errors corresponding to the records
// that aren't supported by this provider. If all records are
// supported, an empty list is returned.
func AuditRecords(records []*models.RecordConfig) []error {
a := rejectif.Auditor{}

a.Add("TXT", rejectif.TxtHasDoubleQuotes) // Last verified 2023-11-11
a.Add("TXT", rejectif.TxtHasBackslash) // Last verified 2023-11-11

return a.Audit(records)
}
40 changes: 40 additions & 0 deletions providers/netnod/convert.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package netnod

import (
"strings"

"github.com/StackExchange/dnscontrol/v4/models"
netnodPrimaryDNS "github.com/netnod/netnod-primary-dns-client"
)

// toRecordConfig converts a Netnod DNS Record to a RecordConfig. #rtype_variations
func toRecordConfig(domain string, r netnodPrimaryDNS.Record, ttl int, name string, rtype string) (*models.RecordConfig, error) {
// trimming trailing dot and domain from name
name = strings.TrimSuffix(name, domain+".")
name = strings.TrimSuffix(name, ".")

rc := &models.RecordConfig{
TTL: uint32(ttl),
Original: r,
Type: rtype,
}
rc.SetLabel(name, domain)

switch rtype {
case "TXT":
// API accepts long TXTs without requiring to split them.
// The API then returns them as they initially came in, e.g. "averylooooooo[...]oooooongstring" or "string" "string"
// So we need to strip away " and split into multiple string
// We can't use SetTargetRFC1035Quoted, it would split the long strings into multiple parts
return rc, rc.SetTargetTXTs(parseTxt(r.Content))
default:
return rc, rc.PopulateFromString(rtype, r.Content, domain)
}
}

func parseTxt(content string) (result []string) {
for r := range strings.SplitSeq(content, "\" ") {
result = append(result, strings.Trim(r, "\""))
}
return
}
67 changes: 67 additions & 0 deletions providers/netnod/convert_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package netnod

import (
"fmt"
"strings"
"testing"

netnodPrimaryDNS "github.com/netnod/netnod-primary-dns-client"
"github.com/stretchr/testify/assert"
)

func TestToRecordConfig(t *testing.T) {
record := netnodPrimaryDNS.Record{
Content: "simple",
}
recordConfig, err := toRecordConfig("example.com", record, 120, "test", "TXT")

assert.NoError(t, err)
assert.Equal(t, "test.example.com", recordConfig.NameFQDN)
assert.Equal(t, "\"simple\"", recordConfig.String())
assert.Equal(t, uint32(120), recordConfig.TTL)
assert.Equal(t, "TXT", recordConfig.Type)

largeContent := fmt.Sprintf("\"%s\" \"%s\"", strings.Repeat("A", 300), strings.Repeat("B", 300))
largeRecord := netnodPrimaryDNS.Record{
Content: largeContent,
}
recordConfig, err = toRecordConfig("example.com", largeRecord, 5, "large", "TXT")

assert.NoError(t, err)
assert.Equal(t, "large.example.com", recordConfig.NameFQDN)
assert.Equal(t, `"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"`,
recordConfig.String())
assert.Equal(t, uint32(5), recordConfig.TTL)
assert.Equal(t, "TXT", recordConfig.Type)

luaRecord := netnodPrimaryDNS.Record{
Content: "TXT \"return 'Hello, world!'\"",
}
recordConfig, err = toRecordConfig("example.com", luaRecord, 3600, "script", "LUA")

assert.NoError(t, err)
assert.Equal(t, "script.example.com", recordConfig.NameFQDN)
assert.Equal(t, "LUA", recordConfig.Type)
assert.Equal(t, "TXT", recordConfig.LuaRType)
assert.Equal(t, "return 'Hello, world!'", recordConfig.GetTargetTXTJoined())
assert.Equal(t, "TXT \"return 'Hello, world!'\"", recordConfig.GetTargetCombined())
assert.Equal(t, uint32(3600), recordConfig.TTL)
}

func TestParseText(t *testing.T) {
// short TXT record
short := parseTxt("\"simple\"")
assert.Equal(t, []string{"simple"}, short)

// TXT record with multiple parts
multiple := parseTxt("\"simple\" \"simple2\"")
assert.Equal(t, []string{"simple", "simple2"}, multiple)

// long TXT record
long := parseTxt(fmt.Sprintf("\"%s\"", strings.Repeat("A", 300)))
assert.Equal(t, []string{strings.Repeat("A", 300)}, long)

// multiple long TXT record
multipleLong := parseTxt(fmt.Sprintf("\"%s\" \"%s\"", strings.Repeat("A", 300), strings.Repeat("B", 300)))
assert.Equal(t, []string{strings.Repeat("A", 300), strings.Repeat("B", 300)}, multipleLong)
}
Loading