Skip to content

Commit aa2f693

Browse files
authored
feat(translator): relax backend restrictions for localhost when running standalone with Host infrastructure (#7427)
1 parent 1e295b6 commit aa2f693

12 files changed

+773
-36
lines changed

internal/gatewayapi/backend.go

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717

1818
egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1"
1919
"github.com/envoyproxy/gateway/internal/gatewayapi/status"
20+
"github.com/envoyproxy/gateway/internal/utils/net"
2021
)
2122

2223
func (t *Translator) ProcessBackends(backends []*egv1a1.Backend, backendTLSPolicies []*gwapiv1.BackendTLSPolicy) []*egv1a1.Backend {
@@ -27,7 +28,7 @@ func (t *Translator) ProcessBackends(backends []*egv1a1.Backend, backendTLSPolic
2728
status.UpdateBackendStatusAcceptedCondition(backend, false,
2829
"The Backend was not accepted since Backend is not enabled in Envoy Gateway Config")
2930
} else {
30-
if err := validateBackend(backend, backendTLSPolicies); err != nil {
31+
if err := validateBackend(backend, backendTLSPolicies, t.RunningOnHost); err != nil {
3132
status.UpdateBackendStatusAcceptedCondition(backend, false, fmt.Sprintf("The Backend was not accepted: %s", err.Error()))
3233
} else {
3334
status.UpdateBackendStatusAcceptedCondition(backend, true, "The Backend was accepted")
@@ -40,7 +41,7 @@ func (t *Translator) ProcessBackends(backends []*egv1a1.Backend, backendTLSPolic
4041
return res
4142
}
4243

43-
func validateBackend(backend *egv1a1.Backend, backendTLSPolicies []*gwapiv1.BackendTLSPolicy) status.Error {
44+
func validateBackend(backend *egv1a1.Backend, backendTLSPolicies []*gwapiv1.BackendTLSPolicy, runningOnHost bool) status.Error {
4445
if backend.Spec.Type != nil && *backend.Spec.Type == egv1a1.BackendTypeDynamicResolver {
4546
if len(backend.Spec.Endpoints) > 0 {
4647
return status.NewRouteStatusError(
@@ -57,13 +58,13 @@ func validateBackend(backend *egv1a1.Backend, backendTLSPolicies []*gwapiv1.Back
5758

5859
for _, ep := range backend.Spec.Endpoints {
5960
if ep.Hostname != nil {
60-
routeErr := validateHostname(*ep.Hostname, "hostname")
61+
routeErr := validateHostname(*ep.Hostname, "hostname", runningOnHost)
6162
if routeErr != nil {
6263
return routeErr
6364
}
6465
}
6566
if ep.FQDN != nil {
66-
routeErr := validateHostname(ep.FQDN.Hostname, "FQDN")
67+
routeErr := validateHostname(ep.FQDN.Hostname, "FQDN", runningOnHost)
6768
if routeErr != nil {
6869
return routeErr
6970
}
@@ -74,9 +75,9 @@ func validateBackend(backend *egv1a1.Backend, backendTLSPolicies []*gwapiv1.Back
7475
fmt.Errorf("IP address %s is invalid", ep.IP.Address),
7576
status.RouteReasonInvalidAddress,
7677
)
77-
} else if ip.IsLoopback() {
78+
} else if ip.IsLoopback() && !runningOnHost {
7879
return status.NewRouteStatusError(
79-
fmt.Errorf("IP address %s in the loopback range is not supported", ep.IP.Address),
80+
fmt.Errorf("IP address %s in the loopback range is only supported when using the Host infrastructure", ep.IP.Address),
8081
status.RouteReasonInvalidAddress,
8182
)
8283
}
@@ -169,15 +170,16 @@ func validateBackendTLSSettings(backend *egv1a1.Backend, backendTLSPolicies []*g
169170
return nil
170171
}
171172

172-
func validateHostname(hostname, typeName string) *status.RouteStatusError {
173+
func validateHostname(hostname, typeName string, allowLocalhost bool) *status.RouteStatusError {
173174
// must be a valid hostname
174175
if errs := validation.IsDNS1123Subdomain(hostname); errs != nil {
175176
return status.NewRouteStatusError(
176177
fmt.Errorf("hostname %s is not a valid %s", hostname, typeName),
177178
status.RouteReasonInvalidAddress,
178179
)
179180
}
180-
if len(strings.Split(hostname, ".")) < 2 {
181+
isLocalHostname := allowLocalhost && hostname == net.DefaultLocalAddress
182+
if !isLocalHostname && len(strings.Split(hostname, ".")) < 2 {
181183
return status.NewRouteStatusError(
182184
fmt.Errorf("hostname %s should be a domain with at least two segments separated by dots", hostname),
183185
status.RouteReasonInvalidAddress,

internal/gatewayapi/listener.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -891,7 +891,9 @@ func validCELExpression(expr string) bool {
891891
// servicePortToContainerPort translates a service port into an ephemeral
892892
// container port.
893893
func (t *Translator) servicePortToContainerPort(servicePort int32, envoyProxy *egv1a1.EnvoyProxy) int32 {
894-
if t.ListenerPortShiftDisabled {
894+
// When running on the local host using the Host infrastructure provider, disable translating the
895+
// gateway listener port into a non-privileged port and reuse the specified value.
896+
if t.RunningOnHost {
895897
return servicePort
896898
}
897899

internal/gatewayapi/runner/runner.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -146,17 +146,17 @@ func (r *Runner) subscribeAndTranslate(sub <-chan watchable.Snapshot[string, *re
146146
for _, resources := range *val {
147147
// Translate and publish IRs.
148148
t := &gatewayapi.Translator{
149-
GatewayControllerName: r.EnvoyGateway.Gateway.ControllerName,
150-
GatewayClassName: gwapiv1.ObjectName(resources.GatewayClass.Name),
151-
GlobalRateLimitEnabled: r.EnvoyGateway.RateLimit != nil,
152-
EnvoyPatchPolicyEnabled: r.EnvoyGateway.ExtensionAPIs != nil && r.EnvoyGateway.ExtensionAPIs.EnableEnvoyPatchPolicy,
153-
BackendEnabled: r.EnvoyGateway.ExtensionAPIs != nil && r.EnvoyGateway.ExtensionAPIs.EnableBackend,
154-
ControllerNamespace: r.ControllerNamespace,
155-
GatewayNamespaceMode: r.EnvoyGateway.GatewayNamespaceMode(),
156-
MergeGateways: gatewayapi.IsMergeGatewaysEnabled(resources),
157-
WasmCache: r.wasmCache,
158-
ListenerPortShiftDisabled: r.EnvoyGateway.Provider != nil && r.EnvoyGateway.Provider.IsRunningOnHost(),
159-
Logger: r.Logger,
149+
GatewayControllerName: r.EnvoyGateway.Gateway.ControllerName,
150+
GatewayClassName: gwapiv1.ObjectName(resources.GatewayClass.Name),
151+
GlobalRateLimitEnabled: r.EnvoyGateway.RateLimit != nil,
152+
EnvoyPatchPolicyEnabled: r.EnvoyGateway.ExtensionAPIs != nil && r.EnvoyGateway.ExtensionAPIs.EnableEnvoyPatchPolicy,
153+
BackendEnabled: r.EnvoyGateway.ExtensionAPIs != nil && r.EnvoyGateway.ExtensionAPIs.EnableBackend,
154+
ControllerNamespace: r.ControllerNamespace,
155+
GatewayNamespaceMode: r.EnvoyGateway.GatewayNamespaceMode(),
156+
MergeGateways: gatewayapi.IsMergeGatewaysEnabled(resources),
157+
WasmCache: r.wasmCache,
158+
RunningOnHost: r.EnvoyGateway.Provider != nil && r.EnvoyGateway.Provider.IsRunningOnHost(),
159+
Logger: r.Logger,
160160
}
161161

162162
// If an extension is loaded, pass its supported groups/kinds to the translator

internal/gatewayapi/testdata/backend-invalid-hostname-address.out.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ backends:
4848
conditions:
4949
- lastTransitionTime: null
5050
message: 'The Backend was not accepted: IP address 127.0.0.3 in the loopback
51-
range is not supported'
51+
range is only supported when using the Host infrastructure'
5252
reason: Accepted
5353
status: "False"
5454
type: Invalid
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
backends:
2+
- apiVersion: gateway.envoyproxy.io/v1alpha1
3+
kind: Backend
4+
metadata:
5+
name: backend-fqdn
6+
namespace: default
7+
spec:
8+
endpoints:
9+
- fqdn:
10+
hostname: 'localhost'
11+
port: 3000
12+
- apiVersion: gateway.envoyproxy.io/v1alpha1
13+
kind: Backend
14+
metadata:
15+
name: backend-fqdn
16+
namespace: default
17+
spec:
18+
endpoints:
19+
- ip:
20+
address: '127.0.0.3'
21+
port: 3000
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
backends:
2+
- apiVersion: gateway.envoyproxy.io/v1alpha1
3+
kind: Backend
4+
metadata:
5+
name: backend-fqdn
6+
namespace: default
7+
spec:
8+
endpoints:
9+
- fqdn:
10+
hostname: localhost
11+
port: 3000
12+
status:
13+
conditions:
14+
- lastTransitionTime: null
15+
message: The Backend was accepted
16+
reason: Accepted
17+
status: "True"
18+
type: Accepted
19+
- apiVersion: gateway.envoyproxy.io/v1alpha1
20+
kind: Backend
21+
metadata:
22+
name: backend-fqdn
23+
namespace: default
24+
spec:
25+
endpoints:
26+
- ip:
27+
address: 127.0.0.3
28+
port: 3000
29+
status:
30+
conditions:
31+
- lastTransitionTime: null
32+
message: The Backend was accepted
33+
reason: Accepted
34+
status: "True"
35+
type: Accepted
36+
infraIR: {}
37+
xdsIR: {}
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
gateways:
2+
- apiVersion: gateway.networking.k8s.io/v1
3+
kind: Gateway
4+
metadata:
5+
namespace: envoy-gateway
6+
name: gateway-1
7+
spec:
8+
gatewayClassName: envoy-gateway-class
9+
listeners:
10+
- name: http
11+
protocol: HTTP
12+
port: 80
13+
allowedRoutes:
14+
namespaces:
15+
from: All
16+
httpRoutes:
17+
- apiVersion: gateway.networking.k8s.io/v1
18+
kind: HTTPRoute
19+
metadata:
20+
namespace: default
21+
name: httproute-1
22+
spec:
23+
parentRefs:
24+
- namespace: envoy-gateway
25+
name: gateway-1
26+
sectionName: http
27+
rules:
28+
- matches:
29+
- path:
30+
value: "/1"
31+
backendRefs:
32+
- group: gateway.envoyproxy.io
33+
kind: Backend
34+
name: backend-uds
35+
- apiVersion: gateway.networking.k8s.io/v1
36+
kind: HTTPRoute
37+
metadata:
38+
namespace: default
39+
name: httproute-3
40+
spec:
41+
parentRefs:
42+
- namespace: envoy-gateway
43+
name: gateway-1
44+
sectionName: http
45+
rules:
46+
- matches:
47+
- path:
48+
value: "/3"
49+
backendRefs:
50+
- group: gateway.envoyproxy.io
51+
kind: Backend
52+
name: backend-fqdn
53+
- apiVersion: gateway.networking.k8s.io/v1
54+
kind: HTTPRoute
55+
metadata:
56+
namespace: default
57+
name: httproute-2
58+
spec:
59+
parentRefs:
60+
- namespace: envoy-gateway
61+
name: gateway-1
62+
sectionName: http
63+
rules:
64+
- matches:
65+
- path:
66+
value: "/2"
67+
backendRefs:
68+
- group: gateway.envoyproxy.io
69+
kind: Backend
70+
name: backend-ip
71+
- apiVersion: gateway.networking.k8s.io/v1
72+
kind: HTTPRoute
73+
metadata:
74+
namespace: default
75+
name: httproute-4
76+
spec:
77+
parentRefs:
78+
- namespace: envoy-gateway
79+
name: gateway-1
80+
sectionName: http
81+
rules:
82+
- matches:
83+
- path:
84+
value: "/4"
85+
backendRefs:
86+
- group: gateway.envoyproxy.io
87+
kind: Backend
88+
name: backend-ip-localhost
89+
- apiVersion: gateway.networking.k8s.io/v1
90+
kind: HTTPRoute
91+
metadata:
92+
namespace: default
93+
name: httproute-5
94+
spec:
95+
parentRefs:
96+
- namespace: envoy-gateway
97+
name: gateway-1
98+
sectionName: http
99+
rules:
100+
- matches:
101+
- path:
102+
value: "/5"
103+
backendRefs:
104+
- group: gateway.envoyproxy.io
105+
kind: Backend
106+
name: backend-fqdn-localhost
107+
backends:
108+
- apiVersion: gateway.envoyproxy.io/v1alpha1
109+
kind: Backend
110+
metadata:
111+
name: backend-uds
112+
namespace: default
113+
spec:
114+
endpoints:
115+
- unix:
116+
path: /var/run/backend.sock
117+
- apiVersion: gateway.envoyproxy.io/v1alpha1
118+
kind: Backend
119+
metadata:
120+
name: backend-fqdn
121+
namespace: default
122+
spec:
123+
endpoints:
124+
- fqdn:
125+
hostname: primary.foo.com
126+
port: 3000
127+
- apiVersion: gateway.envoyproxy.io/v1alpha1
128+
kind: Backend
129+
metadata:
130+
name: backend-ip
131+
namespace: default
132+
spec:
133+
endpoints:
134+
- ip:
135+
address: 1.1.1.1
136+
port: 3001
137+
- apiVersion: gateway.envoyproxy.io/v1alpha1
138+
kind: Backend
139+
metadata:
140+
name: backend-ip-localhost
141+
namespace: default
142+
spec:
143+
endpoints:
144+
- ip:
145+
address: 127.0.0.1
146+
port: 3001
147+
- apiVersion: gateway.envoyproxy.io/v1alpha1
148+
kind: Backend
149+
metadata:
150+
name: backend-fqdn-localhost
151+
namespace: default
152+
spec:
153+
endpoints:
154+
- fqdn:
155+
hostname: localhost
156+
port: 3001

0 commit comments

Comments
 (0)