diff --git a/documentation/providers.md b/documentation/providers.md index 6ad2b0c5d..c2700b20c 100644 --- a/documentation/providers.md +++ b/documentation/providers.md @@ -22,7 +22,7 @@ If a feature is definitively not supported for whatever reason, we would also li | [`BIND`](provider/bind.md) | ✅ | ✅ | ❌ | ❌ | ❔ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | [`BUNNY_DNS`](provider/bunny_dns.md) | ❌ | ✅ | ❌ | ❌ | ✅ | ✅ | ❌ | ❔ | ❌ | ❌ | ✅ | ❌ | ✅ | ❌ | ❔ | ❌ | ❌ | ❌ | ❔ | ❔ | ❌ | ✅ | ✅ | | [`CLOUDFLAREAPI`](provider/cloudflareapi.md) | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ❔ | ✅ | ❌ | ✅ | ✅ | ❔ | ✅ | ✅ | ✅ | ✅ | ❔ | ❔ | ❔ | ❌ | ❌ | ✅ | ✅ | -| [`CLOUDNS`](provider/cloudns.md) | ❌ | ✅ | ❌ | ❌ | ✅ | ✅ | ❔ | ❔ | ❌ | ❔ | ✅ | ❔ | ✅ | ✅ | ❔ | ✅ | ❔ | ❔ | ✅ | ❔ | ❔ | ✅ | ✅ | +| [`CLOUDNS`](provider/cloudns.md) | ❌ | ✅ | ❌ | ❌ | ✅ | ✅ | ✅ | ❔ | ❌ | ❔ | ✅ | ❔ | ✅ | ✅ | ❔ | ✅ | ❔ | ❔ | ✅ | ❔ | ❔ | ✅ | ✅ | | [`CSCGLOBAL`](provider/cscglobal.md) | ✅ | ✅ | ✅ | ✅ | ❔ | ✅ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ✅ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❌ | ✅ | | [`DESEC`](provider/desec.md) | ❌ | ✅ | ❌ | ✅ | ❔ | ✅ | ✅ | ✅ | ❔ | ✅ | ✅ | ❔ | ✅ | ✅ | ✅ | ✅ | ✅ | ❔ | ❔ | ✅ | ❔ | ✅ | ✅ | | [`DIGITALOCEAN`](provider/digitalocean.md) | ❌ | ✅ | ❌ | ❌ | ❔ | ✅ | ❔ | ❔ | ❌ | ❔ | ❔ | ❔ | ✅ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ✅ | ✅ | diff --git a/providers/cloudns/api.go b/providers/cloudns/api.go index f5f5ac35a..f4b4a7e2e 100644 --- a/providers/cloudns/api.go +++ b/providers/cloudns/api.go @@ -196,6 +196,43 @@ func (c *cloudnsProvider) getRecords(id string) ([]domainRecord, error) { return records, nil } +func (c *cloudnsProvider) isDnssecEnabled(id string) (bool, error) { + params := requestParams{"domain-name": id} + + var bodyString, err = c.get("/dns/get-dnssec-ds-records.json", params) + if err != nil { + // DNSSEC disabled is indicated by an error fetching the DS records. + var errResp errorResponse + err = json.Unmarshal(bodyString, &errResp) + if err == nil { + if errResp.Description == "The DNSSEC is not active." { + return false, nil + } + return false, fmt.Errorf("failed fetching DS records from ClouDNS: %s", err) + } + } + + return true, nil +} + +func (c *cloudnsProvider) setDnssec(id string, enabled bool) error { + params := requestParams{"domain-name": id} + + var endpoint string + if enabled { + endpoint = "/dns/activate-dnssec.json" + } else { + endpoint = "/dns/deactivate-dnssec.json" + } + + var _, err = c.get(endpoint, params) + if err != nil { + return fmt.Errorf("failed setting DNSSEC at ClouDNS: %s", err) + } + + return nil +} + func (c *cloudnsProvider) get(endpoint string, params requestParams) ([]byte, error) { client := &http.Client{} req, _ := http.NewRequest("GET", "https://api.cloudns.net"+endpoint, nil) diff --git a/providers/cloudns/cloudnsProvider.go b/providers/cloudns/cloudnsProvider.go index 36076fccb..b5fab1f7f 100644 --- a/providers/cloudns/cloudnsProvider.go +++ b/providers/cloudns/cloudnsProvider.go @@ -40,6 +40,7 @@ func NewCloudns(m map[string]string, metadata json.RawMessage) (providers.DNSSer var features = providers.DocumentationNotes{ // The default for unlisted capabilities is 'Cannot'. // See providers/capabilities.go for the entire list of capabilities. + providers.CanAutoDNSSEC: providers.Can(), providers.CanGetZones: providers.Can(), providers.CanConcur: providers.Cannot(), providers.CanUseAlias: providers.Can(), @@ -134,12 +135,18 @@ func (c *cloudnsProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, exi record.TTL = fixTTL(record.TTL) } + dnssecFixes, err := c.getDNSSECCorrections(dc) + if err != nil { + return nil, 0, err + } + toReport, create, del, modify, actualChangeCount, err := diff.NewCompat(dc).IncrementalDiff(existingRecords) if err != nil { return nil, 0, err } // Start corrections with the reports corrections := diff.GenerateMessageCorrections(toReport) + corrections = append(corrections, dnssecFixes...) // Deletes first so changing type works etc. for _, m := range del { @@ -225,6 +232,34 @@ func (c *cloudnsProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, exi } +// getDNSSECCorrections returns corrections that update a domain's DNSSEC state. +func (c *cloudnsProvider) getDNSSECCorrections(dc *models.DomainConfig) ([]*models.Correction, error) { + enabled, err := c.isDnssecEnabled(dc.Name) + if err != nil { + return nil, err + } + + if enabled && dc.AutoDNSSEC == "off" { + return []*models.Correction{ + { + Msg: "Disable DNSSEC", + F: func() error { err := c.setDnssec(dc.Name, false); return err }, + }, + }, nil + } + + if !enabled && dc.AutoDNSSEC == "on" { + return []*models.Correction{ + { + Msg: "Enable DNSSEC", + F: func() error { err := c.setDnssec(dc.Name, true); return err }, + }, + }, nil + } + + return []*models.Correction{}, nil +} + // GetZoneRecords gets the records of a zone and returns them in RecordConfig format. func (c *cloudnsProvider) GetZoneRecords(domain string, meta map[string]string) (models.Records, error) { records, err := c.getRecords(domain)