@@ -34,9 +34,10 @@ import (
3434 "google.golang.org/grpc/codes"
3535 "google.golang.org/grpc/credentials/insecure"
3636 "google.golang.org/grpc/internal/testutils"
37+ "google.golang.org/grpc/internal/xds/env"
3738 "google.golang.org/grpc/status"
3839 "google.golang.org/grpc/xds"
39- _ "google.golang.org/grpc/xds/internal/httpfilter/rbac"
40+ "google.golang.org/grpc/xds/internal/httpfilter/rbac"
4041 "google.golang.org/grpc/xds/internal/testutils/e2e"
4142
4243 v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
@@ -374,6 +375,11 @@ func (s) TestServerSideXDS_SecurityConfigChange(t *testing.T) {
374375// (NonForwardingAction), and the RPC's matching those routes should proceed as
375376// normal.
376377func (s ) TestServerSideXDS_RouteConfiguration (t * testing.T ) {
378+ oldRBAC := env .RBACSupport
379+ env .RBACSupport = true
380+ defer func () {
381+ env .RBACSupport = oldRBAC
382+ }()
377383 managementServer , nodeID , bootstrapContents , resolver , cleanup1 := setupManagementServer (t )
378384 defer cleanup1 ()
379385
@@ -711,6 +717,13 @@ func serverListenerWithRBACHTTPFilters(host string, port uint32, rbacCfg *rpb.RB
711717// as normal and certain RPC's are denied by the RBAC HTTP Filter which gets
712718// called by hooked xds interceptors.
713719func (s ) TestRBACHTTPFilter (t * testing.T ) {
720+ oldRBAC := env .RBACSupport
721+ env .RBACSupport = true
722+ defer func () {
723+ env .RBACSupport = oldRBAC
724+ }()
725+ rbac .RegisterForTesting ()
726+ defer rbac .UnregisterForTesting ()
714727 tests := []struct {
715728 name string
716729 rbacCfg * rpb.RBAC
@@ -823,7 +836,218 @@ func (s) TestRBACHTTPFilter(t *testing.T) {
823836 if _ , err := client .UnaryCall (ctx , & testpb.SimpleRequest {}); status .Code (err ) != test .wantStatusUnaryCall {
824837 t .Fatalf ("UnaryCall() returned err with status: %v, wantStatusUnaryCall: %v" , err , test .wantStatusUnaryCall )
825838 }
839+
840+ // Toggle the RBAC Env variable off, this should disable RBAC and allow any RPC"s through (will not go through
841+ // routing or processed by HTTP Filters and thus will never get denied by RBAC).
842+ env .RBACSupport = false
843+ if _ , err := client .EmptyCall (ctx , & testpb.Empty {}); status .Code (err ) != codes .OK {
844+ t .Fatalf ("EmptyCall() returned err with status: %v, once RBAC is disabled all RPC's should proceed as normal" , status .Code (err ))
845+ }
846+ if _ , err := client .UnaryCall (ctx , & testpb.SimpleRequest {}); status .Code (err ) != codes .OK {
847+ t .Fatalf ("UnaryCall() returned err with status: %v, once RBAC is disabled all RPC's should proceed as normal" , status .Code (err ))
848+ }
849+ // Toggle RBAC back on for next iterations.
850+ env .RBACSupport = true
826851 }()
827852 })
828853 }
829854}
855+
856+ // serverListenerWithBadRouteConfiguration returns an xds Listener resource with
857+ // a Route Configuration that will never successfully match in order to test
858+ // RBAC Environment variable being toggled on and off.
859+ func serverListenerWithBadRouteConfiguration (host string , port uint32 ) * v3listenerpb.Listener {
860+ return & v3listenerpb.Listener {
861+ Name : fmt .Sprintf (e2e .ServerListenerResourceNameTemplate , net .JoinHostPort (host , strconv .Itoa (int (port )))),
862+ Address : & v3corepb.Address {
863+ Address : & v3corepb.Address_SocketAddress {
864+ SocketAddress : & v3corepb.SocketAddress {
865+ Address : host ,
866+ PortSpecifier : & v3corepb.SocketAddress_PortValue {
867+ PortValue : port ,
868+ },
869+ },
870+ },
871+ },
872+ FilterChains : []* v3listenerpb.FilterChain {
873+ {
874+ Name : "v4-wildcard" ,
875+ FilterChainMatch : & v3listenerpb.FilterChainMatch {
876+ PrefixRanges : []* v3corepb.CidrRange {
877+ {
878+ AddressPrefix : "0.0.0.0" ,
879+ PrefixLen : & wrapperspb.UInt32Value {
880+ Value : uint32 (0 ),
881+ },
882+ },
883+ },
884+ SourceType : v3listenerpb .FilterChainMatch_SAME_IP_OR_LOOPBACK ,
885+ SourcePrefixRanges : []* v3corepb.CidrRange {
886+ {
887+ AddressPrefix : "0.0.0.0" ,
888+ PrefixLen : & wrapperspb.UInt32Value {
889+ Value : uint32 (0 ),
890+ },
891+ },
892+ },
893+ },
894+ Filters : []* v3listenerpb.Filter {
895+ {
896+ Name : "filter-1" ,
897+ ConfigType : & v3listenerpb.Filter_TypedConfig {
898+ TypedConfig : testutils .MarshalAny (& v3httppb.HttpConnectionManager {
899+ RouteSpecifier : & v3httppb.HttpConnectionManager_RouteConfig {
900+ RouteConfig : & v3routepb.RouteConfiguration {
901+ Name : "routeName" ,
902+ VirtualHosts : []* v3routepb.VirtualHost {{
903+ // Incoming RPC's will try and match to Virtual Hosts based on their :authority header.
904+ // Thus, incoming RPC's will never match to a Virtual Host (server side requires matching
905+ // to a VH/Route of type Non Forwarding Action to proceed normally), and all incoming RPC's
906+ // with this route configuration will be denied.
907+ Domains : []string {"will-never-match" },
908+ Routes : []* v3routepb.Route {{
909+ Match : & v3routepb.RouteMatch {
910+ PathSpecifier : & v3routepb.RouteMatch_Prefix {Prefix : "/" },
911+ },
912+ Action : & v3routepb.Route_NonForwardingAction {},
913+ }}}}},
914+ },
915+ HttpFilters : []* v3httppb.HttpFilter {e2e .RouterHTTPFilter },
916+ }),
917+ },
918+ },
919+ },
920+ },
921+ {
922+ Name : "v6-wildcard" ,
923+ FilterChainMatch : & v3listenerpb.FilterChainMatch {
924+ PrefixRanges : []* v3corepb.CidrRange {
925+ {
926+ AddressPrefix : "::" ,
927+ PrefixLen : & wrapperspb.UInt32Value {
928+ Value : uint32 (0 ),
929+ },
930+ },
931+ },
932+ SourceType : v3listenerpb .FilterChainMatch_SAME_IP_OR_LOOPBACK ,
933+ SourcePrefixRanges : []* v3corepb.CidrRange {
934+ {
935+ AddressPrefix : "::" ,
936+ PrefixLen : & wrapperspb.UInt32Value {
937+ Value : uint32 (0 ),
938+ },
939+ },
940+ },
941+ },
942+ Filters : []* v3listenerpb.Filter {
943+ {
944+ Name : "filter-1" ,
945+ ConfigType : & v3listenerpb.Filter_TypedConfig {
946+ TypedConfig : testutils .MarshalAny (& v3httppb.HttpConnectionManager {
947+ RouteSpecifier : & v3httppb.HttpConnectionManager_RouteConfig {
948+ RouteConfig : & v3routepb.RouteConfiguration {
949+ Name : "routeName" ,
950+ VirtualHosts : []* v3routepb.VirtualHost {{
951+ // Incoming RPC's will try and match to Virtual Hosts based on their :authority header.
952+ // Thus, incoming RPC's will never match to a Virtual Host (server side requires matching
953+ // to a VH/Route of type Non Forwarding Action to proceed normally), and all incoming RPC's
954+ // with this route configuration will be denied.
955+ Domains : []string {"will-never-match" },
956+ Routes : []* v3routepb.Route {{
957+ Match : & v3routepb.RouteMatch {
958+ PathSpecifier : & v3routepb.RouteMatch_Prefix {Prefix : "/" },
959+ },
960+ Action : & v3routepb.Route_NonForwardingAction {},
961+ }}}}},
962+ },
963+ HttpFilters : []* v3httppb.HttpFilter {e2e .RouterHTTPFilter },
964+ }),
965+ },
966+ },
967+ },
968+ },
969+ },
970+ }
971+ }
972+
973+ // TestRBACToggledOffThenToggledOnWithBadRouteConfiguration tests a scenario
974+ // where the server gets a listener configuration with a route table that is
975+ // garbage, with incoming RPC's never matching to a VH/Route of type Non
976+ // Forwarding Action, thus never proceeding as normal. In the default scenario
977+ // (RBAC Env Var turned off, thus all logic related to Route Configuration
978+ // protected), the RPC's should simply proceed as normal due to ignoring the
979+ // route configuration. Once toggling the route configuration on, the RPC's
980+ // should all fail after updating the Server.
981+ func (s ) TestRBACToggledOffThenToggledOnWithBadRouteConfiguration (t * testing.T ) {
982+ managementServer , nodeID , bootstrapContents , resolver , cleanup1 := setupManagementServer (t )
983+ defer cleanup1 ()
984+
985+ lis , cleanup2 := setupGRPCServer (t , bootstrapContents )
986+ defer cleanup2 ()
987+
988+ host , port , err := hostPortFromListener (lis )
989+ if err != nil {
990+ t .Fatalf ("failed to retrieve host and port of server: %v" , err )
991+ }
992+ const serviceName = "my-service-fallback"
993+
994+ // The inbound listener needs a route table that will never match on a VH,
995+ // and thus shouldn't allow incoming RPC's to proceed.
996+ resources := e2e .DefaultClientResources (e2e.ResourceParams {
997+ DialTarget : serviceName ,
998+ NodeID : nodeID ,
999+ Host : host ,
1000+ Port : port ,
1001+ SecLevel : e2e .SecurityLevelNone ,
1002+ })
1003+ // This bad route configuration shouldn't affect incoming RPC's from
1004+ // proceeding as normal, as the configuration shouldn't be parsed due to the
1005+ // RBAC Environment variable not being set to true.
1006+ inboundLis := serverListenerWithBadRouteConfiguration (host , port )
1007+ resources .Listeners = append (resources .Listeners , inboundLis )
1008+
1009+ ctx , cancel := context .WithTimeout (context .Background (), defaultTestTimeout )
1010+ defer cancel ()
1011+ // Setup the management server with client and server-side resources.
1012+ if err := managementServer .Update (ctx , resources ); err != nil {
1013+ t .Fatal (err )
1014+ }
1015+
1016+ cc , err := grpc .DialContext (ctx , fmt .Sprintf ("xds:///%s" , serviceName ), grpc .WithInsecure (), grpc .WithResolvers (resolver ))
1017+ if err != nil {
1018+ t .Fatalf ("failed to dial local test server: %v" , err )
1019+ }
1020+ defer cc .Close ()
1021+
1022+ client := testpb .NewTestServiceClient (cc )
1023+
1024+ // The default setting of RBAC being disabled should allow any RPC's to
1025+ // proceed as normal.
1026+ if _ , err := client .EmptyCall (ctx , & testpb.Empty {}); status .Code (err ) != codes .OK {
1027+ t .Fatalf ("EmptyCall() returned err with status: %v, if RBAC is disabled all RPC's should proceed as normal" , status .Code (err ))
1028+ }
1029+ if _ , err := client .UnaryCall (ctx , & testpb.SimpleRequest {}); status .Code (err ) != codes .OK {
1030+ t .Fatalf ("UnaryCall() returned err with status: %v, if RBAC is disabled all RPC's should proceed as normal" , status .Code (err ))
1031+ }
1032+
1033+ // After toggling RBAC support on, all the RPC's should get denied with
1034+ // status code Unavailable due to not matching to a route of type Non
1035+ // Forwarding Action (Route Table not configured properly).
1036+ oldRBAC := env .RBACSupport
1037+ env .RBACSupport = true
1038+ defer func () {
1039+ env .RBACSupport = oldRBAC
1040+ }()
1041+ // Update the server with the same configuration, this is blocking on server
1042+ // side so no raciness here.
1043+ if err := managementServer .Update (ctx , resources ); err != nil {
1044+ t .Fatal (err )
1045+ }
1046+
1047+ if _ , err := client .EmptyCall (ctx , & testpb.Empty {}); status .Code (err ) != codes .Unavailable {
1048+ t .Fatalf ("EmptyCall() returned err with status: %v, if RBAC is disabled all RPC's should proceed as normal" , status .Code (err ))
1049+ }
1050+ if _ , err := client .UnaryCall (ctx , & testpb.SimpleRequest {}); status .Code (err ) != codes .Unavailable {
1051+ t .Fatalf ("UnaryCall() returned err with status: %v, if RBAC is disabled all RPC's should proceed as normal" , status .Code (err ))
1052+ }
1053+ }
0 commit comments