From 9aad2926fb48315c863115f5c36ad24a9a3097a4 Mon Sep 17 00:00:00 2001 From: Patrik Kernstock Date: Sat, 29 Nov 2025 18:17:13 +0100 Subject: [PATCH] INWX: Fix INWX provider after their unexpected data-type breaking-change (#3855) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #3854 Unfortunately I couldn't run the integrationTests properly as INWX doesn't seem to have properly updated their sandbox environment (it still presents `int` instead of `string` like production). Hence, the tests do fail. I don't want to run this against my own production account, to be frank. See: ```shell $ curl -X POST https://api.ote.domrobot.com/xmlrpc/ -H "Content-Type: application/xml" -d ' nameserver.info user [USER] lang en pass [PASS] domain [DOMAIN] ' | xmllint --format - | grep -iE "id|roId" -C3 % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 3968 0 2971 100 997 13375 4488 --:--:-- --:--:-- --:--:-- 17954 roId 9677 -- id 118057 -- id 118060 -- id 79610 -- id 77243 -- svTRID 20251127--ote ``` Hence, only done manualy tests via `dnscontrol push --domains `: (tested create, delete and modify) ```text CONCURRENTLY checking for 0 zone(s) SERIALLY checking for 1 zone(s) Serially checking for zone: "example.tld" CONCURRENTLY gathering records of 0 zone(s) SERIALLY gathering records of 1 zone(s) Serially Gathering: "example.tld" ******************** Domain: example.tld 3 corrections (PK-INWX) #1: - DELETE _test1.example.tld TXT "123" ttl=43200 SUCCESS! #2: ± MODIFY _test2.example.tld TXT ("1234" ttl=43200) -> ("12345" ttl=43200) SUCCESS! #3: + CREATE _test4.example.tld TXT "123" ttl=43200 SUCCESS! Done. 3 corrections. ``` --- go.mod | 2 +- go.sum | 4 ++-- pkg/zonerecs/zonerecords.go | 2 +- providers/inwx/auditrecords.go | 8 ++------ providers/inwx/dnssec.go | 11 ++--------- providers/inwx/inwxProvider.go | 35 +++++++++++++++++++--------------- 6 files changed, 28 insertions(+), 34 deletions(-) diff --git a/go.mod b/go.mod index 8c3fa5482..d8e7a6098 100644 --- a/go.mod +++ b/go.mod @@ -38,7 +38,7 @@ require ( github.com/miekg/dns v1.1.68 github.com/mittwald/go-powerdns v0.6.7 github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 - github.com/nrdcg/goinwx v0.11.0 + github.com/nrdcg/goinwx v0.12.0 github.com/ovh/go-ovh v1.9.0 github.com/philhug/opensrs-go v0.0.0-20171126225031-9dfa7433020d github.com/pkg/errors v0.9.1 diff --git a/go.sum b/go.sum index 4b5b7cf3d..ce2279391 100644 --- a/go.sum +++ b/go.sum @@ -307,8 +307,8 @@ github.com/nicholas-fedor/shoutrrr v0.12.0 h1:8mwJdfU+uBEybSymwQJMGl/grG7lvVUKbV github.com/nicholas-fedor/shoutrrr v0.12.0/go.mod h1:WYiRalR4C43Qmd2zhPWGIFIxu633NB1hDM6Ap/DQcsA= github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481 h1:Up6+btDp321ZG5/zdSLo48H9Iaq0UQGthrhWC6pCxzE= github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481/go.mod h1:yKZQO8QE2bHlgozqWDiRVqTFlLQSj30K/6SAK8EeYFw= -github.com/nrdcg/goinwx v0.11.0 h1:GER0SE3POub7rxARt3Y3jRy1OON1hwF1LRxHz5xsFBw= -github.com/nrdcg/goinwx v0.11.0/go.mod h1:0BXSC0FxVtU4aTjX0Zw3x0DK32tjugLzeNIAGtwXvPQ= +github.com/nrdcg/goinwx v0.12.0 h1:ujdUqDBnaRSFwzVnImvPHYw3w3m9XgmGImNUw1GyMb4= +github.com/nrdcg/goinwx v0.12.0/go.mod h1:IrVKd3ZDbFiMjdPgML4CSxZAY9wOoqLvH44zv3NodJ0= github.com/onsi/ginkgo/v2 v2.27.2 h1:LzwLj0b89qtIy6SSASkzlNvX6WktqurSHwkk2ipF/Ns= github.com/onsi/ginkgo/v2 v2.27.2/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo= github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= diff --git a/pkg/zonerecs/zonerecords.go b/pkg/zonerecs/zonerecords.go index 7904ee99c..84876bad0 100644 --- a/pkg/zonerecs/zonerecords.go +++ b/pkg/zonerecs/zonerecords.go @@ -19,7 +19,7 @@ func CorrectZoneRecords(driver models.DNSProvider, dc *models.DomainConfig) ([]* models.CanonicalizeTargets(existingRecords, dc.Name) models.CanonicalizeTargets(dc.Records, dc.Name) - // Copy dc so that any corrections code that wants to + // Copy dc so that any correction code that wants to // modify the records may. For example, if the provider only // supports certain TTL values, it will adjust the ones in // dc.Records. diff --git a/providers/inwx/auditrecords.go b/providers/inwx/auditrecords.go index 7a2853d19..95ca7b771 100644 --- a/providers/inwx/auditrecords.go +++ b/providers/inwx/auditrecords.go @@ -10,12 +10,8 @@ import ( // supported, an empty list is returned. func AuditRecords(records []*models.RecordConfig) []error { a := rejectif.Auditor{} - - a.Add("TXT", rejectif.TxtHasBackticks) // Last verified 2021-03-01 - + a.Add("TXT", rejectif.TxtHasBackticks) // Last verified 2021-03-01 a.Add("TXT", rejectif.TxtHasTrailingSpace) // Last verified 2021-03-01 - - a.Add("TXT", rejectif.TxtIsEmpty) // Last verified 2021-03-01 - + a.Add("TXT", rejectif.TxtIsEmpty) // Last verified 2021-03-01 return a.Audit(records) } diff --git a/providers/inwx/dnssec.go b/providers/inwx/dnssec.go index 7053644f0..c9ccfabda 100644 --- a/providers/inwx/dnssec.go +++ b/providers/inwx/dnssec.go @@ -10,25 +10,22 @@ const ( // testing shows 'AUTO' is what to expect if the domain has automatic // DNSSEC enabled. - // AutoDNSSEC is the status for DNSSEC enabled with automatic management + // AutoDNSSECStatus is the status for DNSSEC enabled with automatic management AutoDNSSECStatus = "AUTO" - // ManualDNSSEC is the status for DNSSEC enabled with manual management + // ManualDNSSECStatus is the status for DNSSEC enabled with manual management ManualDNSSECStatus = "MANUAL" ) // DNSSecStatus returns domain dnssec status func (api *inwxAPI) DNSSecStatus(domain string) (string, error) { - resp, err := api.client.Dnssec.Info([]string{domain}) if err != nil { return "", err } - // domain has no DNSSEC configuration if len(resp.Data) == 0 { return "", nil } - return resp.Data[0].DNSSecStatus, nil } @@ -40,16 +37,12 @@ func (api *inwxAPI) enableAutoDNSSEC(domain string) error { if err != nil { return err } - err = api.client.Dnssec.Enable(domain) - return err } // disableAutoDNSSEC disables automatic management of DNSSEC func (api *inwxAPI) disableAutoDNSSEC(domain string) error { - err := api.client.Dnssec.Disable(domain) - return err } diff --git a/providers/inwx/inwxProvider.go b/providers/inwx/inwxProvider.go index 3a4eb4097..ef5461eee 100644 --- a/providers/inwx/inwxProvider.go +++ b/providers/inwx/inwxProvider.go @@ -22,6 +22,9 @@ import ( /* INWX Registrar and DNS provider +Based on this great INWX API implementation: +https://github.com/nrdcg/goinwx + Info required in `creds.json`: - username - password @@ -34,7 +37,6 @@ Either of the following settings is required when two factor authentication is e Additional settings available in `creds.json`: - sandbox (set to 1 to use the sandbox API from INWX) - */ // InwxProductionDefaultNs contains the default INWX nameservers. @@ -182,10 +184,10 @@ func makeNameserverRecordRequest(domain string, rec *models.RecordConfig) *goinw switch rType := rec.Type; rType { /* - INWX is a little bit special for CNAME,NS,MX and SRV records: + INWX is a little bit special for CNAME, NS, MX and SRV records: The API will not accept any target with a final dot but will instead always add this final dot internally. - Records with empty targets (i.e. records with target ".") + Records with empty targets (i.e., records with target ".") are allowed. */ case "CNAME", "NS", "ALIAS": @@ -219,14 +221,14 @@ func (api *inwxAPI) createRecord(domain string, rec *models.RecordConfig) error } // updateRecord is used by GetDomainCorrections to update an existing record. -func (api *inwxAPI) updateRecord(RecordID int, rec *models.RecordConfig) error { +func (api *inwxAPI) updateRecord(RecordID string, rec *models.RecordConfig) error { req := makeNameserverRecordRequest("", rec) err := api.client.Nameservers.UpdateRecord(RecordID, req) return err } // deleteRecord is used by GetDomainCorrections to delete a record. -func (api *inwxAPI) deleteRecord(RecordID int) error { +func (api *inwxAPI) deleteRecord(RecordID string) error { return api.client.Nameservers.DeleteRecord(RecordID) } @@ -244,7 +246,8 @@ func (api *inwxAPI) AutoDnssecToggle(dc *models.DomainConfig, corrections []*mod } if dnssecStatus == ManualDNSSECStatus && dc.AutoDNSSEC != "" { - return corrections, fmt.Errorf("INWX: Domain %s has manual DNSSEC enabled. Disable it before using AUTODNSSEC_ON/AUTODNSSEC_OFF", dc.Name) + return corrections, fmt.Errorf("INWX: Domain %s has manual DNSSEC enabled. Disable it before using "+ + "AUTODNSSEC_ON/AUTODNSSEC_OFF", dc.Name) } if dnssecStatus != AutoDNSSECStatus && dc.AutoDNSSEC == "on" { @@ -289,23 +292,25 @@ func (api *inwxAPI) GetZoneRecordsCorrections(dc *models.DomainConfig, foundReco return nil, 0, err } for _, change := range changes { - changeMsgs := change.MsgsJoined + changeMessage := change.MsgsJoined switch change.Type { case diff2.REPORT: - corrections = append(corrections, &models.Correction{Msg: changeMsgs}) + corrections = append(corrections, &models.Correction{Msg: changeMessage}) case diff2.CHANGE: oldRec := change.Old[0] newRec := change.New[0] if isNullMX(newRec) || isNullMX(oldRec) { - // changing to or from a Null MX has to be delete then create + // changing to or from a Null MX has to be deleted then create deletes = append(deletes, &models.Correction{ - Msg: color.RedString("- DELETE %s %s %s ttl=%d", oldRec.GetLabelFQDN(), oldRec.Type, oldRec.ToComparableNoTTL(), oldRec.TTL), + Msg: color.RedString("- DELETE %s %s %s ttl=%d", oldRec.GetLabelFQDN(), oldRec.Type, + oldRec.ToComparableNoTTL(), oldRec.TTL), F: func() error { return api.deleteRecord(oldRec.Original.(goinwx.NameserverRecord).ID) }, }) deferred = append(deferred, &models.Correction{ - Msg: color.GreenString("+ CREATE %s %s %s ttl=%d", newRec.GetLabelFQDN(), newRec.Type, newRec.ToComparableNoTTL(), newRec.TTL), + Msg: color.GreenString("+ CREATE %s %s %s ttl=%d", newRec.GetLabelFQDN(), newRec.Type, + newRec.ToComparableNoTTL(), newRec.TTL), F: func() error { return api.createRecord(dc.Name, newRec) }, @@ -313,7 +318,7 @@ func (api *inwxAPI) GetZoneRecordsCorrections(dc *models.DomainConfig, foundReco } else { recID := oldRec.Original.(goinwx.NameserverRecord).ID corrections = append(corrections, &models.Correction{ - Msg: changeMsgs, + Msg: changeMessage, F: func() error { return api.updateRecord(recID, newRec) }, @@ -322,7 +327,7 @@ func (api *inwxAPI) GetZoneRecordsCorrections(dc *models.DomainConfig, foundReco } case diff2.CREATE: creates = append(creates, &models.Correction{ - Msg: changeMsgs, + Msg: changeMessage, F: func() error { return api.createRecord(dc.Name, change.New[0]) }, @@ -330,7 +335,7 @@ func (api *inwxAPI) GetZoneRecordsCorrections(dc *models.DomainConfig, foundReco case diff2.DELETE: recID := change.Old[0].Original.(goinwx.NameserverRecord).ID deletes = append(deletes, &models.Correction{ - Msg: changeMsgs, + Msg: changeMessage, F: func() error { return api.deleteRecord(recID) }, }) default: @@ -343,7 +348,7 @@ func (api *inwxAPI) GetZoneRecordsCorrections(dc *models.DomainConfig, foundReco return corrections, actualChangeCount, nil } -// getDefaultNameservers returns string map with default nameservers based on e.g. sandbox mode. +// getDefaultNameservers returns a string map with default nameservers based on e.g. sandbox mode. func (api *inwxAPI) getDefaultNameservers() []string { if api.sandbox { return InwxSandboxDefaultNs