forked from TrueCloudLab/lego
cloudflare: add support for API tokens (#937)
This commit is contained in:
parent
f6230a2662
commit
a5a29187fe
5 changed files with 101 additions and 17 deletions
|
@ -220,7 +220,9 @@ func displayDNSHelp(name string) error {
|
|||
ew.writeln(`Credentials:`)
|
||||
ew.writeln(` - "CF_API_EMAIL": Account email`)
|
||||
ew.writeln(` - "CF_API_KEY": API key`)
|
||||
ew.writeln(` - "CLOUDFLARE_API_KEY": Alias to CLOUDFLARE_API_KEY`)
|
||||
ew.writeln(` - "CF_API_TOKEN": API token`)
|
||||
ew.writeln(` - "CLOUDFLARE_API_KEY": Alias to CF_API_KEY`)
|
||||
ew.writeln(` - "CLOUDFLARE_API_TOKEN": Alias to CF_API_TOKEN`)
|
||||
ew.writeln(` - "CLOUDFLARE_EMAIL": Alias to CF_API_EMAIL`)
|
||||
ew.writeln()
|
||||
|
||||
|
|
|
@ -24,6 +24,11 @@ Here is an example bash command using the Cloudflare provider:
|
|||
CLOUDFLARE_EMAIL=foo@bar.com \
|
||||
CLOUDFLARE_API_KEY=b9841238feb177a84330febba8a83208921177bffe733 \
|
||||
lego --dns cloudflare --domains my.domain.com --email my@email.com run
|
||||
|
||||
# or
|
||||
|
||||
CLOUDFLARE_API_TOKEN=1234567890abcdefghijklmnopqrstuvwxyz \
|
||||
lego --dns cloudflare --domains my.domain.com --email my@email.com run
|
||||
```
|
||||
|
||||
|
||||
|
@ -35,7 +40,9 @@ lego --dns cloudflare --domains my.domain.com --email my@email.com run
|
|||
|-----------------------|-------------|
|
||||
| `CF_API_EMAIL` | Account email |
|
||||
| `CF_API_KEY` | API key |
|
||||
| `CLOUDFLARE_API_KEY` | Alias to CLOUDFLARE_API_KEY |
|
||||
| `CF_API_TOKEN` | API token |
|
||||
| `CLOUDFLARE_API_KEY` | Alias to CF_API_KEY |
|
||||
| `CLOUDFLARE_API_TOKEN` | Alias to CF_API_TOKEN |
|
||||
| `CLOUDFLARE_EMAIL` | Alias to CF_API_EMAIL |
|
||||
|
||||
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
|
||||
|
@ -54,7 +61,20 @@ More information [here](/lego/dns/#configuration-and-credentials).
|
|||
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).
|
||||
|
||||
The Global API Key needs to be used, not the Origin CA Key.
|
||||
## Description
|
||||
|
||||
You may use `CF_API_EMAIL` and `CF_API_KEY` to authenticate, or `CF_API_TOKEN`.
|
||||
|
||||
### API keys
|
||||
|
||||
If using API keys (`CF_API_EMAIL` and `CF_API_KEY`), the Global API Key needs to be used, not the Origin CA Key.
|
||||
|
||||
### API tokens
|
||||
|
||||
If using [API tokens](https://api.cloudflare.com/#getting-started-endpoints) (`CF_API_TOKEN`), the following permissions are required:
|
||||
|
||||
* `Zone:Read`
|
||||
* `DNS:Edit`
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ const (
|
|||
type Config struct {
|
||||
AuthEmail string
|
||||
AuthKey string
|
||||
AuthToken string
|
||||
TTL int
|
||||
PropagationTimeout time.Duration
|
||||
PollingInterval time.Duration
|
||||
|
@ -47,18 +48,26 @@ type DNSProvider struct {
|
|||
|
||||
// NewDNSProvider returns a DNSProvider instance configured for Cloudflare.
|
||||
// Credentials must be passed in the environment variables:
|
||||
// CLOUDFLARE_EMAIL and CLOUDFLARE_API_KEY.
|
||||
// CLOUDFLARE_EMAIL, CLOUDFLARE_API_KEY, CLOUDFLARE_API_TOKEN.
|
||||
func NewDNSProvider() (*DNSProvider, error) {
|
||||
values, err := env.GetWithFallback(
|
||||
[]string{"CLOUDFLARE_EMAIL", "CF_API_EMAIL"},
|
||||
[]string{"CLOUDFLARE_API_KEY", "CF_API_KEY"})
|
||||
[]string{"CLOUDFLARE_API_KEY", "CF_API_KEY"},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cloudflare: %v", err)
|
||||
var errT error
|
||||
values, errT = env.GetWithFallback(
|
||||
[]string{"CLOUDFLARE_API_TOKEN", "CF_API_TOKEN"},
|
||||
)
|
||||
if errT != nil {
|
||||
return nil, fmt.Errorf("cloudflare: %v or %v", err, errT)
|
||||
}
|
||||
}
|
||||
|
||||
config := NewDefaultConfig()
|
||||
config.AuthEmail = values["CLOUDFLARE_EMAIL"]
|
||||
config.AuthKey = values["CLOUDFLARE_API_KEY"]
|
||||
config.AuthToken = values["CLOUDFLARE_API_TOKEN"]
|
||||
|
||||
return NewDNSProviderConfig(config)
|
||||
}
|
||||
|
@ -73,7 +82,7 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
|||
return nil, fmt.Errorf("cloudflare: invalid TTL, TTL (%d) must be greater than %d", config.TTL, minTTL)
|
||||
}
|
||||
|
||||
client, err := cloudflare.New(config.AuthKey, config.AuthEmail, cloudflare.HTTPClient(config.HTTPClient))
|
||||
client, err := getClient(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -81,6 +90,14 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
|||
return &DNSProvider{client: client, config: config}, nil
|
||||
}
|
||||
|
||||
func getClient(config *Config) (*cloudflare.API, error) {
|
||||
if config.AuthToken == "" {
|
||||
return cloudflare.New(config.AuthKey, config.AuthEmail, cloudflare.HTTPClient(config.HTTPClient))
|
||||
}
|
||||
|
||||
return cloudflare.NewWithAPIToken(config.AuthToken, cloudflare.HTTPClient(config.HTTPClient))
|
||||
}
|
||||
|
||||
// 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) {
|
||||
|
|
|
@ -8,18 +8,38 @@ Example = '''
|
|||
CLOUDFLARE_EMAIL=foo@bar.com \
|
||||
CLOUDFLARE_API_KEY=b9841238feb177a84330febba8a83208921177bffe733 \
|
||||
lego --dns cloudflare --domains my.domain.com --email my@email.com run
|
||||
|
||||
# or
|
||||
|
||||
CLOUDFLARE_API_TOKEN=1234567890abcdefghijklmnopqrstuvwxyz \
|
||||
lego --dns cloudflare --domains my.domain.com --email my@email.com run
|
||||
'''
|
||||
|
||||
Additional = '''
|
||||
The Global API Key needs to be used, not the Origin CA Key.
|
||||
## Description
|
||||
|
||||
You may use `CF_API_EMAIL` and `CF_API_KEY` to authenticate, or `CF_API_TOKEN`.
|
||||
|
||||
### API keys
|
||||
|
||||
If using API keys (`CF_API_EMAIL` and `CF_API_KEY`), the Global API Key needs to be used, not the Origin CA Key.
|
||||
|
||||
### API tokens
|
||||
|
||||
If using [API tokens](https://api.cloudflare.com/#getting-started-endpoints) (`CF_API_TOKEN`), the following permissions are required:
|
||||
|
||||
* `Zone:Read`
|
||||
* `DNS:Edit`
|
||||
'''
|
||||
|
||||
[Configuration]
|
||||
[Configuration.Credentials]
|
||||
CF_API_EMAIL = "Account email"
|
||||
CF_API_KEY = "API key"
|
||||
CF_API_TOKEN = "API token"
|
||||
CLOUDFLARE_EMAIL = "Alias to CF_API_EMAIL"
|
||||
CLOUDFLARE_API_KEY = "Alias to CLOUDFLARE_API_KEY"
|
||||
CLOUDFLARE_API_KEY = "Alias to CF_API_KEY"
|
||||
CLOUDFLARE_API_TOKEN = "Alias to CF_API_TOKEN"
|
||||
[Configuration.Additional]
|
||||
CLOUDFLARE_POLLING_INTERVAL = "Time between DNS propagation check"
|
||||
CLOUDFLARE_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
|
||||
|
|
|
@ -11,7 +11,8 @@ import (
|
|||
|
||||
var envTest = tester.NewEnvTest(
|
||||
"CLOUDFLARE_EMAIL",
|
||||
"CLOUDFLARE_API_KEY").
|
||||
"CLOUDFLARE_API_KEY",
|
||||
"CLOUDFLARE_API_TOKEN").
|
||||
WithDomain("CLOUDFLARE_DOMAIN")
|
||||
|
||||
func TestNewDNSProvider(t *testing.T) {
|
||||
|
@ -21,19 +22,26 @@ func TestNewDNSProvider(t *testing.T) {
|
|||
expected string
|
||||
}{
|
||||
{
|
||||
desc: "success",
|
||||
desc: "success email, API key",
|
||||
envVars: map[string]string{
|
||||
"CLOUDFLARE_EMAIL": "test@example.com",
|
||||
"CLOUDFLARE_API_KEY": "123",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "success API token",
|
||||
envVars: map[string]string{
|
||||
"CLOUDFLARE_API_TOKEN": "012345abcdef",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "missing credentials",
|
||||
envVars: map[string]string{
|
||||
"CLOUDFLARE_EMAIL": "",
|
||||
"CLOUDFLARE_API_KEY": "",
|
||||
"CLOUDFLARE_EMAIL": "",
|
||||
"CLOUDFLARE_API_KEY": "",
|
||||
"CLOUDFLARE_API_TOKEN": "",
|
||||
},
|
||||
expected: "cloudflare: some credentials information are missing: CLOUDFLARE_EMAIL,CLOUDFLARE_API_KEY",
|
||||
expected: "cloudflare: some credentials information are missing: CLOUDFLARE_EMAIL,CLOUDFLARE_API_KEY or some credentials information are missing: CLOUDFLARE_API_TOKEN",
|
||||
},
|
||||
{
|
||||
desc: "missing email",
|
||||
|
@ -41,7 +49,7 @@ func TestNewDNSProvider(t *testing.T) {
|
|||
"CLOUDFLARE_EMAIL": "",
|
||||
"CLOUDFLARE_API_KEY": "key",
|
||||
},
|
||||
expected: "cloudflare: some credentials information are missing: CLOUDFLARE_EMAIL",
|
||||
expected: "cloudflare: some credentials information are missing: CLOUDFLARE_EMAIL or some credentials information are missing: CLOUDFLARE_API_TOKEN",
|
||||
},
|
||||
{
|
||||
desc: "missing api key",
|
||||
|
@ -49,7 +57,7 @@ func TestNewDNSProvider(t *testing.T) {
|
|||
"CLOUDFLARE_EMAIL": "awesome@possum.com",
|
||||
"CLOUDFLARE_API_KEY": "",
|
||||
},
|
||||
expected: "cloudflare: some credentials information are missing: CLOUDFLARE_API_KEY",
|
||||
expected: "cloudflare: some credentials information are missing: CLOUDFLARE_API_KEY or some credentials information are missing: CLOUDFLARE_API_TOKEN",
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -79,10 +87,21 @@ func TestNewDNSProviderConfig(t *testing.T) {
|
|||
desc string
|
||||
authEmail string
|
||||
authKey string
|
||||
authToken string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
desc: "success",
|
||||
desc: "success with email and api key",
|
||||
authEmail: "test@example.com",
|
||||
authKey: "123",
|
||||
},
|
||||
{
|
||||
desc: "success with api token",
|
||||
authToken: "012345abcdef",
|
||||
},
|
||||
{
|
||||
desc: "prefer api token",
|
||||
authToken: "012345abcdef",
|
||||
authEmail: "test@example.com",
|
||||
authKey: "123",
|
||||
},
|
||||
|
@ -100,6 +119,11 @@ func TestNewDNSProviderConfig(t *testing.T) {
|
|||
authEmail: "test@example.com",
|
||||
expected: "invalid credentials: key & email must not be empty",
|
||||
},
|
||||
{
|
||||
desc: "missing api token, fallback to api key/email",
|
||||
authToken: "",
|
||||
expected: "invalid credentials: key & email must not be empty",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
|
@ -107,6 +131,7 @@ func TestNewDNSProviderConfig(t *testing.T) {
|
|||
config := NewDefaultConfig()
|
||||
config.AuthEmail = test.authEmail
|
||||
config.AuthKey = test.authKey
|
||||
config.AuthToken = test.authToken
|
||||
|
||||
p, err := NewDNSProviderConfig(config)
|
||||
|
||||
|
|
Loading…
Reference in a new issue