cloudflare: add support for API tokens (#937)

This commit is contained in:
Jarred Trainor 2019-09-01 05:32:20 -07:00 committed by Ludovic Fernandez
parent f6230a2662
commit a5a29187fe
5 changed files with 101 additions and 17 deletions

View file

@ -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()

View file

@ -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`

View file

@ -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) {

View file

@ -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"

View file

@ -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)