mirror of
https://github.com/wahyd4/cert-manager.git
synced 2026-08-09 05:06:38 +10:00
Merge pull request #2170 from matevzmihalic/cloudflare-apitoken
Add API token authentication option to Cloudflare issuer
This commit is contained in:
@@ -240,7 +240,6 @@ spec:
|
||||
containing the DNS configuration for Cloudflare
|
||||
type: object
|
||||
required:
|
||||
- apiKeySecretRef
|
||||
- email
|
||||
properties:
|
||||
apiKeySecretRef:
|
||||
@@ -256,6 +255,19 @@ spec:
|
||||
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||
TODO: Add other useful fields. apiVersion, kind, uid?'
|
||||
type: string
|
||||
apiTokenSecretRef:
|
||||
type: object
|
||||
required:
|
||||
- name
|
||||
properties:
|
||||
key:
|
||||
description: The key of the secret to select from. Must
|
||||
be a valid secret key.
|
||||
type: string
|
||||
name:
|
||||
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||
TODO: Add other useful fields. apiVersion, kind, uid?'
|
||||
type: string
|
||||
email:
|
||||
type: string
|
||||
cnameStrategy:
|
||||
|
||||
@@ -245,7 +245,6 @@ spec:
|
||||
containing the DNS configuration for Cloudflare
|
||||
type: object
|
||||
required:
|
||||
- apiKeySecretRef
|
||||
- email
|
||||
properties:
|
||||
apiKeySecretRef:
|
||||
@@ -263,6 +262,21 @@ spec:
|
||||
TODO: Add other useful fields. apiVersion, kind,
|
||||
uid?'
|
||||
type: string
|
||||
apiTokenSecretRef:
|
||||
type: object
|
||||
required:
|
||||
- name
|
||||
properties:
|
||||
key:
|
||||
description: The key of the secret to select from.
|
||||
Must be a valid secret key.
|
||||
type: string
|
||||
name:
|
||||
description: 'Name of the referent. More info:
|
||||
https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||
TODO: Add other useful fields. apiVersion, kind,
|
||||
uid?'
|
||||
type: string
|
||||
email:
|
||||
type: string
|
||||
cnameStrategy:
|
||||
|
||||
@@ -245,7 +245,6 @@ spec:
|
||||
containing the DNS configuration for Cloudflare
|
||||
type: object
|
||||
required:
|
||||
- apiKeySecretRef
|
||||
- email
|
||||
properties:
|
||||
apiKeySecretRef:
|
||||
@@ -263,6 +262,21 @@ spec:
|
||||
TODO: Add other useful fields. apiVersion, kind,
|
||||
uid?'
|
||||
type: string
|
||||
apiTokenSecretRef:
|
||||
type: object
|
||||
required:
|
||||
- name
|
||||
properties:
|
||||
key:
|
||||
description: The key of the secret to select from.
|
||||
Must be a valid secret key.
|
||||
type: string
|
||||
name:
|
||||
description: 'Name of the referent. More info:
|
||||
https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||
TODO: Add other useful fields. apiVersion, kind,
|
||||
uid?'
|
||||
type: string
|
||||
email:
|
||||
type: string
|
||||
cnameStrategy:
|
||||
|
||||
@@ -675,7 +675,6 @@ spec:
|
||||
containing the DNS configuration for Cloudflare
|
||||
type: object
|
||||
required:
|
||||
- apiKeySecretRef
|
||||
- email
|
||||
properties:
|
||||
apiKeySecretRef:
|
||||
@@ -691,6 +690,19 @@ spec:
|
||||
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||
TODO: Add other useful fields. apiVersion, kind, uid?'
|
||||
type: string
|
||||
apiTokenSecretRef:
|
||||
type: object
|
||||
required:
|
||||
- name
|
||||
properties:
|
||||
key:
|
||||
description: The key of the secret to select from. Must
|
||||
be a valid secret key.
|
||||
type: string
|
||||
name:
|
||||
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||
TODO: Add other useful fields. apiVersion, kind, uid?'
|
||||
type: string
|
||||
email:
|
||||
type: string
|
||||
cnameStrategy:
|
||||
@@ -2060,7 +2072,6 @@ spec:
|
||||
containing the DNS configuration for Cloudflare
|
||||
type: object
|
||||
required:
|
||||
- apiKeySecretRef
|
||||
- email
|
||||
properties:
|
||||
apiKeySecretRef:
|
||||
@@ -2078,6 +2089,21 @@ spec:
|
||||
TODO: Add other useful fields. apiVersion, kind,
|
||||
uid?'
|
||||
type: string
|
||||
apiTokenSecretRef:
|
||||
type: object
|
||||
required:
|
||||
- name
|
||||
properties:
|
||||
key:
|
||||
description: The key of the secret to select from.
|
||||
Must be a valid secret key.
|
||||
type: string
|
||||
name:
|
||||
description: 'Name of the referent. More info:
|
||||
https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||
TODO: Add other useful fields. apiVersion, kind,
|
||||
uid?'
|
||||
type: string
|
||||
email:
|
||||
type: string
|
||||
cnameStrategy:
|
||||
@@ -3744,7 +3770,6 @@ spec:
|
||||
containing the DNS configuration for Cloudflare
|
||||
type: object
|
||||
required:
|
||||
- apiKeySecretRef
|
||||
- email
|
||||
properties:
|
||||
apiKeySecretRef:
|
||||
@@ -3762,6 +3787,21 @@ spec:
|
||||
TODO: Add other useful fields. apiVersion, kind,
|
||||
uid?'
|
||||
type: string
|
||||
apiTokenSecretRef:
|
||||
type: object
|
||||
required:
|
||||
- name
|
||||
properties:
|
||||
key:
|
||||
description: The key of the secret to select from.
|
||||
Must be a valid secret key.
|
||||
type: string
|
||||
name:
|
||||
description: 'Name of the referent. More info:
|
||||
https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||
TODO: Add other useful fields. apiVersion, kind,
|
||||
uid?'
|
||||
type: string
|
||||
email:
|
||||
type: string
|
||||
cnameStrategy:
|
||||
|
||||
@@ -242,8 +242,9 @@ type ACMEIssuerDNS01ProviderCloudDNS struct {
|
||||
// ACMEIssuerDNS01ProviderCloudflare is a structure containing the DNS
|
||||
// configuration for Cloudflare
|
||||
type ACMEIssuerDNS01ProviderCloudflare struct {
|
||||
Email string `json:"email"`
|
||||
APIKey cmmeta.SecretKeySelector `json:"apiKeySecretRef"`
|
||||
Email string `json:"email"`
|
||||
APIKey *cmmeta.SecretKeySelector `json:"apiKeySecretRef,omitempty"`
|
||||
APIToken *cmmeta.SecretKeySelector `json:"apiTokenSecretRef,omitempty"`
|
||||
}
|
||||
|
||||
// ACMEIssuerDNS01ProviderDigitalOcean is a structure containing the DNS
|
||||
|
||||
@@ -116,7 +116,7 @@ func (in *ACMEChallengeSolverDNS01) DeepCopyInto(out *ACMEChallengeSolverDNS01)
|
||||
if in.Cloudflare != nil {
|
||||
in, out := &in.Cloudflare, &out.Cloudflare
|
||||
*out = new(ACMEIssuerDNS01ProviderCloudflare)
|
||||
**out = **in
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.Route53 != nil {
|
||||
in, out := &in.Route53, &out.Route53
|
||||
@@ -392,7 +392,16 @@ func (in *ACMEIssuerDNS01ProviderCloudDNS) DeepCopy() *ACMEIssuerDNS01ProviderCl
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ACMEIssuerDNS01ProviderCloudflare) DeepCopyInto(out *ACMEIssuerDNS01ProviderCloudflare) {
|
||||
*out = *in
|
||||
out.APIKey = in.APIKey
|
||||
if in.APIKey != nil {
|
||||
in, out := &in.APIKey, &out.APIKey
|
||||
*out = new(metav1.SecretKeySelector)
|
||||
**out = **in
|
||||
}
|
||||
if in.APIToken != nil {
|
||||
in, out := &in.APIToken, &out.APIToken
|
||||
*out = new(metav1.SecretKeySelector)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -242,8 +242,9 @@ type ACMEIssuerDNS01ProviderCloudDNS struct {
|
||||
// ACMEIssuerDNS01ProviderCloudflare is a structure containing the DNS
|
||||
// configuration for Cloudflare
|
||||
type ACMEIssuerDNS01ProviderCloudflare struct {
|
||||
Email string `json:"email"`
|
||||
APIKey cmmeta.SecretKeySelector `json:"apiKeySecretRef"`
|
||||
Email string `json:"email"`
|
||||
APIKey *cmmeta.SecretKeySelector `json:"apiKeySecretRef,omitempty"`
|
||||
APIToken *cmmeta.SecretKeySelector `json:"apiTokenSecretRef,omitempty"`
|
||||
}
|
||||
|
||||
// ACMEIssuerDNS01ProviderDigitalOcean is a structure containing the DNS
|
||||
|
||||
@@ -736,10 +736,8 @@ func Convert_acme_ACMEIssuerDNS01ProviderCloudDNS_To_v1alpha2_ACMEIssuerDNS01Pro
|
||||
|
||||
func autoConvert_v1alpha2_ACMEIssuerDNS01ProviderCloudflare_To_acme_ACMEIssuerDNS01ProviderCloudflare(in *v1alpha2.ACMEIssuerDNS01ProviderCloudflare, out *acme.ACMEIssuerDNS01ProviderCloudflare, s conversion.Scope) error {
|
||||
out.Email = in.Email
|
||||
// TODO: Inefficient conversion - can we improve it?
|
||||
if err := s.Convert(&in.APIKey, &out.APIKey, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
out.APIKey = (*meta.SecretKeySelector)(unsafe.Pointer(in.APIKey))
|
||||
out.APIToken = (*meta.SecretKeySelector)(unsafe.Pointer(in.APIToken))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -750,10 +748,8 @@ func Convert_v1alpha2_ACMEIssuerDNS01ProviderCloudflare_To_acme_ACMEIssuerDNS01P
|
||||
|
||||
func autoConvert_acme_ACMEIssuerDNS01ProviderCloudflare_To_v1alpha2_ACMEIssuerDNS01ProviderCloudflare(in *acme.ACMEIssuerDNS01ProviderCloudflare, out *v1alpha2.ACMEIssuerDNS01ProviderCloudflare, s conversion.Scope) error {
|
||||
out.Email = in.Email
|
||||
// TODO: Inefficient conversion - can we improve it?
|
||||
if err := s.Convert(&in.APIKey, &out.APIKey, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
out.APIKey = (*metav1.SecretKeySelector)(unsafe.Pointer(in.APIKey))
|
||||
out.APIToken = (*metav1.SecretKeySelector)(unsafe.Pointer(in.APIToken))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -116,7 +116,7 @@ func (in *ACMEChallengeSolverDNS01) DeepCopyInto(out *ACMEChallengeSolverDNS01)
|
||||
if in.Cloudflare != nil {
|
||||
in, out := &in.Cloudflare, &out.Cloudflare
|
||||
*out = new(ACMEIssuerDNS01ProviderCloudflare)
|
||||
**out = **in
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.Route53 != nil {
|
||||
in, out := &in.Route53, &out.Route53
|
||||
@@ -392,7 +392,16 @@ func (in *ACMEIssuerDNS01ProviderCloudDNS) DeepCopy() *ACMEIssuerDNS01ProviderCl
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ACMEIssuerDNS01ProviderCloudflare) DeepCopyInto(out *ACMEIssuerDNS01ProviderCloudflare) {
|
||||
*out = *in
|
||||
out.APIKey = in.APIKey
|
||||
if in.APIKey != nil {
|
||||
in, out := &in.APIKey, &out.APIKey
|
||||
*out = new(meta.SecretKeySelector)
|
||||
**out = **in
|
||||
}
|
||||
if in.APIToken != nil {
|
||||
in, out := &in.APIToken, &out.APIToken
|
||||
*out = new(meta.SecretKeySelector)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -280,7 +280,18 @@ func ValidateACMEChallengeSolverDNS01(p *cmacme.ACMEChallengeSolverDNS01, fldPat
|
||||
el = append(el, field.Forbidden(fldPath.Child("cloudflare"), "may not specify more than one provider type"))
|
||||
} else {
|
||||
numProviders++
|
||||
el = append(el, ValidateSecretKeySelector(&p.Cloudflare.APIKey, fldPath.Child("cloudflare", "apiKeySecretRef"))...)
|
||||
if p.Cloudflare.APIKey != nil {
|
||||
el = append(el, ValidateSecretKeySelector(p.Cloudflare.APIKey, fldPath.Child("cloudflare", "apiKeySecretRef"))...)
|
||||
}
|
||||
if p.Cloudflare.APIToken != nil {
|
||||
el = append(el, ValidateSecretKeySelector(p.Cloudflare.APIToken, fldPath.Child("cloudflare", "apiTokenSecretRef"))...)
|
||||
}
|
||||
if p.Cloudflare.APIKey != nil && p.Cloudflare.APIToken != nil {
|
||||
el = append(el, field.Forbidden(fldPath.Child("cloudflare"), "apiKeySecretRef and apiTokenSecretRef cannot both be specified"))
|
||||
}
|
||||
if p.Cloudflare.APIKey == nil && p.Cloudflare.APIToken == nil {
|
||||
el = append(el, field.Required(fldPath.Child("cloudflare"), "apiKeySecretRef or apiTokenSecretRef is required"))
|
||||
}
|
||||
if len(p.Cloudflare.Email) == 0 {
|
||||
el = append(el, field.Required(fldPath.Child("cloudflare", "email"), ""))
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ var (
|
||||
Key: "validkey",
|
||||
}
|
||||
validCloudflareProvider = cmacme.ACMEIssuerDNS01ProviderCloudflare{
|
||||
APIKey: validSecretKeyRef,
|
||||
APIKey: &validSecretKeyRef,
|
||||
Email: "valid",
|
||||
}
|
||||
validACMEIssuer = cmacme.ACMEIssuer{
|
||||
@@ -494,10 +494,11 @@ func TestValidateACMEIssuerDNS01Config(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
"missing cloudflare token": {
|
||||
"missing cloudflare api key fields": {
|
||||
cfg: &cmacme.ACMEChallengeSolverDNS01{
|
||||
Cloudflare: &cmacme.ACMEIssuerDNS01ProviderCloudflare{
|
||||
Email: "valid",
|
||||
Email: "valid",
|
||||
APIKey: &cmmeta.SecretKeySelector{},
|
||||
},
|
||||
},
|
||||
errs: []*field.Error{
|
||||
@@ -505,10 +506,44 @@ func TestValidateACMEIssuerDNS01Config(t *testing.T) {
|
||||
field.Required(fldPath.Child("cloudflare", "apiKeySecretRef", "key"), "secret key is required"),
|
||||
},
|
||||
},
|
||||
"missing cloudflare api token fields": {
|
||||
cfg: &cmacme.ACMEChallengeSolverDNS01{
|
||||
Cloudflare: &cmacme.ACMEIssuerDNS01ProviderCloudflare{
|
||||
Email: "valid",
|
||||
APIToken: &cmmeta.SecretKeySelector{},
|
||||
},
|
||||
},
|
||||
errs: []*field.Error{
|
||||
field.Required(fldPath.Child("cloudflare", "apiTokenSecretRef", "name"), "secret name is required"),
|
||||
field.Required(fldPath.Child("cloudflare", "apiTokenSecretRef", "key"), "secret key is required"),
|
||||
},
|
||||
},
|
||||
"missing cloudflare api token or key": {
|
||||
cfg: &cmacme.ACMEChallengeSolverDNS01{
|
||||
Cloudflare: &cmacme.ACMEIssuerDNS01ProviderCloudflare{
|
||||
Email: "valid",
|
||||
},
|
||||
},
|
||||
errs: []*field.Error{
|
||||
field.Required(fldPath.Child("cloudflare"), "apiKeySecretRef or apiTokenSecretRef is required"),
|
||||
},
|
||||
},
|
||||
"both cloudflare api token and key specified": {
|
||||
cfg: &cmacme.ACMEChallengeSolverDNS01{
|
||||
Cloudflare: &cmacme.ACMEIssuerDNS01ProviderCloudflare{
|
||||
Email: "valid",
|
||||
APIToken: &validSecretKeyRef,
|
||||
APIKey: &validSecretKeyRef,
|
||||
},
|
||||
},
|
||||
errs: []*field.Error{
|
||||
field.Forbidden(fldPath.Child("cloudflare"), "apiKeySecretRef and apiTokenSecretRef cannot both be specified"),
|
||||
},
|
||||
},
|
||||
"missing cloudflare email": {
|
||||
cfg: &cmacme.ACMEChallengeSolverDNS01{
|
||||
Cloudflare: &cmacme.ACMEIssuerDNS01ProviderCloudflare{
|
||||
APIKey: validSecretKeyRef,
|
||||
APIKey: &validSecretKeyRef,
|
||||
},
|
||||
},
|
||||
errs: []*field.Error{
|
||||
|
||||
@@ -33,6 +33,7 @@ type DNSProvider struct {
|
||||
dns01Nameservers []string
|
||||
authEmail string
|
||||
authKey string
|
||||
authToken string
|
||||
}
|
||||
|
||||
// NewDNSProvider returns a DNSProvider instance configured for cloudflare.
|
||||
@@ -41,15 +42,18 @@ type DNSProvider struct {
|
||||
func NewDNSProvider(dns01Nameservers []string) (*DNSProvider, error) {
|
||||
email := os.Getenv("CLOUDFLARE_EMAIL")
|
||||
key := os.Getenv("CLOUDFLARE_API_KEY")
|
||||
return NewDNSProviderCredentials(email, key, dns01Nameservers)
|
||||
return NewDNSProviderCredentials(email, key, "", dns01Nameservers)
|
||||
}
|
||||
|
||||
// NewDNSProviderCredentials uses the supplied credentials to return a
|
||||
// DNSProvider instance configured for cloudflare.
|
||||
func NewDNSProviderCredentials(email, key string, dns01Nameservers []string) (*DNSProvider, error) {
|
||||
if email == "" || key == "" {
|
||||
func NewDNSProviderCredentials(email, key, token string, dns01Nameservers []string) (*DNSProvider, error) {
|
||||
if email == "" || (key == "" && token == "") {
|
||||
return nil, fmt.Errorf("CloudFlare credentials missing")
|
||||
}
|
||||
if key != "" && token != "" {
|
||||
return nil, fmt.Errorf("CloudFlare key and token are both present")
|
||||
}
|
||||
// cloudflare uses X-Auth-Key as a header for its
|
||||
// authentication. However, if it's an invalid value, the go
|
||||
// http library will "helpfully" print out the value to help with
|
||||
@@ -60,9 +64,14 @@ func NewDNSProviderCredentials(email, key string, dns01Nameservers []string) (*D
|
||||
return nil, fmt.Errorf("Cloudflare key invalid (does the key contain a newline?)")
|
||||
}
|
||||
|
||||
if !validHeaderFieldValue(token) {
|
||||
return nil, fmt.Errorf("Cloudflare token invalid (does the token contain a newline?)")
|
||||
}
|
||||
|
||||
return &DNSProvider{
|
||||
authEmail: email,
|
||||
authKey: key,
|
||||
authToken: token,
|
||||
dns01Nameservers: dns01Nameservers,
|
||||
}, nil
|
||||
}
|
||||
@@ -213,7 +222,11 @@ func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawM
|
||||
}
|
||||
|
||||
req.Header.Set("X-Auth-Email", c.authEmail)
|
||||
req.Header.Set("X-Auth-Key", c.authKey)
|
||||
if c.authToken != "" {
|
||||
req.Header.Set("Authorization", "Bearer "+c.authToken)
|
||||
} else {
|
||||
req.Header.Set("X-Auth-Key", c.authKey)
|
||||
}
|
||||
req.Header.Set("User-Agent", pkgutil.CertManagerUserAgent)
|
||||
|
||||
client := http.Client{
|
||||
|
||||
@@ -21,6 +21,7 @@ var (
|
||||
cflareLiveTest bool
|
||||
cflareEmail string
|
||||
cflareAPIKey string
|
||||
cflareAPIToken string
|
||||
cflareDomain string
|
||||
)
|
||||
|
||||
@@ -38,15 +39,31 @@ func restoreCloudFlareEnv() {
|
||||
os.Setenv("CLOUDFLARE_API_KEY", cflareAPIKey)
|
||||
}
|
||||
|
||||
func TestNewDNSProviderValid(t *testing.T) {
|
||||
func TestNewDNSProviderValidAPIKey(t *testing.T) {
|
||||
os.Setenv("CLOUDFLARE_EMAIL", "")
|
||||
os.Setenv("CLOUDFLARE_API_KEY", "")
|
||||
_, err := NewDNSProviderCredentials("123", "123", util.RecursiveNameservers)
|
||||
_, err := NewDNSProviderCredentials("123", "123", "", util.RecursiveNameservers)
|
||||
assert.NoError(t, err)
|
||||
restoreCloudFlareEnv()
|
||||
}
|
||||
|
||||
func TestNewDNSProviderValidEnv(t *testing.T) {
|
||||
func TestNewDNSProviderValidAPIToken(t *testing.T) {
|
||||
os.Setenv("CLOUDFLARE_EMAIL", "")
|
||||
os.Setenv("CLOUDFLARE_API_KEY", "")
|
||||
_, err := NewDNSProviderCredentials("123", "", "123", util.RecursiveNameservers)
|
||||
assert.NoError(t, err)
|
||||
restoreCloudFlareEnv()
|
||||
}
|
||||
|
||||
func TestNewDNSProviderKeyAndTokenProvided(t *testing.T) {
|
||||
os.Setenv("CLOUDFLARE_EMAIL", "")
|
||||
os.Setenv("CLOUDFLARE_API_KEY", "")
|
||||
_, err := NewDNSProviderCredentials("123", "123", "123", util.RecursiveNameservers)
|
||||
assert.EqualError(t, err, "CloudFlare key and token are both present")
|
||||
restoreCloudFlareEnv()
|
||||
}
|
||||
|
||||
func TestNewDNSProviderValidApiKeyEnv(t *testing.T) {
|
||||
os.Setenv("CLOUDFLARE_EMAIL", "test@example.com")
|
||||
os.Setenv("CLOUDFLARE_API_KEY", "123")
|
||||
_, err := NewDNSProvider(util.RecursiveNameservers)
|
||||
@@ -67,7 +84,7 @@ func TestCloudFlarePresent(t *testing.T) {
|
||||
t.Skip("skipping live test")
|
||||
}
|
||||
|
||||
provider, err := NewDNSProviderCredentials(cflareEmail, cflareAPIKey, util.RecursiveNameservers)
|
||||
provider, err := NewDNSProviderCredentials(cflareEmail, cflareAPIKey, cflareAPIToken, util.RecursiveNameservers)
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = provider.Present(cflareDomain, "_acme-challenge."+cflareDomain+".", "123d==")
|
||||
@@ -81,7 +98,7 @@ func TestCloudFlareCleanUp(t *testing.T) {
|
||||
|
||||
time.Sleep(time.Second * 2)
|
||||
|
||||
provider, err := NewDNSProviderCredentials(cflareEmail, cflareAPIKey, util.RecursiveNameservers)
|
||||
provider, err := NewDNSProviderCredentials(cflareEmail, cflareAPIKey, cflareAPIToken, util.RecursiveNameservers)
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = provider.CleanUp(cflareDomain, "_acme-challenge."+cflareDomain+".", "123d==")
|
||||
|
||||
@@ -62,7 +62,7 @@ type solver interface {
|
||||
// constructors may be set.
|
||||
type dnsProviderConstructors struct {
|
||||
cloudDNS func(project string, serviceAccount []byte, dns01Nameservers []string, ambient bool) (*clouddns.DNSProvider, error)
|
||||
cloudFlare func(email, apikey string, dns01Nameservers []string) (*cloudflare.DNSProvider, error)
|
||||
cloudFlare func(email, apikey, apiToken string, dns01Nameservers []string) (*cloudflare.DNSProvider, error)
|
||||
route53 func(accessKey, secretKey, hostedZoneID, region, role string, ambient bool, dns01Nameservers []string) (*route53.DNSProvider, error)
|
||||
azureDNS func(environment, clientID, clientSecret, subscriptionID, tenantID, resourceGroupName, hostedZoneName string, dns01Nameservers []string) (*azuredns.DNSProvider, error)
|
||||
acmeDNS func(host string, accountJson []byte, dns01Nameservers []string) (*acmedns.DNSProvider, error)
|
||||
@@ -252,15 +252,38 @@ func (s *Solver) solverForChallenge(ctx context.Context, issuer v1alpha2.Generic
|
||||
}
|
||||
case providerConfig.Cloudflare != nil:
|
||||
dbg.Info("preparing to create Cloudflare provider")
|
||||
apiKeySecret, err := s.secretLister.Secrets(resourceNamespace).Get(providerConfig.Cloudflare.APIKey.Name)
|
||||
if providerConfig.Cloudflare.APIKey != nil && providerConfig.Cloudflare.APIToken != nil {
|
||||
return nil, nil, fmt.Errorf("API key and API token secret references are both present")
|
||||
}
|
||||
|
||||
var saSecretName, saSecretKey string
|
||||
if providerConfig.Cloudflare.APIKey != nil {
|
||||
saSecretName = providerConfig.Cloudflare.APIKey.Name
|
||||
saSecretKey = providerConfig.Cloudflare.APIKey.Key
|
||||
} else {
|
||||
saSecretName = providerConfig.Cloudflare.APIToken.Name
|
||||
saSecretKey = providerConfig.Cloudflare.APIToken.Key
|
||||
}
|
||||
|
||||
saSecret, err := s.secretLister.Secrets(resourceNamespace).Get(saSecretName)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("error getting cloudflare service account: %s", err)
|
||||
return nil, nil, fmt.Errorf("error getting cloudflare secret: %s", err)
|
||||
}
|
||||
|
||||
keyData, ok := saSecret.Data[saSecretKey]
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("specfied key %q not found in secret %s/%s", saSecretKey, saSecret.Namespace, saSecret.Name)
|
||||
}
|
||||
|
||||
var apiKey, apiToken string
|
||||
if providerConfig.Cloudflare.APIKey != nil {
|
||||
apiKey = string(keyData)
|
||||
} else {
|
||||
apiToken = string(keyData)
|
||||
}
|
||||
|
||||
email := providerConfig.Cloudflare.Email
|
||||
apiKey := string(apiKeySecret.Data[providerConfig.Cloudflare.APIKey.Key])
|
||||
|
||||
impl, err = s.dnsProviderConstructors.cloudFlare(email, apiKey, s.DNS01Nameservers)
|
||||
impl, err = s.dnsProviderConstructors.cloudFlare(email, apiKey, apiToken, s.DNS01Nameservers)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("error instantiating cloudflare challenge solver: %s", err)
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ func TestSolverFor(t *testing.T) {
|
||||
expectedSolverType reflect.Type
|
||||
}
|
||||
tests := map[string]testT{
|
||||
"loads secret for cloudflare provider": {
|
||||
"loads api key for cloudflare provider": {
|
||||
solverFixture: &solverFixture{
|
||||
Builder: &test.Builder{
|
||||
KubeObjects: []runtime.Object{
|
||||
@@ -82,7 +82,7 @@ func TestSolverFor(t *testing.T) {
|
||||
DNS01: &cmacme.ACMEChallengeSolverDNS01{
|
||||
Cloudflare: &cmacme.ACMEIssuerDNS01ProviderCloudflare{
|
||||
Email: "test",
|
||||
APIKey: cmmeta.SecretKeySelector{
|
||||
APIKey: &cmmeta.SecretKeySelector{
|
||||
LocalObjectReference: cmmeta.LocalObjectReference{
|
||||
Name: "cloudflare-key",
|
||||
},
|
||||
@@ -97,6 +97,37 @@ func TestSolverFor(t *testing.T) {
|
||||
domain: "example.com",
|
||||
expectedSolverType: reflect.TypeOf(&cloudflare.DNSProvider{}),
|
||||
},
|
||||
"loads api token for cloudflare provider": {
|
||||
solverFixture: &solverFixture{
|
||||
Builder: &test.Builder{
|
||||
KubeObjects: []runtime.Object{
|
||||
newSecret("cloudflare-token", "default", map[string][]byte{
|
||||
"api-token": []byte("a-cloudflare-api-token"),
|
||||
}),
|
||||
},
|
||||
},
|
||||
Issuer: newIssuer("test", "default"),
|
||||
Challenge: &cmacme.Challenge{
|
||||
Spec: cmacme.ChallengeSpec{
|
||||
Solver: &cmacme.ACMEChallengeSolver{
|
||||
DNS01: &cmacme.ACMEChallengeSolverDNS01{
|
||||
Cloudflare: &cmacme.ACMEIssuerDNS01ProviderCloudflare{
|
||||
Email: "test",
|
||||
APIToken: &cmmeta.SecretKeySelector{
|
||||
LocalObjectReference: cmmeta.LocalObjectReference{
|
||||
Name: "cloudflare-token",
|
||||
},
|
||||
Key: "api-token",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
domain: "example.com",
|
||||
expectedSolverType: reflect.TypeOf(&cloudflare.DNSProvider{}),
|
||||
},
|
||||
"fails to load a cloudflare provider with a missing secret": {
|
||||
solverFixture: &solverFixture{
|
||||
Issuer: newIssuer("test", "default"),
|
||||
@@ -107,7 +138,38 @@ func TestSolverFor(t *testing.T) {
|
||||
DNS01: &cmacme.ACMEChallengeSolverDNS01{
|
||||
Cloudflare: &cmacme.ACMEIssuerDNS01ProviderCloudflare{
|
||||
Email: "test",
|
||||
APIKey: cmmeta.SecretKeySelector{
|
||||
APIToken: &cmmeta.SecretKeySelector{
|
||||
LocalObjectReference: cmmeta.LocalObjectReference{
|
||||
Name: "cloudflare-token",
|
||||
},
|
||||
Key: "api-token",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
domain: "example.com",
|
||||
expectErr: true,
|
||||
},
|
||||
"fails to load a cloudflare provider when key and token are provided": {
|
||||
solverFixture: &solverFixture{
|
||||
Issuer: newIssuer("test", "default"),
|
||||
// don't include any secrets in the lister
|
||||
Challenge: &cmacme.Challenge{
|
||||
Spec: cmacme.ChallengeSpec{
|
||||
Solver: &cmacme.ACMEChallengeSolver{
|
||||
DNS01: &cmacme.ACMEChallengeSolverDNS01{
|
||||
Cloudflare: &cmacme.ACMEIssuerDNS01ProviderCloudflare{
|
||||
Email: "test",
|
||||
APIToken: &cmmeta.SecretKeySelector{
|
||||
LocalObjectReference: cmmeta.LocalObjectReference{
|
||||
Name: "cloudflare-token",
|
||||
},
|
||||
Key: "api-token",
|
||||
},
|
||||
APIKey: &cmmeta.SecretKeySelector{
|
||||
LocalObjectReference: cmmeta.LocalObjectReference{
|
||||
Name: "cloudflare-key",
|
||||
},
|
||||
@@ -122,7 +184,7 @@ func TestSolverFor(t *testing.T) {
|
||||
domain: "example.com",
|
||||
expectErr: true,
|
||||
},
|
||||
"fails to load a cloudflare provider with an invalid secret": {
|
||||
"fails to load a cloudflare provider with an invalid key secret": {
|
||||
solverFixture: &solverFixture{
|
||||
Builder: &test.Builder{
|
||||
KubeObjects: []runtime.Object{
|
||||
@@ -138,7 +200,7 @@ func TestSolverFor(t *testing.T) {
|
||||
DNS01: &cmacme.ACMEChallengeSolverDNS01{
|
||||
Cloudflare: &cmacme.ACMEIssuerDNS01ProviderCloudflare{
|
||||
Email: "test",
|
||||
APIKey: cmmeta.SecretKeySelector{
|
||||
APIKey: &cmmeta.SecretKeySelector{
|
||||
LocalObjectReference: cmmeta.LocalObjectReference{
|
||||
Name: "cloudflare-key",
|
||||
},
|
||||
@@ -153,6 +215,37 @@ func TestSolverFor(t *testing.T) {
|
||||
domain: "example.com",
|
||||
expectErr: true,
|
||||
},
|
||||
"fails to load a cloudflare provider with an invalid token secret": {
|
||||
solverFixture: &solverFixture{
|
||||
Builder: &test.Builder{
|
||||
KubeObjects: []runtime.Object{
|
||||
newSecret("cloudflare-token", "default", map[string][]byte{
|
||||
"api-key-oops": []byte("a-cloudflare-api-token"),
|
||||
}),
|
||||
},
|
||||
},
|
||||
Issuer: newIssuer("test", "default"),
|
||||
Challenge: &cmacme.Challenge{
|
||||
Spec: cmacme.ChallengeSpec{
|
||||
Solver: &cmacme.ACMEChallengeSolver{
|
||||
DNS01: &cmacme.ACMEChallengeSolverDNS01{
|
||||
Cloudflare: &cmacme.ACMEIssuerDNS01ProviderCloudflare{
|
||||
Email: "test",
|
||||
APIToken: &cmmeta.SecretKeySelector{
|
||||
LocalObjectReference: cmmeta.LocalObjectReference{
|
||||
Name: "cloudflare-token",
|
||||
},
|
||||
Key: "api-token",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
domain: "example.com",
|
||||
expectErr: true,
|
||||
},
|
||||
"loads json for acmedns provider": {
|
||||
solverFixture: &solverFixture{
|
||||
Builder: &test.Builder{
|
||||
|
||||
@@ -139,10 +139,10 @@ func newFakeDNSProviders() *fakeDNSProviders {
|
||||
f.call("clouddns", project, serviceAccount, util.RecursiveNameservers, ambient)
|
||||
return nil, nil
|
||||
},
|
||||
cloudFlare: func(email, apikey string, dns01Nameservers []string) (*cloudflare.DNSProvider, error) {
|
||||
f.call("cloudflare", email, apikey, util.RecursiveNameservers)
|
||||
if email == "" || apikey == "" {
|
||||
return nil, errors.New("invalid email or apikey")
|
||||
cloudFlare: func(email, apikey, apiToken string, dns01Nameservers []string) (*cloudflare.DNSProvider, error) {
|
||||
f.call("cloudflare", email, apikey, apiToken, util.RecursiveNameservers)
|
||||
if email == "" || (apikey == "" && apiToken == "") {
|
||||
return nil, errors.New("invalid email or apikey or apitoken")
|
||||
}
|
||||
return nil, nil
|
||||
},
|
||||
|
||||
@@ -86,7 +86,7 @@ func (b *Cloudflare) Provision() error {
|
||||
b.details.ProviderConfig = cmacme.ACMEChallengeSolverDNS01{
|
||||
Cloudflare: &cmacme.ACMEIssuerDNS01ProviderCloudflare{
|
||||
Email: b.cf.Email,
|
||||
APIKey: cmmeta.SecretKeySelector{
|
||||
APIKey: &cmmeta.SecretKeySelector{
|
||||
LocalObjectReference: cmmeta.LocalObjectReference{
|
||||
Name: b.createdSecret.Name,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user