Skip to content

Commit eb65bc0

Browse files
zirainrudrakhpAndyMorelandzhaohuabingwoodgear
authored
[release-1.5] cherry-pick for v1.5.5 (envoyproxy#7509)
* fix: bug in overlap detection of cert SANs (envoyproxy#7234) Signed-off-by: zirain <[email protected]> * fix(translator): Fix panic with request mirror + grpcroute (envoyproxy#6875) Signed-off-by: Andrew Moreland <[email protected]> Signed-off-by: zirain <[email protected]> * fix: watch change for the ca cert in the Backend (envoyproxy#7294) * watch change for the ca cert in the Backend Signed-off-by: Huabing Zhao <[email protected]> Signed-off-by: zirain <[email protected]> * fix ipFamily not set in UDPListener (envoyproxy#7313) fix: set ipfamily in udpistener (envoyproxy#7312) Signed-off-by: cong <[email protected]> Signed-off-by: zirain <[email protected]> * coalesce updates to reduce intermediate updates (envoyproxy#7328) * coalesce updates to reduce redundant processing in subscription handler Signed-off-by: Huabing Zhao <[email protected]> * retain order Signed-off-by: Huabing Zhao <[email protected]> * keep intermediate delete updates Signed-off-by: Huabing Zhao <[email protected]> * minor wording Signed-off-by: Huabing Zhao <[email protected]> * treat delete as normal operations Signed-off-by: Huabing Zhao <[email protected]> * retain the original order of the last updates for each key Signed-off-by: Huabing Zhao <[email protected]> * address comments Signed-off-by: Huabing Zhao <[email protected]> * fix test Signed-off-by: Huabing Zhao <[email protected]> --------- Signed-off-by: Huabing Zhao <[email protected]> Signed-off-by: zirain <[email protected]> * fix: port typo (envoyproxy#7397) Signed-off-by: cong <[email protected]> Signed-off-by: zirain <[email protected]> * fix: validate EnvoyGateway configuration before reload (envoyproxy#7412) Signed-off-by: zirain <[email protected]> * fix: missing jwt provider when jwt is configured on multiple listeners sharing the same port (envoyproxy#7337) * fix jwt provider missing when jwt is configured at multiple ir listeners Signed-off-by: Huabing Zhao <[email protected]> Signed-off-by: zirain <[email protected]> * fix: memory leak (envoyproxy#7429) Fix memory leak. Two watchable.Maps were never closed when shutting down the provider: - GatewayClassStatuses.Close() - missing in GatewayAPIStatuses.Close() - BackendTrafficPolicyStatuses.Close() - missing in PolicyStatuses.Close() Each unclosed map leaked 3 goroutines: 1. Internal watchable.Map.coalesce goroutine 2. HandleSubscription goroutine blocked on channel read 3. Error handler goroutine blocked on channel read Signed-off-by: Gonzalo Serrano <[email protected]> Signed-off-by: zirain <[email protected]> * fix gen after cherry-pick Signed-off-by: zirain <[email protected]> * fix watchutil test Signed-off-by: Huabing Zhao <[email protected]> --------- Signed-off-by: zirain <[email protected]> Signed-off-by: Andrew Moreland <[email protected]> Signed-off-by: Huabing Zhao <[email protected]> Signed-off-by: cong <[email protected]> Signed-off-by: Gonzalo Serrano <[email protected]> Co-authored-by: Rudrakh Panigrahi <[email protected]> Co-authored-by: Andrew Moreland <[email protected]> Co-authored-by: Huabing (Robin) Zhao <[email protected]> Co-authored-by: 聪 <[email protected]> Co-authored-by: Gonzalo Serrano <[email protected]>
1 parent ba98d75 commit eb65bc0

20 files changed

+977
-73
lines changed

internal/envoygateway/config/loader/configloader.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"io"
1111
"time"
1212

13+
"github.com/envoyproxy/gateway/api/v1alpha1/validation"
1314
"github.com/envoyproxy/gateway/internal/envoygateway/config"
1415
"github.com/envoyproxy/gateway/internal/filewatcher"
1516
"github.com/envoyproxy/gateway/internal/logging"
@@ -67,6 +68,12 @@ func (r *Loader) Start(ctx context.Context, logOut io.Writer) error {
6768
// TODO: add a metric for this?
6869
continue
6970
}
71+
72+
if err := validation.ValidateEnvoyGateway(eg); err != nil {
73+
r.logger.Error(err, "failed to validate EnvoyGateway config")
74+
continue
75+
}
76+
7077
// Set defaults for unset fields
7178
eg.SetEnvoyGatewayDefaults()
7279
r.cfg.EnvoyGateway = eg

internal/gatewayapi/filters.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -945,22 +945,22 @@ func (t *Translator) processRequestMirrorFilter(
945945
return nil
946946
}
947947

948+
// Get the route type from the filter context to determine the correct BackendRef type
949+
routeType := filterContext.Route.GetRouteType()
948950
weight := int32(1)
949951
mirrorBackend := mirrorFilter.BackendRef
950-
951-
// Create a DirectBackendRef for the mirror backend (no filters needed)
952952
mirrorBackendRef := DirectBackendRef{
953953
BackendRef: &gwapiv1.BackendRef{
954-
BackendObjectReference: mirrorBackend,
954+
BackendObjectReference: mirrorFilter.BackendRef,
955955
Weight: &weight,
956956
},
957957
}
958958

959-
// This sets the status on the HTTPRoute, should the usage be changed so that the status message reflects that the backendRef is from the filter?
959+
// This sets the status on the Route, should the usage be changed so that the status message reflects that the backendRef is from the filter?
960960
filterNs := filterContext.Route.GetNamespace()
961961
serviceNamespace := NamespaceDerefOr(mirrorBackend.Namespace, filterNs)
962962
err = t.validateBackendRef(mirrorBackendRef, filterContext.Route,
963-
resources, serviceNamespace, resource.KindHTTPRoute)
963+
resources, serviceNamespace, routeType)
964964
if err != nil {
965965
return status.NewRouteStatusError(
966966
fmt.Errorf("failed to validate the RequestMirror filter: %w", err), err.Reason()).WithType(gwapiv1.RouteConditionResolvedRefs)

internal/gatewayapi/listener.go

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ func (t *Translator) ProcessListeners(gateways []*GatewayContext, xdsIR resource
168168
Address: address,
169169
Port: uint32(containerPort),
170170
ExternalPort: uint32(listener.Port),
171+
IPFamily: ipFamily,
171172
},
172173
}
173174
xdsIR[irKey].UDP = append(xdsIR[irKey].UDP, irListener)
@@ -244,7 +245,7 @@ func checkOverlappingHostnames(httpsListeners []*ListenerContext) {
244245
if httpsListeners[i].Port != httpsListeners[j].Port {
245246
continue
246247
}
247-
if isOverlappingHostname(httpsListeners[i].Hostname, httpsListeners[j].Hostname) {
248+
if areOverlappingHostnames(httpsListeners[i].Hostname, httpsListeners[j].Hostname) {
248249
// Overlapping listeners can be more than two, we only report the first two for simplicity.
249250
overlappingListeners[i] = &overlappingListener{
250251
gateway1: httpsListeners[i].gateway,
@@ -396,7 +397,7 @@ type overlappingCertificate struct {
396397
func isOverlappingCertificate(cert1DNSNames, cert2DNSNames []string) *overlappingCertificate {
397398
for _, dns1 := range cert1DNSNames {
398399
for _, dns2 := range cert2DNSNames {
399-
if isOverlappingHostname(ptr.To(gwapiv1.Hostname(dns1)), ptr.To(gwapiv1.Hostname(dns2))) {
400+
if areOverlappingHostnames(ptr.To(gwapiv1.Hostname(dns1)), ptr.To(gwapiv1.Hostname(dns2))) {
400401
return &overlappingCertificate{
401402
san1: dns1,
402403
san2: dns2,
@@ -407,22 +408,31 @@ func isOverlappingCertificate(cert1DNSNames, cert2DNSNames []string) *overlappin
407408
return nil
408409
}
409410

410-
// isOverlappingHostname checks if two hostnames overlap.
411-
func isOverlappingHostname(hostname1, hostname2 *gwapiv1.Hostname) bool {
412-
if hostname1 == nil || hostname2 == nil {
411+
func areOverlappingHostnames(this, other *gwapiv1.Hostname) bool {
412+
if this == nil || other == nil {
413413
return true
414414
}
415-
domain1 := strings.Replace(string(*hostname1), "*.", "", 1)
416-
domain2 := strings.Replace(string(*hostname2), "*.", "", 1)
417-
return isSubdomain(domain1, domain2) || isSubdomain(domain2, domain1)
415+
return hostnameMatchesWithOther(this, other) || hostnameMatchesWithOther(other, this)
418416
}
419417

420-
// isSubdomain checks if subDomain is a sub-domain of domain
421-
func isSubdomain(subDomain, domain string) bool {
422-
if subDomain == domain {
423-
return true
418+
// hostnameMatchesWithOther returns true if this hostname matches other hostname.
419+
// Assumes that hostnames will either be fully qualified or a wildcard hostname prefixed with a single wildcard.
420+
// E.g. "*.*.example.com" is not valid.
421+
func hostnameMatchesWithOther(this, other *gwapiv1.Hostname) bool {
422+
thisString := string(*this)
423+
otherString := string(*other)
424+
if hasWildcardPrefix(other) && !hasWildcardPrefix(this) {
425+
return strings.HasSuffix(thisString, otherString[1:]) &&
426+
!strings.Contains(strings.TrimSuffix(thisString, otherString[1:]), ".") // not a subdomain
427+
}
428+
return thisString == otherString
429+
}
430+
431+
func hasWildcardPrefix(h *gwapiv1.Hostname) bool {
432+
if h == nil {
433+
return false
424434
}
425-
return strings.HasSuffix(subDomain, fmt.Sprintf(".%s", domain))
435+
return len(string(*h)) > 1 && string(*h)[0] == '*'
426436
}
427437

428438
func buildListenerMetadata(listener *ListenerContext, gateway *GatewayContext) *ir.ResourceMetadata {

internal/gatewayapi/listener_test.go

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ func TestProxySamplingRate(t *testing.T) {
100100
}
101101
}
102102

103-
func TestIsOverlappingHostname(t *testing.T) {
103+
func TestAreOverlappingHostnames(t *testing.T) {
104104
tests := []struct {
105105
name string
106106
hostname1 *gwapiv1.Hostname
@@ -120,10 +120,10 @@ func TestIsOverlappingHostname(t *testing.T) {
120120
want: true,
121121
},
122122
{
123-
name: "two wildcards matches subdomain",
123+
name: "two wildcards with subdomain does not match",
124124
hostname1: ptr.To(gwapiv1.Hostname("*.example.com")),
125125
hostname2: ptr.To(gwapiv1.Hostname("*.test.example.com")),
126-
want: true,
126+
want: false,
127127
},
128128
{
129129
name: "nil hostname matches all",
@@ -144,22 +144,22 @@ func TestIsOverlappingHostname(t *testing.T) {
144144
want: true,
145145
},
146146
{
147-
name: "wildcard matches exact",
147+
name: "wildcard matches exactly one level of subdomain",
148148
hostname1: ptr.To(gwapiv1.Hostname("*.example.com")),
149149
hostname2: ptr.To(gwapiv1.Hostname("test.example.com")),
150150
want: true,
151151
},
152152
{
153-
name: "wildcard matches subdomain",
153+
name: "wildcard matches only one level of subdomain",
154154
hostname1: ptr.To(gwapiv1.Hostname("*.example.com")),
155155
hostname2: ptr.To(gwapiv1.Hostname("sub.test.example.com")),
156-
want: true,
156+
want: false,
157157
},
158158
{
159-
name: "wildcard matches empty subdomain",
159+
name: "wildcard does not match empty subdomain",
160160
hostname1: ptr.To(gwapiv1.Hostname("*.example.com")),
161161
hostname2: ptr.To(gwapiv1.Hostname("example.com")),
162-
want: true,
162+
want: false,
163163
},
164164
{
165165
name: "different domains",
@@ -185,15 +185,21 @@ func TestIsOverlappingHostname(t *testing.T) {
185185
hostname2: ptr.To(gwapiv1.Hostname("testing-api.foo.dev")),
186186
want: false,
187187
},
188+
{
189+
name: "sub domain does not match with parent domain",
190+
hostname1: ptr.To(gwapiv1.Hostname("api.foo.dev")),
191+
hostname2: ptr.To(gwapiv1.Hostname("foo.dev")),
192+
want: false,
193+
},
188194
}
189195

190196
for _, tt := range tests {
191197
t.Run(tt.name, func(t *testing.T) {
192-
if got := isOverlappingHostname(tt.hostname1, tt.hostname2); got != tt.want {
198+
if got := areOverlappingHostnames(tt.hostname1, tt.hostname2); got != tt.want {
193199
t.Errorf("isOverlappingHostname(%q, %q) = %v, want %v", ptr.Deref(tt.hostname1, ""), ptr.Deref(tt.hostname2, ""), got, tt.want)
194200
}
195201
// Test should be symmetric
196-
if got := isOverlappingHostname(tt.hostname2, tt.hostname1); got != tt.want {
202+
if got := areOverlappingHostnames(tt.hostname2, tt.hostname1); got != tt.want {
197203
t.Errorf("isOverlappingHostname(%q, %q) = %v, want %v", ptr.Deref(tt.hostname2, ""), ptr.Deref(tt.hostname1, ""), got, tt.want)
198204
}
199205
})
@@ -306,15 +312,14 @@ func TestCheckOverlappingHostnames(t *testing.T) {
306312
Name: "listener-3",
307313
Protocol: gwapiv1.HTTPSProtocolType,
308314
Port: 443,
309-
Hostname: ptr.To(gwapiv1.Hostname("sub.test.example.com")),
315+
Hostname: ptr.To(gwapiv1.Hostname("sub.test.example.com")), // sub domain does not match with parent domain
310316
},
311317
},
312318
},
313319
},
314320
expected: map[int]string{
315321
0: "test.example.com",
316322
1: "*.example.com",
317-
2: "*.example.com",
318323
},
319324
},
320325
{
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
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+
grpcRoutes:
17+
- apiVersion: gateway.networking.k8s.io/v1alpha2
18+
kind: GRPCRoute
19+
metadata:
20+
namespace: default
21+
name: grpcroute-1
22+
spec:
23+
parentRefs:
24+
- namespace: envoy-gateway
25+
name: gateway-1
26+
sectionName: http
27+
rules:
28+
- backendRefs:
29+
- name: service-1
30+
port: 8080
31+
filters:
32+
- type: RequestMirror
33+
requestMirror:
34+
backendRef:
35+
kind: Service
36+
name: service-2
37+
port: 8080

0 commit comments

Comments
 (0)