Skip to content

Commit 1a82eff

Browse files
authored
inwx: Two-Factor-Authentication (#1176)
1 parent 47ed092 commit 1a82eff

File tree

7 files changed

+77
-17
lines changed

7 files changed

+77
-17
lines changed

cmd/zz_gen_cmd_dnshelp.go

+1
Original file line numberDiff line numberDiff line change
@@ -941,6 +941,7 @@ func displayDNSHelp(name string) error {
941941
ew.writeln(` - "INWX_POLLING_INTERVAL": Time between DNS propagation check`)
942942
ew.writeln(` - "INWX_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
943943
ew.writeln(` - "INWX_SANDBOX": Activate the sandbox (boolean)`)
944+
ew.writeln(` - "INWX_SHARED_SECRET": shared secret related to 2FA`)
944945
ew.writeln(` - "INWX_TTL": The TTL of the TXT record used for the DNS challenge`)
945946

946947
ew.writeln()

docs/content/dns/zz_gen_inwx.md

+14-3
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,19 @@ Configuration for [INWX](https://www.inwx.de/en).
1818

1919
- Code: `inwx`
2020

21-
{{% notice note %}}
22-
_Please contribute by adding a CLI example._
23-
{{% /notice %}}
21+
Here is an example bash command using the INWX provider:
22+
23+
```bash
24+
INWX_USERNAME=xxxxxxxxxx \
25+
INWX_PASSWORD=yyyyyyyyyy \
26+
lego --dns inwx --domains my.domain.com --email my@email.com run
27+
28+
# 2FA
29+
INWX_USERNAME=xxxxxxxxxx \
30+
INWX_PASSWORD=yyyyyyyyyy \
31+
INWX_SHARED_SECRET=zzzzzzzzzz \
32+
lego --dns inwx --domains my.domain.com --email my@email.com run
33+
```
2434

2535

2636

@@ -43,6 +53,7 @@ More information [here](/lego/dns/#configuration-and-credentials).
4353
| `INWX_POLLING_INTERVAL` | Time between DNS propagation check |
4454
| `INWX_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
4555
| `INWX_SANDBOX` | Activate the sandbox (boolean) |
56+
| `INWX_SHARED_SECRET` | shared secret related to 2FA |
4657
| `INWX_TTL` | The TTL of the TXT record used for the DNS challenge |
4758

4859
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.

go.mod

+2-1
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,11 @@ require (
3030
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04
3131
github.com/nrdcg/auroradns v1.0.1
3232
github.com/nrdcg/dnspod-go v0.4.0
33-
github.com/nrdcg/goinwx v0.6.1
33+
github.com/nrdcg/goinwx v0.7.0
3434
github.com/nrdcg/namesilo v0.2.1
3535
github.com/oracle/oci-go-sdk v7.0.0+incompatible
3636
github.com/ovh/go-ovh v0.0.0-20181109152953-ba5adb4cf014
37+
github.com/pquerna/otp v1.2.0
3738
github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2
3839
github.com/sacloud/libsacloud v1.26.1
3940
github.com/stretchr/testify v1.5.1

go.sum

+10-6
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ github.com/aws/aws-sdk-go v1.30.20/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZve
7070
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
7171
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
7272
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
73+
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI=
74+
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
7375
github.com/cenkalti/backoff/v4 v4.0.0 h1:6VeaLF9aI+MAUQ95106HwWzYZgJJpZ4stumjj6RFYAU=
7476
github.com/cenkalti/backoff/v4 v4.0.0/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8Bog/87DQnVg=
7577
github.com/census-instrumentation/opencensus-proto v0.2.0 h1:LzQXZOgg4CQfE6bFvXGM30YZL1WW/M337pXml+GrcZ4=
@@ -202,8 +204,8 @@ github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7
202204
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
203205
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
204206
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
205-
github.com/kolo/xmlrpc v0.0.0-20190717152603-07c4ee3fd181 h1:TrxPzApUukas24OMMVDUMlCs1XCExJtnGaDEiIAR4oQ=
206-
github.com/kolo/xmlrpc v0.0.0-20190717152603-07c4ee3fd181/go.mod h1:o03bZfuBwAXHetKXuInt4S7omeXUu62/A845kiycsSQ=
207+
github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b h1:DzHy0GlWeF0KAglaTMY7Q+khIFoG8toHP+wLFBVBQJc=
208+
github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b/go.mod h1:o03bZfuBwAXHetKXuInt4S7omeXUu62/A845kiycsSQ=
207209
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
208210
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
209211
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@@ -231,8 +233,8 @@ github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7
231233
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
232234
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
233235
github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed/go.mod h1:3rdaFaCv4AyBgu5ALFM0+tSuHrBh6v692nyQe3ikrq0=
234-
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
235-
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
236+
github.com/mitchellh/mapstructure v1.3.1 h1:cCBH2gTD2K0OtLlv/Y5H01VQCqmlDxz30kS5Y5bqfLA=
237+
github.com/mitchellh/mapstructure v1.3.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
236238
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
237239
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
238240
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -249,8 +251,8 @@ github.com/nrdcg/auroradns v1.0.1 h1:m/kBq83Xvy3cU261MOknd8BdnOk12q4lAWM+kOdsC2Y
249251
github.com/nrdcg/auroradns v1.0.1/go.mod h1:y4pc0i9QXYlFCWrhWrUSIETnZgrf4KuwjDIWmmXo3JI=
250252
github.com/nrdcg/dnspod-go v0.4.0 h1:c/jn1mLZNKF3/osJ6mz3QPxTudvPArXTjpkmYj0uK6U=
251253
github.com/nrdcg/dnspod-go v0.4.0/go.mod h1:vZSoFSFeQVm2gWLMkyX61LZ8HI3BaqtHZWgPTGKr6KQ=
252-
github.com/nrdcg/goinwx v0.6.1 h1:AJnjoWPELyCtofhGcmzzcEMFd9YdF2JB/LgutWsWt/s=
253-
github.com/nrdcg/goinwx v0.6.1/go.mod h1:XPiut7enlbEdntAqalBIqcYcTEVhpv/dKWgDCX2SwKQ=
254+
github.com/nrdcg/goinwx v0.7.0 h1:j6JlOp0nNwtvaP09TvKqc9pktjH81nOad0+Gx9S1t9U=
255+
github.com/nrdcg/goinwx v0.7.0/go.mod h1:4tKJOCi/1lTxuw9/yB2Ez0aojwtUCSkckjc22eALpqE=
254256
github.com/nrdcg/namesilo v0.2.1 h1:kLjCjsufdW/IlC+iSfAqj0iQGgKjlbUUeDJio5Y6eMg=
255257
github.com/nrdcg/namesilo v0.2.1/go.mod h1:lwMvfQTyYq+BbjJd30ylEG4GPSS6PII0Tia4rRpRiyw=
256258
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
@@ -270,6 +272,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
270272
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
271273
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
272274
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
275+
github.com/pquerna/otp v1.2.0 h1:/A3+Jn+cagqayeR3iHs/L62m5ue7710D35zl1zJ1kok=
276+
github.com/pquerna/otp v1.2.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
273277
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
274278
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
275279
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=

providers/dns/inwx/inwx.go

+37-6
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,17 @@ import (
1010
"github.com/go-acme/lego/v3/log"
1111
"github.com/go-acme/lego/v3/platform/config/env"
1212
"github.com/nrdcg/goinwx"
13+
"github.com/pquerna/otp/totp"
1314
)
1415

1516
// Environment variables names.
1617
const (
1718
envNamespace = "INWX_"
1819

19-
EnvUsername = envNamespace + "USERNAME"
20-
EnvPassword = envNamespace + "PASSWORD"
21-
EnvSandbox = envNamespace + "SANDBOX"
20+
EnvUsername = envNamespace + "USERNAME"
21+
EnvPassword = envNamespace + "PASSWORD"
22+
EnvSharedSecret = envNamespace + "SHARED_SECRET"
23+
EnvSandbox = envNamespace + "SANDBOX"
2224

2325
EnvTTL = envNamespace + "TTL"
2426
EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
@@ -29,6 +31,7 @@ const (
2931
type Config struct {
3032
Username string
3133
Password string
34+
SharedSecret string
3235
Sandbox bool
3336
PropagationTimeout time.Duration
3437
PollingInterval time.Duration
@@ -53,7 +56,7 @@ type DNSProvider struct {
5356

5457
// NewDNSProvider returns a DNSProvider instance configured for Dyn DNS.
5558
// Credentials must be passed in the environment variables:
56-
// INWX_USERNAME and INWX_PASSWORD.
59+
// INWX_USERNAME, INWX_PASSWORD, and INWX_SHARED_SECRET.
5760
func NewDNSProvider() (*DNSProvider, error) {
5861
values, err := env.Get(EnvUsername, EnvPassword)
5962
if err != nil {
@@ -63,6 +66,7 @@ func NewDNSProvider() (*DNSProvider, error) {
6366
config := NewDefaultConfig()
6467
config.Username = values[EnvUsername]
6568
config.Password = values[EnvPassword]
69+
config.SharedSecret = env.GetOrFile(EnvSharedSecret)
6670

6771
return NewDNSProviderConfig(config)
6872
}
@@ -95,7 +99,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
9599
return fmt.Errorf("inwx: %w", err)
96100
}
97101

98-
err = d.client.Account.Login()
102+
info, err := d.client.Account.Login()
99103
if err != nil {
100104
return fmt.Errorf("inwx: %w", err)
101105
}
@@ -107,6 +111,11 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
107111
}
108112
}()
109113

114+
err = d.twoFactorAuth(info)
115+
if err != nil {
116+
return fmt.Errorf("inwx: %w", err)
117+
}
118+
110119
var request = &goinwx.NameserverRecordRequest{
111120
Domain: dns01.UnFqdn(authZone),
112121
Name: dns01.UnFqdn(fqdn),
@@ -140,7 +149,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
140149
return fmt.Errorf("inwx: %w", err)
141150
}
142151

143-
err = d.client.Account.Login()
152+
info, err := d.client.Account.Login()
144153
if err != nil {
145154
return fmt.Errorf("inwx: %w", err)
146155
}
@@ -152,6 +161,11 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
152161
}
153162
}()
154163

164+
err = d.twoFactorAuth(info)
165+
if err != nil {
166+
return fmt.Errorf("inwx: %w", err)
167+
}
168+
155169
response, err := d.client.Nameservers.Info(&goinwx.NameserverInfoRequest{
156170
Domain: dns01.UnFqdn(authZone),
157171
Name: dns01.UnFqdn(fqdn),
@@ -177,3 +191,20 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
177191
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
178192
return d.config.PropagationTimeout, d.config.PollingInterval
179193
}
194+
195+
func (d *DNSProvider) twoFactorAuth(info *goinwx.LoginResponse) error {
196+
if info.TFA != "GOOGLE-AUTH" {
197+
return nil
198+
}
199+
200+
if d.config.SharedSecret == "" {
201+
return errors.New("two factor authentication but no shared secret is given")
202+
}
203+
204+
tan, err := totp.GenerateCode(d.config.SharedSecret, time.Now())
205+
if err != nil {
206+
return err
207+
}
208+
209+
return d.client.Account.Unlock(tan)
210+
}

providers/dns/inwx/inwx.toml

+12-1
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,24 @@ URL = "https://www.inwx.de/en"
44
Code = "inwx"
55
Since = "v2.0.0"
66

7-
Example = ''''''
7+
Example = '''
8+
INWX_USERNAME=xxxxxxxxxx \
9+
INWX_PASSWORD=yyyyyyyyyy \
10+
lego --dns inwx --domains my.domain.com --email my@email.com run
11+
12+
# 2FA
13+
INWX_USERNAME=xxxxxxxxxx \
14+
INWX_PASSWORD=yyyyyyyyyy \
15+
INWX_SHARED_SECRET=zzzzzzzzzz \
16+
lego --dns inwx --domains my.domain.com --email my@email.com run
17+
'''
818

919
[Configuration]
1020
[Configuration.Credentials]
1121
INWX_USERNAME = "Username"
1222
INWX_PASSWORD = "Password"
1323
[Configuration.Additional]
24+
INWX_SHARED_SECRET = "shared secret related to 2FA"
1425
INWX_POLLING_INTERVAL = "Time between DNS propagation check"
1526
INWX_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
1627
INWX_TTL = "The TTL of the TXT record used for the DNS challenge"

providers/dns/inwx/inwx_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const envDomain = envNamespace + "DOMAIN"
1212
var envTest = tester.NewEnvTest(
1313
EnvUsername,
1414
EnvPassword,
15+
EnvSharedSecret,
1516
EnvSandbox,
1617
EnvTTL).
1718
WithDomain(envDomain).

0 commit comments

Comments
 (0)