Skip to content

Commit 7d26857

Browse files
authored
Add support for backendref service appProtocol (#3511)
Add support for backendref service appProtocol Problem: Users who want to specify the appProtocol on a Service port should correctly have checks in place so traffic does not flow to those services that do not have the matching appProtocol. Solution: Mark BackendRefs on routes that cannot send traffic using the specified protocol as invalid and support new NewRouteBackendRefUnsupportedProtocol condition.
1 parent fb42edd commit 7d26857

File tree

12 files changed

+592
-36
lines changed

12 files changed

+592
-36
lines changed

internal/controller/manager.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,7 @@ func registerControllers(
440440
objectType: &apiv1.Service{},
441441
name: "user-service", // unique controller names are needed and we have multiple Service ctlrs
442442
options: []controller.Option{
443-
controller.WithK8sPredicate(predicate.ServicePortsChangedPredicate{}),
443+
controller.WithK8sPredicate(predicate.ServiceChangedPredicate{}),
444444
},
445445
},
446446
{

internal/controller/state/conditions/conditions.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,17 @@ func NewRouteBackendRefUnsupportedValue(msg string) Condition {
423423
}
424424
}
425425

426+
// NewRouteBackendRefUnsupportedProtocol returns a Condition that indicates that the Route has a backendRef with
427+
// an unsupported protocol.
428+
func NewRouteBackendRefUnsupportedProtocol(msg string) Condition {
429+
return Condition{
430+
Type: string(v1.RouteConditionResolvedRefs),
431+
Status: metav1.ConditionFalse,
432+
Reason: string(v1.RouteReasonUnsupportedProtocol),
433+
Message: msg,
434+
}
435+
}
436+
426437
// NewRouteInvalidGateway returns a Condition that indicates that the Route is not Accepted because the Gateway it
427438
// references is invalid.
428439
func NewRouteInvalidGateway() Condition {

internal/controller/state/graph/backend_refs.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ import (
1717
"github.com/nginx/nginx-gateway-fabric/internal/framework/helpers"
1818
)
1919

20+
const (
21+
AppProtocolTypeH2C string = "kubernetes.io/h2c"
22+
AppProtocolTypeWS string = "kubernetes.io/ws"
23+
AppProtocolTypeWSS string = "kubernetes.io/wss"
24+
)
25+
2026
// BackendRef is an internal representation of a backendRef in an HTTP/GRPC/TLSRoute.
2127
type BackendRef struct {
2228
// BackendTLSPolicy is the BackendTLSPolicy of the Service which is referenced by the backendRef.
@@ -200,6 +206,23 @@ func createBackendRef(
200206
return backendRef, append(conds, conditions.NewRouteBackendRefUnsupportedValue(err.Error()))
201207
}
202208

209+
if svcPort.AppProtocol != nil {
210+
err = validateRouteBackendRefAppProtocol(route.RouteType, *svcPort.AppProtocol, backendTLSPolicy)
211+
if err != nil {
212+
backendRef := BackendRef{
213+
SvcNsName: svcNsName,
214+
BackendTLSPolicy: backendTLSPolicy,
215+
ServicePort: svcPort,
216+
Weight: weight,
217+
Valid: false,
218+
IsMirrorBackend: ref.MirrorBackendIdx != nil,
219+
InvalidForGateways: invalidForGateways,
220+
}
221+
222+
return backendRef, append(conds, conditions.NewRouteBackendRefUnsupportedProtocol(err.Error()))
223+
}
224+
}
225+
203226
backendRef := BackendRef{
204227
SvcNsName: svcNsName,
205228
BackendTLSPolicy: backendTLSPolicy,
@@ -414,6 +437,56 @@ func validateBackendRef(
414437
return true, conditions.Condition{}
415438
}
416439

440+
// validateRouteBackendRefAppProtocol checks if a given RouteType supports sending traffic to a service AppProtocol.
441+
// Returns nil if true or AppProtocol is not a Kubernetes Standard Application Protocol.
442+
func validateRouteBackendRefAppProtocol(
443+
routeType RouteType,
444+
appProtocol string,
445+
backendTLSPolicy *BackendTLSPolicy,
446+
) error {
447+
err := fmt.Errorf(
448+
"route type %s does not support service port appProtocol %s",
449+
routeType,
450+
appProtocol,
451+
)
452+
453+
// Currently we only support recognition of the Kubernetes Standard Application Protocols defined in KEP-3726.
454+
switch appProtocol {
455+
case AppProtocolTypeH2C:
456+
if routeType == RouteTypeGRPC {
457+
return nil
458+
}
459+
460+
if routeType == RouteTypeHTTP {
461+
return fmt.Errorf("%w; nginx does not support proxying to upstreams with http2 or h2c", err)
462+
}
463+
464+
return err
465+
case AppProtocolTypeWS:
466+
if routeType == RouteTypeHTTP {
467+
return nil
468+
}
469+
470+
return err
471+
case AppProtocolTypeWSS:
472+
if routeType == RouteTypeHTTP {
473+
if backendTLSPolicy != nil {
474+
return nil
475+
}
476+
477+
return fmt.Errorf("%w; missing corresponding BackendTLSPolicy", err)
478+
}
479+
480+
if routeType == RouteTypeTLS {
481+
return nil
482+
}
483+
484+
return err
485+
}
486+
487+
return nil
488+
}
489+
417490
func validateWeight(weight int32) error {
418491
const (
419492
minWeight = 0

0 commit comments

Comments
 (0)