Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for autodns #957

Merged
merged 38 commits into from
Nov 1, 2019
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
4b694ec
Started adding autodns support
Aug 30, 2019
7c8d591
Autodns support roughly working
Aug 30, 2019
1b8f676
Fixed clearing all records when adding/removing the challenge record
Aug 30, 2019
beedce7
Started adding tests
Aug 30, 2019
570fc61
Added more tests
Aug 30, 2019
83c3955
Implemented adding/removing of records using the stream api
Aug 30, 2019
f209a5f
Fixed lint
Aug 30, 2019
3d32893
Fixed lint
Aug 30, 2019
58b82de
Updated readme
Sep 2, 2019
38177e5
Updated tests
Sep 2, 2019
e126098
Added more config variables
Sep 2, 2019
b3d3840
make generate
Sep 2, 2019
ba90111
Fixed lint
Sep 2, 2019
fd8b474
Tidy go mod
Sep 2, 2019
91dbe58
Merge branch 'master' into feature/autodns
kolaente Sep 2, 2019
4492220
Added timeouts
Sep 3, 2019
c8d9af3
Added example
Sep 3, 2019
388d597
Moved more stuff to default config
Sep 3, 2019
b0058c4
Added http timeout setting
Sep 3, 2019
e2ece28
Moved getting new dns provider config to own function
Sep 3, 2019
5047429
Fixed tests
Sep 3, 2019
e44adc5
Added more tests
Sep 3, 2019
1e23110
Added more checks
Sep 6, 2019
a24ee75
Merge branch 'master' into feature/autodns
kolaente Sep 18, 2019
bb23549
Merge branch 'master' into feature/autodns
kolaente Sep 19, 2019
5fcf189
Merge branch 'master' into feature/autodns
kolaente Sep 23, 2019
a1904c7
Merge branch 'master' into feature/autodns
kolaente Oct 7, 2019
0a3a026
Merge branch 'master' into feature/autodns
kolaente Oct 9, 2019
e4060f7
Merge branch 'master' into feature/autodns
kolaente Oct 22, 2019
e1010a6
Merge branch 'master' into feature/autodns
kolaente Oct 25, 2019
3935ea4
Pass the value when deleting a challenge record
Oct 25, 2019
5c66aec
Fixed lint
Oct 25, 2019
dc91e3f
review quick review.
ldez Oct 30, 2019
77ebb41
Merge branch 'master' into feature/autodns
kolaente Oct 30, 2019
73276e5
review: add documentation.
ldez Oct 30, 2019
6653372
review: homogeneous unit tests with the other providers.
ldez Oct 30, 2019
9f5c102
Merge pull request #1 from ldez/review/957
kolaente Oct 31, 2019
d792fdc
Fixed lint
Nov 1, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
30 changes: 15 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,18 +44,18 @@ 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/) | [Bindman](https://go-acme.github.io/lego/dns/bindman/) | [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/) |
| [EasyDNS](https://go-acme.github.io/lego/dns/easydns/) | [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/) | [Joker](https://go-acme.github.io/lego/dns/joker/) | [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/) | [Liquid Web](https://go-acme.github.io/lego/dns/liquidweb/) | [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/) | [Namesilo](https://go-acme.github.io/lego/dns/namesilo/) | [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/) | [Versio](https://go-acme.github.io/lego/dns/versio/) | [Vultr](https://go-acme.github.io/lego/dns/vultr/) |
| [Zone.ee](https://go-acme.github.io/lego/dns/zoneee/)
| [Autodns](https://go-acme.github.io/lego/dns/autodns/) | [Azure](https://go-acme.github.io/lego/dns/azure/) | [Bindman](https://go-acme.github.io/lego/dns/bindman/) | [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/) | [EasyDNS](https://go-acme.github.io/lego/dns/easydns/) | [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/) | [Joker](https://go-acme.github.io/lego/dns/joker/) | [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/) | [Liquid Web](https://go-acme.github.io/lego/dns/liquidweb/) | [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/) | [Namesilo](https://go-acme.github.io/lego/dns/namesilo/) |
| [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/) | [Versio](https://go-acme.github.io/lego/dns/versio/) |
| [Vultr](https://go-acme.github.io/lego/dns/vultr/) | [Zone.ee](https://go-acme.github.io/lego/dns/zoneee/)
19 changes: 19 additions & 0 deletions cmd/zz_gen_cmd_dnshelp.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ func allDNSCodes() string {
"acme-dns",
"alidns",
"auroradns",
"autodns",
"azure",
"bindman",
"bluecat",
Expand Down Expand Up @@ -141,6 +142,24 @@ func displayDNSHelp(name string) error {
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/auroradns`)

case "autodns":
// generated from: providers/dns/autodns/autodns.toml
ew.writeln(`Configuration for Autodns.`)
ew.writeln(`Code: 'autodns'`)
ew.writeln(`Since: 'v3.1.0'`)
ew.writeln()

ew.writeln(`Credentials:`)
ew.writeln(` - "AUTODNS_API_PASSWORD": User Password`)
ew.writeln(` - "AUTODNS_API_USER": Username`)
ew.writeln(` - "AUTODNS_CONTEXT": API context (4 for production, 1 for testing. Defaults to 4)`)
ew.writeln(` - "AUTODNS_ENDPOINT": API endpoint URL`)
ew.writeln(` - "AUTODNS_TTL": The TTL of the TXT record used for the DNS challenge`)
ew.writeln()

ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/autodns`)
kolaente marked this conversation as resolved.
Show resolved Hide resolved

case "azure":
// generated from: providers/dns/azure/azure.toml
ew.writeln(`Configuration for Azure.`)
Expand Down
111 changes: 111 additions & 0 deletions providers/dns/autodns/autodns.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package autodns

import (
"fmt"
"net/http"
"net/url"
"time"

"github.com/go-acme/lego/v3/challenge/dns01"
"github.com/go-acme/lego/v3/platform/config/env"
)

const (
envAPIUser = `AUTODNS_API_USER`
envAPIPassword = `AUTODNS_API_PASSWORD`
envAPIEndpoint = `AUTODNS_ENDPOINT`
envAPIEndpointContext = `AUTODNS_CONTEXT`
envTTL = `AUTODNS_TTL`
envPropagationTimeout = `AUTODNS_PROPAGATION_TIMEOUT`
envPollingInterval = `AUTODNS_POLLING_INTERVAL`
envHTTPTimeout = `AUTODNS_HTTP_TIMEOUT`

defaultEndpoint = `https://api.autodns.com/v1/`
demoEndpoint = `https://api.demo.autodns.com/v1/`

defaultEndpointContext int = 4
defaultTTL int = 600
)

type Config struct {
Endpoint *url.URL
Username string `json:"username"`
Password string `json:"password"`
Context int `json:"-"`
TTL int `json:"-"`
PropagationTimeout time.Duration `json:"-"`
PollingInterval time.Duration `json:"-"`
HTTPClient *http.Client
}

func NewDefaultConfig() *Config {
endpoint, _ := url.Parse(env.GetOrDefaultString(envAPIEndpoint, defaultEndpoint))

return &Config{
Endpoint: endpoint,
Context: env.GetOrDefaultInt(envAPIEndpointContext, defaultEndpointContext),
TTL: env.GetOrDefaultInt(envTTL, defaultTTL),
PropagationTimeout: env.GetOrDefaultSecond(envPropagationTimeout, 2*time.Minute),
PollingInterval: env.GetOrDefaultSecond(envPollingInterval, 2*time.Second),
HTTPClient: &http.Client{
Timeout: env.GetOrDefaultSecond(envHTTPTimeout, 30*time.Second),
},
}
}

type DNSProvider struct {
config *Config
}

func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
return d.config.PropagationTimeout, d.config.PollingInterval
}

func NewDNSProvider() (*DNSProvider, error) {
values, err := env.Get(envAPIUser, envAPIPassword)
if err != nil {
return nil, fmt.Errorf("autodns: %v", err)
}

config := NewDefaultConfig()
config.Username = values[envAPIUser]
config.Password = values[envAPIPassword]

return NewDNSProviderConfig(config)
}

func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
if config == nil {
return nil, fmt.Errorf("autodns: NewDNSProviderConfig: config is nil")
}

if config.Username == "" {
return nil, fmt.Errorf("autodns: NewDNSProviderConfig: missing user")
}

if config.Password == "" {
return nil, fmt.Errorf("autodns: NewDNSProviderConfig: missing password")
}

return &DNSProvider{config: config}, nil
}

// Present creates a TXT record to fulfill the dns-01 challenge
func (d *DNSProvider) Present(domain, token, keyAuth string) (err error) {
fqdn, value := dns01.GetRecord(domain, keyAuth)
_, err = d.addTxtRecord(domain, fqdn, value)
if err != nil {
return fmt.Errorf("autodns: %v", err)
}
return nil
}

// CleanUp removes the TXT record previously created
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
fqdn, realToken := dns01.GetRecord(domain, keyAuth)
if err := d.removeTXTRecord(domain, fqdn, realToken); err != nil {
return fmt.Errorf("autodns: removeTXTRecord: %v", err)
}

return nil
}
25 changes: 25 additions & 0 deletions providers/dns/autodns/autodns.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
Name = "Autodns"
Description = ''''''
URL = "https://www.internetx.com/domains/autodns/"
Code = "autodns"
Since = "v3.1.0"

Example = '''
AUTODNS_API_USER=usernam \
AUTODNS_API_PASSWORD=supersecretpassword \
lego --dns autodns --domains my.domain.com --email my@email.com run
'''

[Configuration]
[Configuration.Credentials]
AUTODNS_API_USER = "Username"
AUTODNS_API_PASSWORD = "User Password"
[Configuration.Additional]
AUTODNS_ENDPOINT = "API endpoint URL, defaults to https://api.autodns.com/v1/"
AUTODNS_CONTEXT = "API context (4 for production, 1 for testing. Defaults to 4)"
AUTODNS_TTL = "The TTL of the TXT record used for the DNS challenge"
kolaente marked this conversation as resolved.
Show resolved Hide resolved
AUTODNS_POLLING_INTERVAL = "Time between DNS propagation check"
AUTODNS_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
AUTODNS_HTTP_TIMEOUT = "API request timeout, defaults to 30 seconds"
[Links]
API = "https://help.internetx.com/display/APIJSONEN"
143 changes: 143 additions & 0 deletions providers/dns/autodns/autodns_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
package autodns

import (
"net/http"
"net/url"
"reflect"
"testing"
"time"

"github.com/go-acme/lego/v3/platform/tester"
"github.com/stretchr/testify/assert"
)

var envTest = tester.NewEnvTest(envAPIEndpoint, envAPIUser, envAPIPassword)

func TestNewDNSProvider(t *testing.T) {
defaultEndpointURL, _ := url.Parse(defaultEndpoint)
examplEndpointURL, _ := url.Parse(demoEndpoint)

tests := []struct {
name string
want *DNSProvider
wantErr bool
expectedErr string
env map[string]string
}{
{
name: "complete, no errors",
want: &DNSProvider{
config: &Config{
Endpoint: defaultEndpointURL,
Username: "test",
Password: "1234",
Context: defaultEndpointContext,
TTL: defaultTTL,
PropagationTimeout: 2 * time.Minute,
PollingInterval: 2 * time.Second,
HTTPClient: &http.Client{
Timeout: 30 * time.Second,
},
},
},
env: map[string]string{
envAPIUser: "test",
envAPIPassword: "1234",
},
},
{
name: "different endpoint url",
want: &DNSProvider{
config: &Config{
Endpoint: examplEndpointURL,
Username: "test",
Password: "1234",
Context: defaultEndpointContext,
TTL: defaultTTL,
PropagationTimeout: 2 * time.Minute,
PollingInterval: 2 * time.Second,
HTTPClient: &http.Client{
Timeout: 30 * time.Second,
},
},
},
env: map[string]string{
envAPIUser: "test",
envAPIPassword: "1234",
envAPIEndpoint: demoEndpoint,
},
},
{
name: "missing credentials",
env: map[string]string{
envAPIUser: "",
envAPIPassword: "",
},
wantErr: true,
expectedErr: "autodns: some credentials information are missing: AUTODNS_API_USER,AUTODNS_API_PASSWORD",
},
{
name: "missing username",
env: map[string]string{
envAPIUser: "",
envAPIPassword: "1234",
},
wantErr: true,
expectedErr: "autodns: some credentials information are missing: AUTODNS_API_USER",
},
{
name: "missing password",
env: map[string]string{
envAPIUser: "user",
envAPIPassword: "",
},
wantErr: true,
expectedErr: "autodns: some credentials information are missing: AUTODNS_API_PASSWORD",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
defer envTest.RestoreEnv()
envTest.ClearEnv()
envTest.Apply(tt.env)

got, err := NewDNSProvider()
if (err != nil) != tt.wantErr {
t.Errorf("NewDNSProvider() error = %v, wantErr %v", err, tt.wantErr)
return
}
if (err != nil) && tt.wantErr {
assert.EqualError(t, err, tt.expectedErr)
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("NewDNSProvider() got = %v, want %v", got, tt.want)
}
})
}
}

func TestLivePresent(t *testing.T) {
if !envTest.IsLiveTest() {
t.Skip("skipping live test")
}

envTest.RestoreEnv()
provider, err := NewDNSProvider()
assert.NoError(t, err)

err = provider.Present(envTest.GetDomain(), "", "123d==")
assert.NoError(t, err)
}

func TestLiveCleanUp(t *testing.T) {
if !envTest.IsLiveTest() {
t.Skip("skipping live test")
}

envTest.RestoreEnv()
provider, err := NewDNSProvider()
assert.NoError(t, err)

err = provider.CleanUp(envTest.GetDomain(), "", "123d==")
assert.NoError(t, err)
}