mirror of
https://github.com/StackExchange/dnscontrol.git
synced 2024-05-11 05:55:12 +00:00
* OVH DNS Provider (#143) This adds the OVH Provider along with its documentation. Unfortunately we can't set this DNS provider to support `CanUsePTR`, because OVH only supports setting PTR target on the Arpa zone. * OVH Registrar provider (#143) This implements OVH as a registrar provider. Note that NS modifications are done in a "best effort" mode, as the provider doesn't wait for the modifications to be fully applied (the operation that can take a long time). * Allow support for dual providers scenarios Since OVH released their APIv6, it is now possible to update zone apex NS records, opening the door to complete dual providers scenarii. This change implements apex NS management in an OVH zone.
203 lines
4.7 KiB
Go
203 lines
4.7 KiB
Go
package ovh
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"github.com/StackExchange/dnscontrol/models"
|
|
"github.com/StackExchange/dnscontrol/providers"
|
|
"github.com/StackExchange/dnscontrol/providers/diff"
|
|
"github.com/miekg/dns/dnsutil"
|
|
"github.com/xlucas/go-ovh/ovh"
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
type ovhProvider struct {
|
|
client *ovh.Client
|
|
zones map[string]bool
|
|
}
|
|
|
|
var docNotes = providers.DocumentationNotes{
|
|
providers.DocDualHost: providers.Can(),
|
|
providers.DocCreateDomains: providers.Cannot("New domains require registration"),
|
|
providers.DocOfficiallySupported: providers.Cannot(),
|
|
providers.CanUseAlias: providers.Cannot(),
|
|
providers.CanUseTLSA: providers.Can(),
|
|
providers.CanUseCAA: providers.Cannot(),
|
|
providers.CanUsePTR: providers.Cannot(),
|
|
}
|
|
|
|
func newOVH(m map[string]string, metadata json.RawMessage) (*ovhProvider, error) {
|
|
appKey, appSecretKey, consumerKey := m["app-key"], m["app-secret-key"], m["consumer-key"]
|
|
|
|
c := ovh.NewClient(ovh.ENDPOINT_EU_OVHCOM, appKey, appSecretKey, consumerKey, false)
|
|
|
|
// Check for time lag
|
|
if err := c.PollTimeshift(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &ovhProvider{client: c}, nil
|
|
}
|
|
|
|
func newDsp(conf map[string]string, metadata json.RawMessage) (providers.DNSServiceProvider, error) {
|
|
return newOVH(conf, metadata)
|
|
}
|
|
|
|
func newReg(conf map[string]string) (providers.Registrar, error) {
|
|
return newOVH(conf, nil)
|
|
}
|
|
|
|
func init() {
|
|
providers.RegisterRegistrarType("OVH", newReg)
|
|
providers.RegisterDomainServiceProviderType("OVH", newDsp, providers.CanUseSRV, providers.CanUseTLSA, docNotes)
|
|
}
|
|
|
|
func (c *ovhProvider) GetNameservers(domain string) ([]*models.Nameserver, error) {
|
|
if err := c.fetchZones(); err != nil {
|
|
return nil, err
|
|
}
|
|
_, ok := c.zones[domain]
|
|
if !ok {
|
|
return nil, fmt.Errorf("%s not listed in zones for ovh account", domain)
|
|
}
|
|
|
|
ns, err := c.fetchNS(domain)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return models.StringsToNameservers(ns), nil
|
|
}
|
|
|
|
type errNoExist struct {
|
|
domain string
|
|
}
|
|
|
|
func (e errNoExist) Error() string {
|
|
return fmt.Sprintf("Domain %s not found in your ovh account", e.domain)
|
|
}
|
|
|
|
func (c *ovhProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
|
|
dc.Punycode()
|
|
dc.CombineMXs()
|
|
|
|
if !c.zones[dc.Name] {
|
|
return nil, errNoExist{dc.Name}
|
|
}
|
|
|
|
records, err := c.fetchRecords(dc.Name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var actual []*models.RecordConfig
|
|
for _, r := range records {
|
|
if r.FieldType == "SOA" {
|
|
continue
|
|
}
|
|
|
|
if r.SubDomain == "" {
|
|
r.SubDomain = "@"
|
|
}
|
|
|
|
// ovh uses a custom type for SPF and DKIM
|
|
if r.FieldType == "SPF" || r.FieldType == "DKIM" {
|
|
r.FieldType = "TXT"
|
|
}
|
|
|
|
// ovh default is 3600
|
|
if r.TTL == 0 {
|
|
r.TTL = 3600
|
|
}
|
|
|
|
rec := &models.RecordConfig{
|
|
NameFQDN: dnsutil.AddOrigin(r.SubDomain, dc.Name),
|
|
Name: r.SubDomain,
|
|
Type: r.FieldType,
|
|
Target: r.Target,
|
|
TTL: uint32(r.TTL),
|
|
CombinedTarget: true,
|
|
Original: r,
|
|
}
|
|
actual = append(actual, rec)
|
|
}
|
|
|
|
// Normalize
|
|
models.Downcase(actual)
|
|
|
|
differ := diff.New(dc)
|
|
_, create, delete, modify := differ.IncrementalDiff(actual)
|
|
|
|
corrections := []*models.Correction{}
|
|
|
|
for _, del := range delete {
|
|
rec := del.Existing.Original.(*Record)
|
|
corrections = append(corrections, &models.Correction{
|
|
Msg: del.String(),
|
|
F: c.deleteRecordFunc(rec.Id, dc.Name),
|
|
})
|
|
}
|
|
|
|
for _, cre := range create {
|
|
rec := cre.Desired
|
|
corrections = append(corrections, &models.Correction{
|
|
Msg: cre.String(),
|
|
F: c.createRecordFunc(rec, dc.Name),
|
|
})
|
|
}
|
|
|
|
for _, mod := range modify {
|
|
oldR := mod.Existing.Original.(*Record)
|
|
newR := mod.Desired
|
|
corrections = append(corrections, &models.Correction{
|
|
Msg: mod.String(),
|
|
F: c.updateRecordFunc(oldR, newR, dc.Name),
|
|
})
|
|
}
|
|
|
|
if len(corrections) > 0 {
|
|
corrections = append(corrections, &models.Correction{
|
|
Msg: "REFRESH zone " + dc.Name,
|
|
F: func() error {
|
|
return c.refreshZone(dc.Name)
|
|
},
|
|
})
|
|
}
|
|
|
|
return corrections, nil
|
|
}
|
|
|
|
func (c *ovhProvider) GetRegistrarCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
|
|
|
|
ns, err := c.fetchRegistrarNS(dc.Name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
sort.Strings(ns)
|
|
found := strings.Join(ns, ",")
|
|
|
|
desiredNs := []string{}
|
|
for _, d := range dc.Nameservers {
|
|
desiredNs = append(desiredNs, d.Name)
|
|
}
|
|
sort.Strings(desiredNs)
|
|
desired := strings.Join(desiredNs, ",")
|
|
|
|
if found != desired {
|
|
return []*models.Correction{
|
|
{
|
|
Msg: fmt.Sprintf("Change Nameservers from '%s' to '%s'", found, desired),
|
|
F: func() error {
|
|
err := c.updateNS(dc.Name, desiredNs)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}},
|
|
}, nil
|
|
}
|
|
return nil, nil
|
|
}
|