mirror of
https://github.com/StackExchange/dnscontrol.git
synced 2025-09-14 17:14:47 +08:00
Merge 40e77916b2
into 8b73de0765
This commit is contained in:
commit
cff75cbd0f
9 changed files with 373 additions and 0 deletions
1
OWNERS
1
OWNERS
|
@ -28,6 +28,7 @@ providers/hetzner @das7pad
|
|||
providers/hexonet @KaiSchwarz-cnic
|
||||
providers/hostingde @juliusrickert
|
||||
providers/huaweicloud @huihuimoe
|
||||
providers/infomaniak @jbelien
|
||||
providers/internetbs @pragmaton
|
||||
providers/inwx @patschi
|
||||
providers/joker @atrull
|
||||
|
|
|
@ -43,6 +43,7 @@ Currently supported DNS providers:
|
|||
- hosting.de
|
||||
- Huawei Cloud DNS
|
||||
- Hurricane Electric DNS
|
||||
- Infomaniak
|
||||
- INWX
|
||||
- Joker
|
||||
- Linode
|
||||
|
|
|
@ -138,6 +138,7 @@
|
|||
* [hosting.de](provider/hostingde.md)
|
||||
* [Huawei Cloud DNS](provider/huaweicloud.md)
|
||||
* [Hurricane Electric DNS](provider/hedns.md)
|
||||
* [Infomaniak](provider/infomaniak.md)
|
||||
* [Internet.bs](provider/internetbs.md)
|
||||
* [INWX](provider/inwx.md)
|
||||
* [Joker](provider/joker.md)
|
||||
|
|
38
documentation/provider/infomaniak.md
Normal file
38
documentation/provider/infomaniak.md
Normal file
|
@ -0,0 +1,38 @@
|
|||
This is the provider for [Infomaniak](https://www.infomaniak.com/).
|
||||
|
||||
## Configuration
|
||||
|
||||
To use this provider, add an entry to `creds.json` with `TYPE` set to `INFOMANIAK` along with a Infomaniak account personal access token.
|
||||
|
||||
Examples:
|
||||
|
||||
{% code title="creds.json" %}
|
||||
```json
|
||||
{
|
||||
"infomaniak": {
|
||||
"TYPE": "INFOMANIAK",
|
||||
"token": "your-infomaniak-account-access-token",
|
||||
}
|
||||
}
|
||||
```
|
||||
{% endcode %}
|
||||
|
||||
## Metadata
|
||||
This provider does not recognize any special metadata fields unique to Infomaniak.
|
||||
|
||||
## Usage
|
||||
An example configuration:
|
||||
|
||||
{% code title="dnsconfig.js" %}
|
||||
```javascript
|
||||
var REG_NONE = NewRegistrar("none");
|
||||
var DSP_INFOMANIAK = NewDnsProvider("infomaniak");
|
||||
|
||||
D("example.com", REG_NONE, DnsProvider(DSP_INFOMANIAK),
|
||||
A("test", "1.2.3.4"),
|
||||
);
|
||||
```
|
||||
{% endcode %}
|
||||
|
||||
## Activation
|
||||
DNSControl depends on a Infomaniak account personal access token.
|
|
@ -197,6 +197,11 @@
|
|||
"TYPE": "HUAWEICLOUD",
|
||||
"domain": "$HUAWEICLOUD_DOMAIN"
|
||||
},
|
||||
"INFOMANIAK": {
|
||||
"TYPE": "INFOMANIAK",
|
||||
"domain": "$INFOMANIAK_DOMAIN",
|
||||
"token": "$INFOMANIAK_TOKEN"
|
||||
},
|
||||
"INWX": {
|
||||
"TYPE": "INWX",
|
||||
"domain": "$INWX_DOMAIN",
|
||||
|
|
|
@ -33,6 +33,7 @@ import (
|
|||
_ "github.com/StackExchange/dnscontrol/v4/providers/hexonet"
|
||||
_ "github.com/StackExchange/dnscontrol/v4/providers/hostingde"
|
||||
_ "github.com/StackExchange/dnscontrol/v4/providers/huaweicloud"
|
||||
_ "github.com/StackExchange/dnscontrol/v4/providers/infomaniak"
|
||||
_ "github.com/StackExchange/dnscontrol/v4/providers/internetbs"
|
||||
_ "github.com/StackExchange/dnscontrol/v4/providers/inwx"
|
||||
_ "github.com/StackExchange/dnscontrol/v4/providers/joker"
|
||||
|
|
186
providers/infomaniak/api.go
Normal file
186
providers/infomaniak/api.go
Normal file
|
@ -0,0 +1,186 @@
|
|||
package infomaniak
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const baseURL = "https://api.infomaniak.com/2"
|
||||
|
||||
type dnssecRecord struct {
|
||||
IsEnabled bool `json:"is_enabled"`
|
||||
}
|
||||
|
||||
type errorRecord struct {
|
||||
Code string `json:"code"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
type dnsZoneResponse struct {
|
||||
Result string `json:"result"`
|
||||
Data dnsZone `json:"data,omitempty"`
|
||||
Error errorRecord `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
type dnsRecordsResponse struct {
|
||||
Result string `json:"result"`
|
||||
Data []dnsRecord `json:"data,omitempty"`
|
||||
Error errorRecord `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
type dnsRecordResponse struct {
|
||||
Result string `json:"result"`
|
||||
Data dnsRecord `json:"data,omitempty"`
|
||||
Error errorRecord `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
type boolResponse struct {
|
||||
Result string `json:"result"`
|
||||
Data bool `json:"data,omitempty"`
|
||||
Error errorRecord `json:"error,omitempty"`
|
||||
}
|
||||
type dnsZone struct {
|
||||
ID int64 `json:"id,omitempty"`
|
||||
FQDN string `json:"fqdn,omitempty"`
|
||||
DNSSEC dnssecRecord `json:"dnssec,omitempty"`
|
||||
Nameservers []string `json:"nameservers,omitempty"`
|
||||
}
|
||||
|
||||
type dnsRecord struct {
|
||||
ID int64 `json:"id,omitempty"`
|
||||
Source string `json:"source,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
TTL int64 `json:"ttl,omitempty"`
|
||||
Target string `json:"target,omitempty"`
|
||||
UpdatedAt int64 `json:"updated_at,omitempty"`
|
||||
}
|
||||
|
||||
type dnsRecordCreate struct {
|
||||
Source string `json:"source,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
TTL int64 `json:"ttl,omitempty"`
|
||||
Target string `json:"target,omitempty"`
|
||||
}
|
||||
|
||||
// Get zone information
|
||||
// See https://developer.infomaniak.com/docs/api/get/2/zones/%7Bzone%7D
|
||||
func (p *infomaniakProvider) getDNSZone(zone string) (*dnsZone, error) {
|
||||
reqURL := fmt.Sprintf("%s/zones/%s", baseURL, zone)
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, reqURL, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Add("Authorization", "Bearer "+p.apiToken)
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
|
||||
res, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
response := &dnsZoneResponse{}
|
||||
err = json.NewDecoder(res.Body).Decode(response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &response.Data, nil
|
||||
}
|
||||
|
||||
// Retrieve all dns record for a given zone
|
||||
// See https://developer.infomaniak.com/docs/api/get/2/zones/%7Bzone%7D/records
|
||||
func (p *infomaniakProvider) getDNSRecords(zone string) ([]dnsRecord, error) {
|
||||
reqURL := fmt.Sprintf("%s/zones/%s/records", baseURL, zone)
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, reqURL, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Add("Authorization", "Bearer "+p.apiToken)
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
|
||||
res, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
response := &dnsRecordsResponse{}
|
||||
err = json.NewDecoder(res.Body).Decode(response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response.Data, nil
|
||||
}
|
||||
|
||||
// Delete a dns record
|
||||
// See https://developer.infomaniak.com/docs/api/delete/2/zones/%7Bzone%7D/records/%7Brecord%7D
|
||||
func (p *infomaniakProvider) deleteDNSRecord(zone string, recordID string) error {
|
||||
reqURL := fmt.Sprintf("%s/zones/%s/records/%s", baseURL, zone, recordID)
|
||||
|
||||
req, err := http.NewRequest(http.MethodDelete, reqURL, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Add("Authorization", "Bearer "+p.apiToken)
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
|
||||
res, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
response := &boolResponse{}
|
||||
err = json.NewDecoder(res.Body).Decode(response)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if response.Result == "error" {
|
||||
return fmt.Errorf("failed to delete record %s in zone %s: %s", recordID, zone, response.Error.Description)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a dns record in a given zone
|
||||
// See https://developer.infomaniak.com/docs/api/post/2/zones/%7Bzone%7D/records
|
||||
func (p *infomaniakProvider) createDNSRecord(zone string, rec *dnsRecordCreate) (*dnsRecord, error) {
|
||||
reqURL := fmt.Sprintf("%s/zones/%s/records", baseURL, zone)
|
||||
|
||||
data, err := json.Marshal(rec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(http.MethodPost, reqURL, bytes.NewReader(data))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Add("Authorization", "Bearer "+p.apiToken)
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
|
||||
res, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
response := &dnsRecordResponse{}
|
||||
err = json.NewDecoder(res.Body).Decode(response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if response.Result == "error" {
|
||||
return nil, fmt.Errorf("failed to create %s record in zone %s: %s", rec.Type, zone, response.Error.Description)
|
||||
}
|
||||
|
||||
return &response.Data, nil
|
||||
}
|
15
providers/infomaniak/auditrecords.go
Normal file
15
providers/infomaniak/auditrecords.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
package infomaniak
|
||||
|
||||
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{}
|
||||
|
||||
return a.Audit(records)
|
||||
}
|
125
providers/infomaniak/infomaniakProvider.go
Normal file
125
providers/infomaniak/infomaniakProvider.go
Normal file
|
@ -0,0 +1,125 @@
|
|||
package infomaniak
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/StackExchange/dnscontrol/v4/models"
|
||||
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
|
||||
"github.com/StackExchange/dnscontrol/v4/providers"
|
||||
)
|
||||
|
||||
// infomaniakProvider is the handle for operations.
|
||||
type infomaniakProvider struct {
|
||||
apiToken string // the account access token
|
||||
}
|
||||
|
||||
var features = providers.DocumentationNotes{
|
||||
// The default for unlisted capabilities is 'Cannot'.
|
||||
// See providers/capabilities.go for the entire list of capabilities.
|
||||
providers.CanGetZones: providers.Can(),
|
||||
providers.CanUseCAA: providers.Can(),
|
||||
providers.CanUseDNAME: providers.Can(),
|
||||
providers.CanUseDS: providers.Can(),
|
||||
providers.CanUseSSHFP: providers.Can(),
|
||||
providers.CanUseTLSA: providers.Can(),
|
||||
providers.CanUseSRV: providers.Can(),
|
||||
// providers.DocCreateDomains: providers.Can(),
|
||||
}
|
||||
|
||||
func newInfomaniak(m map[string]string, message json.RawMessage) (providers.DNSServiceProvider, error) {
|
||||
api := &infomaniakProvider{}
|
||||
api.apiToken = m["token"]
|
||||
if api.apiToken == "" {
|
||||
return nil, errors.New("missing Infomaniak personal access token")
|
||||
}
|
||||
|
||||
return api, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
const providerName = "INFOMANIAK"
|
||||
const providerMaintainer = "@jbelien"
|
||||
fns := providers.DspFuncs{
|
||||
Initializer: newInfomaniak,
|
||||
RecordAuditor: AuditRecords,
|
||||
}
|
||||
providers.RegisterDomainServiceProviderType(providerName, fns, features)
|
||||
providers.RegisterMaintainer(providerName, providerMaintainer)
|
||||
}
|
||||
|
||||
func (p *infomaniakProvider) GetNameservers(domain string) ([]*models.Nameserver, error) {
|
||||
zone, err := p.getDNSZone(domain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return models.ToNameservers(zone.Nameservers)
|
||||
}
|
||||
|
||||
func (p *infomaniakProvider) GetZoneRecords(domain string, meta map[string]string) (models.Records, error) {
|
||||
records, err := p.getDNSRecords(domain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cleanRecords := make(models.Records, 0)
|
||||
|
||||
for _, r := range records {
|
||||
recConfig := &models.RecordConfig{
|
||||
Original: r,
|
||||
TTL: uint32(r.TTL),
|
||||
Type: r.Type,
|
||||
}
|
||||
recConfig.SetLabelFromFQDN(r.Source, domain)
|
||||
recConfig.SetTarget(r.Target)
|
||||
|
||||
cleanRecords = append(cleanRecords, recConfig)
|
||||
}
|
||||
|
||||
return cleanRecords, nil
|
||||
}
|
||||
|
||||
func (p *infomaniakProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) ([]*models.Correction, int, error) {
|
||||
var corrections []*models.Correction
|
||||
|
||||
changes, actualChangeCount, err := diff2.ByRecord(existingRecords, dc, nil)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
for _, change := range changes {
|
||||
switch change.Type {
|
||||
case diff2.REPORT:
|
||||
corrections = append(corrections, &models.Correction{Msg: change.MsgsJoined})
|
||||
case diff2.CHANGE:
|
||||
fmt.Printf("CHANGE: %+v\n", change.New)
|
||||
// corrections = append(corrections, &models.Correction{
|
||||
// Msg: change.Msgs[0],
|
||||
// F: func() error {
|
||||
// return p.updateRecord(change.Old[0].Original.(dnsRecord), change.New[0], dc.Name)
|
||||
// },
|
||||
// })
|
||||
case diff2.CREATE:
|
||||
fmt.Printf("CREATE: %+v\n", change.New)
|
||||
// corrections = append(corrections, &models.Correction{
|
||||
// Msg: change.Msgs[0],
|
||||
// F: func() error {
|
||||
// _, err := p.createDNSRecord(dc.Name, change.New[0])
|
||||
// return err
|
||||
// },
|
||||
// })
|
||||
case diff2.DELETE:
|
||||
rec := change.Old[0].Original.(dnsRecord)
|
||||
corrections = append(corrections, &models.Correction{
|
||||
Msg: change.Msgs[0],
|
||||
F: func() error {
|
||||
return p.deleteDNSRecord(dc.Name, fmt.Sprintf("%v", rec.ID))
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return corrections, actualChangeCount, nil
|
||||
}
|
Loading…
Add table
Reference in a new issue