GANDI: Adopt new Gandi v5 auth changes (#2726)

This commit is contained in:
llange 2024-08-09 14:58:54 +02:00 committed by GitHub
parent 24b5c4b0e4
commit 663be6e268
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 78 additions and 33 deletions

View file

@ -1,19 +1,31 @@
`GANDI_V5` uses the v5 API and can act as a registrar provider
or a DNS provider. It is only able to work with domains
migrated to the new LiveDNS API, which should be all domains.
API keys are assigned to particular users. Go to User Settings,
"Manage the user account and security settings", the "Security"
tab, then regenerate the "Production API key".
* API Documentation: https://api.gandi.net/docs
* API Endpoint: https://api.gandi.net/
* Sandbox API Documentation: https://api.sandbox.gandi.net/docs/
* Sandbox API Endpoint: https://api.sandbox.gandi.net/
## Configuration
To use this provider, add an entry to `creds.json` with `TYPE` set to `GANDI_V5`
along your Gandi.net API key. The [sharing_id](https://api.gandi.net/docs/reference/) is optional.
along with other settings:
The `sharing_id` selects between different organizations which your account is
* (mandatory, string) your Gandi.net access credentials (see below) - one of:
* `token`: Personal Access Token (PAT)
* `apikey` API Key (deprecated)
* `apiurl`: (optional, string) the endpoint of the API. When empty or absent the production
endpoint is used (default) ; you can use it to select the Sandbox API Endpoint instead.
* `sharing_id`: (optional, string) let you scope to a specific organization. When empty or absent
calls are not scoped to a specific organization.
When both `token` and `apikey` are defined, the priority is given to `token` which will
be used for API communication (as if `apikey` was not set).
See [the Authentication section](#authentication) for details on obtaining these credentials.
The [sharing_id](https://api.gandi.net/docs/reference/#Sharing-ID) selects between different organizations which your account is
a member of; to manage domains in multiple organizations, you can use multiple
`creds.json` entries.
@ -33,13 +45,32 @@ Example:
{
"gandi": {
"TYPE": "GANDI_V5",
"apikey": "your-gandi-key",
"token": "your-gandi-personal-access-token",
"sharing_id": "your-sharing_id"
}
}
```
{% endcode %}
## Authentication
(Cf [official documentation of the API](https://api.gandi.net/docs/authentication/)
The **Personal Access Token** (PAT) is configured in the [Account Settings of the
Gandi Admin application](https://admin.gandi.net/organizations/account/pat), then
click on "Create a token" button.
Choose an organisation (if your account happens to have multiple ones).
Then, choose a name (limited to 42 chars), an expiration date.
You can choose to limit the scope to a select number of products (domain names).
Finally, choose the permissions : the needed one is "Manage domain name technical configurations"
(in French: "Gérer la configuration technique des domaines"), which automatically
implies "See and renew domain names" (in French: "Voir et renouveler les domaines").
You then have only one (1) chance to copy and save the token somewhere.
The **API Key** is the previous (deprecated) mechanism used to do api calls.
To generate or delete your API key, go to User Settings,
"Manage the user account and security settings", the "Authentication options"
tab, then regenerate the "Production API key" under "Developer access"
## Metadata
This provider does not recognize any special metadata fields unique to Gandi.
@ -80,7 +111,7 @@ If a domain does not exist in your Gandi account, DNSControl will *not* automati
Error getting corrections: 401: The server could not verify that you authorized to access the document you requested. Either you supplied the wrong credentials (e.g., bad api key), or your access token has expired
```
This is the error you'll see if your `apikey` in `creds.json` is wrong or invalid.
This is the error you'll see if your `token` (or (deprecated) `apikey`) in `creds.json` is wrong or invalid.
#### Domain does not exist in profile
@ -97,3 +128,12 @@ If a `dnscontrol get-zones --format=nameonly CredId - all` returns nothing,
this is usually because your `creds.json` information is pointing at an empty
organization or no organization. The solution is to set `sharing_id` in
`creds.json`.
## Development
### Debugging
Set `GANDI_V5_DEBUG` environment variable to a [boolean-compatible](https://pkg.go.dev/strconv#ParseBool) value to dump all API calls made by this provider.
### Testing
Set `apiurl` key to the endpoint url for the sandbox (https://api.sandbox.gandi.net/), along with corresponding `token` (or (deprecated) `apikey`) created in this sandbox environment (Cf https://api.sandbox.gandi.net/docs/sandbox/) to make all API calls against Gandi sandbox environment.

View file

@ -112,7 +112,8 @@
"GANDI_V5": {
"TYPE": "GANDI_V5",
"apikey": "$GANDI_V5_APIKEY",
"domain": "$GANDI_V5_DOMAIN"
"domain": "$GANDI_V5_DOMAIN",
"token": "$GANDI_V5_TOKEN"
},
"GCLOUD": {
"TYPE": "GCLOUD",

View file

@ -27,6 +27,7 @@ import (
"github.com/StackExchange/dnscontrol/v4/providers"
"github.com/go-gandi/go-gandi"
"github.com/go-gandi/go-gandi/config"
"github.com/go-gandi/go-gandi/livedns"
"github.com/miekg/dns/dnsutil"
)
@ -72,8 +73,10 @@ var features = providers.DocumentationNotes{
// gandiv5Provider is the gandiv5Provider handle used to store any client-related state.
type gandiv5Provider struct {
apikey string
token string
sharingid string
debug bool
apiurl string
}
// newDsp generates a DNS Service Provider client handle.
@ -90,10 +93,12 @@ func newReg(conf map[string]string) (providers.Registrar, error) {
func newHelper(m map[string]string, _ json.RawMessage) (*gandiv5Provider, error) {
api := &gandiv5Provider{}
api.apikey = m["apikey"]
if api.apikey == "" {
return nil, fmt.Errorf("missing Gandi apikey")
api.token = m["token"]
if (api.apikey == "") && (api.token == "") {
return nil, fmt.Errorf("missing Gandi personal access token (or apikey - deprecated)")
}
api.sharingid = m["sharing_id"]
api.apiurl = m["apiurl"]
debug, err := strconv.ParseBool(os.Getenv("GANDI_V5_DEBUG"))
if err == nil {
api.debug = debug
@ -104,15 +109,24 @@ func newHelper(m map[string]string, _ json.RawMessage) (*gandiv5Provider, error)
// Section 3: Domain Service Provider (DSP) related functions
// newLiveDNSClient returns a client to the Gandi Domains API
// It expects an API key, available from https://account.gandi.net/en/
func newLiveDNSClient(client *gandiv5Provider) *livedns.LiveDNS {
g := gandi.NewLiveDNSClient(config.Config{
APIKey: client.apikey,
PersonalAccessToken: client.token,
SharingID: client.sharingid,
Debug: client.debug,
APIURL: client.apiurl,
})
return g
}
// // ListZones lists the zones on this account.
// This no longer works. Until we can figure out why, we're removing this
// feature for Gandi.
// func (client *gandiv5Provider) ListZones() ([]string, error) {
// g := gandi.NewLiveDNSClient(config.Config{
// APIKey: client.apikey,
// SharingID: client.sharingid,
// Debug: client.debug,
// })
// g := newLiveDNSClient(client)
// listResp, err := g.ListDomains()
// if err != nil {
@ -133,11 +147,7 @@ func newHelper(m map[string]string, _ json.RawMessage) (*gandiv5Provider, error)
// GetZoneRecords gathers the DNS records and converts them to
// dnscontrol's format.
func (client *gandiv5Provider) GetZoneRecords(domain string, meta map[string]string) (models.Records, error) {
g := gandi.NewLiveDNSClient(config.Config{
APIKey: client.apikey,
SharingID: client.sharingid,
Debug: client.debug,
})
g := newLiveDNSClient(client)
// Get all the existing records:
records, err := g.GetDomainRecords(domain)
@ -200,11 +210,7 @@ func (client *gandiv5Provider) GetZoneRecordsCorrections(dc *models.DomainConfig
PrepDesiredRecords(dc)
g := gandi.NewLiveDNSClient(config.Config{
APIKey: client.apikey,
SharingID: client.sharingid,
Debug: client.debug,
})
g := newLiveDNSClient(client)
// Gandi is a "ByLabel" API with the odd exception that changes must be
// done one label:rtype at a time.
@ -301,11 +307,7 @@ func debugRecords(note string, recs []*models.RecordConfig) {
// GetNameservers returns a list of nameservers for domain.
func (client *gandiv5Provider) GetNameservers(domain string) ([]*models.Nameserver, error) {
g := gandi.NewLiveDNSClient(config.Config{
APIKey: client.apikey,
SharingID: client.sharingid,
Debug: client.debug,
})
g := newLiveDNSClient(client)
nameservers, err := g.GetDomainNS(domain)
if err != nil {
return nil, err
@ -316,9 +318,11 @@ func (client *gandiv5Provider) GetNameservers(domain string) ([]*models.Nameserv
// GetRegistrarCorrections returns a list of corrections for this registrar.
func (client *gandiv5Provider) GetRegistrarCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
gd := gandi.NewDomainClient(config.Config{
APIKey: client.apikey,
SharingID: client.sharingid,
Debug: client.debug,
APIKey: client.apikey,
PersonalAccessToken: client.token,
SharingID: client.sharingid,
Debug: client.debug,
APIURL: client.apiurl,
})
existingNs, err := gd.GetNameServers(dc.Name)