HOSTINGDE: Implement AutoDNSSEC (#2022)

Co-authored-by: Yannik Sembritzki <yannik@sembritzki.org>
This commit is contained in:
Yannik Sembritzki 2023-02-01 00:02:32 +08:00 committed by GitHub
parent 1073e04577
commit 83299e178e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 120 additions and 54 deletions

View file

@ -125,50 +125,7 @@ func (hp *hostingdeProvider) updateNameservers(nss []string, domain string) func
}
}
func (hp *hostingdeProvider) getRecords(domain string) ([]*record, error) {
zc, err := hp.getZoneConfig(domain)
if err != nil {
return nil, err
}
records := []*record{}
page := uint(1)
for {
params := request{
Filter: &filter{
Field: "ZoneConfigId",
Value: zc.ID,
},
Limit: 1000,
Page: page,
}
resp, err := hp.get("dns", "recordsFind", params)
if err != nil {
return nil, err
}
newRecords := []*record{}
if err := json.Unmarshal(resp.Data, &newRecords); err != nil {
return nil, err
}
records = append(records, newRecords...)
if page >= resp.TotalPages {
break
}
page++
}
return records, nil
}
func (hp *hostingdeProvider) updateRecords(domain string, create, del, mod diff.Changeset) error {
zc, err := hp.getZoneConfig(domain)
if err != nil {
return err
}
func (hp *hostingdeProvider) updateZone(zc *zoneConfig, DnsSecOptions *dnsSecOptions, create, del, mod diff.Changeset) error {
toAdd := []*record{}
for _, c := range create {
r := recordToNative(c.Desired)
@ -194,15 +151,46 @@ func (hp *hostingdeProvider) updateRecords(domain string, create, del, mod diff.
RecordsToAdd: toAdd,
RecordsToDelete: toDelete,
RecordsToModify: toModify,
DNSSECOptions: DnsSecOptions,
}
_, err = hp.get("dns", "zoneUpdate", params)
_, err := hp.get("dns", "zoneUpdate", params)
if err != nil {
return err
}
return nil
}
func (hp *hostingdeProvider) getZone(domain string) (*zone, error) {
t, err := idna.ToASCII(domain)
if err != nil {
return nil, err
}
params := request{
Filter: &filter{
Field: "ZoneName",
Value: t,
},
}
resp, err := hp.get("dns", "zonesFind", params)
if err != nil {
return nil, fmt.Errorf("could not get zone config: %w", err)
}
zones := []*zone{}
if err := json.Unmarshal(resp.Data, &zones); err != nil {
return nil, fmt.Errorf("could not parse response: %w", err)
}
if len(zones) == 0 {
return nil, errZoneNotFound
}
return zones[0], nil
}
func (hp *hostingdeProvider) getZoneConfig(domain string) (*zoneConfig, error) {
t, err := idna.ToASCII(domain)
if err != nil {
@ -233,6 +221,31 @@ func (hp *hostingdeProvider) getZoneConfig(domain string) (*zoneConfig, error) {
return zc[0], nil
}
func (hp *hostingdeProvider) getDNSSECOptions(zoneConfigId string) (*dnsSecOptions, error) {
params := request{
Filter: &filter{
Field: "zoneConfigId",
Value: zoneConfigId,
},
}
resp, err := hp.get("dns", "dnsSecOptionsFind", params)
if err != nil {
return nil, fmt.Errorf("could not get dnssec options: %w", err)
}
dnsSecOptions := []*dnsSecOptions{}
if err := json.Unmarshal(resp.Data, &dnsSecOptions); err != nil {
return nil, fmt.Errorf("could not parse response: %w", err)
}
if len(dnsSecOptions) == 0 {
return nil, nil
}
return dnsSecOptions[0], nil
}
func (hp *hostingdeProvider) get(service, method string, params request) (*responseData, error) {
params.AuthToken = hp.authToken
params.OwnerAccountID = hp.ownerAccountID

View file

@ -17,7 +17,7 @@ import (
var defaultNameservers = []string{"ns1.hosting.de.", "ns2.hosting.de.", "ns3.hosting.de."}
var features = providers.DocumentationNotes{
providers.CanAutoDNSSEC: providers.Unimplemented("Supported but not implemented yet."),
providers.CanAutoDNSSEC: providers.Can(),
providers.CanGetZones: providers.Can(),
providers.CanUseAlias: providers.Can(),
providers.CanUseCAA: providers.Can(),
@ -91,11 +91,14 @@ func (hp *hostingdeProvider) GetNameservers(domain string) ([]*models.Nameserver
}
func (hp *hostingdeProvider) GetZoneRecords(domain string) (models.Records, error) {
src, err := hp.getRecords(domain)
zone, err := hp.getZone(domain)
if err != nil {
return nil, err
}
return hp.ApiRecordsToStandardRecordsModel(domain, zone.Records), nil
}
func (hp *hostingdeProvider) ApiRecordsToStandardRecordsModel(domain string, src []record) models.Records {
records := []*models.RecordConfig{}
for _, r := range src {
if r.Type == "SOA" {
@ -104,7 +107,7 @@ func (hp *hostingdeProvider) GetZoneRecords(domain string) (models.Records, erro
records = append(records, r.nativeToRecord(domain))
}
return records, nil
return records
}
func (hp *hostingdeProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
@ -122,12 +125,13 @@ func (hp *hostingdeProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*m
r.TTL = 31556926
}
}
records, err := hp.GetZoneRecords(dc.Name)
zone, err := hp.getZone(dc.Name)
if err != nil {
return nil, err
}
records := hp.ApiRecordsToStandardRecordsModel(dc.Name, zone.Records)
var create, del, mod diff.Changeset
if !diff2.EnableDiff2 {
_, create, del, mod, err = diff.New(dc).IncrementalDiff(records)
@ -148,7 +152,37 @@ func (hp *hostingdeProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*m
msg = append(msg, c.String())
}
if len(create) == 0 && len(del) == 0 && len(mod) == 0 {
existingAutoDNSSecEnabled := zone.ZoneConfig.DNSSECMode == "automatic"
desiredAutoDNSSecEnabled := dc.AutoDNSSEC == "on"
var DnsSecOptions *dnsSecOptions = nil
// ensure that publishKsk is set for domains with AutoDNSSec
if existingAutoDNSSecEnabled == desiredAutoDNSSecEnabled == true {
CurrentDnsSecOptions, err := hp.getDNSSECOptions(zone.ZoneConfig.ID)
if err != nil {
return nil, err
}
if !CurrentDnsSecOptions.PublishKSK {
msg = append(msg, "Enabling publishKsk for AutoDNSSec")
DnsSecOptions = CurrentDnsSecOptions
DnsSecOptions.PublishKSK = true
}
}
if !existingAutoDNSSecEnabled && desiredAutoDNSSecEnabled {
msg = append(msg, "Enable AutoDNSSEC")
DnsSecOptions = &dnsSecOptions{
NSECMode: "nsec3",
PublishKSK: true,
}
zone.ZoneConfig.DNSSECMode = "automatic"
} else if existingAutoDNSSecEnabled && !desiredAutoDNSSecEnabled {
msg = append(msg, "Disable AutoDNSSEC")
zone.ZoneConfig.DNSSECMode = "off"
}
if len(create) == 0 && len(del) == 0 && len(mod) == 0 && existingAutoDNSSecEnabled == desiredAutoDNSSecEnabled && DnsSecOptions == nil {
return nil, nil
}
@ -157,7 +191,7 @@ func (hp *hostingdeProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*m
Msg: fmt.Sprintf("\n%s", strings.Join(msg, "\n")),
F: func() error {
for i := 0; i < 10; i++ {
err := hp.updateRecords(dc.Name, create, del, mod)
err := hp.updateZone(&zone.ZoneConfig, DnsSecOptions, create, del, mod)
if err == nil {
return nil
}
@ -209,8 +243,6 @@ func (hp *hostingdeProvider) GetRegistrarCorrections(dc *models.DomainConfig) ([
}
return nil, nil
// TODO: Handle AutoDNSSEC
}
func (hp *hostingdeProvider) EnsureDomainExists(domain string) error {

View file

@ -33,6 +33,8 @@ type request struct {
// Domain
Domain *domainConfig `json:"domain"`
DNSSECOptions *dnsSecOptions `json:"dnsSecOptions,omitempty"`
}
type filter struct {
@ -71,6 +73,25 @@ type zoneConfig struct {
ZoneTransferWhitelist []string `json:"zoneTransferWhitelist"`
}
type zone struct {
ZoneConfig zoneConfig `json:"zoneConfig"`
Records []record `json:"records"`
}
type dnsSecOptions struct {
Keys []dnsSecKey `json:"flags,omitempty"`
Algorithms []string `json:"algorithms,omitempty"`
NSECMode string `json:"nsecMode"`
PublishKSK bool `json:"publishKsk"`
}
type dnsSecKey struct {
Flags uint32 `json:"flags"`
Protocol uint32 `json:"protocol"`
Algorithm uint32 `json:"algorithm"`
PublicKey string `json:"publicKey"`
}
type record struct {
ID string `json:"id"`
Name string `json:"name"`