Add DNS Provider for Domain Offensive (do.de) (#835)

This commit is contained in:
andig 2019-03-21 11:52:01 +01:00 committed by Ludovic Fernandez
parent 8ed39fe981
commit 0ce6ba36b9
8 changed files with 379 additions and 16 deletions

View file

@ -42,18 +42,18 @@ Documentation is hosted live at https://go-acme.github.io/lego/.
Detailed documentation is available [here](https://go-acme.github.io/lego/dns).
| | | | |
|----------------------------------------------------------------|--------------------------------------------------------------------------------|-------------------------------------------------------------------|------------------------------------------------------------------|
|---------------------------------------------------------------------------------|---------------------------------------------------------------------------------|---------------------------------------------------------------------------------|---------------------------------------------------------------------------------|
| [Alibaba Cloud DNS](https://go-acme.github.io/lego/dns/alidns/) | [Amazon Lightsail](https://go-acme.github.io/lego/dns/lightsail/) | [Amazon Route 53](https://go-acme.github.io/lego/dns/route53/) | [Aurora DNS](https://go-acme.github.io/lego/dns/auroradns/) |
| [Azure](https://go-acme.github.io/lego/dns/azure/) | [Bluecat](https://go-acme.github.io/lego/dns/bluecat/) | [ClouDNS](https://go-acme.github.io/lego/dns/cloudns/) | [CloudXNS](https://go-acme.github.io/lego/dns/cloudxns/) |
| [Cloudflare](https://go-acme.github.io/lego/dns/cloudflare/) | [ConoHa](https://go-acme.github.io/lego/dns/conoha/) | [DNS Made Easy](https://go-acme.github.io/lego/dns/dnsmadeeasy/) | [DNSPod](https://go-acme.github.io/lego/dns/dnspod/) |
| [DNSimple](https://go-acme.github.io/lego/dns/dnsimple/) | [Designate DNSaaS for Openstack](https://go-acme.github.io/lego/dns/designate/) | [Digital Ocean](https://go-acme.github.io/lego/dns/digitalocean/) | [DreamHost](https://go-acme.github.io/lego/dns/dreamhost/) |
| [Duck DNS](https://go-acme.github.io/lego/dns/duckdns/) | [Dyn](https://go-acme.github.io/lego/dns/dyn/) | [Exoscale](https://go-acme.github.io/lego/dns/exoscale/) | [External program](https://go-acme.github.io/lego/dns/exec/) |
| [FastDNS](https://go-acme.github.io/lego/dns/fastdns/) | [Gandi](https://go-acme.github.io/lego/dns/gandi/) | [Gandi Live DNS (v5)](https://go-acme.github.io/lego/dns/gandiv5/) | [Glesys](https://go-acme.github.io/lego/dns/glesys/) |
| [Go Daddy](https://go-acme.github.io/lego/dns/godaddy/) | [Google Cloud](https://go-acme.github.io/lego/dns/gcloud/) | [HTTP request](https://go-acme.github.io/lego/dns/httpreq/) | [Hosting.de](https://go-acme.github.io/lego/dns/hostingde/) |
| [INWX](https://go-acme.github.io/lego/dns/inwx/) | [Internet Initiative Japan](https://go-acme.github.io/lego/dns/iij/) | [Joohoi's ACME-DNS](https://go-acme.github.io/lego/dns/acme-dns) | [Linode (deprecated)](https://go-acme.github.io/lego/dns/linode/) |
| [Linode (v4)](https://go-acme.github.io/lego/dns/linodev4/) | [Manual](https://go-acme.github.io/lego/dns/manual/) | [MyDNS.jp](https://go-acme.github.io/lego/dns/mydnsjp/) | [NIFCloud](https://go-acme.github.io/lego/dns/nifcloud/) |
| [NS1](https://go-acme.github.io/lego/dns/ns1/) | [Name.com](https://go-acme.github.io/lego/dns/namedotcom/) | [Namecheap](https://go-acme.github.io/lego/dns/namecheap/) | [Netcup](https://go-acme.github.io/lego/dns/netcup/) |
| [OVH](https://go-acme.github.io/lego/dns/ovh/) | [Open Telekom Cloud](https://go-acme.github.io/lego/dns/otc/) | [Oracle Cloud](https://go-acme.github.io/lego/dns/oraclecloud/) | [PowerDNS](https://go-acme.github.io/lego/dns/pdns/) |
| [RFC2136](https://go-acme.github.io/lego/dns/rfc2136/) | [Rackspace](https://go-acme.github.io/lego/dns/rackspace/) | [Sakura Cloud](https://go-acme.github.io/lego/dns/sakuracloud/) | [Selectel](https://go-acme.github.io/lego/dns/selectel/) |
| [Stackpath](https://go-acme.github.io/lego/dns/stackpath/) | [TransIP](https://go-acme.github.io/lego/dns/transip/) | [VegaDNS](https://go-acme.github.io/lego/dns/vegadns/) | [Vscale](https://go-acme.github.io/lego/dns/vscale/) |
| [Vultr](https://go-acme.github.io/lego/dns/vultr/) | [Zone.ee](https://go-acme.github.io/lego/dns/zoneee/) | | |
| [Azure](https://go-acme.github.io/lego/dns/azure/) | [Bluecat](https://go-acme.github.io/lego/dns/bluecat/) | [Cloudflare](https://go-acme.github.io/lego/dns/cloudflare/) | [ClouDNS](https://go-acme.github.io/lego/dns/cloudns/) |
| [CloudXNS](https://go-acme.github.io/lego/dns/cloudxns/) | [ConoHa](https://go-acme.github.io/lego/dns/conoha/) | [Designate DNSaaS for Openstack](https://go-acme.github.io/lego/dns/designate/) | [Digital Ocean](https://go-acme.github.io/lego/dns/digitalocean/) |
| [DNS Made Easy](https://go-acme.github.io/lego/dns/dnsmadeeasy/) | [DNSimple](https://go-acme.github.io/lego/dns/dnsimple/) | [DNSPod](https://go-acme.github.io/lego/dns/dnspod/) | [Domain Offensive (do.de)](https://go-acme.github.io/lego/dns/dode/) |
| [DreamHost](https://go-acme.github.io/lego/dns/dreamhost/) | [Duck DNS](https://go-acme.github.io/lego/dns/duckdns/) | [Dyn](https://go-acme.github.io/lego/dns/dyn/) | [Exoscale](https://go-acme.github.io/lego/dns/exoscale/) |
| [External program](https://go-acme.github.io/lego/dns/exec/) | [FastDNS](https://go-acme.github.io/lego/dns/fastdns/) | [Gandi Live DNS (v5)](https://go-acme.github.io/lego/dns/gandiv5/) | [Gandi](https://go-acme.github.io/lego/dns/gandi/) |
| [Glesys](https://go-acme.github.io/lego/dns/glesys/) | [Go Daddy](https://go-acme.github.io/lego/dns/godaddy/) | [Google Cloud](https://go-acme.github.io/lego/dns/gcloud/) | [Hosting.de](https://go-acme.github.io/lego/dns/hostingde/) |
| [HTTP request](https://go-acme.github.io/lego/dns/httpreq/) | [Internet Initiative Japan](https://go-acme.github.io/lego/dns/iij/) | [INWX](https://go-acme.github.io/lego/dns/inwx/) | [Joohoi's ACME-DNS](https://go-acme.github.io/lego/dns/acme-dns) |
| [Linode (deprecated)](https://go-acme.github.io/lego/dns/linode/) | [Linode (v4)](https://go-acme.github.io/lego/dns/linodev4/) | [Manual](https://go-acme.github.io/lego/dns/manual/) | [MyDNS.jp](https://go-acme.github.io/lego/dns/mydnsjp/) |
| [Name.com](https://go-acme.github.io/lego/dns/namedotcom/) | [Namecheap](https://go-acme.github.io/lego/dns/namecheap/) | [Netcup](https://go-acme.github.io/lego/dns/netcup/) | [NIFCloud](https://go-acme.github.io/lego/dns/nifcloud/) |
| [NS1](https://go-acme.github.io/lego/dns/ns1/) | [Open Telekom Cloud](https://go-acme.github.io/lego/dns/otc/) | [Oracle Cloud](https://go-acme.github.io/lego/dns/oraclecloud/) | [OVH](https://go-acme.github.io/lego/dns/ovh/) |
| [PowerDNS](https://go-acme.github.io/lego/dns/pdns/) | [Rackspace](https://go-acme.github.io/lego/dns/rackspace/) | [RFC2136](https://go-acme.github.io/lego/dns/rfc2136/) | [Sakura Cloud](https://go-acme.github.io/lego/dns/sakuracloud/) |
| [Selectel](https://go-acme.github.io/lego/dns/selectel/) | [Stackpath](https://go-acme.github.io/lego/dns/stackpath/) | [TransIP](https://go-acme.github.io/lego/dns/transip/) | [VegaDNS](https://go-acme.github.io/lego/dns/vegadns/) |
| [Vscale](https://go-acme.github.io/lego/dns/vscale/) | [Vultr](https://go-acme.github.io/lego/dns/vultr/) | [Zone.ee](https://go-acme.github.io/lego/dns/zoneee/) | |

View file

@ -30,6 +30,7 @@ func allDNSCodes() string {
"dnsimple",
"dnsmadeeasy",
"dnspod",
"dode",
"dreamhost",
"duckdns",
"dyn",
@ -361,6 +362,26 @@ func displayDNSHelp(name string) {
fmt.Fprintln(w)
fmt.Fprintln(w, `More information: https://go-acme.github.io/lego/dns/dnspod`)
case "dode":
// generated from: providers/dns/dode/dode.toml
fmt.Fprintln(w, `Configuration for Domain Offensive (do.de).`)
fmt.Fprintln(w, `Code: 'dode'`)
fmt.Fprintln(w)
fmt.Fprintln(w, `Credentials:`)
fmt.Fprintln(w, ` - "DODE_TOKEN": API token`)
fmt.Fprintln(w)
fmt.Fprintln(w, `Additional Configuration:`)
fmt.Fprintln(w, ` - "DODE_HTTP_TIMEOUT": API request timeout`)
fmt.Fprintln(w, ` - "DODE_POLLING_INTERVAL": Time between DNS propagation check`)
fmt.Fprintln(w, ` - "DODE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
fmt.Fprintln(w, ` - "DODE_SEQUENCE_INTERVAL": Interval between iteration`)
fmt.Fprintln(w, ` - "DODE_TTL": The TTL of the TXT record used for the DNS challenge`)
fmt.Fprintln(w)
fmt.Fprintln(w, `More information: https://go-acme.github.io/lego/dns/dode`)
case "dreamhost":
// generated from: providers/dns/dreamhost/dreamhost.toml
fmt.Fprintln(w, `Configuration for DreamHost.`)

View file

@ -0,0 +1,59 @@
---
title: "Domain Offensive (do.de)"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: dode
---
<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->
<!-- providers/dns/dode/dode.toml -->
<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->
Configuration for [Domain Offensive (do.de)](https://www.do.de/).
<!--more-->
- Code: `dode`
{{% notice note %}}
_Please contribute by adding a CLI example._
{{% /notice %}}
## Credentials
| Environment Variable Name | Description |
|-----------------------|-------------|
| `DODE_TOKEN` | API token |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here](/lego/dns/#configuration-and-credentials).
## Additional Configuration
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `DODE_HTTP_TIMEOUT` | API request timeout |
| `DODE_POLLING_INTERVAL` | Time between DNS propagation check |
| `DODE_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `DODE_SEQUENCE_INTERVAL` | Interval between iteration |
| `DODE_TTL` | The TTL of the TXT record used for the DNS challenge |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
More information [here](/lego/dns/#configuration-and-credentials).
## More information
- [API documentation](https://www.do.de/wiki/LetsEncrypt_-_Entwickler)
<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->
<!-- providers/dns/dode/dode.toml -->
<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->

View file

@ -19,6 +19,7 @@ import (
"github.com/go-acme/lego/providers/dns/dnsimple"
"github.com/go-acme/lego/providers/dns/dnsmadeeasy"
"github.com/go-acme/lego/providers/dns/dnspod"
"github.com/go-acme/lego/providers/dns/dode"
"github.com/go-acme/lego/providers/dns/dreamhost"
"github.com/go-acme/lego/providers/dns/duckdns"
"github.com/go-acme/lego/providers/dns/dyn"
@ -91,6 +92,8 @@ func NewDNSChallengeProviderByName(name string) (challenge.Provider, error) {
return dnsmadeeasy.NewDNSProvider()
case "dnspod":
return dnspod.NewDNSProvider()
case "dode":
return dode.NewDNSProvider()
case "dreamhost":
return dreamhost.NewDNSProvider()
case "duckdns":

View file

@ -0,0 +1,57 @@
package dode
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/url"
"github.com/go-acme/lego/challenge/dns01"
)
type apiResponse struct {
Domain string
Success bool
}
// updateTxtRecord Update the domains TXT record
// To update the TXT record we just need to make one simple get request.
func (d *DNSProvider) updateTxtRecord(fqdn, token, txt string, clear bool) error {
u, _ := url.Parse("https://www.do.de/api/letsencrypt")
query := u.Query()
query.Set("token", token)
query.Set("domain", dns01.UnFqdn(fqdn))
// api call differs per set/delete
if clear {
query.Set("action", "delete")
} else {
query.Set("value", txt)
}
u.RawQuery = query.Encode()
response, err := d.config.HTTPClient.Get(u.String())
if err != nil {
return err
}
defer response.Body.Close()
bodyBytes, err := ioutil.ReadAll(response.Body)
if err != nil {
return err
}
var r apiResponse
err = json.Unmarshal(bodyBytes, &r)
if err != nil {
return fmt.Errorf("request to change TXT record for do.de returned the following invalid json (%s); used url [%s]", string(bodyBytes), u)
}
body := string(bodyBytes)
if !r.Success {
return fmt.Errorf("request to change TXT record for do.de returned the following error result (%s); used url [%s]", body, u)
}
return nil
}

View file

@ -0,0 +1,89 @@
// Package dode implements a DNS provider for solving the DNS-01 challenge using do.de.
package dode
import (
"errors"
"fmt"
"net/http"
"time"
"github.com/go-acme/lego/challenge/dns01"
"github.com/go-acme/lego/platform/config/env"
)
// Config is used to configure the creation of the DNSProvider
type Config struct {
Token string
PropagationTimeout time.Duration
PollingInterval time.Duration
SequenceInterval time.Duration
HTTPClient *http.Client
}
// NewDefaultConfig returns a default configuration for the DNSProvider
func NewDefaultConfig() *Config {
return &Config{
PropagationTimeout: env.GetOrDefaultSecond("DODE_PROPAGATION_TIMEOUT", dns01.DefaultPropagationTimeout),
PollingInterval: env.GetOrDefaultSecond("DODE_POLLING_INTERVAL", dns01.DefaultPollingInterval),
SequenceInterval: env.GetOrDefaultSecond("DODE_SEQUENCE_INTERVAL", dns01.DefaultPropagationTimeout),
HTTPClient: &http.Client{
Timeout: env.GetOrDefaultSecond("DODE_HTTP_TIMEOUT", 30*time.Second),
},
}
}
// DNSProvider adds and removes the record for the DNS challenge
type DNSProvider struct {
config *Config
}
// NewDNSProvider returns a new DNS provider using
// environment variable DODE_TOKEN for adding and removing the DNS record.
func NewDNSProvider() (*DNSProvider, error) {
values, err := env.Get("DODE_TOKEN")
if err != nil {
return nil, fmt.Errorf("do.de: %v", err)
}
config := NewDefaultConfig()
config.Token = values["DODE_TOKEN"]
return NewDNSProviderConfig(config)
}
// NewDNSProviderConfig return a DNSProvider instance configured for do.de.
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
if config == nil {
return nil, errors.New("do.de: the configuration of the DNS provider is nil")
}
if config.Token == "" {
return nil, errors.New("do.de: credentials missing")
}
return &DNSProvider{config: config}, nil
}
// Present creates a TXT record to fulfill the dns-01 challenge.
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
fqdn, txtRecord := dns01.GetRecord(domain, keyAuth)
return d.updateTxtRecord(fqdn, d.config.Token, txtRecord, false)
}
// CleanUp clears TXT record
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
fqdn, _ := dns01.GetRecord(domain, keyAuth)
return d.updateTxtRecord(fqdn, d.config.Token, "", true)
}
// Timeout returns the timeout and interval to use when checking for DNS propagation.
// Adjusting here to cope with spikes in propagation times.
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
return d.config.PropagationTimeout, d.config.PollingInterval
}
// Sequential All DNS challenges for this provider will be resolved sequentially.
// Returns the interval between each iteration.
func (d *DNSProvider) Sequential() time.Duration {
return d.config.SequenceInterval
}

View file

@ -0,0 +1,19 @@
Name = "Domain Offensive (do.de)"
Description = ''''''
URL = "https://www.do.de/"
Code = "dode"
Example = ''''''
[Configuration]
[Configuration.Credentials]
DODE_TOKEN = "API token"
[Configuration.Additional]
DODE_POLLING_INTERVAL = "Time between DNS propagation check"
DODE_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
DODE_TTL = "The TTL of the TXT record used for the DNS challenge"
DODE_HTTP_TIMEOUT = "API request timeout"
DODE_SEQUENCE_INTERVAL = "Interval between iteration"
[Links]
API = "https://www.do.de/wiki/LetsEncrypt_-_Entwickler"

View file

@ -0,0 +1,115 @@
package dode
import (
"testing"
"time"
"github.com/go-acme/lego/platform/tester"
"github.com/stretchr/testify/require"
)
var envTest = tester.NewEnvTest("DODE_TOKEN").
WithDomain("DODE_DOMAIN")
func TestNewDNSProvider(t *testing.T) {
testCases := []struct {
desc string
envVars map[string]string
expected string
}{
{
desc: "success",
envVars: map[string]string{
"DODE_TOKEN": "123",
},
},
{
desc: "missing api key",
envVars: map[string]string{
"DODE_TOKEN": "",
},
expected: "do.de: some credentials information are missing: DODE_TOKEN",
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
defer envTest.RestoreEnv()
envTest.ClearEnv()
envTest.Apply(test.envVars)
p, err := NewDNSProvider()
if len(test.expected) == 0 {
require.NoError(t, err)
require.NotNil(t, p)
require.NotNil(t, p.config)
} else {
require.EqualError(t, err, test.expected)
}
})
}
}
func TestNewDNSProviderConfig(t *testing.T) {
testCases := []struct {
desc string
token string
expected string
}{
{
desc: "success",
token: "123",
},
{
desc: "missing credentials",
expected: "do.de: credentials missing",
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
config := NewDefaultConfig()
config.Token = test.token
p, err := NewDNSProviderConfig(config)
if len(test.expected) == 0 {
require.NoError(t, err)
require.NotNil(t, p)
require.NotNil(t, p.config)
} else {
require.EqualError(t, err, test.expected)
}
})
}
}
func TestLivePresent(t *testing.T) {
if !envTest.IsLiveTest() {
t.Skip("skipping live test")
}
envTest.RestoreEnv()
provider, err := NewDNSProvider()
require.NoError(t, err)
err = provider.Present(envTest.GetDomain(), "", "123d==")
require.NoError(t, err)
}
func TestLiveCleanUp(t *testing.T) {
if !envTest.IsLiveTest() {
t.Skip("skipping live test")
}
envTest.RestoreEnv()
provider, err := NewDNSProvider()
require.NoError(t, err)
time.Sleep(1 * time.Second)
err = provider.CleanUp(envTest.GetDomain(), "", "123d==")
require.NoError(t, err)
}