@@ -34,9 +34,10 @@ import (
34
34
"google.golang.org/grpc/codes"
35
35
"google.golang.org/grpc/credentials/insecure"
36
36
"google.golang.org/grpc/internal/testutils"
37
+ "google.golang.org/grpc/internal/xds/env"
37
38
"google.golang.org/grpc/status"
38
39
"google.golang.org/grpc/xds"
39
- _ "google.golang.org/grpc/xds/internal/httpfilter/rbac"
40
+ "google.golang.org/grpc/xds/internal/httpfilter/rbac"
40
41
"google.golang.org/grpc/xds/internal/testutils/e2e"
41
42
42
43
v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
@@ -374,6 +375,11 @@ func (s) TestServerSideXDS_SecurityConfigChange(t *testing.T) {
374
375
// (NonForwardingAction), and the RPC's matching those routes should proceed as
375
376
// normal.
376
377
func (s ) TestServerSideXDS_RouteConfiguration (t * testing.T ) {
378
+ oldRBAC := env .RBACSupport
379
+ env .RBACSupport = true
380
+ defer func () {
381
+ env .RBACSupport = oldRBAC
382
+ }()
377
383
managementServer , nodeID , bootstrapContents , resolver , cleanup1 := setupManagementServer (t )
378
384
defer cleanup1 ()
379
385
@@ -711,6 +717,13 @@ func serverListenerWithRBACHTTPFilters(host string, port uint32, rbacCfg *rpb.RB
711
717
// as normal and certain RPC's are denied by the RBAC HTTP Filter which gets
712
718
// called by hooked xds interceptors.
713
719
func (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 ()
714
727
tests := []struct {
715
728
name string
716
729
rbacCfg * rpb.RBAC
@@ -823,7 +836,218 @@ func (s) TestRBACHTTPFilter(t *testing.T) {
823
836
if _ , err := client .UnaryCall (ctx , & testpb.SimpleRequest {}); status .Code (err ) != test .wantStatusUnaryCall {
824
837
t .Fatalf ("UnaryCall() returned err with status: %v, wantStatusUnaryCall: %v" , err , test .wantStatusUnaryCall )
825
838
}
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
826
851
}()
827
852
})
828
853
}
829
854
}
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