Skip to content

Commit

Permalink
feat: gateway http listener isolation (#4000)
Browse files Browse the repository at this point in the history
Signed-off-by: Kobi Levi <[email protected]>
  • Loading branch information
levikobi authored Aug 15, 2024
1 parent 8ba1e0a commit 97830e9
Show file tree
Hide file tree
Showing 6 changed files with 345 additions and 15 deletions.
1 change: 0 additions & 1 deletion internal/gatewayapi/conformance/suite.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import (
// SkipTests is a list of tests that are skipped in the conformance suite.
var SkipTests = []suite.ConformanceTest{
tests.GatewayStaticAddresses,
tests.GatewayHTTPListenerIsolation, // https://github.com/envoyproxy/gateway/issues/3352
}

func skipTestsShortNames(skipTests []suite.ConformanceTest) []string {
Expand Down
45 changes: 35 additions & 10 deletions internal/gatewayapi/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,12 +262,12 @@ func servicePortToContainerPort(servicePort int32, envoyProxy *egv1a1.EnvoyProxy
return servicePort
}

// computeHosts returns a list of the intersecting hostnames between the route
// and the listener.
func computeHosts(routeHostnames []string, listenerHostname *gwapiv1.Hostname) []string {
// computeHosts returns a list of intersecting listener hostnames and route hostnames
// that don't intersect with other listener hostnames.
func computeHosts(routeHostnames []string, listenerContext *ListenerContext) []string {
var listenerHostnameVal string
if listenerHostname != nil {
listenerHostnameVal = string(*listenerHostname)
if listenerContext != nil && listenerContext.Hostname != nil {
listenerHostnameVal = string(*listenerContext.Hostname)
}

// No route hostnames specified: use the listener hostname if specified,
Expand All @@ -280,8 +280,9 @@ func computeHosts(routeHostnames []string, listenerHostname *gwapiv1.Hostname) [
return []string{"*"}
}

var hostnames []string
hostnamesSet := map[string]struct{}{}

// Find intersecting hostnames
for i := range routeHostnames {
routeHostname := routeHostnames[i]

Expand All @@ -290,27 +291,51 @@ func computeHosts(routeHostnames []string, listenerHostname *gwapiv1.Hostname) [
switch {
// No listener hostname: use the route hostname.
case len(listenerHostnameVal) == 0:
hostnames = append(hostnames, routeHostname)
hostnamesSet[routeHostname] = struct{}{}

// Listener hostname matches the route hostname: use it.
case listenerHostnameVal == routeHostname:
hostnames = append(hostnames, routeHostname)
hostnamesSet[routeHostname] = struct{}{}

// Listener has a wildcard hostname: check if the route hostname matches.
case strings.HasPrefix(listenerHostnameVal, "*"):
if hostnameMatchesWildcardHostname(routeHostname, listenerHostnameVal) {
hostnames = append(hostnames, routeHostname)
hostnamesSet[routeHostname] = struct{}{}
}

// Route has a wildcard hostname: check if the listener hostname matches.
case strings.HasPrefix(routeHostname, "*"):
if hostnameMatchesWildcardHostname(listenerHostnameVal, routeHostname) {
hostnames = append(hostnames, listenerHostnameVal)
hostnamesSet[listenerHostnameVal] = struct{}{}
}

}
}

// Filter out route hostnames that intersect with other listener hostnames
var listeners []*ListenerContext
if listenerContext != nil && listenerContext.gateway != nil {
listeners = listenerContext.gateway.listeners
}

for _, listener := range listeners {
if listenerContext == listener {
continue
}
if listenerContext != nil && listenerContext.Port != listener.Port {
continue
}
if listener.Hostname == nil {
continue
}
delete(hostnamesSet, string(*listener.Hostname))
}

var hostnames []string
for host := range hostnamesSet {
hostnames = append(hostnames, host)
}

return hostnames
}

Expand Down
4 changes: 2 additions & 2 deletions internal/gatewayapi/route.go
Original file line number Diff line number Diff line change
Expand Up @@ -697,7 +697,7 @@ func (t *Translator) processHTTPRouteParentRefListener(route RouteContext, route
var hasHostnameIntersection bool

for _, listener := range parentRef.listeners {
hosts := computeHosts(GetHostnames(route), listener.Hostname)
hosts := computeHosts(GetHostnames(route), listener)
if len(hosts) == 0 {
continue
}
Expand Down Expand Up @@ -864,7 +864,7 @@ func (t *Translator) processTLSRouteParentRefs(tlsRoute *TLSRouteContext, resour

var hasHostnameIntersection bool
for _, listener := range parentRef.listeners {
hosts := computeHosts(GetHostnames(tlsRoute), listener.Hostname)
hosts := computeHosts(GetHostnames(tlsRoute), listener)
if len(hosts) == 0 {
continue
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
gateways:
- apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: gateway-1
namespace: envoy-gateway
spec:
gatewayClassName: envoy-gateway-class
listeners:
- name: empty-hostname
port: 80
protocol: HTTP
allowedRoutes:
namespaces:
from: All
- name: wildcard-example-com
port: 80
protocol: HTTP
hostname: "*.example.com"
allowedRoutes:
namespaces:
from: All
httpRoutes:
- apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: httproute-1
namespace: envoy-gateway
spec:
parentRefs:
- name: gateway-1
namespace: envoy-gateway
sectionName: empty-hostname
hostnames:
- "bar.com"
- "*.example.com" # request matching is prevented by the isolation wildcard-example-com listener

Check warning on line 36 in internal/gatewayapi/testdata/gateway-http-listener-with-hostname-intersection.in.yaml

View workflow job for this annotation

GitHub Actions / lint

36:27 [comments] too few spaces before comment

Check warning on line 36 in internal/gatewayapi/testdata/gateway-http-listener-with-hostname-intersection.in.yaml

View workflow job for this annotation

GitHub Actions / lint

36:27 [comments] too few spaces before comment
rules:
- matches:
- path:
type: PathPrefix
value: /empty-hostname
backendRefs:
- name: service-1
port: 8080
- apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: httproute-2
namespace: envoy-gateway
spec:
parentRefs:
- name: gateway-1
namespace: envoy-gateway
sectionName: wildcard-example-com
hostnames:
- "bar.com" # doesn't match wildcard-example-com listener

Check warning on line 56 in internal/gatewayapi/testdata/gateway-http-listener-with-hostname-intersection.in.yaml

View workflow job for this annotation

GitHub Actions / lint

56:21 [comments] too few spaces before comment

Check warning on line 56 in internal/gatewayapi/testdata/gateway-http-listener-with-hostname-intersection.in.yaml

View workflow job for this annotation

GitHub Actions / lint

56:21 [comments] too few spaces before comment
- "*.example.com"
rules:
- matches:
- path:
type: PathPrefix
value: /wildcard-example-com
backendRefs:
- name: service-1
port: 8080
Loading

0 comments on commit 97830e9

Please sign in to comment.