Skip to content

Commit 7b9a6c0

Browse files
SinghVikram97VikramBediRyan Lymburner
authored
feat: implement Gateway API allowedRoutes namespace and kind validation (#845)
* feat: implement Gateway API allowedRoutes namespace and kind validation * fix tests * fix unit tests * add assertions to wait for gateway to be updated before each test * use diff namespace for label selector test * Add namespace RBAC permissions for Gateway listener namespace selectors --------- Co-authored-by: VikramBedi <vbedi@amazon.com> Co-authored-by: Ryan Lymburner <137918933+rlymbur@users.noreply.github.com>
1 parent 64e7968 commit 7b9a6c0

File tree

11 files changed

+3444
-459
lines changed

11 files changed

+3444
-459
lines changed

config/rbac/cluster-role-controller.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,14 @@ rules:
102102
- get
103103
- patch
104104
- update
105+
- apiGroups:
106+
- ""
107+
resources:
108+
- namespaces
109+
verbs:
110+
- get
111+
- list
112+
- watch
105113
- apiGroups:
106114
- ""
107115
resources:

helm/templates/cluster-role-controller.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,14 @@ rules:
117117
- get
118118
- patch
119119
- update
120+
- apiGroups:
121+
- ""
122+
resources:
123+
- namespaces
124+
verbs:
125+
- get
126+
- list
127+
- watch
120128
- apiGroups:
121129
- ""
122130
resources:

pkg/controllers/route_controller.go

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,14 @@ func (r *routeReconciler) validateRoute(ctx context.Context, route core.Route) e
482482
return fmt.Errorf("validate route: %w", err)
483483
}
484484

485+
if core.HasAllParentRefsRejected(route) {
486+
r.eventRecorder.Event(route.K8sObject(), corev1.EventTypeWarning,
487+
k8s.RouteEventReasonFailedBuildModel,
488+
"No VPC Lattice resources created. Route's parentRefs rejected by all Gateway listeners due to allowedRoutes policies. Check route status conditions for more detail.")
489+
return fmt.Errorf("%w: route has validation errors, see status", ErrValidation)
490+
}
491+
492+
// Additional broader validation check for any issues
485493
if r.hasNotAcceptedCondition(route) {
486494
return fmt.Errorf("%w: route has validation errors, see status", ErrValidation)
487495
}
@@ -509,8 +517,9 @@ func (r *routeReconciler) hasNotAcceptedCondition(route core.Route) bool {
509517
//
510518
// If parent GW exists will check:
511519
// - NoMatchingParent: parentRef sectionName and port matches Listener name and port
520+
// - NotAllowedByListeners: listener allowedRoutes.namespaces allows route
521+
// - NotAllowedByListeners: listener allowedRoutes.kinds contains route GroupKind
512522
// - TODO: NoMatchingListenerHostname: listener hostname matches one of route hostnames
513-
// - TODO: NotAllowedByListeners: listener allowedRoutes contains route GroupKind
514523
func (r *routeReconciler) validateRouteParentRefs(ctx context.Context, route core.Route) ([]gwv1.RouteParentStatus, error) {
515524
if len(route.Spec().ParentRefs()) == 0 {
516525
return nil, ErrParentRefsNotFound
@@ -525,6 +534,8 @@ func (r *routeReconciler) validateRouteParentRefs(ctx context.Context, route cor
525534
gw := gws[0]
526535
for _, parentRef := range route.Spec().ParentRefs() {
527536
noMatchingParent := true
537+
notAllowedByAnyMatchingListener := true
538+
528539
for _, listener := range gw.Spec.Listeners {
529540
if parentRef.Port != nil && *parentRef.Port != listener.Port {
530541
continue
@@ -533,6 +544,16 @@ func (r *routeReconciler) validateRouteParentRefs(ctx context.Context, route cor
533544
continue
534545
}
535546
noMatchingParent = false
547+
548+
allowed, err := core.IsRouteAllowedByListener(ctx, r.client, route, gw, listener)
549+
if err != nil {
550+
return nil, err
551+
}
552+
553+
if allowed {
554+
notAllowedByAnyMatchingListener = false
555+
break
556+
}
536557
}
537558

538559
parentStatus := gwv1.RouteParentStatus{
@@ -545,6 +566,9 @@ func (r *routeReconciler) validateRouteParentRefs(ctx context.Context, route cor
545566
switch {
546567
case noMatchingParent:
547568
cnd = r.newCondition(route, gwv1.RouteConditionAccepted, gwv1.RouteReasonNoMatchingParent, "")
569+
case notAllowedByAnyMatchingListener:
570+
cnd = r.newCondition(route, gwv1.RouteConditionAccepted, gwv1.RouteReasonNotAllowedByListeners,
571+
"No matching listeners allow this route. Check Gateway listener allowedRoutes policies")
548572
default:
549573
cnd = r.newCondition(route, gwv1.RouteConditionAccepted, gwv1.RouteReasonAccepted, "")
550574
}

pkg/gateway/model_build_lattice_service.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,11 @@ func (t *latticeServiceModelBuildTask) buildModel(ctx context.Context) error {
7474
return err
7575
}
7676

77+
if modelSvc == nil {
78+
t.log.Debugf(ctx, "Service creation skipped no further processing needed")
79+
return nil
80+
}
81+
7782
err = t.buildListeners(ctx, modelSvc.ID())
7883
if err != nil {
7984
return fmt.Errorf("failed to build listener due to %w", err)
@@ -103,6 +108,11 @@ func (t *latticeServiceModelBuildTask) buildModel(ctx context.Context) error {
103108
}
104109

105110
func (t *latticeServiceModelBuildTask) buildLatticeService(ctx context.Context) (*model.Service, error) {
111+
if core.HasAllParentRefsRejected(t.route) {
112+
t.log.Debugf(ctx, "Skipping VPC Lattice Service creation all parentRefs rejected")
113+
return nil, nil
114+
}
115+
106116
var routeType core.RouteType
107117
switch t.route.(type) {
108118
case *core.HTTPRoute:
@@ -141,7 +151,15 @@ func (t *latticeServiceModelBuildTask) buildLatticeService(ctx context.Context)
141151

142152
if !standalone {
143153
// Standard mode: populate ServiceNetworkNames from parent references
154+
155+
// For deduping ServiceNetworkNames since 2 parentRefs can point to same ServiceNetwork(Gateway)
156+
serviceNetworkSet := make(map[string]struct{})
144157
for _, parentRef := range t.route.Spec().ParentRefs() {
158+
if !core.IsParentRefAccepted(t.route, parentRef) {
159+
t.log.Debugf(ctx, "Skipping service network association for rejected parentRef %s", parentRef.Name)
160+
continue
161+
}
162+
145163
gw := &gwv1.Gateway{}
146164
parentNamespace := t.route.Namespace()
147165
if parentRef.Namespace != nil {
@@ -153,11 +171,16 @@ func (t *latticeServiceModelBuildTask) buildLatticeService(ctx context.Context)
153171
continue
154172
}
155173
if k8s.IsControlledByLatticeGatewayController(ctx, t.client, gw) {
156-
spec.ServiceNetworkNames = append(spec.ServiceNetworkNames, string(parentRef.Name))
174+
serviceNetworkSet[string(parentRef.Name)] = struct{}{}
157175
} else {
158176
t.log.Infof(ctx, "Ignoring route %s because gateway %s is not managed by lattice gateway controller", t.route.Name(), gw.Name)
159177
}
160178
}
179+
180+
for serviceNetwork := range serviceNetworkSet {
181+
spec.ServiceNetworkNames = append(spec.ServiceNetworkNames, serviceNetwork)
182+
}
183+
161184
if config.ServiceNetworkOverrideMode {
162185
spec.ServiceNetworkNames = []string{config.DefaultServiceNetwork}
163186
}

0 commit comments

Comments
 (0)