diff --git a/README.md b/README.md index 427c9c0..c6d7416 100644 --- a/README.md +++ b/README.md @@ -10,14 +10,13 @@ A [cert-manager][2] ACME DNS01 solver webhook for [DNSimple][1]. ## Quickstart -Take note of your DNSimple API token and account ID from the account settings in the automation tab. Run the following commands replacing the account ID, API token placeholders and email address: +Take note of your DNSimple API token from the account settings in the automation tab. Run the following commands replacing the API token placeholders and email address: ```bash $ helm repo add neoskop https://charts.neoskop.dev $ helm install cert-manager-webhook-dnsimple \ --namespace cert-manager \ --dry-run \ - --set dnsimple.account='' \ --set dnsimple.token='' \ --set clusterIssuer.production.enabled=true \ --set clusterIssuer.staging.enabled=true \ @@ -52,7 +51,6 @@ The Helm chart accepts the following values: | name | required | description | default value | | ---------------------------------- | -------- | ----------------------------------------------- | --------------------------------------- | -| `dnsimple.account` | ✔️ | DNSimple Account ID | _empty_ | | `dnsimple.token` | ✔️ | DNSimple API Token | _empty_ | | `clusterIssuer.email` | | LetsEncrypt Admin Email | `name@example.com` | | `clusterIssuer.production.enabled` | | Create a production `ClusterIssuer` | `false` | diff --git a/deploy/dnsimple/templates/production.cluster-issuer.yaml b/deploy/dnsimple/templates/production.cluster-issuer.yaml index 9dc9974..6b1988b 100644 --- a/deploy/dnsimple/templates/production.cluster-issuer.yaml +++ b/deploy/dnsimple/templates/production.cluster-issuer.yaml @@ -18,7 +18,6 @@ spec: - dns01: webhook: config: - account: {{ .Values.dnsimple.account | quote }} tokenSecretRef: key: token name: {{ include "dnsimple-webhook.fullname" . }} diff --git a/deploy/dnsimple/templates/staging.cluster-issuer.yaml b/deploy/dnsimple/templates/staging.cluster-issuer.yaml index f1a3033..a1d6e3c 100644 --- a/deploy/dnsimple/templates/staging.cluster-issuer.yaml +++ b/deploy/dnsimple/templates/staging.cluster-issuer.yaml @@ -18,7 +18,6 @@ spec: - dns01: webhook: config: - account: {{ .Values.dnsimple.account | quote }} tokenSecretRef: key: token name: {{ include "dnsimple-webhook.fullname" . }} diff --git a/deploy/dnsimple/values.yaml b/deploy/dnsimple/values.yaml index c8b3db6..43e1130 100644 --- a/deploy/dnsimple/values.yaml +++ b/deploy/dnsimple/values.yaml @@ -12,7 +12,6 @@ certManager: serviceAccountName: cert-manager # logLevel: 3 dnsimple: - account: "" token: "" clusterIssuer: email: name@example.com diff --git a/main.go b/main.go index a8f9c39..d891f48 100644 --- a/main.go +++ b/main.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "os" + "strconv" "strings" extapi "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" @@ -71,7 +72,6 @@ type dnsimpleDNSProviderConfig struct { // These fields will be set by users in the // `issuer.spec.acme.dns01.providers.webhook.config` field. - Account string `json:"account"` TokenSecretRef cmmeta.SecretKeySelector `json:"tokenSecretRef"` } @@ -118,15 +118,15 @@ func (c *dnsimpleDNSProviderSolver) getDomainAndEntry(ch *v1alpha1.ChallengeRequ return entry, domain } -func (c *dnsimpleDNSProviderSolver) getExistingRecord(cfg *dnsimpleDNSProviderConfig, client *dnsimple.Client, zoneName string, entry string, key string) (*dnsimple.ZoneRecord, error) { - zone, err := client.Zones.GetZone(context.Background(), cfg.Account, zoneName) +func (c *dnsimpleDNSProviderSolver) getExistingRecord(cfg *dnsimpleDNSProviderConfig, client *dnsimple.Client, accountID string, zoneName string, entry string, key string) (*dnsimple.ZoneRecord, error) { + zone, err := client.Zones.GetZone(context.Background(), accountID, zoneName) if err != nil { return nil, fmt.Errorf("unable to get zone: %s", err) } // Look for existing TXT records. - records, err := client.Zones.ListRecords(context.Background(), cfg.Account, zone.Data.Name, &dnsimple.ZoneRecordListOptions{Type: dnsimple.String("TXT"), Name: dnsimple.String(entry)}) + records, err := client.Zones.ListRecords(context.Background(), accountID, zone.Data.Name, &dnsimple.ZoneRecordListOptions{Type: dnsimple.String("TXT"), Name: dnsimple.String(entry)}) if err != nil { return nil, fmt.Errorf("unable to get resource records: %s", err) @@ -141,9 +141,9 @@ func (c *dnsimpleDNSProviderSolver) getExistingRecord(cfg *dnsimpleDNSProviderCo return nil, nil } -func (c *dnsimpleDNSProviderSolver) updateRecord(cfg *dnsimpleDNSProviderConfig, client *dnsimple.Client, record *dnsimple.ZoneRecord, key string) (*dnsimple.ZoneRecord, error) { +func (c *dnsimpleDNSProviderSolver) updateRecord(cfg *dnsimpleDNSProviderConfig, client *dnsimple.Client, accountID string, record *dnsimple.ZoneRecord, key string) (*dnsimple.ZoneRecord, error) { attributes := dnsimple.ZoneRecordAttributes{Content: key} - updatedRecord, err := client.Zones.UpdateRecord(context.Background(), cfg.Account, record.ZoneID, record.ID, attributes) + updatedRecord, err := client.Zones.UpdateRecord(context.Background(), accountID, record.ZoneID, record.ID, attributes) if err != nil { return nil, fmt.Errorf("unable to update record: %s", err) @@ -152,9 +152,9 @@ func (c *dnsimpleDNSProviderSolver) updateRecord(cfg *dnsimpleDNSProviderConfig, return updatedRecord.Data, nil } -func (c *dnsimpleDNSProviderSolver) createRecord(cfg *dnsimpleDNSProviderConfig, client *dnsimple.Client, entry *string, zoneName string, key string) (*dnsimple.ZoneRecord, error) { +func (c *dnsimpleDNSProviderSolver) createRecord(cfg *dnsimpleDNSProviderConfig, client *dnsimple.Client, accountID string, entry *string, zoneName string, key string) (*dnsimple.ZoneRecord, error) { attributes := dnsimple.ZoneRecordAttributes{Name: entry, Type: "TXT", Content: key, TTL: 60} - createdRecord, err := client.Zones.CreateRecord(context.Background(), cfg.Account, zoneName, attributes) + createdRecord, err := client.Zones.CreateRecord(context.Background(), accountID, zoneName, attributes) if err != nil { return nil, fmt.Errorf("unable to create record: %s", err) @@ -163,8 +163,8 @@ func (c *dnsimpleDNSProviderSolver) createRecord(cfg *dnsimpleDNSProviderConfig, return createdRecord.Data, nil } -func (c *dnsimpleDNSProviderSolver) deleteRecord(cfg *dnsimpleDNSProviderConfig, client *dnsimple.Client, zoneName string, record *dnsimple.ZoneRecord) (*dnsimple.ZoneRecord, error) { - createdRecord, err := client.Zones.DeleteRecord(context.Background(), cfg.Account, zoneName, record.ID) +func (c *dnsimpleDNSProviderSolver) deleteRecord(cfg *dnsimpleDNSProviderConfig, client *dnsimple.Client, accountID string, zoneName string, record *dnsimple.ZoneRecord) (*dnsimple.ZoneRecord, error) { + createdRecord, err := client.Zones.DeleteRecord(context.Background(), accountID, zoneName, record.ID) if err != nil { return nil, fmt.Errorf("unable to delete record: %s", err) @@ -173,6 +173,15 @@ func (c *dnsimpleDNSProviderSolver) deleteRecord(cfg *dnsimpleDNSProviderConfig, return createdRecord.Data, nil } +func Whoami(client *dnsimple.Client) (string, error) { + whoamiResponse, err := client.Identity.Whoami(context.Background()) + if err != nil { + return "", err + } + + return strconv.FormatInt(whoamiResponse.Data.Account.ID, 10), nil +} + // Present is responsible for actually presenting the DNS record with the // DNS provider. // This method should tolerate being called multiple times with the same value. @@ -190,23 +199,28 @@ func (c *dnsimpleDNSProviderSolver) Present(ch *v1alpha1.ChallengeRequest) error return fmt.Errorf("unable to get client: %s", err) } + accountID, err := Whoami(client) + if err != nil { + return fmt.Errorf("unable to fetch account ID: %s", err) + } + entry, domain := c.getDomainAndEntry(ch) klog.V(6).Infof("present for entry=%s, domain=%s", entry, domain) - existingRecord, err := c.getExistingRecord(&cfg, client, domain, entry, ch.Key) + existingRecord, err := c.getExistingRecord(&cfg, client, accountID, domain, entry, ch.Key) if err != nil { return fmt.Errorf("unable to find txt records: %s", err) } if existingRecord != nil { - _, err = c.updateRecord(&cfg, client, existingRecord, ch.Key) + _, err = c.updateRecord(&cfg, client, accountID, existingRecord, ch.Key) if err != nil { return fmt.Errorf("unable to update record: %s", err) } } else { - _, err = c.createRecord(&cfg, client, &entry, domain, ch.Key) + _, err = c.createRecord(&cfg, client, accountID, &entry, domain, ch.Key) if err != nil { return fmt.Errorf("unable to create record: %s", err) @@ -234,13 +248,18 @@ func (c *dnsimpleDNSProviderSolver) CleanUp(ch *v1alpha1.ChallengeRequest) error return fmt.Errorf("unable to get client: %s", err) } + accountID, err := Whoami(client) + if err != nil { + return fmt.Errorf("unable to fetch account ID: %s", err) + } + entry, domain := c.getDomainAndEntry(ch) klog.V(6).Infof("present for entry=%s, domain=%s", entry, domain) - existingRecord, err := c.getExistingRecord(&cfg, client, domain, entry, ch.Key) + existingRecord, err := c.getExistingRecord(&cfg, client, accountID, domain, entry, ch.Key) if existingRecord != nil { - _, err = c.deleteRecord(&cfg, client, domain, existingRecord) + _, err = c.deleteRecord(&cfg, client, accountID, domain, existingRecord) if err != nil { return fmt.Errorf("unable to delete record: %s", err) diff --git a/testdata/dnsimple/.gitignore b/testdata/dnsimple/.gitignore index 563de97..eb4ea5b 100644 --- a/testdata/dnsimple/.gitignore +++ b/testdata/dnsimple/.gitignore @@ -1,2 +1 @@ -config.json dnsimple-token.yaml \ No newline at end of file diff --git a/testdata/dnsimple/README.md b/testdata/dnsimple/README.md index 57c1f86..6ff2abd 100644 --- a/testdata/dnsimple/README.md +++ b/testdata/dnsimple/README.md @@ -1,10 +1,9 @@ # Solver testdata directory -Copy the example files removing the `.example` suffix: +Copy the `dnsimple-token.yaml.example` example file removing the `.example` suffix: ```bash -$ cp config.json{.example,} $ cp dnsimple-token.yaml{.example,} ``` -Replace the placeholders for the account ID in `config.json` and for the API token in `dnsimple-token.yaml`. Both can be found/generate in you DNSimple account settings in the automation tab. +Replace the placeholders for the API token in `dnsimple-token.yaml`. The API token can be generated in your DNSimple account settings in the automation tab. diff --git a/testdata/dnsimple/config.json.example b/testdata/dnsimple/config.json similarity index 69% rename from testdata/dnsimple/config.json.example rename to testdata/dnsimple/config.json index 1819ccd..9bef050 100644 --- a/testdata/dnsimple/config.json.example +++ b/testdata/dnsimple/config.json @@ -2,6 +2,5 @@ "tokenSecretRef": { "name": "dnsimple-token", "key": "token" - }, - "account": "" + } } \ No newline at end of file