HTTP01 solver support for the Gateway API

Signed-off-by: Jake Sanders <i@am.so-aweso.me>
This commit is contained in:
Jake Sanders 2021-07-29 11:11:05 +01:00
parent 6f6213c5fd
commit deb9ccc5a9
No known key found for this signature in database
GPG Key ID: 7E708D7933B84690
26 changed files with 369 additions and 105 deletions

View File

@ -307,9 +307,12 @@ func buildControllerContext(ctx context.Context, opts *options.ControllerOptions
KubeSharedInformerFactory: kubeSharedInformerFactory,
SharedInformerFactory: sharedInformerFactory,
GWShared: gwSharedInformerFactory,
Namespace: opts.Namespace,
Clock: clock.RealClock{},
Metrics: metrics.New(log, clock.RealClock{}),
// TODO (@jakexks) / code reviewer: should this be automatically enabled or disabled based on discovering the gateway
// api or a flag?
GatewaySolverEnabled: true,
Namespace: opts.Namespace,
Clock: clock.RealClock{},
Metrics: metrics.New(log, clock.RealClock{}),
ACMEOptions: controller.ACMEOptions{
HTTP01SolverImage: opts.ACMEHTTP01SolverImage,
HTTP01SolverResourceRequestCPU: HTTP01SolverResourceRequestCPU,

View File

@ -383,9 +383,11 @@ spec:
description: The Gateway API is a sig-network community API that models service networking in Kubernetes (https://gateway-api.sigs.k8s.io/). The Gateway solver will create or modify HTTPRoutes for a particular gateway class.
type: object
properties:
class:
description: The Gateway class to use when creating HTTPRoute resources to solve ACME challenges that use this challenge solver.
type: string
labels:
description: The labels to set on HTTPRoute resources to solve ACME challenges that use this challenge solver.
type: object
additionalProperties:
type: string
podTemplate:
description: Optional pod template used to configure the ACME challenge solver pods used for HTTP01 challenges
type: object
@ -1852,12 +1854,14 @@ spec:
type: object
properties:
gateway:
description: The Gateway API is a sig-network community API that models service networking in Kubernetes (https://gateway-api.sigs.k8s.io/). The Gateway solver will create or modify HTTPRoutes for a particular gateway class.
description: The Gateway API is a sig-network community API that models service networking in Kubernetes (https://gateway-api.sigs.k8s.io/). The Gateway solver will create or modify HTTPRoutes.
type: object
properties:
class:
description: The Gateway class to use when creating HTTPRoute resources to solve ACME challenges that use this challenge solver.
type: string
labels:
description: The labels to set on HTTPRoute resources to solve ACME challenges that use this challenge solver.
type: object
additionalProperties:
type: string
podTemplate:
description: Optional pod template used to configure the ACME challenge solver pods used for HTTP01 challenges
type: object
@ -3328,9 +3332,11 @@ spec:
description: The Gateway API is a sig-network community API that models service networking in Kubernetes (https://gateway-api.sigs.k8s.io/). The Gateway solver will create or modify HTTPRoutes for a particular gateway class.
type: object
properties:
class:
description: The Gateway class to use when creating HTTPRoute resources to solve ACME challenges that use this challenge solver.
type: string
labels:
description: The labels to set on HTTPRoute resources to solve ACME challenges that use this challenge solver.
type: object
additionalProperties:
type: string
podTemplate:
description: Optional pod template used to configure the ACME challenge solver pods used for HTTP01 challenges
type: object
@ -4801,9 +4807,11 @@ spec:
description: The Gateway API is a sig-network community API that models service networking in Kubernetes (https://gateway-api.sigs.k8s.io/). The Gateway solver will create or modify HTTPRoutes for a particular gateway class.
type: object
properties:
class:
description: The Gateway class to use when creating HTTPRoute resources to solve ACME challenges that use this challenge solver.
type: string
labels:
description: The labels to set on HTTPRoute resources to solve ACME challenges that use this challenge solver.
type: object
additionalProperties:
type: string
podTemplate:
description: Optional pod template used to configure the ACME challenge solver pods used for HTTP01 challenges
type: object

View File

@ -417,9 +417,11 @@ spec:
description: The Gateway API is a sig-network community API that models service networking in Kubernetes (https://gateway-api.sigs.k8s.io/). The Gateway solver will create or modify HTTPRoutes for a particular gateway class.
type: object
properties:
class:
description: The Gateway class to use when creating HTTPRoute resources to solve ACME challenges that use this challenge solver.
type: string
labels:
description: The labels to set on HTTPRoute resources to solve ACME challenges that use this challenge solver.
type: object
additionalProperties:
type: string
podTemplate:
description: Optional pod template used to configure the ACME challenge solver pods used for HTTP01 challenges
type: object
@ -2098,12 +2100,14 @@ spec:
type: object
properties:
gateway:
description: The Gateway API is a sig-network community API that models service networking in Kubernetes (https://gateway-api.sigs.k8s.io/). The Gateway solver will create or modify HTTPRoutes for a particular gateway class.
description: The Gateway API is a sig-network community API that models service networking in Kubernetes (https://gateway-api.sigs.k8s.io/). The Gateway solver will create or modify HTTPRoutes.
type: object
properties:
class:
description: The Gateway class to use when creating HTTPRoute resources to solve ACME challenges that use this challenge solver.
type: string
labels:
description: The labels to set on HTTPRoute resources to solve ACME challenges that use this challenge solver.
type: object
additionalProperties:
type: string
podTemplate:
description: Optional pod template used to configure the ACME challenge solver pods used for HTTP01 challenges
type: object
@ -3787,9 +3791,11 @@ spec:
description: The Gateway API is a sig-network community API that models service networking in Kubernetes (https://gateway-api.sigs.k8s.io/). The Gateway solver will create or modify HTTPRoutes for a particular gateway class.
type: object
properties:
class:
description: The Gateway class to use when creating HTTPRoute resources to solve ACME challenges that use this challenge solver.
type: string
labels:
description: The labels to set on HTTPRoute resources to solve ACME challenges that use this challenge solver.
type: object
additionalProperties:
type: string
podTemplate:
description: Optional pod template used to configure the ACME challenge solver pods used for HTTP01 challenges
type: object
@ -5473,9 +5479,11 @@ spec:
description: The Gateway API is a sig-network community API that models service networking in Kubernetes (https://gateway-api.sigs.k8s.io/). The Gateway solver will create or modify HTTPRoutes for a particular gateway class.
type: object
properties:
class:
description: The Gateway class to use when creating HTTPRoute resources to solve ACME challenges that use this challenge solver.
type: string
labels:
description: The labels to set on HTTPRoute resources to solve ACME challenges that use this challenge solver.
type: object
additionalProperties:
type: string
podTemplate:
description: Optional pod template used to configure the ACME challenge solver pods used for HTTP01 challenges
type: object

View File

@ -417,9 +417,11 @@ spec:
description: The Gateway API is a sig-network community API that models service networking in Kubernetes (https://gateway-api.sigs.k8s.io/). The Gateway solver will create or modify HTTPRoutes for a particular gateway class.
type: object
properties:
class:
description: The Gateway class to use when creating HTTPRoute resources to solve ACME challenges that use this challenge solver.
type: string
labels:
description: The labels to set on HTTPRoute resources to solve ACME challenges that use this challenge solver.
type: object
additionalProperties:
type: string
podTemplate:
description: Optional pod template used to configure the ACME challenge solver pods used for HTTP01 challenges
type: object
@ -2098,12 +2100,14 @@ spec:
type: object
properties:
gateway:
description: The Gateway API is a sig-network community API that models service networking in Kubernetes (https://gateway-api.sigs.k8s.io/). The Gateway solver will create or modify HTTPRoutes for a particular gateway class.
description: The Gateway API is a sig-network community API that models service networking in Kubernetes (https://gateway-api.sigs.k8s.io/). The Gateway solver will create or modify HTTPRoutes.
type: object
properties:
class:
description: The Gateway class to use when creating HTTPRoute resources to solve ACME challenges that use this challenge solver.
type: string
labels:
description: The labels to set on HTTPRoute resources to solve ACME challenges that use this challenge solver.
type: object
additionalProperties:
type: string
podTemplate:
description: Optional pod template used to configure the ACME challenge solver pods used for HTTP01 challenges
type: object
@ -3787,9 +3791,11 @@ spec:
description: The Gateway API is a sig-network community API that models service networking in Kubernetes (https://gateway-api.sigs.k8s.io/). The Gateway solver will create or modify HTTPRoutes for a particular gateway class.
type: object
properties:
class:
description: The Gateway class to use when creating HTTPRoute resources to solve ACME challenges that use this challenge solver.
type: string
labels:
description: The labels to set on HTTPRoute resources to solve ACME challenges that use this challenge solver.
type: object
additionalProperties:
type: string
podTemplate:
description: Optional pod template used to configure the ACME challenge solver pods used for HTTP01 challenges
type: object
@ -5473,9 +5479,11 @@ spec:
description: The Gateway API is a sig-network community API that models service networking in Kubernetes (https://gateway-api.sigs.k8s.io/). The Gateway solver will create or modify HTTPRoutes for a particular gateway class.
type: object
properties:
class:
description: The Gateway class to use when creating HTTPRoute resources to solve ACME challenges that use this challenge solver.
type: string
labels:
description: The labels to set on HTTPRoute resources to solve ACME challenges that use this challenge solver.
type: object
additionalProperties:
type: string
podTemplate:
description: Optional pod template used to configure the ACME challenge solver pods used for HTTP01 challenges
type: object

View File

@ -242,15 +242,17 @@ type ACMEChallengeSolverHTTP01Ingress struct {
IngressTemplate *ACMEChallengeSolverHTTP01IngressTemplate `json:"ingressTemplate,omitempty"`
}
// The ACMEChallengeSolverHTTP01Gateway solver will create HTTPRoute objects for a Gateway class
// routing to an ACME challenge solver pod.
type ACMEChallengeSolverHTTP01Gateway struct {
// Optional service type for Kubernetes solver service
// +optional
ServiceType corev1.ServiceType `json:"serviceType,omitempty"`
// The Gateway class to use when creating HTTPRoute resources to solve ACME
// The labels to set on HTTPRoute resources to solve ACME
// challenges that use this challenge solver.
// +optional
Class *string `json:"class,omitempty"`
Labels map[string]string `json:"labels,omitempty"`
// Optional pod template used to configure the ACME challenge solver pods
// used for HTTP01 challenges

View File

@ -191,10 +191,12 @@ func (in *ACMEChallengeSolverHTTP01) DeepCopy() *ACMEChallengeSolverHTTP01 {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ACMEChallengeSolverHTTP01Gateway) DeepCopyInto(out *ACMEChallengeSolverHTTP01Gateway) {
*out = *in
if in.Class != nil {
in, out := &in.Class, &out.Class
*out = new(string)
**out = **in
if in.Labels != nil {
in, out := &in.Labels, &out.Labels
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
if in.PodTemplate != nil {
in, out := &in.PodTemplate, &out.PodTemplate

View File

@ -246,10 +246,10 @@ type ACMEChallengeSolverHTTP01Gateway struct {
// +optional
ServiceType corev1.ServiceType `json:"serviceType,omitempty"`
// The Gateway class to use when creating HTTPRoute resources to solve ACME
// The labels to set on HTTPRoute resources to solve ACME
// challenges that use this challenge solver.
// +optional
Class *string `json:"class,omitempty"`
Labels map[string]string `json:"labels,omitempty"`
// Optional pod template used to configure the ACME challenge solver pods
// used for HTTP01 challenges

View File

@ -191,10 +191,12 @@ func (in *ACMEChallengeSolverHTTP01) DeepCopy() *ACMEChallengeSolverHTTP01 {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ACMEChallengeSolverHTTP01Gateway) DeepCopyInto(out *ACMEChallengeSolverHTTP01Gateway) {
*out = *in
if in.Class != nil {
in, out := &in.Class, &out.Class
*out = new(string)
**out = **in
if in.Labels != nil {
in, out := &in.Labels, &out.Labels
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
if in.PodTemplate != nil {
in, out := &in.PodTemplate, &out.PodTemplate

View File

@ -207,7 +207,7 @@ type ACMEChallengeSolverHTTP01 struct {
// The Gateway API is a sig-network community API that models service networking
// in Kubernetes (https://gateway-api.sigs.k8s.io/). The Gateway solver will
// create or modify HTTPRoutes for a particular gateway class.
// create or modify HTTPRoutes.
Gateway *ACMEChallengeSolverHTTP01Gateway `json:"gateway,omitempty"`
}
@ -246,10 +246,10 @@ type ACMEChallengeSolverHTTP01Gateway struct {
// +optional
ServiceType corev1.ServiceType `json:"serviceType,omitempty"`
// The Gateway class to use when creating HTTPRoute resources to solve ACME
// The labels to set on HTTPRoute resources to solve ACME
// challenges that use this challenge solver.
// +optional
Class *string `json:"class,omitempty"`
Labels map[string]string `json:"labels,omitempty"`
// Optional pod template used to configure the ACME challenge solver pods
// used for HTTP01 challenges

View File

@ -191,10 +191,12 @@ func (in *ACMEChallengeSolverHTTP01) DeepCopy() *ACMEChallengeSolverHTTP01 {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ACMEChallengeSolverHTTP01Gateway) DeepCopyInto(out *ACMEChallengeSolverHTTP01Gateway) {
*out = *in
if in.Class != nil {
in, out := &in.Class, &out.Class
*out = new(string)
**out = **in
if in.Labels != nil {
in, out := &in.Labels, &out.Labels
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
if in.PodTemplate != nil {
in, out := &in.PodTemplate, &out.PodTemplate

View File

@ -246,10 +246,10 @@ type ACMEChallengeSolverHTTP01Gateway struct {
// +optional
ServiceType corev1.ServiceType `json:"serviceType,omitempty"`
// The Gateway class to use when creating HTTPRoute resources to solve ACME
// The labels to set on HTTPRoute resources to solve ACME
// challenges that use this challenge solver.
// +optional
Class *string `json:"class,omitempty"`
Labels map[string]string `json:"labels,omitempty"`
// Optional pod template used to configure the ACME challenge solver pods
// used for HTTP01 challenges

View File

@ -191,10 +191,12 @@ func (in *ACMEChallengeSolverHTTP01) DeepCopy() *ACMEChallengeSolverHTTP01 {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ACMEChallengeSolverHTTP01Gateway) DeepCopyInto(out *ACMEChallengeSolverHTTP01Gateway) {
*out = *in
if in.Class != nil {
in, out := &in.Class, &out.Class
*out = new(string)
**out = **in
if in.Labels != nil {
in, out := &in.Labels, &out.Labels
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
if in.PodTemplate != nil {
in, out := &in.PodTemplate, &out.PodTemplate

View File

@ -21,14 +21,6 @@ import (
"time"
"github.com/go-logr/logr"
corev1 "k8s.io/api/core/v1"
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
corelisters "k8s.io/client-go/listers/core/v1"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/record"
"k8s.io/client-go/util/workqueue"
"github.com/jetstack/cert-manager/pkg/acme/accounts"
cmclient "github.com/jetstack/cert-manager/pkg/client/clientset/versioned"
cmacmelisters "github.com/jetstack/cert-manager/pkg/client/listers/acme/v1"
@ -40,6 +32,13 @@ import (
"github.com/jetstack/cert-manager/pkg/issuer/acme/dns"
"github.com/jetstack/cert-manager/pkg/issuer/acme/http"
logf "github.com/jetstack/cert-manager/pkg/logs"
corev1 "k8s.io/api/core/v1"
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
corelisters "k8s.io/client-go/listers/core/v1"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/record"
"k8s.io/client-go/util/workqueue"
)
type controller struct {
@ -114,6 +113,11 @@ func (c *controller) Register(ctx *controllerpkg.Context) (workqueue.RateLimitin
ingressInformer.HasSynced,
}
if ctx.GatewaySolverEnabled {
gwAPIHTTPRouteInformer := ctx.GWShared.Networking().V1alpha1().HTTPRoutes()
mustSync = append(mustSync, gwAPIHTTPRouteInformer.Informer().HasSynced)
}
// set all the references to the listers for used by the Sync function
c.challengeLister = challengeInformer.Lister()
c.issuerLister = issuerInformer.Lister()

View File

@ -70,7 +70,8 @@ type Context struct {
// The Gateway API is an external CRD, which means its shared informers are
// not available in controllerpkg.Context.
GWShared gwinformers.SharedInformerFactory
GWShared gwinformers.SharedInformerFactory
GatewaySolverEnabled bool
// Namespace is the namespace to operate within.
// If unset, operates on all namespaces

View File

@ -223,11 +223,10 @@ type ACMEChallengeSolverHTTP01Gateway struct {
// +optional
ServiceType corev1.ServiceType `json:"serviceType,omitempty"`
// The ingress class to use when creating Ingress resources to solve ACME
// The labels to set on HTTPRoute resources to solve ACME
// challenges that use this challenge solver.
// Only one of 'class' or 'name' may be specified.
// +optional
Class *string `json:"class,omitempty"`
Labels map[string]string `json:"labels,omitempty"`
// Optional pod template used to configure the ACME challenge solver pods
// used for HTTP01 challenges

View File

@ -657,7 +657,7 @@ func Convert_acme_ACMEChallengeSolverHTTP01_To_v1_ACMEChallengeSolverHTTP01(in *
func autoConvert_v1_ACMEChallengeSolverHTTP01Gateway_To_acme_ACMEChallengeSolverHTTP01Gateway(in *v1.ACMEChallengeSolverHTTP01Gateway, out *acme.ACMEChallengeSolverHTTP01Gateway, s conversion.Scope) error {
out.ServiceType = corev1.ServiceType(in.ServiceType)
out.Class = (*string)(unsafe.Pointer(in.Class))
out.Labels = *(*map[string]string)(unsafe.Pointer(&in.Labels))
out.PodTemplate = (*acme.ACMEChallengeSolverHTTP01IngressPodTemplate)(unsafe.Pointer(in.PodTemplate))
return nil
}
@ -669,7 +669,7 @@ func Convert_v1_ACMEChallengeSolverHTTP01Gateway_To_acme_ACMEChallengeSolverHTTP
func autoConvert_acme_ACMEChallengeSolverHTTP01Gateway_To_v1_ACMEChallengeSolverHTTP01Gateway(in *acme.ACMEChallengeSolverHTTP01Gateway, out *v1.ACMEChallengeSolverHTTP01Gateway, s conversion.Scope) error {
out.ServiceType = corev1.ServiceType(in.ServiceType)
out.Class = (*string)(unsafe.Pointer(in.Class))
out.Labels = *(*map[string]string)(unsafe.Pointer(&in.Labels))
out.PodTemplate = (*v1.ACMEChallengeSolverHTTP01IngressPodTemplate)(unsafe.Pointer(in.PodTemplate))
return nil
}

View File

@ -657,7 +657,7 @@ func Convert_acme_ACMEChallengeSolverHTTP01_To_v1alpha2_ACMEChallengeSolverHTTP0
func autoConvert_v1alpha2_ACMEChallengeSolverHTTP01Gateway_To_acme_ACMEChallengeSolverHTTP01Gateway(in *v1alpha2.ACMEChallengeSolverHTTP01Gateway, out *acme.ACMEChallengeSolverHTTP01Gateway, s conversion.Scope) error {
out.ServiceType = v1.ServiceType(in.ServiceType)
out.Class = (*string)(unsafe.Pointer(in.Class))
out.Labels = *(*map[string]string)(unsafe.Pointer(&in.Labels))
out.PodTemplate = (*acme.ACMEChallengeSolverHTTP01IngressPodTemplate)(unsafe.Pointer(in.PodTemplate))
return nil
}
@ -669,7 +669,7 @@ func Convert_v1alpha2_ACMEChallengeSolverHTTP01Gateway_To_acme_ACMEChallengeSolv
func autoConvert_acme_ACMEChallengeSolverHTTP01Gateway_To_v1alpha2_ACMEChallengeSolverHTTP01Gateway(in *acme.ACMEChallengeSolverHTTP01Gateway, out *v1alpha2.ACMEChallengeSolverHTTP01Gateway, s conversion.Scope) error {
out.ServiceType = v1.ServiceType(in.ServiceType)
out.Class = (*string)(unsafe.Pointer(in.Class))
out.Labels = *(*map[string]string)(unsafe.Pointer(&in.Labels))
out.PodTemplate = (*v1alpha2.ACMEChallengeSolverHTTP01IngressPodTemplate)(unsafe.Pointer(in.PodTemplate))
return nil
}

View File

@ -657,7 +657,7 @@ func Convert_acme_ACMEChallengeSolverHTTP01_To_v1alpha3_ACMEChallengeSolverHTTP0
func autoConvert_v1alpha3_ACMEChallengeSolverHTTP01Gateway_To_acme_ACMEChallengeSolverHTTP01Gateway(in *v1alpha3.ACMEChallengeSolverHTTP01Gateway, out *acme.ACMEChallengeSolverHTTP01Gateway, s conversion.Scope) error {
out.ServiceType = v1.ServiceType(in.ServiceType)
out.Class = (*string)(unsafe.Pointer(in.Class))
out.Labels = *(*map[string]string)(unsafe.Pointer(&in.Labels))
out.PodTemplate = (*acme.ACMEChallengeSolverHTTP01IngressPodTemplate)(unsafe.Pointer(in.PodTemplate))
return nil
}
@ -669,7 +669,7 @@ func Convert_v1alpha3_ACMEChallengeSolverHTTP01Gateway_To_acme_ACMEChallengeSolv
func autoConvert_acme_ACMEChallengeSolverHTTP01Gateway_To_v1alpha3_ACMEChallengeSolverHTTP01Gateway(in *acme.ACMEChallengeSolverHTTP01Gateway, out *v1alpha3.ACMEChallengeSolverHTTP01Gateway, s conversion.Scope) error {
out.ServiceType = v1.ServiceType(in.ServiceType)
out.Class = (*string)(unsafe.Pointer(in.Class))
out.Labels = *(*map[string]string)(unsafe.Pointer(&in.Labels))
out.PodTemplate = (*v1alpha3.ACMEChallengeSolverHTTP01IngressPodTemplate)(unsafe.Pointer(in.PodTemplate))
return nil
}

View File

@ -657,7 +657,7 @@ func Convert_acme_ACMEChallengeSolverHTTP01_To_v1beta1_ACMEChallengeSolverHTTP01
func autoConvert_v1beta1_ACMEChallengeSolverHTTP01Gateway_To_acme_ACMEChallengeSolverHTTP01Gateway(in *v1beta1.ACMEChallengeSolverHTTP01Gateway, out *acme.ACMEChallengeSolverHTTP01Gateway, s conversion.Scope) error {
out.ServiceType = v1.ServiceType(in.ServiceType)
out.Class = (*string)(unsafe.Pointer(in.Class))
out.Labels = *(*map[string]string)(unsafe.Pointer(&in.Labels))
out.PodTemplate = (*acme.ACMEChallengeSolverHTTP01IngressPodTemplate)(unsafe.Pointer(in.PodTemplate))
return nil
}
@ -669,7 +669,7 @@ func Convert_v1beta1_ACMEChallengeSolverHTTP01Gateway_To_acme_ACMEChallengeSolve
func autoConvert_acme_ACMEChallengeSolverHTTP01Gateway_To_v1beta1_ACMEChallengeSolverHTTP01Gateway(in *acme.ACMEChallengeSolverHTTP01Gateway, out *v1beta1.ACMEChallengeSolverHTTP01Gateway, s conversion.Scope) error {
out.ServiceType = v1.ServiceType(in.ServiceType)
out.Class = (*string)(unsafe.Pointer(in.Class))
out.Labels = *(*map[string]string)(unsafe.Pointer(&in.Labels))
out.PodTemplate = (*v1beta1.ACMEChallengeSolverHTTP01IngressPodTemplate)(unsafe.Pointer(in.PodTemplate))
return nil
}

View File

@ -191,10 +191,12 @@ func (in *ACMEChallengeSolverHTTP01) DeepCopy() *ACMEChallengeSolverHTTP01 {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ACMEChallengeSolverHTTP01Gateway) DeepCopyInto(out *ACMEChallengeSolverHTTP01Gateway) {
*out = *in
if in.Class != nil {
in, out := &in.Class, &out.Class
*out = new(string)
**out = **in
if in.Labels != nil {
in, out := &in.Labels, &out.Labels
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
if in.PodTemplate != nil {
in, out := &in.PodTemplate, &out.PodTemplate

View File

@ -4,6 +4,7 @@ go_library(
name = "go_default_library",
srcs = [
"http.go",
"httproute.go",
"ingress.go",
"pod.go",
"service.go",
@ -27,6 +28,9 @@ go_library(
"@io_k8s_apimachinery//pkg/util/errors:go_default_library",
"@io_k8s_apimachinery//pkg/util/intstr:go_default_library",
"@io_k8s_client_go//listers/core/v1:go_default_library",
"@io_k8s_client_go//util/retry:go_default_library",
"@io_k8s_sigs_gateway_api//apis/v1alpha1:go_default_library",
"@io_k8s_sigs_gateway_api//pkg/client/listers/apis/v1alpha1:go_default_library",
"@io_k8s_utils//net:go_default_library",
"@io_k8s_utils//pointer:go_default_library",
],

View File

@ -27,10 +27,11 @@ import (
"strings"
"time"
k8snet "k8s.io/utils/net"
corev1 "k8s.io/api/core/v1"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
corev1listers "k8s.io/client-go/listers/core/v1"
k8snet "k8s.io/utils/net"
gwapilisters "sigs.k8s.io/gateway-api/pkg/client/listers/apis/v1alpha1"
cmacme "github.com/jetstack/cert-manager/pkg/apis/acme/v1"
v1 "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1"
@ -61,6 +62,7 @@ type Solver struct {
serviceLister corev1listers.ServiceLister
ingressLister ingress.InternalIngressLister
ingressCreateUpdater ingress.InternalIngressCreateUpdater
httpRouteLister gwapilisters.HTTPRouteLister
testReachability reachabilityTest
requiredPasses int
@ -84,6 +86,7 @@ func NewSolver(ctx *controller.Context) (*Solver, error) {
serviceLister: ctx.KubeSharedInformerFactory.Core().V1().Services().Lister(),
ingressLister: ingressLister,
ingressCreateUpdater: ingressCreateUpdater,
httpRouteLister: ctx.GWShared.Networking().V1alpha1().HTTPRoutes().Lister(),
testReachability: testReachability,
requiredPasses: 5,
}, nil
@ -101,6 +104,16 @@ func httpDomainCfgForChallenge(ch *cmacme.Challenge) (*cmacme.ACMEChallengeSolve
return ch.Spec.Solver.HTTP01.Ingress, nil
}
func getServiceType(ch *cmacme.Challenge) (corev1.ServiceType, error) {
if ch.Spec.Solver.HTTP01 != nil && ch.Spec.Solver.HTTP01.Ingress != nil {
return ch.Spec.Solver.HTTP01.Ingress.ServiceType, nil
}
if ch.Spec.Solver.HTTP01 != nil && ch.Spec.Solver.HTTP01.Gateway != nil {
return ch.Spec.Solver.HTTP01.Gateway.ServiceType, nil
}
return "", fmt.Errorf("neither HTTP01 Ingress nor Gateway solvers were found")
}
// Present will realise the resources required to solve the given HTTP01
// challenge validation in the apiserver. If those resources already exist, it
// will return nil (i.e. this function is idempotent).
@ -112,8 +125,26 @@ func (s *Solver) Present(ctx context.Context, issuer v1.GenericIssuer, ch *cmacm
if svcErr != nil {
return utilerrors.NewAggregate([]error{podErr, svcErr})
}
_, ingressErr := s.ensureIngress(ctx, ch, svc.Name)
return utilerrors.NewAggregate([]error{podErr, svcErr, ingressErr})
var ingressErr, gatewayErr error
if ch.Spec.Solver.HTTP01 != nil {
if ch.Spec.Solver.HTTP01.Ingress != nil {
_, ingressErr = s.ensureIngress(ctx, ch, svc.Name)
return utilerrors.NewAggregate([]error{podErr, svcErr, ingressErr})
}
if ch.Spec.Solver.HTTP01.Gateway != nil {
_, gatewayErr = s.ensureGatewayHTTPRoute(ctx, ch, svc.Name)
return utilerrors.NewAggregate([]error{podErr, svcErr, gatewayErr})
}
}
return utilerrors.NewAggregate(
[]error{
podErr,
svcErr,
ingressErr,
gatewayErr,
fmt.Errorf("couldn't Present challenge %s/%s: no Ingress nor Gateway HTTP01 solvers were specified", ch.Namespace, ch.Name),
},
)
}
func (s *Solver) Check(ctx context.Context, issuer v1.GenericIssuer, ch *cmacme.Challenge) error {

View File

@ -0,0 +1,180 @@
/*
Copyright 2021 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package http
import (
"context"
"fmt"
"reflect"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/util/retry"
"k8s.io/utils/pointer"
gwapi "sigs.k8s.io/gateway-api/apis/v1alpha1"
cmacme "github.com/jetstack/cert-manager/pkg/apis/acme/v1"
logf "github.com/jetstack/cert-manager/pkg/logs"
)
// ensureGatewayHTTPRoute ensures that the HTTPRoutes needed to solve a challenge exist.
func (s *Solver) ensureGatewayHTTPRoute(ctx context.Context, ch *cmacme.Challenge, svcName string) (*gwapi.HTTPRoute, error) {
if ch == nil {
return nil, fmt.Errorf("ensureGatewayHTTPRoute received nil *acme.Challenge")
}
log := logf.FromContext(ctx).WithName("ensureGatewayHTTPRoute")
httpRoute, err := s.getGatewayHTTPRoute(ctx, ch)
if err != nil {
return nil, err
}
if httpRoute == nil {
log.Info("creating HTTPRoute for challenge", "name", ch.Name, "namespace", ch.Namespace)
httpRoute, err = s.createGatewayHTTPRoute(ctx, ch, svcName)
if err != nil {
return nil, err
}
return httpRoute, nil
}
log.Info("Found existing HTTPRoute for challenge", "name", ch.Name, "namespace", ch.Namespace)
httpRoute, err = s.checkAndUpdateGatewayHTTPRoute(ctx, ch, svcName, httpRoute)
if err != nil {
return nil, err
}
return nil, fmt.Errorf("not implemented")
}
func (s *Solver) getGatewayHTTPRoute(ctx context.Context, ch *cmacme.Challenge) (*gwapi.HTTPRoute, error) {
log := logf.FromContext(ctx).WithName("getGatewayHTTPRoute")
log.Info("getting httpRoutes for challenge", "name", ch.Name, "namespace", ch.Namespace)
httpRoutes, err := s.httpRouteLister.HTTPRoutes(ch.Namespace).List(labels.Set(podLabels(ch)).AsSelector())
if err != nil {
return nil, err
}
switch len(httpRoutes) {
case 0:
return nil, nil
case 1:
return httpRoutes[0], nil
default:
for _, httpRoute := range httpRoutes[1:] {
log.Info("Deleting extra HTTPRoute", "name", httpRoute.Name, "namespace", httpRoute.Namespace)
err := s.GWClient.NetworkingV1alpha1().HTTPRoutes(httpRoute.Namespace).Delete(ctx, httpRoute.Name, metav1.DeleteOptions{})
if err != nil {
return nil, err
}
}
return nil, fmt.Errorf("multiple HTTPRoutes found")
}
}
func (s *Solver) createGatewayHTTPRoute(ctx context.Context, ch *cmacme.Challenge, svcName string) (*gwapi.HTTPRoute, error) {
labels := podLabels(ch)
if ch.Spec.Solver.HTTP01.Gateway.Labels != nil {
for k, v := range ch.Spec.Solver.HTTP01.Gateway.Labels {
labels[k] = v
}
}
httpRoute := &gwapi.HTTPRoute{
ObjectMeta: metav1.ObjectMeta{
GenerateName: "cm-acme-http-solver",
Namespace: ch.Namespace,
Labels: labels,
OwnerReferences: []metav1.OwnerReference{*metav1.NewControllerRef(ch, challengeGvk)},
},
Spec: generateHTTPRouteSpec(ch, svcName),
}
newHTTPRoute, err := s.GWClient.NetworkingV1alpha1().HTTPRoutes(ch.Namespace).Create(ctx, httpRoute, metav1.CreateOptions{})
if err != nil {
return nil, err
}
return newHTTPRoute, nil
}
func (s *Solver) checkAndUpdateGatewayHTTPRoute(ctx context.Context, ch *cmacme.Challenge, svcName string, httpRoute *gwapi.HTTPRoute) (*gwapi.HTTPRoute, error) {
log := logf.FromContext(ctx, "checkAndUpdateGatewayHTTPRoute")
expectedSpec := generateHTTPRouteSpec(ch, svcName)
actualSpec := httpRoute.Spec
expectedLabels := podLabels(ch)
if ch.Spec.Solver.HTTP01.Gateway.Labels != nil {
for k, v := range ch.Spec.Solver.HTTP01.Gateway.Labels {
expectedLabels[k] = v
}
}
actualLabels := ch.Labels
if reflect.DeepEqual(expectedSpec, actualSpec) && reflect.DeepEqual(expectedLabels, actualLabels) {
return httpRoute, nil
}
log.Info("HTTPRoute is out of date, updating", "name", httpRoute.Name, "namespace", httpRoute.Namespace)
var ret *gwapi.HTTPRoute
var err error
if err = retry.RetryOnConflict(retry.DefaultBackoff, func() error {
oldHTTPRoute, err := s.GWClient.NetworkingV1alpha1().HTTPRoutes(httpRoute.Namespace).Get(ctx, httpRoute.Name, metav1.GetOptions{})
if err != nil {
return err
}
newHTTPRoute := oldHTTPRoute.DeepCopy()
newHTTPRoute.Spec = expectedSpec
newHTTPRoute.Labels = expectedLabels
ret, err = s.GWClient.NetworkingV1alpha1().HTTPRoutes(newHTTPRoute.Namespace).Update(ctx, newHTTPRoute, metav1.UpdateOptions{})
if err != nil {
return err
}
return nil
}); err != nil {
return nil, err
}
return ret, nil
}
func generateHTTPRouteSpec(ch *cmacme.Challenge, svcName string) gwapi.HTTPRouteSpec {
return gwapi.HTTPRouteSpec{
Gateways: &gwapi.RouteGateways{
Allow: func() *gwapi.GatewayAllowType { a := gwapi.GatewayAllowAll; return &a }(),
},
Hostnames: []gwapi.Hostname{
gwapi.Hostname(ch.Spec.DNSName),
},
Rules: []gwapi.HTTPRouteRule{
{
Matches: []gwapi.HTTPRouteMatch{
{
Path: &gwapi.HTTPPathMatch{
Type: func() *gwapi.PathMatchType { p := gwapi.PathMatchExact; return &p }(),
Value: pointer.String(fmt.Sprintf("/.well-known/acme-challenge/%s", ch.Spec.Token)),
},
},
},
ForwardTo: []gwapi.HTTPRouteForwardTo{
{
ServiceName: pointer.String(svcName),
Port: func() *gwapi.PortNumber { p := gwapi.PortNumber(acmeSolverListenPort); return &p }(),
},
},
},
},
}
}
func (s *Solver) cleanupGatewayHTTPRoutes(_ context.Context, _ *cmacme.Challenge) error {
// Unlike Ingress, we don't modify existing HTTPRoutes so there is nothing to do here.
return nil
}

View File

@ -264,7 +264,11 @@ func (s *Solver) addChallengePathToIngress(ctx context.Context, ch *cmacme.Chall
// ingress, or delete the ingress if an existing ingress name is not specified
// on the certificate.
func (s *Solver) cleanupIngresses(ctx context.Context, ch *cmacme.Challenge) error {
log := logf.FromContext(ctx, "cleanupPods")
log := logf.FromContext(ctx, "cleanupIngresses")
if ch.Spec.Solver.HTTP01.Ingress == nil {
return nil
}
httpDomainCfg, err := httpDomainCfgForChallenge(ch)
if err != nil {

View File

@ -145,10 +145,14 @@ func (s *Solver) buildPod(ch *cmacme.Challenge) *corev1.Pod {
pod := s.buildDefaultPod(ch)
// Override defaults if they have changed in the pod template.
if ch.Spec.Solver.HTTP01 != nil &&
ch.Spec.Solver.HTTP01.Ingress != nil {
pod = s.mergePodObjectMetaWithPodTemplate(pod,
ch.Spec.Solver.HTTP01.Ingress.PodTemplate)
if ch.Spec.Solver.HTTP01 != nil {
if ch.Spec.Solver.HTTP01.Ingress != nil {
pod = s.mergePodObjectMetaWithPodTemplate(pod,
ch.Spec.Solver.HTTP01.Ingress.PodTemplate)
} else if ch.Spec.Solver.HTTP01.Gateway != nil {
pod = s.mergePodObjectMetaWithPodTemplate(pod,
ch.Spec.Solver.HTTP01.Gateway.PodTemplate)
}
}
return pod

View File

@ -59,7 +59,7 @@ func (s *Solver) ensureService(ctx context.Context, ch *cmacme.Challenge) (*core
// getServicesForChallenge returns a list of services that were created to solve
// http challenges for the given domain
func (s *Solver) getServicesForChallenge(ctx context.Context, ch *cmacme.Challenge) ([]*corev1.Service, error) {
log := logf.FromContext(ctx)
log := logf.FromContext(ctx).WithName("getServicesForChallenge")
podLabels := podLabels(ch)
selector := labels.NewSelector()
@ -125,13 +125,11 @@ func buildService(ch *cmacme.Challenge) (*corev1.Service, error) {
}
// checking for presence of http01 config and if set serviceType is set, override our default (NodePort)
httpDomainCfg, err := httpDomainCfgForChallenge(ch)
serviceType, err := getServiceType(ch)
if err != nil {
return nil, err
}
if httpDomainCfg.ServiceType != "" {
service.Spec.Type = httpDomainCfg.ServiceType
}
service.Spec.Type = serviceType
return service, nil
}