Skip to content

Commit e8eecf8

Browse files
authored
Merge pull request #1902 from M00nF1sh/ing_class_p
add validating webhook for ingress_class_params
2 parents 2d17139 + 935515f commit e8eecf8

File tree

7 files changed

+1060
-4
lines changed

7 files changed

+1060
-4
lines changed

main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ func main() {
141141
corewebhook.NewPodMutator(podReadinessGateInjector).SetupWithManager(mgr)
142142
elbv2webhook.NewTargetGroupBindingMutator(cloud.ELBV2(), ctrl.Log).SetupWithManager(mgr)
143143
elbv2webhook.NewTargetGroupBindingValidator(ctrl.Log).SetupWithManager(mgr)
144-
networkingwebhook.NewIngressValidator(controllerCFG.IngressConfig, ctrl.Log).SetupWithManager(mgr)
144+
networkingwebhook.NewIngressValidator(mgr.GetClient(), controllerCFG.IngressConfig, ctrl.Log).SetupWithManager(mgr)
145145
//+kubebuilder:scaffold:builder
146146

147147
stopChan := ctrl.SetupSignalHandler()

pkg/ingress/class.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package ingress
2+
3+
import (
4+
networking "k8s.io/api/networking/v1beta1"
5+
elbv2api "sigs.k8s.io/aws-load-balancer-controller/apis/elbv2/v1beta1"
6+
)
7+
8+
// ClassConfiguration contains configurations for IngressClass
9+
type ClassConfiguration struct {
10+
// The IngressClass for Ingress if any.
11+
IngClass *networking.IngressClass
12+
// The IngressClassParams for Ingress if any.
13+
IngClassParams *elbv2api.IngressClassParams
14+
}

pkg/ingress/class_loader.go

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package ingress
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"github.com/pkg/errors"
7+
corev1 "k8s.io/api/core/v1"
8+
networking "k8s.io/api/networking/v1beta1"
9+
apierrors "k8s.io/apimachinery/pkg/api/errors"
10+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
11+
"k8s.io/apimachinery/pkg/labels"
12+
"k8s.io/apimachinery/pkg/types"
13+
elbv2api "sigs.k8s.io/aws-load-balancer-controller/apis/elbv2/v1beta1"
14+
"sigs.k8s.io/aws-load-balancer-controller/pkg/webhook"
15+
"sigs.k8s.io/controller-runtime/pkg/client"
16+
)
17+
18+
const (
19+
// the controller name used in IngressClass for ALB.
20+
ingressClassControllerALB = "ingress.k8s.aws/alb"
21+
// the Kind for IngressClassParams CRD.
22+
ingressClassParamsKind = "IngressClassParams"
23+
)
24+
25+
// ErrInvalidIngressClass is an sentinel error that represents the IngressClass configuration for Ingress is invalid.
26+
var ErrInvalidIngressClass = errors.New("invalid ingress class")
27+
28+
// ClassLoader loads IngressClass configurations for Ingress.
29+
type ClassLoader interface {
30+
Load(ctx context.Context, ing *networking.Ingress) (ClassConfiguration, error)
31+
}
32+
33+
// NewDefaultClassLoader constructs new defaultClassLoader instance.
34+
func NewDefaultClassLoader(client client.Client) *defaultClassLoader {
35+
return &defaultClassLoader{
36+
client: client,
37+
}
38+
}
39+
40+
// default implementation for ClassLoader
41+
type defaultClassLoader struct {
42+
client client.Client
43+
}
44+
45+
func (l *defaultClassLoader) Load(ctx context.Context, ing *networking.Ingress) (ClassConfiguration, error) {
46+
if ing.Spec.IngressClassName == nil {
47+
return ClassConfiguration{}, nil
48+
}
49+
50+
ingClassKey := types.NamespacedName{Name: *ing.Spec.IngressClassName}
51+
ingClass := &networking.IngressClass{}
52+
if err := l.client.Get(ctx, ingClassKey, ingClass); err != nil {
53+
if apierrors.IsNotFound(err) {
54+
return ClassConfiguration{}, fmt.Errorf("%w: %v", ErrInvalidIngressClass, err.Error())
55+
}
56+
return ClassConfiguration{}, err
57+
}
58+
if ingClass.Spec.Controller != ingressClassControllerALB || ingClass.Spec.Parameters == nil {
59+
return ClassConfiguration{
60+
IngClass: ingClass,
61+
}, nil
62+
}
63+
64+
if ingClass.Spec.Parameters.APIGroup == nil ||
65+
(*ingClass.Spec.Parameters.APIGroup) != elbv2api.GroupVersion.Group ||
66+
ingClass.Spec.Parameters.Kind != ingressClassParamsKind {
67+
return ClassConfiguration{}, fmt.Errorf("%w: IngressClass %v references unknown parameters", ErrInvalidIngressClass, ingClass.Name)
68+
}
69+
ingClassParamsKey := types.NamespacedName{Name: ingClass.Spec.Parameters.Name}
70+
ingClassParams := &elbv2api.IngressClassParams{}
71+
if err := l.client.Get(ctx, ingClassParamsKey, ingClassParams); err != nil {
72+
if apierrors.IsNotFound(err) {
73+
return ClassConfiguration{}, fmt.Errorf("%w: %v", ErrInvalidIngressClass, err.Error())
74+
}
75+
return ClassConfiguration{}, err
76+
}
77+
if err := l.validateIngressClassParamsNamespaceRestriction(ctx, ing, ingClassParams); err != nil {
78+
return ClassConfiguration{}, fmt.Errorf("%w: %v", ErrInvalidIngressClass, err.Error())
79+
}
80+
81+
return ClassConfiguration{
82+
IngClass: ingClass,
83+
IngClassParams: ingClassParams,
84+
}, nil
85+
}
86+
87+
func (l *defaultClassLoader) validateIngressClassParamsNamespaceRestriction(ctx context.Context, ing *networking.Ingress, ingClassParams *elbv2api.IngressClassParams) error {
88+
// when namespaceSelector is empty, it matches every namespace
89+
if ingClassParams.Spec.NamespaceSelector == nil {
90+
return nil
91+
}
92+
93+
ingNamespace := ing.Namespace
94+
// see https://github.com/kubernetes/kubernetes/issues/88282 and https://github.com/kubernetes/kubernetes/issues/76680
95+
if admissionReq := webhook.ContextGetAdmissionRequest(ctx); admissionReq != nil {
96+
ingNamespace = admissionReq.Namespace
97+
}
98+
ingNSKey := types.NamespacedName{Name: ingNamespace}
99+
ingNS := &corev1.Namespace{}
100+
if err := l.client.Get(ctx, ingNSKey, ingNS); err != nil {
101+
return err
102+
}
103+
selector, err := metav1.LabelSelectorAsSelector(ingClassParams.Spec.NamespaceSelector)
104+
if err != nil {
105+
return err
106+
}
107+
if !selector.Matches(labels.Set(ingNS.Labels)) {
108+
return errors.Errorf("namespaceSelector of IngressClassParams %v mismatch", ingClassParams.Name)
109+
}
110+
return nil
111+
}

0 commit comments

Comments
 (0)