Skip to content
Draft
1 change: 1 addition & 0 deletions OWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ providers/hetzner @das7pad
providers/hexonet @KaiSchwarz-cnic
providers/hostingde @juliusrickert
providers/huaweicloud @huihuimoe
providers/infomaniak @jbelien
providers/internetbs @pragmaton
providers/inwx @patschi
providers/linode @koesie10
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ Currently supported DNS providers:
- hosting.de
- Huawei Cloud DNS
- Hurricane Electric DNS
- Infomaniak
- INWX
- Linode
- Loopia
Expand Down
1 change: 1 addition & 0 deletions documentation/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,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)
* [Linode](provider/linode.md)
Expand Down
38 changes: 38 additions & 0 deletions documentation/provider/infomaniak.md
Original file line number Diff line number Diff line change
@@ -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.
5 changes: 5 additions & 0 deletions integrationTest/profiles.json
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,11 @@
"TYPE": "HUAWEICLOUD",
"domain": "$HUAWEICLOUD_DOMAIN"
},
"INFOMANIAK": {
"TYPE": "INFOMANIAK",
"domain": "$INFOMANIAK_DOMAIN",
"token": "$INFOMANIAK_TOKEN"
},
"INWX": {
"TYPE": "INWX",
"domain": "$INWX_DOMAIN",
Expand Down
1 change: 1 addition & 0 deletions providers/_all/all.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,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/linode"
Expand Down
186 changes: 186 additions & 0 deletions providers/infomaniak/api.go
Original file line number Diff line number Diff line change
@@ -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 changes: 15 additions & 0 deletions providers/infomaniak/auditrecords.go
Original file line number Diff line number Diff line change
@@ -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)
}
Loading