Skip to content

Commit baad3de

Browse files
shuppldez
authored andcommitted
Add EXEC_MODE=RAW support to DNS Provider exec (#586)
1 parent 6edbd15 commit baad3de

File tree

3 files changed

+106
-42
lines changed

3 files changed

+106
-42
lines changed

cli.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ Here is an example bash command using the CloudFlare DNS provider:
230230
fmt.Fprintln(w, "\tdnspod:\tDNSPOD_API_KEY")
231231
fmt.Fprintln(w, "\totc:\tOTC_USER_NAME, OTC_PASSWORD, OTC_PROJECT_NAME, OTC_DOMAIN_NAME, OTC_IDENTITY_ENDPOINT")
232232
fmt.Fprintln(w, "\tsakuracloud:\tSAKURACLOUD_ACCESS_TOKEN, SAKURACLOUD_ACCESS_TOKEN_SECRET")
233-
fmt.Fprintln(w, "\texec:\tEXEC_PATH")
233+
fmt.Fprintln(w, "\texec:\tEXEC_PATH, EXEC_MODE")
234234
w.Flush()
235235

236236
fmt.Println(`

providers/dns/exec/doc.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
Package exec implements a manual DNS provider which runs a program for adding/removing the DNS record.
3+
4+
The file name of the external program is specified in the environment variable `EXEC_PATH`.
5+
When it is run by lego, three command-line parameters are passed to it:
6+
The action ("present" or "cleanup"), the fully-qualified domain name, the value for the record and the TTL.
7+
8+
For example, requesting a certificate for the domain 'foo.example.com' can be achieved by calling lego as follows:
9+
10+
EXEC_PATH=./update-dns.sh \
11+
lego --dns exec \
12+
--domains foo.example.com \
13+
--email [email protected] run
14+
15+
It will then call the program './update-dns.sh' with like this:
16+
17+
./update-dns.sh "present" "_acme-challenge.foo.example.com." "MsijOYZxqyjGnFGwhjrhfg-Xgbl5r68WPda0J9EgqqI" "120"
18+
19+
The program then needs to make sure the record is inserted.
20+
When it returns an error via a non-zero exit code, lego aborts.
21+
22+
When the record is to be removed again,
23+
the program is called with the first command-line parameter set to "cleanup" instead of "present".
24+
25+
If you want to use the raw domain, token, and keyAuth values with your program, you can set `EXEC_MODE=RAW`:
26+
27+
EXEC_MODE=RAW \
28+
EXEC_PATH=./update-dns.sh \
29+
lego --dns exec \
30+
--domains foo.example.com \
31+
--email [email protected] run
32+
33+
It will then call the program './update-dns.sh' like this:
34+
35+
./update-dns.sh "present" "foo.example.com." "--" "some-token" "KxAy-J3NwUmg9ZQuM-gP_Mq1nStaYSaP9tYQs5_-YsE.ksT-qywTd8058G-SHHWA3RAN72Pr0yWtPYmmY5UBpQ8"
36+
37+
NOTE:
38+
The `--` is because the token MAY start with a `-`, and the called program may try and interpret a - as indicating a flag.
39+
In the case of urfave, which is commonly used,
40+
you can use the `--` delimiter to specify the start of positional arguments, and handle such a string safely.
41+
*/
42+
package exec

providers/dns/exec/exec.go

Lines changed: 63 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,100 @@
1-
// Package exec implements a manual DNS provider which runs a program for
2-
// adding/removing the DNS record.
3-
//
4-
// The file name of the external program is specified in the environment
5-
// variable EXEC_PATH. When it is run by lego, three command-line parameters
6-
// are passed to it: The action ("present" or "cleanup"), the fully-qualified domain
7-
// name, the value for the record and the TTL.
8-
//
9-
// For example, requesting a certificate for the domain 'foo.example.com' can
10-
// be achieved by calling lego as follows:
11-
//
12-
// EXEC_PATH=./update-dns.sh \
13-
// lego --dns exec \
14-
// --domains foo.example.com \
15-
// --email [email protected] run
16-
//
17-
// It will then call the program './update-dns.sh' with like this:
18-
//
19-
// ./update-dns.sh "present" "_acme-challenge.foo.example.com." "MsijOYZxqyjGnFGwhjrhfg-Xgbl5r68WPda0J9EgqqI" "120"
20-
//
21-
// The program then needs to make sure the record is inserted. When it returns
22-
// an error via a non-zero exit code, lego aborts.
23-
//
24-
// When the record is to be removed again, the program is called with the first
25-
// command-line parameter set to "cleanup" instead of "present".
261
package exec
272

283
import (
294
"errors"
5+
"fmt"
306
"os"
317
"os/exec"
328
"strconv"
339

3410
"github.com/xenolf/lego/acme"
11+
"github.com/xenolf/lego/log"
12+
"github.com/xenolf/lego/platform/config/env"
3513
)
3614

15+
// Config Provider configuration.
16+
type Config struct {
17+
Program string
18+
Mode string
19+
}
20+
3721
// DNSProvider adds and removes the record for the DNS challenge by calling a
3822
// program with command-line parameters.
3923
type DNSProvider struct {
40-
program string
24+
config *Config
4125
}
4226

4327
// NewDNSProvider returns a new DNS provider which runs the program in the
4428
// environment variable EXEC_PATH for adding and removing the DNS record.
4529
func NewDNSProvider() (*DNSProvider, error) {
46-
s := os.Getenv("EXEC_PATH")
47-
if s == "" {
48-
return nil, errors.New("environment variable EXEC_PATH not set")
30+
values, err := env.Get("EXEC_PATH")
31+
if err != nil {
32+
return nil, fmt.Errorf("exec: %v", err)
4933
}
5034

51-
return NewDNSProviderProgram(s)
35+
return NewDNSProviderConfig(&Config{
36+
Program: values["EXEC_PATH"],
37+
Mode: os.Getenv("EXEC_MODE"),
38+
})
39+
}
40+
41+
// NewDNSProviderConfig returns a new DNS provider which runs the given configuration
42+
// for adding and removing the DNS record.
43+
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
44+
if config == nil {
45+
return nil, errors.New("the configuration is nil")
46+
}
47+
48+
return &DNSProvider{config: config}, nil
5249
}
5350

5451
// NewDNSProviderProgram returns a new DNS provider which runs the given program
5552
// for adding and removing the DNS record.
53+
// Deprecated: use NewDNSProviderConfig instead
5654
func NewDNSProviderProgram(program string) (*DNSProvider, error) {
57-
return &DNSProvider{program: program}, nil
55+
if len(program) == 0 {
56+
return nil, errors.New("the program is undefined")
57+
}
58+
59+
return NewDNSProviderConfig(&Config{Program: program})
5860
}
5961

6062
// Present creates a TXT record to fulfil the dns-01 challenge.
6163
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
62-
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
63-
cmd := exec.Command(d.program, "present", fqdn, value, strconv.Itoa(ttl))
64-
cmd.Stdout = os.Stdout
65-
cmd.Stderr = os.Stderr
64+
var args []string
65+
if d.config.Mode == "RAW" {
66+
args = []string{"present", "--", domain, token, keyAuth}
67+
} else {
68+
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
69+
args = []string{"present", fqdn, value, strconv.Itoa(ttl)}
70+
}
71+
72+
cmd := exec.Command(d.config.Program, args...)
73+
74+
output, err := cmd.CombinedOutput()
75+
if len(output) > 0 {
76+
log.Println(string(output))
77+
}
6678

67-
return cmd.Run()
79+
return err
6880
}
6981

7082
// CleanUp removes the TXT record matching the specified parameters
7183
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
72-
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
73-
cmd := exec.Command(d.program, "cleanup", fqdn, value, strconv.Itoa(ttl))
74-
cmd.Stdout = os.Stdout
75-
cmd.Stderr = os.Stderr
84+
var args []string
85+
if d.config.Mode == "RAW" {
86+
args = []string{"cleanup", "--", domain, token, keyAuth}
87+
} else {
88+
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
89+
args = []string{"cleanup", fqdn, value, strconv.Itoa(ttl)}
90+
}
91+
92+
cmd := exec.Command(d.config.Program, args...)
93+
94+
output, err := cmd.CombinedOutput()
95+
if len(output) > 0 {
96+
log.Println(string(output))
97+
}
7698

77-
return cmd.Run()
99+
return err
78100
}

0 commit comments

Comments
 (0)