mirror of https://github.com/grpc/grpc-go.git
1860 lines
60 KiB
Go
1860 lines
60 KiB
Go
/*
|
|
*
|
|
* Copyright 2021 gRPC authors.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package xdsresource
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
v2xdspb "github.com/envoyproxy/go-control-plane/envoy/api/v2"
|
|
"github.com/google/go-cmp/cmp"
|
|
"google.golang.org/grpc/internal/pretty"
|
|
"google.golang.org/grpc/internal/testutils"
|
|
"google.golang.org/grpc/internal/testutils/xds/e2e"
|
|
"google.golang.org/grpc/xds/internal/httpfilter"
|
|
"google.golang.org/grpc/xds/internal/xdsclient/xdsresource/version"
|
|
"google.golang.org/protobuf/proto"
|
|
"google.golang.org/protobuf/types/known/anypb"
|
|
"google.golang.org/protobuf/types/known/durationpb"
|
|
"google.golang.org/protobuf/types/known/structpb"
|
|
"google.golang.org/protobuf/types/known/wrapperspb"
|
|
|
|
v1xdsudpatypepb "github.com/cncf/xds/go/udpa/type/v1"
|
|
v3xdsxdstypepb "github.com/cncf/xds/go/xds/type/v3"
|
|
v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
|
|
v3listenerpb "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
|
|
rpb "github.com/envoyproxy/go-control-plane/envoy/config/rbac/v3"
|
|
v3routepb "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
|
|
v3rbacpb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/rbac/v3"
|
|
v3httppb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
|
|
v3tlspb "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
|
|
v3discoverypb "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
|
|
v3matcherpb "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3"
|
|
|
|
_ "google.golang.org/grpc/xds/internal/httpfilter/rbac" // Register the RBAC HTTP filter.
|
|
_ "google.golang.org/grpc/xds/internal/httpfilter/router" // Register the router filter.
|
|
)
|
|
|
|
func (s) TestUnmarshalListener_ClientSide(t *testing.T) {
|
|
const (
|
|
v3LDSTarget = "lds.target.good:3333"
|
|
v3RouteConfigName = "v3RouteConfig"
|
|
routeName = "routeName"
|
|
)
|
|
|
|
var (
|
|
customFilter = &v3httppb.HttpFilter{
|
|
Name: "customFilter",
|
|
ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: customFilterConfig},
|
|
}
|
|
oldTypedStructFilter = &v3httppb.HttpFilter{
|
|
Name: "customFilter",
|
|
ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: testutils.MarshalAny(t, customFilterOldTypedStructConfig)},
|
|
}
|
|
newTypedStructFilter = &v3httppb.HttpFilter{
|
|
Name: "customFilter",
|
|
ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: testutils.MarshalAny(t, customFilterNewTypedStructConfig)},
|
|
}
|
|
customOptionalFilter = &v3httppb.HttpFilter{
|
|
Name: "customFilter",
|
|
ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: customFilterConfig},
|
|
IsOptional: true,
|
|
}
|
|
customFilter2 = &v3httppb.HttpFilter{
|
|
Name: "customFilter2",
|
|
ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: customFilterConfig},
|
|
}
|
|
errFilter = &v3httppb.HttpFilter{
|
|
Name: "errFilter",
|
|
ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: errFilterConfig},
|
|
}
|
|
errOptionalFilter = &v3httppb.HttpFilter{
|
|
Name: "errFilter",
|
|
ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: errFilterConfig},
|
|
IsOptional: true,
|
|
}
|
|
clientOnlyCustomFilter = &v3httppb.HttpFilter{
|
|
Name: "clientOnlyCustomFilter",
|
|
ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: clientOnlyCustomFilterConfig},
|
|
}
|
|
serverOnlyCustomFilter = &v3httppb.HttpFilter{
|
|
Name: "serverOnlyCustomFilter",
|
|
ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: serverOnlyCustomFilterConfig},
|
|
}
|
|
serverOnlyOptionalCustomFilter = &v3httppb.HttpFilter{
|
|
Name: "serverOnlyOptionalCustomFilter",
|
|
ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: serverOnlyCustomFilterConfig},
|
|
IsOptional: true,
|
|
}
|
|
unknownFilter = &v3httppb.HttpFilter{
|
|
Name: "unknownFilter",
|
|
ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: unknownFilterConfig},
|
|
}
|
|
unknownOptionalFilter = &v3httppb.HttpFilter{
|
|
Name: "unknownFilter",
|
|
ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: unknownFilterConfig},
|
|
IsOptional: true,
|
|
}
|
|
v3LisWithInlineRoute = testutils.MarshalAny(t, &v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
ApiListener: &v3listenerpb.ApiListener{
|
|
ApiListener: testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{
|
|
RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
|
|
RouteConfig: &v3routepb.RouteConfiguration{
|
|
Name: routeName,
|
|
VirtualHosts: []*v3routepb.VirtualHost{{
|
|
Domains: []string{v3LDSTarget},
|
|
Routes: []*v3routepb.Route{{
|
|
Match: &v3routepb.RouteMatch{
|
|
PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: "/"},
|
|
},
|
|
Action: &v3routepb.Route_Route{
|
|
Route: &v3routepb.RouteAction{
|
|
ClusterSpecifier: &v3routepb.RouteAction_Cluster{Cluster: clusterName},
|
|
}}}}}}},
|
|
},
|
|
HttpFilters: []*v3httppb.HttpFilter{emptyRouterFilter},
|
|
CommonHttpProtocolOptions: &v3corepb.HttpProtocolOptions{
|
|
MaxStreamDuration: durationpb.New(time.Second),
|
|
},
|
|
}),
|
|
},
|
|
})
|
|
v3LisWithFilters = func(fs ...*v3httppb.HttpFilter) *anypb.Any {
|
|
fs = append(fs, emptyRouterFilter)
|
|
return testutils.MarshalAny(t, &v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
ApiListener: &v3listenerpb.ApiListener{
|
|
ApiListener: testutils.MarshalAny(t,
|
|
&v3httppb.HttpConnectionManager{
|
|
RouteSpecifier: &v3httppb.HttpConnectionManager_Rds{
|
|
Rds: &v3httppb.Rds{
|
|
ConfigSource: &v3corepb.ConfigSource{
|
|
ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{Ads: &v3corepb.AggregatedConfigSource{}},
|
|
},
|
|
RouteConfigName: v3RouteConfigName,
|
|
},
|
|
},
|
|
CommonHttpProtocolOptions: &v3corepb.HttpProtocolOptions{
|
|
MaxStreamDuration: durationpb.New(time.Second),
|
|
},
|
|
HttpFilters: fs,
|
|
}),
|
|
},
|
|
})
|
|
}
|
|
v3LisToTestRBAC = func(xffNumTrustedHops uint32, originalIpDetectionExtensions []*v3corepb.TypedExtensionConfig) *anypb.Any {
|
|
return testutils.MarshalAny(t, &v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
ApiListener: &v3listenerpb.ApiListener{
|
|
ApiListener: testutils.MarshalAny(t,
|
|
&v3httppb.HttpConnectionManager{
|
|
RouteSpecifier: &v3httppb.HttpConnectionManager_Rds{
|
|
Rds: &v3httppb.Rds{
|
|
ConfigSource: &v3corepb.ConfigSource{
|
|
ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{Ads: &v3corepb.AggregatedConfigSource{}},
|
|
},
|
|
RouteConfigName: v3RouteConfigName,
|
|
},
|
|
},
|
|
CommonHttpProtocolOptions: &v3corepb.HttpProtocolOptions{
|
|
MaxStreamDuration: durationpb.New(time.Second),
|
|
},
|
|
HttpFilters: []*v3httppb.HttpFilter{emptyRouterFilter},
|
|
XffNumTrustedHops: xffNumTrustedHops,
|
|
OriginalIpDetectionExtensions: originalIpDetectionExtensions,
|
|
}),
|
|
},
|
|
})
|
|
}
|
|
|
|
v3ListenerWithCDSConfigSourceSelf = testutils.MarshalAny(t, &v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
ApiListener: &v3listenerpb.ApiListener{
|
|
ApiListener: testutils.MarshalAny(t,
|
|
&v3httppb.HttpConnectionManager{
|
|
RouteSpecifier: &v3httppb.HttpConnectionManager_Rds{
|
|
Rds: &v3httppb.Rds{
|
|
ConfigSource: &v3corepb.ConfigSource{
|
|
ConfigSourceSpecifier: &v3corepb.ConfigSource_Self{},
|
|
},
|
|
RouteConfigName: v3RouteConfigName,
|
|
},
|
|
},
|
|
HttpFilters: []*v3httppb.HttpFilter{emptyRouterFilter},
|
|
}),
|
|
},
|
|
})
|
|
)
|
|
|
|
tests := []struct {
|
|
name string
|
|
resource *anypb.Any
|
|
wantName string
|
|
wantUpdate ListenerUpdate
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "non-listener resource",
|
|
resource: &anypb.Any{TypeUrl: version.V3HTTPConnManagerURL},
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "badly marshaled listener resource",
|
|
resource: &anypb.Any{
|
|
TypeUrl: version.V3ListenerURL,
|
|
Value: func() []byte {
|
|
lis := &v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
ApiListener: &v3listenerpb.ApiListener{
|
|
ApiListener: &anypb.Any{
|
|
TypeUrl: version.V3HTTPConnManagerURL,
|
|
Value: []byte{1, 2, 3, 4},
|
|
},
|
|
},
|
|
}
|
|
mLis, _ := proto.Marshal(lis)
|
|
return mLis
|
|
}(),
|
|
},
|
|
wantName: v3LDSTarget,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "wrong type in apiListener",
|
|
resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
ApiListener: &v3listenerpb.ApiListener{
|
|
ApiListener: testutils.MarshalAny(t, &v2xdspb.Listener{}),
|
|
},
|
|
}),
|
|
wantName: v3LDSTarget,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "empty httpConnMgr in apiListener",
|
|
resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
ApiListener: &v3listenerpb.ApiListener{
|
|
ApiListener: testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{
|
|
RouteSpecifier: &v3httppb.HttpConnectionManager_Rds{
|
|
Rds: &v3httppb.Rds{},
|
|
},
|
|
}),
|
|
},
|
|
}),
|
|
wantName: v3LDSTarget,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "scopedRoutes routeConfig in apiListener",
|
|
resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
ApiListener: &v3listenerpb.ApiListener{
|
|
ApiListener: testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{
|
|
RouteSpecifier: &v3httppb.HttpConnectionManager_ScopedRoutes{},
|
|
}),
|
|
},
|
|
}),
|
|
wantName: v3LDSTarget,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "rds.ConfigSource in apiListener is Self",
|
|
resource: v3ListenerWithCDSConfigSourceSelf,
|
|
wantName: v3LDSTarget,
|
|
wantUpdate: ListenerUpdate{
|
|
RouteConfigName: v3RouteConfigName,
|
|
HTTPFilters: []HTTPFilter{makeRouterFilter(t)},
|
|
Raw: v3ListenerWithCDSConfigSourceSelf,
|
|
},
|
|
},
|
|
{
|
|
name: "rds.ConfigSource in apiListener is not ADS or Self",
|
|
resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
ApiListener: &v3listenerpb.ApiListener{
|
|
ApiListener: testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{
|
|
RouteSpecifier: &v3httppb.HttpConnectionManager_Rds{
|
|
Rds: &v3httppb.Rds{
|
|
ConfigSource: &v3corepb.ConfigSource{
|
|
ConfigSourceSpecifier: &v3corepb.ConfigSource_Path{
|
|
Path: "/some/path",
|
|
},
|
|
},
|
|
RouteConfigName: v3RouteConfigName,
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
}),
|
|
wantName: v3LDSTarget,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "v3 with no filters",
|
|
resource: v3LisWithFilters(),
|
|
wantName: v3LDSTarget,
|
|
wantUpdate: ListenerUpdate{
|
|
RouteConfigName: v3RouteConfigName,
|
|
MaxStreamDuration: time.Second,
|
|
HTTPFilters: makeRouterFilterList(t),
|
|
Raw: v3LisWithFilters(),
|
|
},
|
|
},
|
|
{
|
|
name: "v3 no terminal filter",
|
|
resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
ApiListener: &v3listenerpb.ApiListener{
|
|
ApiListener: testutils.MarshalAny(t,
|
|
&v3httppb.HttpConnectionManager{
|
|
RouteSpecifier: &v3httppb.HttpConnectionManager_Rds{
|
|
Rds: &v3httppb.Rds{
|
|
ConfigSource: &v3corepb.ConfigSource{
|
|
ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{Ads: &v3corepb.AggregatedConfigSource{}},
|
|
},
|
|
RouteConfigName: v3RouteConfigName,
|
|
},
|
|
},
|
|
CommonHttpProtocolOptions: &v3corepb.HttpProtocolOptions{
|
|
MaxStreamDuration: durationpb.New(time.Second),
|
|
},
|
|
}),
|
|
},
|
|
}),
|
|
wantName: v3LDSTarget,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "v3 with custom filter",
|
|
resource: v3LisWithFilters(customFilter),
|
|
wantName: v3LDSTarget,
|
|
wantUpdate: ListenerUpdate{
|
|
RouteConfigName: v3RouteConfigName, MaxStreamDuration: time.Second,
|
|
HTTPFilters: []HTTPFilter{
|
|
{
|
|
Name: "customFilter",
|
|
Filter: httpFilter{},
|
|
Config: filterConfig{Cfg: customFilterConfig},
|
|
},
|
|
makeRouterFilter(t),
|
|
},
|
|
Raw: v3LisWithFilters(customFilter),
|
|
},
|
|
},
|
|
{
|
|
name: "v3 with custom filter in old typed struct",
|
|
resource: v3LisWithFilters(oldTypedStructFilter),
|
|
wantName: v3LDSTarget,
|
|
wantUpdate: ListenerUpdate{
|
|
RouteConfigName: v3RouteConfigName, MaxStreamDuration: time.Second,
|
|
HTTPFilters: []HTTPFilter{
|
|
{
|
|
Name: "customFilter",
|
|
Filter: httpFilter{},
|
|
Config: filterConfig{Cfg: customFilterOldTypedStructConfig},
|
|
},
|
|
makeRouterFilter(t),
|
|
},
|
|
Raw: v3LisWithFilters(oldTypedStructFilter),
|
|
},
|
|
},
|
|
{
|
|
name: "v3 with custom filter in new typed struct",
|
|
resource: v3LisWithFilters(newTypedStructFilter),
|
|
wantName: v3LDSTarget,
|
|
wantUpdate: ListenerUpdate{
|
|
RouteConfigName: v3RouteConfigName, MaxStreamDuration: time.Second,
|
|
HTTPFilters: []HTTPFilter{
|
|
{
|
|
Name: "customFilter",
|
|
Filter: httpFilter{},
|
|
Config: filterConfig{Cfg: customFilterNewTypedStructConfig},
|
|
},
|
|
makeRouterFilter(t),
|
|
},
|
|
Raw: v3LisWithFilters(newTypedStructFilter),
|
|
},
|
|
},
|
|
{
|
|
name: "v3 with optional custom filter",
|
|
resource: v3LisWithFilters(customOptionalFilter),
|
|
wantName: v3LDSTarget,
|
|
wantUpdate: ListenerUpdate{
|
|
RouteConfigName: v3RouteConfigName, MaxStreamDuration: time.Second,
|
|
HTTPFilters: []HTTPFilter{
|
|
{
|
|
Name: "customFilter",
|
|
Filter: httpFilter{},
|
|
Config: filterConfig{Cfg: customFilterConfig},
|
|
},
|
|
makeRouterFilter(t),
|
|
},
|
|
Raw: v3LisWithFilters(customOptionalFilter),
|
|
},
|
|
},
|
|
{
|
|
name: "v3 with two filters with same name",
|
|
resource: v3LisWithFilters(customFilter, customFilter),
|
|
wantName: v3LDSTarget,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "v3 with two filters - same type different name",
|
|
resource: v3LisWithFilters(customFilter, customFilter2),
|
|
wantName: v3LDSTarget,
|
|
wantUpdate: ListenerUpdate{
|
|
RouteConfigName: v3RouteConfigName, MaxStreamDuration: time.Second,
|
|
HTTPFilters: []HTTPFilter{{
|
|
Name: "customFilter",
|
|
Filter: httpFilter{},
|
|
Config: filterConfig{Cfg: customFilterConfig},
|
|
}, {
|
|
Name: "customFilter2",
|
|
Filter: httpFilter{},
|
|
Config: filterConfig{Cfg: customFilterConfig},
|
|
},
|
|
makeRouterFilter(t),
|
|
},
|
|
Raw: v3LisWithFilters(customFilter, customFilter2),
|
|
},
|
|
},
|
|
{
|
|
name: "v3 with server-only filter",
|
|
resource: v3LisWithFilters(serverOnlyCustomFilter),
|
|
wantName: v3LDSTarget,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "v3 with optional server-only filter",
|
|
resource: v3LisWithFilters(serverOnlyOptionalCustomFilter),
|
|
wantName: v3LDSTarget,
|
|
wantUpdate: ListenerUpdate{
|
|
RouteConfigName: v3RouteConfigName,
|
|
MaxStreamDuration: time.Second,
|
|
Raw: v3LisWithFilters(serverOnlyOptionalCustomFilter),
|
|
HTTPFilters: makeRouterFilterList(t),
|
|
},
|
|
},
|
|
{
|
|
name: "v3 with client-only filter",
|
|
resource: v3LisWithFilters(clientOnlyCustomFilter),
|
|
wantName: v3LDSTarget,
|
|
wantUpdate: ListenerUpdate{
|
|
RouteConfigName: v3RouteConfigName, MaxStreamDuration: time.Second,
|
|
HTTPFilters: []HTTPFilter{
|
|
{
|
|
Name: "clientOnlyCustomFilter",
|
|
Filter: clientOnlyHTTPFilter{},
|
|
Config: filterConfig{Cfg: clientOnlyCustomFilterConfig},
|
|
},
|
|
makeRouterFilter(t)},
|
|
Raw: v3LisWithFilters(clientOnlyCustomFilter),
|
|
},
|
|
},
|
|
{
|
|
name: "v3 with err filter",
|
|
resource: v3LisWithFilters(errFilter),
|
|
wantName: v3LDSTarget,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "v3 with optional err filter",
|
|
resource: v3LisWithFilters(errOptionalFilter),
|
|
wantName: v3LDSTarget,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "v3 with unknown filter",
|
|
resource: v3LisWithFilters(unknownFilter),
|
|
wantName: v3LDSTarget,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "v3 with unknown filter (optional)",
|
|
resource: v3LisWithFilters(unknownOptionalFilter),
|
|
wantName: v3LDSTarget,
|
|
wantUpdate: ListenerUpdate{
|
|
RouteConfigName: v3RouteConfigName,
|
|
MaxStreamDuration: time.Second,
|
|
HTTPFilters: makeRouterFilterList(t),
|
|
Raw: v3LisWithFilters(unknownOptionalFilter),
|
|
},
|
|
},
|
|
{
|
|
name: "v3 listener resource",
|
|
resource: v3LisWithFilters(),
|
|
wantName: v3LDSTarget,
|
|
wantUpdate: ListenerUpdate{
|
|
RouteConfigName: v3RouteConfigName,
|
|
MaxStreamDuration: time.Second,
|
|
HTTPFilters: makeRouterFilterList(t),
|
|
Raw: v3LisWithFilters(),
|
|
},
|
|
},
|
|
{
|
|
name: "v3 listener resource wrapped",
|
|
resource: testutils.MarshalAny(t, &v3discoverypb.Resource{Resource: v3LisWithFilters()}),
|
|
wantName: v3LDSTarget,
|
|
wantUpdate: ListenerUpdate{
|
|
RouteConfigName: v3RouteConfigName,
|
|
MaxStreamDuration: time.Second,
|
|
HTTPFilters: makeRouterFilterList(t),
|
|
Raw: v3LisWithFilters(),
|
|
},
|
|
},
|
|
// "To allow equating RBAC's direct_remote_ip and
|
|
// remote_ip...HttpConnectionManager.xff_num_trusted_hops must be unset
|
|
// or zero and HttpConnectionManager.original_ip_detection_extensions
|
|
// must be empty." - A41
|
|
{
|
|
name: "rbac-allow-equating-direct-remote-ip-and-remote-ip-valid",
|
|
resource: v3LisToTestRBAC(0, nil),
|
|
wantName: v3LDSTarget,
|
|
wantUpdate: ListenerUpdate{
|
|
RouteConfigName: v3RouteConfigName,
|
|
MaxStreamDuration: time.Second,
|
|
HTTPFilters: []HTTPFilter{makeRouterFilter(t)},
|
|
Raw: v3LisToTestRBAC(0, nil),
|
|
},
|
|
},
|
|
// In order to support xDS Configured RBAC HTTPFilter equating direct
|
|
// remote ip and remote ip, xffNumTrustedHops cannot be greater than
|
|
// zero. This is because if you can trust a ingress proxy hop when
|
|
// determining an origin clients ip address, direct remote ip != remote
|
|
// ip.
|
|
{
|
|
name: "rbac-allow-equating-direct-remote-ip-and-remote-ip-invalid-num-untrusted-hops",
|
|
resource: v3LisToTestRBAC(1, nil),
|
|
wantName: v3LDSTarget,
|
|
wantErr: true,
|
|
},
|
|
// In order to support xDS Configured RBAC HTTPFilter equating direct
|
|
// remote ip and remote ip, originalIpDetectionExtensions must be empty.
|
|
// This is because if you have to ask ip-detection-extension for the
|
|
// original ip, direct remote ip might not equal remote ip.
|
|
{
|
|
name: "rbac-allow-equating-direct-remote-ip-and-remote-ip-invalid-original-ip-detection-extension",
|
|
resource: v3LisToTestRBAC(0, []*v3corepb.TypedExtensionConfig{{Name: "something"}}),
|
|
wantName: v3LDSTarget,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "v3 listener with inline route configuration",
|
|
resource: v3LisWithInlineRoute,
|
|
wantName: v3LDSTarget,
|
|
wantUpdate: ListenerUpdate{
|
|
InlineRouteConfig: &RouteConfigUpdate{
|
|
VirtualHosts: []*VirtualHost{{
|
|
Domains: []string{v3LDSTarget},
|
|
Routes: []*Route{{Prefix: newStringP("/"), WeightedClusters: map[string]WeightedCluster{clusterName: {Weight: 1}}, ActionType: RouteActionRoute}},
|
|
}}},
|
|
MaxStreamDuration: time.Second,
|
|
Raw: v3LisWithInlineRoute,
|
|
HTTPFilters: makeRouterFilterList(t),
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
name, update, err := unmarshalListenerResource(test.resource)
|
|
if (err != nil) != test.wantErr {
|
|
t.Errorf("unmarshalListenerResource(%s), got err: %v, wantErr: %v", pretty.ToJSON(test.resource), err, test.wantErr)
|
|
}
|
|
if name != test.wantName {
|
|
t.Errorf("unmarshalListenerResource(%s), got name: %s, want: %s", pretty.ToJSON(test.resource), name, test.wantName)
|
|
}
|
|
if diff := cmp.Diff(update, test.wantUpdate, cmpOpts); diff != "" {
|
|
t.Errorf("unmarshalListenerResource(%s), got unexpected update, diff (-got +want): %v", pretty.ToJSON(test.resource), diff)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func (s) TestUnmarshalListener_ServerSide(t *testing.T) {
|
|
const (
|
|
v3LDSTarget = "grpc/server?xds.resource.listening_address=0.0.0.0:9999"
|
|
testVersion = "test-version-lds-server"
|
|
)
|
|
|
|
var (
|
|
serverOnlyCustomFilter = &v3httppb.HttpFilter{
|
|
Name: "serverOnlyCustomFilter",
|
|
ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: serverOnlyCustomFilterConfig},
|
|
}
|
|
routeConfig = &v3routepb.RouteConfiguration{
|
|
Name: "routeName",
|
|
VirtualHosts: []*v3routepb.VirtualHost{{
|
|
Domains: []string{"lds.target.good:3333"},
|
|
Routes: []*v3routepb.Route{{
|
|
Match: &v3routepb.RouteMatch{
|
|
PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: "/"},
|
|
},
|
|
Action: &v3routepb.Route_NonForwardingAction{},
|
|
}}}}}
|
|
inlineRouteConfig = &RouteConfigUpdate{
|
|
VirtualHosts: []*VirtualHost{{
|
|
Domains: []string{"lds.target.good:3333"},
|
|
Routes: []*Route{{Prefix: newStringP("/"), ActionType: RouteActionNonForwardingAction}},
|
|
}}}
|
|
emptyValidNetworkFilters = []*v3listenerpb.Filter{
|
|
{
|
|
Name: "filter-1",
|
|
ConfigType: &v3listenerpb.Filter_TypedConfig{
|
|
TypedConfig: testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{
|
|
RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
|
|
RouteConfig: routeConfig,
|
|
},
|
|
HttpFilters: []*v3httppb.HttpFilter{e2e.RouterHTTPFilter},
|
|
}),
|
|
},
|
|
},
|
|
}
|
|
localSocketAddress = &v3corepb.Address{
|
|
Address: &v3corepb.Address_SocketAddress{
|
|
SocketAddress: &v3corepb.SocketAddress{
|
|
Address: "0.0.0.0",
|
|
PortSpecifier: &v3corepb.SocketAddress_PortValue{
|
|
PortValue: 9999,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
listenerEmptyTransportSocket = testutils.MarshalAny(t, &v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
Name: "filter-chain-1",
|
|
Filters: emptyValidNetworkFilters,
|
|
},
|
|
},
|
|
})
|
|
listenerNoValidationContextDeprecatedFields = testutils.MarshalAny(t, &v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
Name: "filter-chain-1",
|
|
Filters: emptyValidNetworkFilters,
|
|
TransportSocket: &v3corepb.TransportSocket{
|
|
Name: "envoy.transport_sockets.tls",
|
|
ConfigType: &v3corepb.TransportSocket_TypedConfig{
|
|
TypedConfig: testutils.MarshalAny(t, &v3tlspb.DownstreamTlsContext{
|
|
CommonTlsContext: &v3tlspb.CommonTlsContext{
|
|
TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
|
|
InstanceName: "identityPluginInstance",
|
|
CertificateName: "identityCertName",
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
DefaultFilterChain: &v3listenerpb.FilterChain{
|
|
Name: "default-filter-chain-1",
|
|
Filters: emptyValidNetworkFilters,
|
|
TransportSocket: &v3corepb.TransportSocket{
|
|
Name: "envoy.transport_sockets.tls",
|
|
ConfigType: &v3corepb.TransportSocket_TypedConfig{
|
|
TypedConfig: testutils.MarshalAny(t, &v3tlspb.DownstreamTlsContext{
|
|
CommonTlsContext: &v3tlspb.CommonTlsContext{
|
|
TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
|
|
InstanceName: "defaultIdentityPluginInstance",
|
|
CertificateName: "defaultIdentityCertName",
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
})
|
|
listenerNoValidationContextNewFields = testutils.MarshalAny(t, &v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
Name: "filter-chain-1",
|
|
Filters: emptyValidNetworkFilters,
|
|
TransportSocket: &v3corepb.TransportSocket{
|
|
Name: "envoy.transport_sockets.tls",
|
|
ConfigType: &v3corepb.TransportSocket_TypedConfig{
|
|
TypedConfig: testutils.MarshalAny(t, &v3tlspb.DownstreamTlsContext{
|
|
CommonTlsContext: &v3tlspb.CommonTlsContext{
|
|
TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
|
|
InstanceName: "identityPluginInstance",
|
|
CertificateName: "identityCertName",
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
DefaultFilterChain: &v3listenerpb.FilterChain{
|
|
Name: "default-filter-chain-1",
|
|
Filters: emptyValidNetworkFilters,
|
|
TransportSocket: &v3corepb.TransportSocket{
|
|
Name: "envoy.transport_sockets.tls",
|
|
ConfigType: &v3corepb.TransportSocket_TypedConfig{
|
|
TypedConfig: testutils.MarshalAny(t, &v3tlspb.DownstreamTlsContext{
|
|
CommonTlsContext: &v3tlspb.CommonTlsContext{
|
|
TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
|
|
InstanceName: "defaultIdentityPluginInstance",
|
|
CertificateName: "defaultIdentityCertName",
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
})
|
|
listenerWithValidationContextDeprecatedFields = testutils.MarshalAny(t, &v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
Name: "filter-chain-1",
|
|
Filters: emptyValidNetworkFilters,
|
|
TransportSocket: &v3corepb.TransportSocket{
|
|
Name: "envoy.transport_sockets.tls",
|
|
ConfigType: &v3corepb.TransportSocket_TypedConfig{
|
|
TypedConfig: testutils.MarshalAny(t, &v3tlspb.DownstreamTlsContext{
|
|
RequireClientCertificate: &wrapperspb.BoolValue{Value: true},
|
|
CommonTlsContext: &v3tlspb.CommonTlsContext{
|
|
TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
|
|
InstanceName: "identityPluginInstance",
|
|
CertificateName: "identityCertName",
|
|
},
|
|
ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextCertificateProviderInstance{
|
|
ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
|
|
InstanceName: "rootPluginInstance",
|
|
CertificateName: "rootCertName",
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
DefaultFilterChain: &v3listenerpb.FilterChain{
|
|
Name: "default-filter-chain-1",
|
|
Filters: emptyValidNetworkFilters,
|
|
TransportSocket: &v3corepb.TransportSocket{
|
|
Name: "envoy.transport_sockets.tls",
|
|
ConfigType: &v3corepb.TransportSocket_TypedConfig{
|
|
TypedConfig: testutils.MarshalAny(t, &v3tlspb.DownstreamTlsContext{
|
|
RequireClientCertificate: &wrapperspb.BoolValue{Value: true},
|
|
CommonTlsContext: &v3tlspb.CommonTlsContext{
|
|
TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
|
|
InstanceName: "defaultIdentityPluginInstance",
|
|
CertificateName: "defaultIdentityCertName",
|
|
},
|
|
ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextCertificateProviderInstance{
|
|
ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
|
|
InstanceName: "defaultRootPluginInstance",
|
|
CertificateName: "defaultRootCertName",
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
})
|
|
listenerWithValidationContextNewFields = testutils.MarshalAny(t, &v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
Name: "filter-chain-1",
|
|
Filters: emptyValidNetworkFilters,
|
|
TransportSocket: &v3corepb.TransportSocket{
|
|
Name: "envoy.transport_sockets.tls",
|
|
ConfigType: &v3corepb.TransportSocket_TypedConfig{
|
|
TypedConfig: testutils.MarshalAny(t, &v3tlspb.DownstreamTlsContext{
|
|
RequireClientCertificate: &wrapperspb.BoolValue{Value: true},
|
|
CommonTlsContext: &v3tlspb.CommonTlsContext{
|
|
TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
|
|
InstanceName: "identityPluginInstance",
|
|
CertificateName: "identityCertName",
|
|
},
|
|
ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{
|
|
ValidationContext: &v3tlspb.CertificateValidationContext{
|
|
CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
|
|
InstanceName: "rootPluginInstance",
|
|
CertificateName: "rootCertName",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
DefaultFilterChain: &v3listenerpb.FilterChain{
|
|
Name: "default-filter-chain-1",
|
|
Filters: emptyValidNetworkFilters,
|
|
TransportSocket: &v3corepb.TransportSocket{
|
|
Name: "envoy.transport_sockets.tls",
|
|
ConfigType: &v3corepb.TransportSocket_TypedConfig{
|
|
TypedConfig: testutils.MarshalAny(t, &v3tlspb.DownstreamTlsContext{
|
|
RequireClientCertificate: &wrapperspb.BoolValue{Value: true},
|
|
CommonTlsContext: &v3tlspb.CommonTlsContext{
|
|
TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
|
|
InstanceName: "defaultIdentityPluginInstance",
|
|
CertificateName: "defaultIdentityCertName",
|
|
},
|
|
ValidationContextType: &v3tlspb.CommonTlsContext_CombinedValidationContext{
|
|
CombinedValidationContext: &v3tlspb.CommonTlsContext_CombinedCertificateValidationContext{
|
|
DefaultValidationContext: &v3tlspb.CertificateValidationContext{
|
|
CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
|
|
InstanceName: "defaultRootPluginInstance",
|
|
CertificateName: "defaultRootCertName",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
})
|
|
)
|
|
v3LisToTestRBAC := func(xffNumTrustedHops uint32, originalIpDetectionExtensions []*v3corepb.TypedExtensionConfig) *anypb.Any {
|
|
return testutils.MarshalAny(t, &v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
Name: "filter-chain-1",
|
|
Filters: []*v3listenerpb.Filter{
|
|
{
|
|
Name: "filter-1",
|
|
ConfigType: &v3listenerpb.Filter_TypedConfig{
|
|
TypedConfig: testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{
|
|
RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
|
|
RouteConfig: routeConfig,
|
|
},
|
|
HttpFilters: []*v3httppb.HttpFilter{e2e.RouterHTTPFilter},
|
|
XffNumTrustedHops: xffNumTrustedHops,
|
|
OriginalIpDetectionExtensions: originalIpDetectionExtensions,
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
})
|
|
}
|
|
v3LisWithBadRBACConfiguration := func(rbacCfg *v3rbacpb.RBAC) *anypb.Any {
|
|
return testutils.MarshalAny(t, &v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
Name: "filter-chain-1",
|
|
Filters: []*v3listenerpb.Filter{
|
|
{
|
|
Name: "filter-1",
|
|
ConfigType: &v3listenerpb.Filter_TypedConfig{
|
|
TypedConfig: testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{
|
|
RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
|
|
RouteConfig: routeConfig,
|
|
},
|
|
HttpFilters: []*v3httppb.HttpFilter{e2e.HTTPFilter("rbac", rbacCfg), e2e.RouterHTTPFilter},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
})
|
|
}
|
|
badRBACCfgRegex := &v3rbacpb.RBAC{
|
|
Rules: &rpb.RBAC{
|
|
Action: rpb.RBAC_ALLOW,
|
|
Policies: map[string]*rpb.Policy{
|
|
"bad-regex-value": {
|
|
Permissions: []*rpb.Permission{
|
|
{Rule: &rpb.Permission_Any{Any: true}},
|
|
},
|
|
Principals: []*rpb.Principal{
|
|
{Identifier: &rpb.Principal_Header{Header: &v3routepb.HeaderMatcher{Name: ":method", HeaderMatchSpecifier: &v3routepb.HeaderMatcher_SafeRegexMatch{SafeRegexMatch: &v3matcherpb.RegexMatcher{Regex: "["}}}}},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
badRBACCfgDestIP := &v3rbacpb.RBAC{
|
|
Rules: &rpb.RBAC{
|
|
Action: rpb.RBAC_ALLOW,
|
|
Policies: map[string]*rpb.Policy{
|
|
"certain-destination-ip": {
|
|
Permissions: []*rpb.Permission{
|
|
{Rule: &rpb.Permission_DestinationIp{DestinationIp: &v3corepb.CidrRange{AddressPrefix: "not a correct address", PrefixLen: &wrapperspb.UInt32Value{Value: uint32(10)}}}},
|
|
},
|
|
Principals: []*rpb.Principal{
|
|
{Identifier: &rpb.Principal_Any{Any: true}},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
resource *anypb.Any
|
|
wantName string
|
|
wantUpdate ListenerUpdate
|
|
wantErr string
|
|
}{
|
|
{
|
|
name: "non-empty listener filters",
|
|
resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
ListenerFilters: []*v3listenerpb.ListenerFilter{
|
|
{Name: "listener-filter-1"},
|
|
},
|
|
}),
|
|
wantName: v3LDSTarget,
|
|
wantErr: "unsupported field 'listener_filters'",
|
|
},
|
|
{
|
|
name: "use_original_dst is set",
|
|
resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
UseOriginalDst: &wrapperspb.BoolValue{Value: true},
|
|
}),
|
|
wantName: v3LDSTarget,
|
|
wantErr: "unsupported field 'use_original_dst'",
|
|
},
|
|
{
|
|
name: "no address field",
|
|
resource: testutils.MarshalAny(t, &v3listenerpb.Listener{Name: v3LDSTarget}),
|
|
wantName: v3LDSTarget,
|
|
wantErr: "no address field in LDS response",
|
|
},
|
|
{
|
|
name: "no socket address field",
|
|
resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: &v3corepb.Address{},
|
|
}),
|
|
wantName: v3LDSTarget,
|
|
wantErr: "no socket_address field in LDS response",
|
|
},
|
|
{
|
|
name: "no filter chains and no default filter chain",
|
|
resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
FilterChainMatch: &v3listenerpb.FilterChainMatch{DestinationPort: &wrapperspb.UInt32Value{Value: 666}},
|
|
Filters: emptyValidNetworkFilters,
|
|
},
|
|
},
|
|
}),
|
|
wantName: v3LDSTarget,
|
|
wantErr: "no supported filter chains and no default filter chain",
|
|
},
|
|
{
|
|
name: "missing http connection manager network filter",
|
|
resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
Name: "filter-chain-1",
|
|
},
|
|
},
|
|
}),
|
|
wantName: v3LDSTarget,
|
|
wantErr: "missing HttpConnectionManager filter",
|
|
},
|
|
{
|
|
name: "missing filter name in http filter",
|
|
resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
Name: "filter-chain-1",
|
|
Filters: []*v3listenerpb.Filter{
|
|
{
|
|
ConfigType: &v3listenerpb.Filter_TypedConfig{
|
|
TypedConfig: testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
wantName: v3LDSTarget,
|
|
wantErr: "missing name field in filter",
|
|
},
|
|
{
|
|
name: "duplicate filter names in http filter",
|
|
resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
Name: "filter-chain-1",
|
|
Filters: []*v3listenerpb.Filter{
|
|
{
|
|
Name: "name",
|
|
ConfigType: &v3listenerpb.Filter_TypedConfig{
|
|
TypedConfig: testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{
|
|
RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
|
|
RouteConfig: routeConfig,
|
|
},
|
|
HttpFilters: []*v3httppb.HttpFilter{emptyRouterFilter},
|
|
}),
|
|
},
|
|
},
|
|
{
|
|
Name: "name",
|
|
ConfigType: &v3listenerpb.Filter_TypedConfig{
|
|
TypedConfig: testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{
|
|
RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
|
|
RouteConfig: routeConfig,
|
|
},
|
|
HttpFilters: []*v3httppb.HttpFilter{emptyRouterFilter},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
wantName: v3LDSTarget,
|
|
wantErr: "duplicate filter name",
|
|
},
|
|
{
|
|
name: "no terminal filter",
|
|
resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
Name: "filter-chain-1",
|
|
Filters: []*v3listenerpb.Filter{
|
|
{
|
|
Name: "name",
|
|
ConfigType: &v3listenerpb.Filter_TypedConfig{
|
|
TypedConfig: testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{
|
|
RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
|
|
RouteConfig: routeConfig,
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
wantName: v3LDSTarget,
|
|
wantErr: "http filters list is empty",
|
|
},
|
|
{
|
|
name: "terminal filter not last",
|
|
resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
Name: "filter-chain-1",
|
|
Filters: []*v3listenerpb.Filter{
|
|
{
|
|
Name: "name",
|
|
ConfigType: &v3listenerpb.Filter_TypedConfig{
|
|
TypedConfig: testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{
|
|
RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
|
|
RouteConfig: routeConfig,
|
|
},
|
|
HttpFilters: []*v3httppb.HttpFilter{emptyRouterFilter, serverOnlyCustomFilter},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
wantName: v3LDSTarget,
|
|
wantErr: "is a terminal filter but it is not last in the filter chain",
|
|
},
|
|
{
|
|
name: "last not terminal filter",
|
|
resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
Name: "filter-chain-1",
|
|
Filters: []*v3listenerpb.Filter{
|
|
{
|
|
Name: "name",
|
|
ConfigType: &v3listenerpb.Filter_TypedConfig{
|
|
TypedConfig: testutils.MarshalAny(t, &v3httppb.HttpConnectionManager{
|
|
RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
|
|
RouteConfig: routeConfig,
|
|
},
|
|
HttpFilters: []*v3httppb.HttpFilter{serverOnlyCustomFilter},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
wantName: v3LDSTarget,
|
|
wantErr: "is not a terminal filter",
|
|
},
|
|
{
|
|
name: "unsupported oneof in typed config of http filter",
|
|
resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
Name: "filter-chain-1",
|
|
Filters: []*v3listenerpb.Filter{
|
|
{
|
|
Name: "name",
|
|
ConfigType: &v3listenerpb.Filter_ConfigDiscovery{},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
wantName: v3LDSTarget,
|
|
wantErr: "unsupported config_type",
|
|
},
|
|
{
|
|
name: "overlapping filter chain match criteria",
|
|
resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
FilterChainMatch: &v3listenerpb.FilterChainMatch{SourcePorts: []uint32{1, 2, 3, 4, 5}},
|
|
Filters: emptyValidNetworkFilters,
|
|
},
|
|
{
|
|
FilterChainMatch: &v3listenerpb.FilterChainMatch{},
|
|
Filters: emptyValidNetworkFilters,
|
|
},
|
|
{
|
|
FilterChainMatch: &v3listenerpb.FilterChainMatch{SourcePorts: []uint32{5, 6, 7}},
|
|
Filters: emptyValidNetworkFilters,
|
|
},
|
|
},
|
|
}),
|
|
wantName: v3LDSTarget,
|
|
wantErr: "multiple filter chains with overlapping matching rules are defined",
|
|
},
|
|
{
|
|
name: "unsupported network filter",
|
|
resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
Name: "filter-chain-1",
|
|
Filters: []*v3listenerpb.Filter{
|
|
{
|
|
Name: "name",
|
|
ConfigType: &v3listenerpb.Filter_TypedConfig{
|
|
TypedConfig: testutils.MarshalAny(t, &v3httppb.LocalReplyConfig{}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
wantName: v3LDSTarget,
|
|
wantErr: "unsupported network filter",
|
|
},
|
|
{
|
|
name: "badly marshaled network filter",
|
|
resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
Name: "filter-chain-1",
|
|
Filters: []*v3listenerpb.Filter{
|
|
{
|
|
Name: "name",
|
|
ConfigType: &v3listenerpb.Filter_TypedConfig{
|
|
TypedConfig: &anypb.Any{
|
|
TypeUrl: version.V3HTTPConnManagerURL,
|
|
Value: []byte{1, 2, 3, 4},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
wantName: v3LDSTarget,
|
|
wantErr: "failed unmarshalling of network filter",
|
|
},
|
|
{
|
|
name: "unexpected transport socket name",
|
|
resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
Name: "filter-chain-1",
|
|
Filters: emptyValidNetworkFilters,
|
|
TransportSocket: &v3corepb.TransportSocket{
|
|
Name: "unsupported-transport-socket-name",
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
wantName: v3LDSTarget,
|
|
wantErr: "transport_socket field has unexpected name",
|
|
},
|
|
{
|
|
name: "unexpected transport socket typedConfig URL",
|
|
resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
Name: "filter-chain-1",
|
|
Filters: emptyValidNetworkFilters,
|
|
TransportSocket: &v3corepb.TransportSocket{
|
|
Name: "envoy.transport_sockets.tls",
|
|
ConfigType: &v3corepb.TransportSocket_TypedConfig{
|
|
TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
wantName: v3LDSTarget,
|
|
wantErr: "transport_socket field has unexpected typeURL",
|
|
},
|
|
{
|
|
name: "badly marshaled transport socket",
|
|
resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
Name: "filter-chain-1",
|
|
Filters: emptyValidNetworkFilters,
|
|
TransportSocket: &v3corepb.TransportSocket{
|
|
Name: "envoy.transport_sockets.tls",
|
|
ConfigType: &v3corepb.TransportSocket_TypedConfig{
|
|
TypedConfig: &anypb.Any{
|
|
TypeUrl: version.V3DownstreamTLSContextURL,
|
|
Value: []byte{1, 2, 3, 4},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
wantName: v3LDSTarget,
|
|
wantErr: "failed to unmarshal DownstreamTlsContext in LDS response",
|
|
},
|
|
{
|
|
name: "missing CommonTlsContext",
|
|
resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
Name: "filter-chain-1",
|
|
Filters: emptyValidNetworkFilters,
|
|
TransportSocket: &v3corepb.TransportSocket{
|
|
Name: "envoy.transport_sockets.tls",
|
|
ConfigType: &v3corepb.TransportSocket_TypedConfig{
|
|
TypedConfig: testutils.MarshalAny(t, &v3tlspb.DownstreamTlsContext{}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
wantName: v3LDSTarget,
|
|
wantErr: "DownstreamTlsContext in LDS response does not contain a CommonTlsContext",
|
|
},
|
|
{
|
|
name: "rbac-allow-equating-direct-remote-ip-and-remote-ip-valid",
|
|
resource: v3LisToTestRBAC(0, nil),
|
|
wantName: v3LDSTarget,
|
|
wantUpdate: ListenerUpdate{
|
|
InboundListenerCfg: &InboundListenerConfig{
|
|
Address: "0.0.0.0",
|
|
Port: "9999",
|
|
FilterChains: &FilterChainManager{
|
|
dstPrefixMap: map[string]*destPrefixEntry{
|
|
unspecifiedPrefixMapKey: {
|
|
srcTypeArr: [3]*sourcePrefixes{
|
|
{
|
|
srcPrefixMap: map[string]*sourcePrefixEntry{
|
|
unspecifiedPrefixMapKey: {
|
|
srcPortMap: map[int]*FilterChain{
|
|
0: {
|
|
InlineRouteConfig: inlineRouteConfig,
|
|
HTTPFilters: makeRouterFilterList(t),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Raw: listenerEmptyTransportSocket,
|
|
},
|
|
},
|
|
{
|
|
name: "rbac-allow-equating-direct-remote-ip-and-remote-ip-invalid-num-untrusted-hops",
|
|
resource: v3LisToTestRBAC(1, nil),
|
|
wantName: v3LDSTarget,
|
|
wantErr: "xff_num_trusted_hops must be unset or zero",
|
|
},
|
|
{
|
|
name: "rbac-allow-equating-direct-remote-ip-and-remote-ip-invalid-original-ip-detection-extension",
|
|
resource: v3LisToTestRBAC(0, []*v3corepb.TypedExtensionConfig{{Name: "something"}}),
|
|
wantName: v3LDSTarget,
|
|
wantErr: "original_ip_detection_extensions must be empty",
|
|
},
|
|
{
|
|
name: "rbac-with-invalid-regex",
|
|
resource: v3LisWithBadRBACConfiguration(badRBACCfgRegex),
|
|
wantName: v3LDSTarget,
|
|
wantErr: "error parsing config for filter",
|
|
},
|
|
{
|
|
name: "rbac-with-invalid-destination-ip-matcher",
|
|
resource: v3LisWithBadRBACConfiguration(badRBACCfgDestIP),
|
|
wantName: v3LDSTarget,
|
|
wantErr: "error parsing config for filter",
|
|
},
|
|
{
|
|
name: "unsupported validation context in transport socket",
|
|
resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
Name: "filter-chain-1",
|
|
Filters: emptyValidNetworkFilters,
|
|
TransportSocket: &v3corepb.TransportSocket{
|
|
Name: "envoy.transport_sockets.tls",
|
|
ConfigType: &v3corepb.TransportSocket_TypedConfig{
|
|
TypedConfig: testutils.MarshalAny(t, &v3tlspb.DownstreamTlsContext{
|
|
CommonTlsContext: &v3tlspb.CommonTlsContext{
|
|
ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextSdsSecretConfig{
|
|
ValidationContextSdsSecretConfig: &v3tlspb.SdsSecretConfig{
|
|
Name: "foo-sds-secret",
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
wantName: v3LDSTarget,
|
|
wantErr: "validation context contains unexpected type",
|
|
},
|
|
{
|
|
name: "empty transport socket",
|
|
resource: listenerEmptyTransportSocket,
|
|
wantName: v3LDSTarget,
|
|
wantUpdate: ListenerUpdate{
|
|
InboundListenerCfg: &InboundListenerConfig{
|
|
Address: "0.0.0.0",
|
|
Port: "9999",
|
|
FilterChains: &FilterChainManager{
|
|
dstPrefixMap: map[string]*destPrefixEntry{
|
|
unspecifiedPrefixMapKey: {
|
|
srcTypeArr: [3]*sourcePrefixes{
|
|
{
|
|
srcPrefixMap: map[string]*sourcePrefixEntry{
|
|
unspecifiedPrefixMapKey: {
|
|
srcPortMap: map[int]*FilterChain{
|
|
0: {
|
|
InlineRouteConfig: inlineRouteConfig,
|
|
HTTPFilters: makeRouterFilterList(t),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Raw: listenerEmptyTransportSocket,
|
|
},
|
|
},
|
|
{
|
|
name: "no identity and root certificate providers using deprecated fields",
|
|
resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
Name: "filter-chain-1",
|
|
Filters: emptyValidNetworkFilters,
|
|
TransportSocket: &v3corepb.TransportSocket{
|
|
Name: "envoy.transport_sockets.tls",
|
|
ConfigType: &v3corepb.TransportSocket_TypedConfig{
|
|
TypedConfig: testutils.MarshalAny(t, &v3tlspb.DownstreamTlsContext{
|
|
RequireClientCertificate: &wrapperspb.BoolValue{Value: true},
|
|
CommonTlsContext: &v3tlspb.CommonTlsContext{
|
|
TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
|
|
InstanceName: "identityPluginInstance",
|
|
CertificateName: "identityCertName",
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
wantName: v3LDSTarget,
|
|
wantErr: "security configuration on the server-side does not contain root certificate provider instance name, but require_client_cert field is set",
|
|
},
|
|
{
|
|
name: "no identity and root certificate providers using new fields",
|
|
resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
Name: "filter-chain-1",
|
|
Filters: emptyValidNetworkFilters,
|
|
TransportSocket: &v3corepb.TransportSocket{
|
|
Name: "envoy.transport_sockets.tls",
|
|
ConfigType: &v3corepb.TransportSocket_TypedConfig{
|
|
TypedConfig: testutils.MarshalAny(t, &v3tlspb.DownstreamTlsContext{
|
|
RequireClientCertificate: &wrapperspb.BoolValue{Value: true},
|
|
CommonTlsContext: &v3tlspb.CommonTlsContext{
|
|
TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{
|
|
InstanceName: "identityPluginInstance",
|
|
CertificateName: "identityCertName",
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
wantName: v3LDSTarget,
|
|
wantErr: "security configuration on the server-side does not contain root certificate provider instance name, but require_client_cert field is set",
|
|
},
|
|
{
|
|
name: "no identity certificate provider with require_client_cert",
|
|
resource: testutils.MarshalAny(t, &v3listenerpb.Listener{
|
|
Name: v3LDSTarget,
|
|
Address: localSocketAddress,
|
|
FilterChains: []*v3listenerpb.FilterChain{
|
|
{
|
|
Name: "filter-chain-1",
|
|
Filters: emptyValidNetworkFilters,
|
|
TransportSocket: &v3corepb.TransportSocket{
|
|
Name: "envoy.transport_sockets.tls",
|
|
ConfigType: &v3corepb.TransportSocket_TypedConfig{
|
|
TypedConfig: testutils.MarshalAny(t, &v3tlspb.DownstreamTlsContext{
|
|
CommonTlsContext: &v3tlspb.CommonTlsContext{},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
wantName: v3LDSTarget,
|
|
wantErr: "security configuration on the server-side does not contain identity certificate provider instance name",
|
|
},
|
|
{
|
|
name: "happy case with no validation context using deprecated fields",
|
|
resource: listenerNoValidationContextDeprecatedFields,
|
|
wantName: v3LDSTarget,
|
|
wantUpdate: ListenerUpdate{
|
|
InboundListenerCfg: &InboundListenerConfig{
|
|
Address: "0.0.0.0",
|
|
Port: "9999",
|
|
FilterChains: &FilterChainManager{
|
|
dstPrefixMap: map[string]*destPrefixEntry{
|
|
unspecifiedPrefixMapKey: {
|
|
srcTypeArr: [3]*sourcePrefixes{
|
|
{
|
|
srcPrefixMap: map[string]*sourcePrefixEntry{
|
|
unspecifiedPrefixMapKey: {
|
|
srcPortMap: map[int]*FilterChain{
|
|
0: {
|
|
SecurityCfg: &SecurityConfig{
|
|
IdentityInstanceName: "identityPluginInstance",
|
|
IdentityCertName: "identityCertName",
|
|
},
|
|
InlineRouteConfig: inlineRouteConfig,
|
|
HTTPFilters: makeRouterFilterList(t),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
def: &FilterChain{
|
|
SecurityCfg: &SecurityConfig{
|
|
IdentityInstanceName: "defaultIdentityPluginInstance",
|
|
IdentityCertName: "defaultIdentityCertName",
|
|
},
|
|
InlineRouteConfig: inlineRouteConfig,
|
|
HTTPFilters: makeRouterFilterList(t),
|
|
},
|
|
},
|
|
},
|
|
Raw: listenerNoValidationContextDeprecatedFields,
|
|
},
|
|
},
|
|
{
|
|
name: "happy case with no validation context using new fields",
|
|
resource: listenerNoValidationContextNewFields,
|
|
wantName: v3LDSTarget,
|
|
wantUpdate: ListenerUpdate{
|
|
InboundListenerCfg: &InboundListenerConfig{
|
|
Address: "0.0.0.0",
|
|
Port: "9999",
|
|
FilterChains: &FilterChainManager{
|
|
dstPrefixMap: map[string]*destPrefixEntry{
|
|
unspecifiedPrefixMapKey: {
|
|
srcTypeArr: [3]*sourcePrefixes{
|
|
{
|
|
srcPrefixMap: map[string]*sourcePrefixEntry{
|
|
unspecifiedPrefixMapKey: {
|
|
srcPortMap: map[int]*FilterChain{
|
|
0: {
|
|
SecurityCfg: &SecurityConfig{
|
|
IdentityInstanceName: "identityPluginInstance",
|
|
IdentityCertName: "identityCertName",
|
|
},
|
|
InlineRouteConfig: inlineRouteConfig,
|
|
HTTPFilters: makeRouterFilterList(t),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
def: &FilterChain{
|
|
SecurityCfg: &SecurityConfig{
|
|
IdentityInstanceName: "defaultIdentityPluginInstance",
|
|
IdentityCertName: "defaultIdentityCertName",
|
|
},
|
|
InlineRouteConfig: inlineRouteConfig,
|
|
HTTPFilters: makeRouterFilterList(t),
|
|
},
|
|
},
|
|
},
|
|
Raw: listenerNoValidationContextNewFields,
|
|
},
|
|
},
|
|
{
|
|
name: "happy case with validation context provider instance with deprecated fields",
|
|
resource: listenerWithValidationContextDeprecatedFields,
|
|
wantName: v3LDSTarget,
|
|
wantUpdate: ListenerUpdate{
|
|
InboundListenerCfg: &InboundListenerConfig{
|
|
Address: "0.0.0.0",
|
|
Port: "9999",
|
|
FilterChains: &FilterChainManager{
|
|
dstPrefixMap: map[string]*destPrefixEntry{
|
|
unspecifiedPrefixMapKey: {
|
|
srcTypeArr: [3]*sourcePrefixes{
|
|
{
|
|
srcPrefixMap: map[string]*sourcePrefixEntry{
|
|
unspecifiedPrefixMapKey: {
|
|
srcPortMap: map[int]*FilterChain{
|
|
0: {
|
|
SecurityCfg: &SecurityConfig{
|
|
RootInstanceName: "rootPluginInstance",
|
|
RootCertName: "rootCertName",
|
|
IdentityInstanceName: "identityPluginInstance",
|
|
IdentityCertName: "identityCertName",
|
|
RequireClientCert: true,
|
|
},
|
|
InlineRouteConfig: inlineRouteConfig,
|
|
HTTPFilters: makeRouterFilterList(t),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
def: &FilterChain{
|
|
SecurityCfg: &SecurityConfig{
|
|
RootInstanceName: "defaultRootPluginInstance",
|
|
RootCertName: "defaultRootCertName",
|
|
IdentityInstanceName: "defaultIdentityPluginInstance",
|
|
IdentityCertName: "defaultIdentityCertName",
|
|
RequireClientCert: true,
|
|
},
|
|
InlineRouteConfig: inlineRouteConfig,
|
|
HTTPFilters: makeRouterFilterList(t),
|
|
},
|
|
},
|
|
},
|
|
Raw: listenerWithValidationContextDeprecatedFields,
|
|
},
|
|
},
|
|
{
|
|
name: "happy case with validation context provider instance with new fields",
|
|
resource: listenerWithValidationContextNewFields,
|
|
wantName: v3LDSTarget,
|
|
wantUpdate: ListenerUpdate{
|
|
InboundListenerCfg: &InboundListenerConfig{
|
|
Address: "0.0.0.0",
|
|
Port: "9999",
|
|
FilterChains: &FilterChainManager{
|
|
dstPrefixMap: map[string]*destPrefixEntry{
|
|
unspecifiedPrefixMapKey: {
|
|
srcTypeArr: [3]*sourcePrefixes{
|
|
{
|
|
srcPrefixMap: map[string]*sourcePrefixEntry{
|
|
unspecifiedPrefixMapKey: {
|
|
srcPortMap: map[int]*FilterChain{
|
|
0: {
|
|
SecurityCfg: &SecurityConfig{
|
|
RootInstanceName: "rootPluginInstance",
|
|
RootCertName: "rootCertName",
|
|
IdentityInstanceName: "identityPluginInstance",
|
|
IdentityCertName: "identityCertName",
|
|
RequireClientCert: true,
|
|
},
|
|
InlineRouteConfig: inlineRouteConfig,
|
|
HTTPFilters: makeRouterFilterList(t),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
def: &FilterChain{
|
|
SecurityCfg: &SecurityConfig{
|
|
RootInstanceName: "defaultRootPluginInstance",
|
|
RootCertName: "defaultRootCertName",
|
|
IdentityInstanceName: "defaultIdentityPluginInstance",
|
|
IdentityCertName: "defaultIdentityCertName",
|
|
RequireClientCert: true,
|
|
},
|
|
InlineRouteConfig: inlineRouteConfig,
|
|
HTTPFilters: makeRouterFilterList(t),
|
|
},
|
|
},
|
|
},
|
|
Raw: listenerWithValidationContextNewFields,
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
name, update, err := unmarshalListenerResource(test.resource)
|
|
if err != nil && !strings.Contains(err.Error(), test.wantErr) {
|
|
t.Errorf("unmarshalListenerResource(%s) = %v wantErr: %q", pretty.ToJSON(test.resource), err, test.wantErr)
|
|
}
|
|
if name != test.wantName {
|
|
t.Errorf("unmarshalListenerResource(%s), got name: %s, want: %s", pretty.ToJSON(test.resource), name, test.wantName)
|
|
}
|
|
if diff := cmp.Diff(update, test.wantUpdate, cmpOpts); diff != "" {
|
|
t.Errorf("unmarshalListenerResource(%s), got unexpected update, diff (-got +want): %v", pretty.ToJSON(test.resource), diff)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
type filterConfig struct {
|
|
httpfilter.FilterConfig
|
|
Cfg proto.Message
|
|
Override proto.Message
|
|
}
|
|
|
|
// httpFilter allows testing the http filter registry and parsing functionality.
|
|
type httpFilter struct {
|
|
httpfilter.ClientInterceptorBuilder
|
|
httpfilter.ServerInterceptorBuilder
|
|
}
|
|
|
|
func (httpFilter) TypeURLs() []string { return []string{"custom.filter"} }
|
|
|
|
func (httpFilter) ParseFilterConfig(cfg proto.Message) (httpfilter.FilterConfig, error) {
|
|
return filterConfig{Cfg: cfg}, nil
|
|
}
|
|
|
|
func (httpFilter) ParseFilterConfigOverride(override proto.Message) (httpfilter.FilterConfig, error) {
|
|
return filterConfig{Override: override}, nil
|
|
}
|
|
|
|
func (httpFilter) IsTerminal() bool {
|
|
return false
|
|
}
|
|
|
|
// errHTTPFilter returns errors no matter what is passed to ParseFilterConfig.
|
|
type errHTTPFilter struct {
|
|
httpfilter.ClientInterceptorBuilder
|
|
}
|
|
|
|
func (errHTTPFilter) TypeURLs() []string { return []string{"err.custom.filter"} }
|
|
|
|
func (errHTTPFilter) ParseFilterConfig(cfg proto.Message) (httpfilter.FilterConfig, error) {
|
|
return nil, fmt.Errorf("error from ParseFilterConfig")
|
|
}
|
|
|
|
func (errHTTPFilter) ParseFilterConfigOverride(override proto.Message) (httpfilter.FilterConfig, error) {
|
|
return nil, fmt.Errorf("error from ParseFilterConfigOverride")
|
|
}
|
|
|
|
func (errHTTPFilter) IsTerminal() bool {
|
|
return false
|
|
}
|
|
|
|
func init() {
|
|
httpfilter.Register(httpFilter{})
|
|
httpfilter.Register(errHTTPFilter{})
|
|
httpfilter.Register(serverOnlyHTTPFilter{})
|
|
httpfilter.Register(clientOnlyHTTPFilter{})
|
|
}
|
|
|
|
// serverOnlyHTTPFilter does not implement ClientInterceptorBuilder
|
|
type serverOnlyHTTPFilter struct {
|
|
httpfilter.ServerInterceptorBuilder
|
|
}
|
|
|
|
func (serverOnlyHTTPFilter) TypeURLs() []string { return []string{"serverOnly.custom.filter"} }
|
|
|
|
func (serverOnlyHTTPFilter) ParseFilterConfig(cfg proto.Message) (httpfilter.FilterConfig, error) {
|
|
return filterConfig{Cfg: cfg}, nil
|
|
}
|
|
|
|
func (serverOnlyHTTPFilter) ParseFilterConfigOverride(override proto.Message) (httpfilter.FilterConfig, error) {
|
|
return filterConfig{Override: override}, nil
|
|
}
|
|
|
|
func (serverOnlyHTTPFilter) IsTerminal() bool {
|
|
return false
|
|
}
|
|
|
|
// clientOnlyHTTPFilter does not implement ServerInterceptorBuilder
|
|
type clientOnlyHTTPFilter struct {
|
|
httpfilter.ClientInterceptorBuilder
|
|
}
|
|
|
|
func (clientOnlyHTTPFilter) TypeURLs() []string { return []string{"clientOnly.custom.filter"} }
|
|
|
|
func (clientOnlyHTTPFilter) ParseFilterConfig(cfg proto.Message) (httpfilter.FilterConfig, error) {
|
|
return filterConfig{Cfg: cfg}, nil
|
|
}
|
|
|
|
func (clientOnlyHTTPFilter) ParseFilterConfigOverride(override proto.Message) (httpfilter.FilterConfig, error) {
|
|
return filterConfig{Override: override}, nil
|
|
}
|
|
|
|
func (clientOnlyHTTPFilter) IsTerminal() bool {
|
|
return false
|
|
}
|
|
|
|
var customFilterConfig = &anypb.Any{
|
|
TypeUrl: "custom.filter",
|
|
Value: []byte{1, 2, 3},
|
|
}
|
|
|
|
var errFilterConfig = &anypb.Any{
|
|
TypeUrl: "err.custom.filter",
|
|
Value: []byte{1, 2, 3},
|
|
}
|
|
|
|
var serverOnlyCustomFilterConfig = &anypb.Any{
|
|
TypeUrl: "serverOnly.custom.filter",
|
|
Value: []byte{1, 2, 3},
|
|
}
|
|
|
|
var clientOnlyCustomFilterConfig = &anypb.Any{
|
|
TypeUrl: "clientOnly.custom.filter",
|
|
Value: []byte{1, 2, 3},
|
|
}
|
|
|
|
// This custom filter uses the old TypedStruct message from the cncf/udpa repo.
|
|
var customFilterOldTypedStructConfig = &v1xdsudpatypepb.TypedStruct{
|
|
TypeUrl: "custom.filter",
|
|
Value: &structpb.Struct{
|
|
Fields: map[string]*structpb.Value{
|
|
"foo": {Kind: &structpb.Value_StringValue{StringValue: "bar"}},
|
|
},
|
|
},
|
|
}
|
|
|
|
// This custom filter uses the new TypedStruct message from the cncf/xds repo.
|
|
var customFilterNewTypedStructConfig = &v3xdsxdstypepb.TypedStruct{
|
|
TypeUrl: "custom.filter",
|
|
Value: &structpb.Struct{
|
|
Fields: map[string]*structpb.Value{
|
|
"foo": {Kind: &structpb.Value_StringValue{StringValue: "bar"}},
|
|
},
|
|
},
|
|
}
|
|
|
|
var unknownFilterConfig = &anypb.Any{
|
|
TypeUrl: "unknown.custom.filter",
|
|
Value: []byte{1, 2, 3},
|
|
}
|
|
|
|
func wrappedOptionalFilter(t *testing.T, name string) *anypb.Any {
|
|
return testutils.MarshalAny(t, &v3routepb.FilterConfig{
|
|
IsOptional: true,
|
|
Config: &anypb.Any{
|
|
TypeUrl: name,
|
|
Value: []byte{1, 2, 3},
|
|
},
|
|
})
|
|
}
|