diff --git a/README.md b/README.md index c384631f5..3b2e82bfd 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,8 @@ spec: # Name of a secret used to store the ACME account private key privateKeySecretRef: name: letsencrypt-staging + # Enable the HTTP-01 challenge provider + http-01: {} # ACME dns-01 provider configurations dns-01: # Here we define a list of DNS-01 providers that can solve DNS challenges diff --git a/docs/api-types/issuer/spec.md b/docs/api-types/issuer/spec.md index d2b4c11ea..85a3c25bb 100644 --- a/docs/api-types/issuer/spec.md +++ b/docs/api-types/issuer/spec.md @@ -34,11 +34,25 @@ configuring credentials for a DNS provider. ### ACME issuer HTTP01 configuration -The ACME issuer does not require any additional configuration in order to -support HTTP01 challenge validation. All valid ACME issuers are able to issue -certificates validated with HTTP01 by creating or manipulating Ingress -resources in the Kubernetes API server. The installed ingress controller will -then configure routes to solve ACME challenges automatically. +In order to allow HTTP01 challenges to be solved, we must enable the HTTP01 +challenge provider on our Issuer resource. This can be done through setting the +`http-01` field on the `issuer.spec.acme` stanza. Cert-manager will then create +and manage Ingress rules in the Kubernetes API server in order to solve HTTP-01 +based challenges. + +```yaml +apiVersion: certmanager.k8s.io +kind: Issuer +metadata: + name: example-issuer +spec: + acme: + email: user@example.com + server: https://acme-staging.api.letsencrypt.org/directory + privateKeySecretRef: + name: example-issuer-account-key + http-01: {} +``` ### ACME issuer with no configured DNS providers diff --git a/docs/examples/acme-issuer.yaml b/docs/examples/acme-issuer.yaml index 7d7bcdec5..6384e83a1 100644 --- a/docs/examples/acme-issuer.yaml +++ b/docs/examples/acme-issuer.yaml @@ -11,6 +11,8 @@ spec: # Name of a secret used to store the ACME account private key privateKeySecretRef: name: letsncrypt-prod + # Enable the HTTP-01 challenge provider + http-01: {} # ACME dns-01 provider configurations dns-01: # Here we define a list of DNS-01 providers that can solve DNS challenges diff --git a/pkg/apis/certmanager/v1alpha1/types.go b/pkg/apis/certmanager/v1alpha1/types.go index f776e1eb9..9604ad2e0 100644 --- a/pkg/apis/certmanager/v1alpha1/types.go +++ b/pkg/apis/certmanager/v1alpha1/types.go @@ -93,13 +93,17 @@ type ACMEIssuer struct { // PrivateKey is the name of a secret containing the private key for this // user account. PrivateKey SecretKeySelector `json:"privateKeySecretRef"` + // HTTP01 config + HTTP01 *ACMEIssuerHTTP01Config `json:"http-01"` // DNS-01 config DNS01 *ACMEIssuerDNS01Config `json:"dns-01"` } +type ACMEIssuerHTTP01Config struct { +} + // ACMEIssuerDNS01Config is a structure containing the ACME DNS configuration -// option. One and only one of the fields within it should be set, when the -// ACME challenge type is set to dns-01 +// options type ACMEIssuerDNS01Config struct { Providers []ACMEIssuerDNS01Provider `json:"providers"` } diff --git a/pkg/apis/certmanager/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/certmanager/v1alpha1/zz_generated.deepcopy.go index a3473a8ec..51fdcebc9 100644 --- a/pkg/apis/certmanager/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/certmanager/v1alpha1/zz_generated.deepcopy.go @@ -80,6 +80,10 @@ func RegisterDeepCopies(scheme *runtime.Scheme) error { in.(*ACMEIssuerDNS01ProviderRoute53).DeepCopyInto(out.(*ACMEIssuerDNS01ProviderRoute53)) return nil }, InType: reflect.TypeOf(&ACMEIssuerDNS01ProviderRoute53{})}, + conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error { + in.(*ACMEIssuerHTTP01Config).DeepCopyInto(out.(*ACMEIssuerHTTP01Config)) + return nil + }, InType: reflect.TypeOf(&ACMEIssuerHTTP01Config{})}, conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error { in.(*ACMEIssuerStatus).DeepCopyInto(out.(*ACMEIssuerStatus)) return nil @@ -282,6 +286,15 @@ func (in *ACMEDomainAuthorization) DeepCopy() *ACMEDomainAuthorization { func (in *ACMEIssuer) DeepCopyInto(out *ACMEIssuer) { *out = *in out.PrivateKey = in.PrivateKey + if in.HTTP01 != nil { + in, out := &in.HTTP01, &out.HTTP01 + if *in == nil { + *out = nil + } else { + *out = new(ACMEIssuerHTTP01Config) + **out = **in + } + } if in.DNS01 != nil { in, out := &in.DNS01, &out.DNS01 if *in == nil { @@ -421,6 +434,22 @@ func (in *ACMEIssuerDNS01ProviderRoute53) DeepCopy() *ACMEIssuerDNS01ProviderRou return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ACMEIssuerHTTP01Config) DeepCopyInto(out *ACMEIssuerHTTP01Config) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ACMEIssuerHTTP01Config. +func (in *ACMEIssuerHTTP01Config) DeepCopy() *ACMEIssuerHTTP01Config { + if in == nil { + return nil + } + out := new(ACMEIssuerHTTP01Config) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ACMEIssuerStatus) DeepCopyInto(out *ACMEIssuerStatus) { *out = *in diff --git a/pkg/issuer/acme/prepare.go b/pkg/issuer/acme/prepare.go index 54589f8f2..b886735a3 100644 --- a/pkg/issuer/acme/prepare.go +++ b/pkg/issuer/acme/prepare.go @@ -134,7 +134,7 @@ func keyForChallenge(cl *acme.Client, challenge *acme.Challenge) (string, error) func (a *Acme) authorize(ctx context.Context, cl *acme.Client, crt *v1alpha1.Certificate, auth authResponse) (*acme.Authorization, error) { glog.V(4).Infof("picking challenge type for domain %q", auth.domain) - challengeType, err := pickChallengeType(auth.domain, auth.auth, crt.Spec.ACME.Config) + challengeType, err := a.pickChallengeType(auth.domain, auth.auth, crt.Spec.ACME.Config) if err != nil { return nil, fmt.Errorf("error picking challenge type to use for domain '%s': %s", auth.domain, err.Error()) } @@ -274,15 +274,15 @@ func getAuthorizations(ctx context.Context, cl *acme.Client, domains ...string) return responses, authResponses(responses).Error() } -func pickChallengeType(domain string, auth *acme.Authorization, cfg []v1alpha1.ACMECertificateDomainConfig) (string, error) { +func (a *Acme) pickChallengeType(domain string, auth *acme.Authorization, cfg []v1alpha1.ACMECertificateDomainConfig) (string, error) { for _, d := range cfg { for _, dom := range d.Domains { if dom == domain { for _, challenge := range auth.Challenges { switch { - case challenge.Type == "http-01" && d.HTTP01 != nil: + case challenge.Type == "http-01" && d.HTTP01 != nil && a.issuer.GetSpec().ACME.HTTP01 != nil: return challenge.Type, nil - case challenge.Type == "dns-01" && d.DNS01 != nil: + case challenge.Type == "dns-01" && d.DNS01 != nil && a.issuer.GetSpec().ACME.DNS01 != nil: return challenge.Type, nil } }