google.golang.org/grpc@v1.62.1/internal/xds/rbac/rbac_engine_test.go (about)

     1  /*
     2   * Copyright 2021 gRPC authors.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package rbac
    18  
    19  import (
    20  	"context"
    21  	"crypto/tls"
    22  	"crypto/x509"
    23  	"crypto/x509/pkix"
    24  	"encoding/json"
    25  	"fmt"
    26  	"net"
    27  	"net/url"
    28  	"reflect"
    29  	"testing"
    30  
    31  	v1xdsudpatypepb "github.com/cncf/xds/go/udpa/type/v1"
    32  	v3xdsxdstypepb "github.com/cncf/xds/go/xds/type/v3"
    33  	v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
    34  	v3rbacpb "github.com/envoyproxy/go-control-plane/envoy/config/rbac/v3"
    35  	v3routepb "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
    36  	v3matcherpb "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3"
    37  	v3typepb "github.com/envoyproxy/go-control-plane/envoy/type/v3"
    38  	"google.golang.org/grpc"
    39  	"google.golang.org/grpc/authz/audit"
    40  	"google.golang.org/grpc/codes"
    41  	"google.golang.org/grpc/credentials"
    42  	"google.golang.org/grpc/internal/grpctest"
    43  	"google.golang.org/grpc/metadata"
    44  	"google.golang.org/grpc/peer"
    45  	"google.golang.org/grpc/status"
    46  	"google.golang.org/protobuf/types/known/anypb"
    47  	"google.golang.org/protobuf/types/known/structpb"
    48  	"google.golang.org/protobuf/types/known/wrapperspb"
    49  )
    50  
    51  type s struct {
    52  	grpctest.Tester
    53  }
    54  
    55  func Test(t *testing.T) {
    56  	grpctest.RunSubTests(t, s{})
    57  }
    58  
    59  type addr struct {
    60  	ipAddress string
    61  }
    62  
    63  func (addr) Network() string   { return "" }
    64  func (a *addr) String() string { return a.ipAddress }
    65  
    66  // TestNewChainEngine tests the construction of the ChainEngine. Due to some
    67  // types of RBAC configuration being logically wrong and returning an error
    68  // rather than successfully constructing the RBAC Engine, this test tests both
    69  // RBAC Configurations deemed successful and also RBAC Configurations that will
    70  // raise errors.
    71  func (s) TestNewChainEngine(t *testing.T) {
    72  	tests := []struct {
    73  		name       string
    74  		policies   []*v3rbacpb.RBAC
    75  		wantErr    bool
    76  		policyName string
    77  	}{
    78  		{
    79  			name: "SuccessCaseAnyMatchSingular",
    80  			policies: []*v3rbacpb.RBAC{
    81  				{
    82  					Action: v3rbacpb.RBAC_ALLOW,
    83  					Policies: map[string]*v3rbacpb.Policy{
    84  						"anyone": {
    85  							Permissions: []*v3rbacpb.Permission{
    86  								{Rule: &v3rbacpb.Permission_Any{Any: true}},
    87  							},
    88  							Principals: []*v3rbacpb.Principal{
    89  								{Identifier: &v3rbacpb.Principal_Any{Any: true}},
    90  							},
    91  						},
    92  					},
    93  				},
    94  			},
    95  		},
    96  		{
    97  			name: "SuccessCaseAnyMatchMultiple",
    98  			policies: []*v3rbacpb.RBAC{
    99  				{
   100  					Action: v3rbacpb.RBAC_ALLOW,
   101  					Policies: map[string]*v3rbacpb.Policy{
   102  						"anyone": {
   103  							Permissions: []*v3rbacpb.Permission{
   104  								{Rule: &v3rbacpb.Permission_Any{Any: true}},
   105  							},
   106  							Principals: []*v3rbacpb.Principal{
   107  								{Identifier: &v3rbacpb.Principal_Any{Any: true}},
   108  							},
   109  						},
   110  					},
   111  				},
   112  				{
   113  					Action: v3rbacpb.RBAC_DENY,
   114  					Policies: map[string]*v3rbacpb.Policy{
   115  						"anyone": {
   116  							Permissions: []*v3rbacpb.Permission{
   117  								{Rule: &v3rbacpb.Permission_Any{Any: true}},
   118  							},
   119  							Principals: []*v3rbacpb.Principal{
   120  								{Identifier: &v3rbacpb.Principal_Any{Any: true}},
   121  							},
   122  						},
   123  					},
   124  				},
   125  			},
   126  		},
   127  		{
   128  			name: "SuccessCaseSimplePolicySingular",
   129  			policies: []*v3rbacpb.RBAC{
   130  				{
   131  					Action: v3rbacpb.RBAC_ALLOW,
   132  					Policies: map[string]*v3rbacpb.Policy{
   133  						"localhost-fan": {
   134  							Permissions: []*v3rbacpb.Permission{
   135  								{Rule: &v3rbacpb.Permission_DestinationPort{DestinationPort: 8080}},
   136  								{Rule: &v3rbacpb.Permission_UrlPath{UrlPath: &v3matcherpb.PathMatcher{Rule: &v3matcherpb.PathMatcher_Path{Path: &v3matcherpb.StringMatcher{MatchPattern: &v3matcherpb.StringMatcher_Exact{Exact: "localhost-fan-page"}}}}}},
   137  							},
   138  							Principals: []*v3rbacpb.Principal{
   139  								{Identifier: &v3rbacpb.Principal_Any{Any: true}},
   140  							},
   141  						},
   142  					},
   143  				},
   144  			},
   145  		},
   146  		// SuccessCaseSimplePolicyTwoPolicies tests the construction of the
   147  		// chained engines in the case where there are two policies in a list,
   148  		// one with an allow policy and one with a deny policy. A situation
   149  		// where two policies (allow and deny) is a very common use case for
   150  		// this API, and should successfully build.
   151  		{
   152  			name: "SuccessCaseSimplePolicyTwoPolicies",
   153  			policies: []*v3rbacpb.RBAC{
   154  				{
   155  					Action: v3rbacpb.RBAC_ALLOW,
   156  					Policies: map[string]*v3rbacpb.Policy{
   157  						"localhost-fan": {
   158  							Permissions: []*v3rbacpb.Permission{
   159  								{Rule: &v3rbacpb.Permission_DestinationPort{DestinationPort: 8080}},
   160  								{Rule: &v3rbacpb.Permission_UrlPath{UrlPath: &v3matcherpb.PathMatcher{Rule: &v3matcherpb.PathMatcher_Path{Path: &v3matcherpb.StringMatcher{MatchPattern: &v3matcherpb.StringMatcher_Exact{Exact: "localhost-fan-page"}}}}}},
   161  							},
   162  							Principals: []*v3rbacpb.Principal{
   163  								{Identifier: &v3rbacpb.Principal_Any{Any: true}},
   164  							},
   165  						},
   166  					},
   167  				},
   168  				{
   169  					Action: v3rbacpb.RBAC_DENY,
   170  					Policies: map[string]*v3rbacpb.Policy{
   171  						"localhost-fan": {
   172  							Permissions: []*v3rbacpb.Permission{
   173  								{Rule: &v3rbacpb.Permission_DestinationPort{DestinationPort: 8080}},
   174  								{Rule: &v3rbacpb.Permission_UrlPath{UrlPath: &v3matcherpb.PathMatcher{Rule: &v3matcherpb.PathMatcher_Path{Path: &v3matcherpb.StringMatcher{MatchPattern: &v3matcherpb.StringMatcher_Exact{Exact: "localhost-fan-page"}}}}}},
   175  							},
   176  							Principals: []*v3rbacpb.Principal{
   177  								{Identifier: &v3rbacpb.Principal_Any{Any: true}},
   178  							},
   179  						},
   180  					},
   181  				},
   182  			},
   183  		},
   184  		{
   185  			name: "SuccessCaseEnvoyExampleSingular",
   186  			policies: []*v3rbacpb.RBAC{
   187  				{
   188  					Action: v3rbacpb.RBAC_ALLOW,
   189  					Policies: map[string]*v3rbacpb.Policy{
   190  						"service-admin": {
   191  							Permissions: []*v3rbacpb.Permission{
   192  								{Rule: &v3rbacpb.Permission_Any{Any: true}},
   193  							},
   194  							Principals: []*v3rbacpb.Principal{
   195  								{Identifier: &v3rbacpb.Principal_Authenticated_{Authenticated: &v3rbacpb.Principal_Authenticated{PrincipalName: &v3matcherpb.StringMatcher{MatchPattern: &v3matcherpb.StringMatcher_Exact{Exact: "cluster.local/ns/default/sa/admin"}}}}},
   196  								{Identifier: &v3rbacpb.Principal_Authenticated_{Authenticated: &v3rbacpb.Principal_Authenticated{PrincipalName: &v3matcherpb.StringMatcher{MatchPattern: &v3matcherpb.StringMatcher_Exact{Exact: "cluster.local/ns/default/sa/superuser"}}}}},
   197  							},
   198  						},
   199  						"product-viewer": {
   200  							Permissions: []*v3rbacpb.Permission{
   201  								{Rule: &v3rbacpb.Permission_AndRules{AndRules: &v3rbacpb.Permission_Set{
   202  									Rules: []*v3rbacpb.Permission{
   203  										{Rule: &v3rbacpb.Permission_Header{Header: &v3routepb.HeaderMatcher{Name: ":method", HeaderMatchSpecifier: &v3routepb.HeaderMatcher_ExactMatch{ExactMatch: "GET"}}}},
   204  										{Rule: &v3rbacpb.Permission_UrlPath{UrlPath: &v3matcherpb.PathMatcher{Rule: &v3matcherpb.PathMatcher_Path{Path: &v3matcherpb.StringMatcher{MatchPattern: &v3matcherpb.StringMatcher_Prefix{Prefix: "/products"}}}}}},
   205  										{Rule: &v3rbacpb.Permission_OrRules{OrRules: &v3rbacpb.Permission_Set{
   206  											Rules: []*v3rbacpb.Permission{
   207  												{Rule: &v3rbacpb.Permission_DestinationPort{DestinationPort: 80}},
   208  												{Rule: &v3rbacpb.Permission_DestinationPort{DestinationPort: 443}},
   209  											},
   210  										},
   211  										},
   212  										},
   213  									},
   214  								},
   215  								},
   216  								},
   217  							},
   218  							Principals: []*v3rbacpb.Principal{
   219  								{Identifier: &v3rbacpb.Principal_Any{Any: true}},
   220  							},
   221  						},
   222  					},
   223  				},
   224  			},
   225  		},
   226  		{
   227  			name: "SourceIpMatcherSuccessSingular",
   228  			policies: []*v3rbacpb.RBAC{
   229  				{
   230  					Action: v3rbacpb.RBAC_ALLOW,
   231  					Policies: map[string]*v3rbacpb.Policy{
   232  						"certain-source-ip": {
   233  							Permissions: []*v3rbacpb.Permission{
   234  								{Rule: &v3rbacpb.Permission_Any{Any: true}},
   235  							},
   236  							Principals: []*v3rbacpb.Principal{
   237  								{Identifier: &v3rbacpb.Principal_DirectRemoteIp{DirectRemoteIp: &v3corepb.CidrRange{AddressPrefix: "0.0.0.0", PrefixLen: &wrapperspb.UInt32Value{Value: uint32(10)}}}},
   238  							},
   239  						},
   240  					},
   241  				},
   242  			},
   243  		},
   244  		{
   245  			name: "SourceIpMatcherFailureSingular",
   246  			policies: []*v3rbacpb.RBAC{
   247  				{
   248  					Action: v3rbacpb.RBAC_ALLOW,
   249  					Policies: map[string]*v3rbacpb.Policy{
   250  						"certain-source-ip": {
   251  							Permissions: []*v3rbacpb.Permission{
   252  								{Rule: &v3rbacpb.Permission_Any{Any: true}},
   253  							},
   254  							Principals: []*v3rbacpb.Principal{
   255  								{Identifier: &v3rbacpb.Principal_DirectRemoteIp{DirectRemoteIp: &v3corepb.CidrRange{AddressPrefix: "not a correct address", PrefixLen: &wrapperspb.UInt32Value{Value: uint32(10)}}}},
   256  							},
   257  						},
   258  					},
   259  				},
   260  			},
   261  			wantErr: true,
   262  		},
   263  		{
   264  			name: "DestinationIpMatcherSuccess",
   265  			policies: []*v3rbacpb.RBAC{
   266  				{
   267  					Action: v3rbacpb.RBAC_ALLOW,
   268  					Policies: map[string]*v3rbacpb.Policy{
   269  						"certain-destination-ip": {
   270  							Permissions: []*v3rbacpb.Permission{
   271  								{Rule: &v3rbacpb.Permission_DestinationIp{DestinationIp: &v3corepb.CidrRange{AddressPrefix: "0.0.0.0", PrefixLen: &wrapperspb.UInt32Value{Value: uint32(10)}}}},
   272  							},
   273  							Principals: []*v3rbacpb.Principal{
   274  								{Identifier: &v3rbacpb.Principal_Any{Any: true}},
   275  							},
   276  						},
   277  					},
   278  				},
   279  			},
   280  		},
   281  		{
   282  			name: "DestinationIpMatcherFailure",
   283  			policies: []*v3rbacpb.RBAC{
   284  				{
   285  					Action: v3rbacpb.RBAC_ALLOW,
   286  					Policies: map[string]*v3rbacpb.Policy{
   287  						"certain-destination-ip": {
   288  							Permissions: []*v3rbacpb.Permission{
   289  								{Rule: &v3rbacpb.Permission_DestinationIp{DestinationIp: &v3corepb.CidrRange{AddressPrefix: "not a correct address", PrefixLen: &wrapperspb.UInt32Value{Value: uint32(10)}}}},
   290  							},
   291  							Principals: []*v3rbacpb.Principal{
   292  								{Identifier: &v3rbacpb.Principal_Any{Any: true}},
   293  							},
   294  						},
   295  					},
   296  				},
   297  			},
   298  			wantErr: true,
   299  		},
   300  		{
   301  			name: "MatcherToNotPolicy",
   302  			policies: []*v3rbacpb.RBAC{
   303  				{
   304  					Action: v3rbacpb.RBAC_ALLOW,
   305  					Policies: map[string]*v3rbacpb.Policy{
   306  						"not-secret-content": {
   307  							Permissions: []*v3rbacpb.Permission{
   308  								{Rule: &v3rbacpb.Permission_NotRule{NotRule: &v3rbacpb.Permission{Rule: &v3rbacpb.Permission_UrlPath{UrlPath: &v3matcherpb.PathMatcher{Rule: &v3matcherpb.PathMatcher_Path{Path: &v3matcherpb.StringMatcher{MatchPattern: &v3matcherpb.StringMatcher_Prefix{Prefix: "/secret-content"}}}}}}}},
   309  							},
   310  							Principals: []*v3rbacpb.Principal{
   311  								{Identifier: &v3rbacpb.Principal_Any{Any: true}},
   312  							},
   313  						},
   314  					},
   315  				},
   316  			},
   317  		},
   318  		{
   319  			name: "MatcherToNotPrinicipal",
   320  			policies: []*v3rbacpb.RBAC{
   321  				{
   322  					Action: v3rbacpb.RBAC_ALLOW,
   323  					Policies: map[string]*v3rbacpb.Policy{
   324  						"not-from-certain-ip": {
   325  							Permissions: []*v3rbacpb.Permission{
   326  								{Rule: &v3rbacpb.Permission_Any{Any: true}},
   327  							},
   328  							Principals: []*v3rbacpb.Principal{
   329  								{Identifier: &v3rbacpb.Principal_NotId{NotId: &v3rbacpb.Principal{Identifier: &v3rbacpb.Principal_DirectRemoteIp{DirectRemoteIp: &v3corepb.CidrRange{AddressPrefix: "0.0.0.0", PrefixLen: &wrapperspb.UInt32Value{Value: uint32(10)}}}}}},
   330  							},
   331  						},
   332  					},
   333  				},
   334  			},
   335  		},
   336  		// PrinicpalProductViewer tests the construction of a chained engine
   337  		// with a policy that allows any downstream to send a GET request on a
   338  		// certain path.
   339  		{
   340  			name: "PrincipalProductViewer",
   341  			policies: []*v3rbacpb.RBAC{
   342  				{
   343  					Action: v3rbacpb.RBAC_ALLOW,
   344  					Policies: map[string]*v3rbacpb.Policy{
   345  						"product-viewer": {
   346  							Permissions: []*v3rbacpb.Permission{
   347  								{Rule: &v3rbacpb.Permission_Any{Any: true}},
   348  							},
   349  							Principals: []*v3rbacpb.Principal{
   350  								{
   351  									Identifier: &v3rbacpb.Principal_AndIds{AndIds: &v3rbacpb.Principal_Set{Ids: []*v3rbacpb.Principal{
   352  										{Identifier: &v3rbacpb.Principal_Header{Header: &v3routepb.HeaderMatcher{Name: ":method", HeaderMatchSpecifier: &v3routepb.HeaderMatcher_ExactMatch{ExactMatch: "GET"}}}},
   353  										{Identifier: &v3rbacpb.Principal_OrIds{OrIds: &v3rbacpb.Principal_Set{
   354  											Ids: []*v3rbacpb.Principal{
   355  												{Identifier: &v3rbacpb.Principal_UrlPath{UrlPath: &v3matcherpb.PathMatcher{Rule: &v3matcherpb.PathMatcher_Path{Path: &v3matcherpb.StringMatcher{MatchPattern: &v3matcherpb.StringMatcher_Prefix{Prefix: "/books"}}}}}},
   356  												{Identifier: &v3rbacpb.Principal_UrlPath{UrlPath: &v3matcherpb.PathMatcher{Rule: &v3matcherpb.PathMatcher_Path{Path: &v3matcherpb.StringMatcher{MatchPattern: &v3matcherpb.StringMatcher_Prefix{Prefix: "/cars"}}}}}},
   357  											},
   358  										}}},
   359  									}}},
   360  								},
   361  							},
   362  						},
   363  					},
   364  				},
   365  			},
   366  		},
   367  		// Certain Headers tests the construction of a chained engine with a
   368  		// policy that allows any downstream to send an HTTP request with
   369  		// certain headers.
   370  		{
   371  			name: "CertainHeaders",
   372  			policies: []*v3rbacpb.RBAC{
   373  				{
   374  					Policies: map[string]*v3rbacpb.Policy{
   375  						"certain-headers": {
   376  							Permissions: []*v3rbacpb.Permission{
   377  								{Rule: &v3rbacpb.Permission_Any{Any: true}},
   378  							},
   379  							Principals: []*v3rbacpb.Principal{
   380  								{
   381  									Identifier: &v3rbacpb.Principal_OrIds{OrIds: &v3rbacpb.Principal_Set{Ids: []*v3rbacpb.Principal{
   382  										{Identifier: &v3rbacpb.Principal_Header{Header: &v3routepb.HeaderMatcher{Name: ":method", HeaderMatchSpecifier: &v3routepb.HeaderMatcher_ExactMatch{ExactMatch: "GET"}}}},
   383  										{Identifier: &v3rbacpb.Principal_Header{Header: &v3routepb.HeaderMatcher{Name: ":method", HeaderMatchSpecifier: &v3routepb.HeaderMatcher_SafeRegexMatch{SafeRegexMatch: &v3matcherpb.RegexMatcher{Regex: "GET"}}}}},
   384  										{Identifier: &v3rbacpb.Principal_Header{Header: &v3routepb.HeaderMatcher{Name: ":method", HeaderMatchSpecifier: &v3routepb.HeaderMatcher_RangeMatch{RangeMatch: &v3typepb.Int64Range{
   385  											Start: 0,
   386  											End:   64,
   387  										}}}}},
   388  										{Identifier: &v3rbacpb.Principal_Header{Header: &v3routepb.HeaderMatcher{Name: ":method", HeaderMatchSpecifier: &v3routepb.HeaderMatcher_PresentMatch{PresentMatch: true}}}},
   389  										{Identifier: &v3rbacpb.Principal_Header{Header: &v3routepb.HeaderMatcher{Name: ":method", HeaderMatchSpecifier: &v3routepb.HeaderMatcher_PrefixMatch{PrefixMatch: "GET"}}}},
   390  										{Identifier: &v3rbacpb.Principal_Header{Header: &v3routepb.HeaderMatcher{Name: ":method", HeaderMatchSpecifier: &v3routepb.HeaderMatcher_SuffixMatch{SuffixMatch: "GET"}}}},
   391  										{Identifier: &v3rbacpb.Principal_Header{Header: &v3routepb.HeaderMatcher{Name: ":method", HeaderMatchSpecifier: &v3routepb.HeaderMatcher_ContainsMatch{ContainsMatch: "GET"}}}},
   392  										{Identifier: &v3rbacpb.Principal_Header{Header: &v3routepb.HeaderMatcher{Name: ":method", HeaderMatchSpecifier: &v3routepb.HeaderMatcher_ContainsMatch{ContainsMatch: "GET"}}}},
   393  									}}},
   394  								},
   395  							},
   396  						},
   397  					},
   398  				},
   399  			},
   400  		},
   401  		{
   402  			name: "LogAction",
   403  			policies: []*v3rbacpb.RBAC{
   404  				{
   405  					Action: v3rbacpb.RBAC_LOG,
   406  					Policies: map[string]*v3rbacpb.Policy{
   407  						"anyone": {
   408  							Permissions: []*v3rbacpb.Permission{
   409  								{Rule: &v3rbacpb.Permission_Any{Any: true}},
   410  							},
   411  							Principals: []*v3rbacpb.Principal{
   412  								{Identifier: &v3rbacpb.Principal_Any{Any: true}},
   413  							},
   414  						},
   415  					},
   416  				},
   417  			},
   418  			wantErr: true,
   419  		},
   420  		{
   421  			name: "ActionNotSpecified",
   422  			policies: []*v3rbacpb.RBAC{
   423  				{
   424  					Policies: map[string]*v3rbacpb.Policy{
   425  						"anyone": {
   426  							Permissions: []*v3rbacpb.Permission{
   427  								{Rule: &v3rbacpb.Permission_Any{Any: true}},
   428  							},
   429  							Principals: []*v3rbacpb.Principal{
   430  								{Identifier: &v3rbacpb.Principal_Any{Any: true}},
   431  							},
   432  						},
   433  					},
   434  				},
   435  			},
   436  		},
   437  		{
   438  			name: "SimpleAuditLogger",
   439  			policies: []*v3rbacpb.RBAC{
   440  				{
   441  					Action: v3rbacpb.RBAC_ALLOW,
   442  					Policies: map[string]*v3rbacpb.Policy{
   443  						"anyone": {
   444  							Permissions: []*v3rbacpb.Permission{
   445  								{Rule: &v3rbacpb.Permission_Any{Any: true}},
   446  							},
   447  							Principals: []*v3rbacpb.Principal{
   448  								{Identifier: &v3rbacpb.Principal_Any{Any: true}},
   449  							},
   450  						},
   451  					},
   452  					AuditLoggingOptions: &v3rbacpb.RBAC_AuditLoggingOptions{
   453  						AuditCondition: v3rbacpb.RBAC_AuditLoggingOptions_ON_ALLOW,
   454  						LoggerConfigs: []*v3rbacpb.RBAC_AuditLoggingOptions_AuditLoggerConfig{
   455  							{AuditLogger: &v3corepb.TypedExtensionConfig{
   456  								Name:        "TestAuditLoggerBuffer",
   457  								TypedConfig: createUDPATypedStruct(t, map[string]any{}, "SimpleAuditLogger_TestAuditLoggerBuffer")},
   458  								IsOptional: false,
   459  							},
   460  						},
   461  					},
   462  				},
   463  			},
   464  		},
   465  		{
   466  			name: "AuditLoggerCustomConfig",
   467  			policies: []*v3rbacpb.RBAC{
   468  				{
   469  					Action: v3rbacpb.RBAC_ALLOW,
   470  					Policies: map[string]*v3rbacpb.Policy{
   471  						"anyone": {
   472  							Permissions: []*v3rbacpb.Permission{
   473  								{Rule: &v3rbacpb.Permission_Any{Any: true}},
   474  							},
   475  							Principals: []*v3rbacpb.Principal{
   476  								{Identifier: &v3rbacpb.Principal_Any{Any: true}},
   477  							},
   478  						},
   479  					},
   480  					AuditLoggingOptions: &v3rbacpb.RBAC_AuditLoggingOptions{
   481  						AuditCondition: v3rbacpb.RBAC_AuditLoggingOptions_ON_ALLOW,
   482  						LoggerConfigs: []*v3rbacpb.RBAC_AuditLoggingOptions_AuditLoggerConfig{
   483  							{AuditLogger: &v3corepb.TypedExtensionConfig{
   484  								Name:        "TestAuditLoggerCustomConfig",
   485  								TypedConfig: createUDPATypedStruct(t, map[string]any{"abc": 123, "xyz": "123"}, "AuditLoggerCustomConfig_TestAuditLoggerCustomConfig")},
   486  								IsOptional: false,
   487  							},
   488  						},
   489  					},
   490  				},
   491  			},
   492  			policyName: "test_policy",
   493  		},
   494  		{
   495  			name: "AuditLoggerCustomConfigXdsTypedStruct",
   496  			policies: []*v3rbacpb.RBAC{
   497  				{
   498  					Action: v3rbacpb.RBAC_ALLOW,
   499  					Policies: map[string]*v3rbacpb.Policy{
   500  						"anyone": {
   501  							Permissions: []*v3rbacpb.Permission{
   502  								{Rule: &v3rbacpb.Permission_Any{Any: true}},
   503  							},
   504  							Principals: []*v3rbacpb.Principal{
   505  								{Identifier: &v3rbacpb.Principal_Any{Any: true}},
   506  							},
   507  						},
   508  					},
   509  					AuditLoggingOptions: &v3rbacpb.RBAC_AuditLoggingOptions{
   510  						AuditCondition: v3rbacpb.RBAC_AuditLoggingOptions_ON_ALLOW,
   511  						LoggerConfigs: []*v3rbacpb.RBAC_AuditLoggingOptions_AuditLoggerConfig{
   512  							{AuditLogger: &v3corepb.TypedExtensionConfig{
   513  								Name:        "TestAuditLoggerCustomConfig",
   514  								TypedConfig: createXDSTypedStruct(t, map[string]any{"abc": 123, "xyz": "123"}, "AuditLoggerCustomConfigXdsTypedStruct_TestAuditLoggerCustomConfig")},
   515  								IsOptional: false,
   516  							},
   517  						},
   518  					},
   519  				},
   520  			},
   521  			policyName: "test_policy",
   522  		},
   523  		{
   524  			name: "Missing Optional AuditLogger doesn't fail",
   525  			policies: []*v3rbacpb.RBAC{
   526  				{
   527  					Action: v3rbacpb.RBAC_ALLOW,
   528  					Policies: map[string]*v3rbacpb.Policy{
   529  						"anyone": {
   530  							Permissions: []*v3rbacpb.Permission{
   531  								{Rule: &v3rbacpb.Permission_Any{Any: true}},
   532  							},
   533  							Principals: []*v3rbacpb.Principal{
   534  								{Identifier: &v3rbacpb.Principal_Any{Any: true}},
   535  							},
   536  						},
   537  					},
   538  					AuditLoggingOptions: &v3rbacpb.RBAC_AuditLoggingOptions{
   539  						AuditCondition: v3rbacpb.RBAC_AuditLoggingOptions_ON_ALLOW,
   540  						LoggerConfigs: []*v3rbacpb.RBAC_AuditLoggingOptions_AuditLoggerConfig{
   541  							{AuditLogger: &v3corepb.TypedExtensionConfig{
   542  								Name:        "UnsupportedLogger",
   543  								TypedConfig: createUDPATypedStruct(t, map[string]any{}, "Missing Optional AuditLogger doesn't fail_UnsupportedLogger")},
   544  								IsOptional: true,
   545  							},
   546  						},
   547  					},
   548  				},
   549  			},
   550  		},
   551  		{
   552  			name: "Missing Non-Optional AuditLogger fails",
   553  			policies: []*v3rbacpb.RBAC{
   554  				{
   555  					Action: v3rbacpb.RBAC_ALLOW,
   556  					Policies: map[string]*v3rbacpb.Policy{
   557  						"anyone": {
   558  							Permissions: []*v3rbacpb.Permission{
   559  								{Rule: &v3rbacpb.Permission_Any{Any: true}},
   560  							},
   561  							Principals: []*v3rbacpb.Principal{
   562  								{Identifier: &v3rbacpb.Principal_Any{Any: true}},
   563  							},
   564  						},
   565  					},
   566  					AuditLoggingOptions: &v3rbacpb.RBAC_AuditLoggingOptions{
   567  						AuditCondition: v3rbacpb.RBAC_AuditLoggingOptions_ON_ALLOW,
   568  						LoggerConfigs: []*v3rbacpb.RBAC_AuditLoggingOptions_AuditLoggerConfig{
   569  							{AuditLogger: &v3corepb.TypedExtensionConfig{
   570  								Name:        "UnsupportedLogger",
   571  								TypedConfig: createUDPATypedStruct(t, map[string]any{}, "Missing Non-Optional AuditLogger fails_UnsupportedLogger")},
   572  								IsOptional: false,
   573  							},
   574  						},
   575  					},
   576  				},
   577  			},
   578  			wantErr: true,
   579  		},
   580  		{
   581  			name: "Cannot_parse_missing_CustomConfig",
   582  			policies: []*v3rbacpb.RBAC{
   583  				{
   584  					Action: v3rbacpb.RBAC_ALLOW,
   585  					Policies: map[string]*v3rbacpb.Policy{
   586  						"anyone": {
   587  							Permissions: []*v3rbacpb.Permission{
   588  								{Rule: &v3rbacpb.Permission_Any{Any: true}},
   589  							},
   590  							Principals: []*v3rbacpb.Principal{
   591  								{Identifier: &v3rbacpb.Principal_Any{Any: true}},
   592  							},
   593  						},
   594  					},
   595  					AuditLoggingOptions: &v3rbacpb.RBAC_AuditLoggingOptions{
   596  						AuditCondition: v3rbacpb.RBAC_AuditLoggingOptions_ON_ALLOW,
   597  						LoggerConfigs: []*v3rbacpb.RBAC_AuditLoggingOptions_AuditLoggerConfig{
   598  							{AuditLogger: &v3corepb.TypedExtensionConfig{
   599  								Name: "TestAuditLoggerCustomConfig",
   600  							},
   601  								IsOptional: false,
   602  							},
   603  						},
   604  					},
   605  				},
   606  			},
   607  			wantErr: true,
   608  		},
   609  		{
   610  			name: "Cannot_parse_bad_CustomConfig",
   611  			policies: []*v3rbacpb.RBAC{
   612  				{
   613  					Action: v3rbacpb.RBAC_ALLOW,
   614  					Policies: map[string]*v3rbacpb.Policy{
   615  						"anyone": {
   616  							Permissions: []*v3rbacpb.Permission{
   617  								{Rule: &v3rbacpb.Permission_Any{Any: true}},
   618  							},
   619  							Principals: []*v3rbacpb.Principal{
   620  								{Identifier: &v3rbacpb.Principal_Any{Any: true}},
   621  							},
   622  						},
   623  					},
   624  					AuditLoggingOptions: &v3rbacpb.RBAC_AuditLoggingOptions{
   625  						AuditCondition: v3rbacpb.RBAC_AuditLoggingOptions_ON_ALLOW,
   626  						LoggerConfigs: []*v3rbacpb.RBAC_AuditLoggingOptions_AuditLoggerConfig{
   627  							{AuditLogger: &v3corepb.TypedExtensionConfig{
   628  								Name:        "TestAuditLoggerCustomConfig",
   629  								TypedConfig: createUDPATypedStruct(t, map[string]any{"abc": "BADVALUE", "xyz": "123"}, "Cannot_parse_bad_CustomConfig_TestAuditLoggerCustomConfig")},
   630  								IsOptional: false,
   631  							},
   632  						},
   633  					},
   634  				},
   635  			},
   636  			wantErr: true,
   637  		},
   638  		{
   639  			name: "Cannot_parse_missing_typedConfig_name",
   640  			policies: []*v3rbacpb.RBAC{
   641  				{
   642  					Action: v3rbacpb.RBAC_ALLOW,
   643  					Policies: map[string]*v3rbacpb.Policy{
   644  						"anyone": {
   645  							Permissions: []*v3rbacpb.Permission{
   646  								{Rule: &v3rbacpb.Permission_Any{Any: true}},
   647  							},
   648  							Principals: []*v3rbacpb.Principal{
   649  								{Identifier: &v3rbacpb.Principal_Any{Any: true}},
   650  							},
   651  						},
   652  					},
   653  					AuditLoggingOptions: &v3rbacpb.RBAC_AuditLoggingOptions{
   654  						AuditCondition: v3rbacpb.RBAC_AuditLoggingOptions_ON_ALLOW,
   655  						LoggerConfigs: []*v3rbacpb.RBAC_AuditLoggingOptions_AuditLoggerConfig{
   656  							{AuditLogger: &v3corepb.TypedExtensionConfig{
   657  								Name:        "TestAuditLoggerCustomConfig",
   658  								TypedConfig: createUDPATypedStruct(t, map[string]any{"abc": 123, "xyz": "123"}, "")},
   659  								IsOptional: false,
   660  							},
   661  						},
   662  					},
   663  				},
   664  			},
   665  			wantErr: true,
   666  		},
   667  	}
   668  	for _, test := range tests {
   669  		t.Run(test.name, func(t *testing.T) {
   670  			b := TestAuditLoggerBufferBuilder{testName: test.name}
   671  			audit.RegisterLoggerBuilder(&b)
   672  			b2 := TestAuditLoggerCustomConfigBuilder{testName: test.name}
   673  			audit.RegisterLoggerBuilder(&b2)
   674  			if _, err := NewChainEngine(test.policies, test.policyName); (err != nil) != test.wantErr {
   675  				t.Fatalf("NewChainEngine(%+v) returned err: %v, wantErr: %v", test.policies, err, test.wantErr)
   676  			}
   677  		})
   678  	}
   679  }
   680  
   681  type rbacQuery struct {
   682  	rpcData         *rpcData
   683  	wantStatusCode  codes.Code
   684  	wantAuditEvents []*audit.Event
   685  }
   686  
   687  // TestChainEngine tests the chain of RBAC Engines by configuring the chain of
   688  // engines in a certain way in different scenarios. After configuring the chain
   689  // of engines in a certain way, this test pings the chain of engines with
   690  // different types of data representing incoming RPC's (piped into a context),
   691  // and verifies that it works as expected.
   692  func (s) TestChainEngine(t *testing.T) {
   693  	defer func(gc func(ctx context.Context) net.Conn) {
   694  		getConnection = gc
   695  	}(getConnection)
   696  	tests := []struct {
   697  		name        string
   698  		rbacConfigs []*v3rbacpb.RBAC
   699  		rbacQueries []rbacQuery
   700  		policyName  string
   701  	}{
   702  		// SuccessCaseAnyMatch tests a single RBAC Engine instantiated with
   703  		// a config with a policy with any rules for both permissions and
   704  		// principals, meaning that any data about incoming RPC's that the RBAC
   705  		// Engine is queried with should match that policy.
   706  		{
   707  			name: "SuccessCaseAnyMatch",
   708  			rbacConfigs: []*v3rbacpb.RBAC{
   709  				{
   710  					Policies: map[string]*v3rbacpb.Policy{
   711  						"anyone": {
   712  							Permissions: []*v3rbacpb.Permission{
   713  								{Rule: &v3rbacpb.Permission_Any{Any: true}},
   714  							},
   715  							Principals: []*v3rbacpb.Principal{
   716  								{Identifier: &v3rbacpb.Principal_Any{Any: true}},
   717  							},
   718  						},
   719  					},
   720  				},
   721  			},
   722  			rbacQueries: []rbacQuery{
   723  				{
   724  					rpcData: &rpcData{
   725  						fullMethod: "some method",
   726  						peerInfo: &peer.Peer{
   727  							Addr: &addr{ipAddress: "0.0.0.0"},
   728  						},
   729  					},
   730  					wantStatusCode: codes.OK,
   731  				},
   732  			},
   733  		},
   734  		// SuccessCaseSimplePolicy is a test that tests a single policy
   735  		// that only allows an rpc to proceed if the rpc is calling with a certain
   736  		// path.
   737  		{
   738  			name: "SuccessCaseSimplePolicy",
   739  			rbacConfigs: []*v3rbacpb.RBAC{
   740  				{
   741  					Policies: map[string]*v3rbacpb.Policy{
   742  						"localhost-fan": {
   743  							Permissions: []*v3rbacpb.Permission{
   744  								{Rule: &v3rbacpb.Permission_UrlPath{UrlPath: &v3matcherpb.PathMatcher{Rule: &v3matcherpb.PathMatcher_Path{Path: &v3matcherpb.StringMatcher{MatchPattern: &v3matcherpb.StringMatcher_Exact{Exact: "localhost-fan-page"}}}}}},
   745  							},
   746  							Principals: []*v3rbacpb.Principal{
   747  								{Identifier: &v3rbacpb.Principal_Any{Any: true}},
   748  							},
   749  						},
   750  					},
   751  				},
   752  			},
   753  			rbacQueries: []rbacQuery{
   754  				// This RPC should match with the local host fan policy. Thus,
   755  				// this RPC should be allowed to proceed.
   756  				{
   757  					rpcData: &rpcData{
   758  						fullMethod: "localhost-fan-page",
   759  						peerInfo: &peer.Peer{
   760  							Addr: &addr{ipAddress: "0.0.0.0"},
   761  						},
   762  					},
   763  					wantStatusCode: codes.OK,
   764  				},
   765  
   766  				// This RPC shouldn't match with the local host fan policy. Thus,
   767  				// this rpc shouldn't be allowed to proceed.
   768  				{
   769  					rpcData: &rpcData{
   770  						peerInfo: &peer.Peer{
   771  							Addr: &addr{ipAddress: "0.0.0.0"},
   772  						},
   773  					},
   774  					wantStatusCode: codes.PermissionDenied,
   775  				},
   776  			},
   777  		},
   778  		// SuccessCaseEnvoyExample is a test based on the example provided
   779  		// in the EnvoyProxy docs. The RBAC Config contains two policies,
   780  		// service admin and product viewer, that provides an example of a real
   781  		// RBAC Config that might be configured for a given for a given backend
   782  		// service.
   783  		{
   784  			name: "SuccessCaseEnvoyExample",
   785  			rbacConfigs: []*v3rbacpb.RBAC{
   786  				{
   787  					Policies: map[string]*v3rbacpb.Policy{
   788  						"service-admin": {
   789  							Permissions: []*v3rbacpb.Permission{
   790  								{Rule: &v3rbacpb.Permission_Any{Any: true}},
   791  							},
   792  							Principals: []*v3rbacpb.Principal{
   793  								{Identifier: &v3rbacpb.Principal_Authenticated_{Authenticated: &v3rbacpb.Principal_Authenticated{PrincipalName: &v3matcherpb.StringMatcher{MatchPattern: &v3matcherpb.StringMatcher_Exact{Exact: "//cluster.local/ns/default/sa/admin"}}}}},
   794  								{Identifier: &v3rbacpb.Principal_Authenticated_{Authenticated: &v3rbacpb.Principal_Authenticated{PrincipalName: &v3matcherpb.StringMatcher{MatchPattern: &v3matcherpb.StringMatcher_Exact{Exact: "//cluster.local/ns/default/sa/superuser"}}}}},
   795  							},
   796  						},
   797  						"product-viewer": {
   798  							Permissions: []*v3rbacpb.Permission{
   799  								{
   800  									Rule: &v3rbacpb.Permission_AndRules{AndRules: &v3rbacpb.Permission_Set{
   801  										Rules: []*v3rbacpb.Permission{
   802  											{Rule: &v3rbacpb.Permission_Header{Header: &v3routepb.HeaderMatcher{Name: ":method", HeaderMatchSpecifier: &v3routepb.HeaderMatcher_ExactMatch{ExactMatch: "GET"}}}},
   803  											{Rule: &v3rbacpb.Permission_UrlPath{UrlPath: &v3matcherpb.PathMatcher{Rule: &v3matcherpb.PathMatcher_Path{Path: &v3matcherpb.StringMatcher{MatchPattern: &v3matcherpb.StringMatcher_Prefix{Prefix: "/products"}}}}}},
   804  										},
   805  									},
   806  									},
   807  								},
   808  							},
   809  							Principals: []*v3rbacpb.Principal{
   810  								{Identifier: &v3rbacpb.Principal_Any{Any: true}},
   811  							},
   812  						},
   813  					},
   814  				},
   815  			},
   816  			rbacQueries: []rbacQuery{
   817  				// This incoming RPC Call should match with the service admin
   818  				// policy.
   819  				{
   820  					rpcData: &rpcData{
   821  						fullMethod: "some method",
   822  						peerInfo: &peer.Peer{
   823  							Addr: &addr{ipAddress: "0.0.0.0"},
   824  							AuthInfo: credentials.TLSInfo{
   825  								State: tls.ConnectionState{
   826  									PeerCertificates: []*x509.Certificate{
   827  										{
   828  											URIs: []*url.URL{
   829  												{
   830  													Host: "cluster.local",
   831  													Path: "/ns/default/sa/admin",
   832  												},
   833  											},
   834  										},
   835  									},
   836  								},
   837  							},
   838  						},
   839  					},
   840  					wantStatusCode: codes.OK,
   841  				},
   842  				// These incoming RPC calls should not match any policy.
   843  				{
   844  					rpcData: &rpcData{
   845  						peerInfo: &peer.Peer{
   846  							Addr: &addr{ipAddress: "0.0.0.0"},
   847  						},
   848  					},
   849  					wantStatusCode: codes.PermissionDenied,
   850  				},
   851  				{
   852  					rpcData: &rpcData{
   853  						fullMethod: "get-product-list",
   854  						peerInfo: &peer.Peer{
   855  							Addr: &addr{ipAddress: "0.0.0.0"},
   856  						},
   857  					},
   858  					wantStatusCode: codes.PermissionDenied,
   859  				},
   860  				{
   861  					rpcData: &rpcData{
   862  						peerInfo: &peer.Peer{
   863  							Addr: &addr{ipAddress: "0.0.0.0"},
   864  							AuthInfo: credentials.TLSInfo{
   865  								State: tls.ConnectionState{
   866  									PeerCertificates: []*x509.Certificate{
   867  										{
   868  											Subject: pkix.Name{
   869  												CommonName: "localhost",
   870  											},
   871  										},
   872  									},
   873  								},
   874  							},
   875  						},
   876  					},
   877  					wantStatusCode: codes.PermissionDenied,
   878  				},
   879  			},
   880  		},
   881  		{
   882  			name: "NotMatcher",
   883  			rbacConfigs: []*v3rbacpb.RBAC{
   884  				{
   885  					Policies: map[string]*v3rbacpb.Policy{
   886  						"not-secret-content": {
   887  							Permissions: []*v3rbacpb.Permission{
   888  								{
   889  									Rule: &v3rbacpb.Permission_NotRule{
   890  										NotRule: &v3rbacpb.Permission{Rule: &v3rbacpb.Permission_UrlPath{UrlPath: &v3matcherpb.PathMatcher{Rule: &v3matcherpb.PathMatcher_Path{Path: &v3matcherpb.StringMatcher{MatchPattern: &v3matcherpb.StringMatcher_Prefix{Prefix: "/secret-content"}}}}}},
   891  									},
   892  								},
   893  							},
   894  							Principals: []*v3rbacpb.Principal{
   895  								{Identifier: &v3rbacpb.Principal_Any{Any: true}},
   896  							},
   897  						},
   898  					},
   899  				},
   900  			},
   901  			rbacQueries: []rbacQuery{
   902  				// This incoming RPC Call should match with the not-secret-content policy.
   903  				{
   904  					rpcData: &rpcData{
   905  						fullMethod: "/regular-content",
   906  						peerInfo: &peer.Peer{
   907  							Addr: &addr{ipAddress: "0.0.0.0"},
   908  						},
   909  					},
   910  					wantStatusCode: codes.OK,
   911  				},
   912  				// This incoming RPC Call shouldn't match with the not-secret-content-policy.
   913  				{
   914  					rpcData: &rpcData{
   915  						fullMethod: "/secret-content",
   916  						peerInfo: &peer.Peer{
   917  							Addr: &addr{ipAddress: "0.0.0.0"},
   918  						},
   919  					},
   920  					wantStatusCode: codes.PermissionDenied,
   921  				},
   922  			},
   923  		},
   924  		{
   925  			name: "DirectRemoteIpMatcher",
   926  			rbacConfigs: []*v3rbacpb.RBAC{
   927  				{
   928  					Policies: map[string]*v3rbacpb.Policy{
   929  						"certain-direct-remote-ip": {
   930  							Permissions: []*v3rbacpb.Permission{
   931  								{Rule: &v3rbacpb.Permission_Any{Any: true}},
   932  							},
   933  							Principals: []*v3rbacpb.Principal{
   934  								{Identifier: &v3rbacpb.Principal_DirectRemoteIp{DirectRemoteIp: &v3corepb.CidrRange{AddressPrefix: "0.0.0.0", PrefixLen: &wrapperspb.UInt32Value{Value: uint32(10)}}}},
   935  							},
   936  						},
   937  					},
   938  				},
   939  			},
   940  			rbacQueries: []rbacQuery{
   941  				// This incoming RPC Call should match with the certain-direct-remote-ip policy.
   942  				{
   943  					rpcData: &rpcData{
   944  						peerInfo: &peer.Peer{
   945  							Addr: &addr{ipAddress: "0.0.0.0"},
   946  						},
   947  					},
   948  					wantStatusCode: codes.OK,
   949  				},
   950  				// This incoming RPC Call shouldn't match with the certain-direct-remote-ip policy.
   951  				{
   952  					rpcData: &rpcData{
   953  						peerInfo: &peer.Peer{
   954  							Addr: &addr{ipAddress: "10.0.0.0"},
   955  						},
   956  					},
   957  					wantStatusCode: codes.PermissionDenied,
   958  				},
   959  			},
   960  		},
   961  		// This test tests a RBAC policy configured with a remote-ip policy.
   962  		// This should be logically equivalent to configuring a Engine with a
   963  		// direct-remote-ip policy, as per A41 - "allow equating RBAC's
   964  		// direct_remote_ip and remote_ip."
   965  		{
   966  			name: "RemoteIpMatcher",
   967  			rbacConfigs: []*v3rbacpb.RBAC{
   968  				{
   969  					Policies: map[string]*v3rbacpb.Policy{
   970  						"certain-remote-ip": {
   971  							Permissions: []*v3rbacpb.Permission{
   972  								{Rule: &v3rbacpb.Permission_Any{Any: true}},
   973  							},
   974  							Principals: []*v3rbacpb.Principal{
   975  								{Identifier: &v3rbacpb.Principal_RemoteIp{RemoteIp: &v3corepb.CidrRange{AddressPrefix: "0.0.0.0", PrefixLen: &wrapperspb.UInt32Value{Value: uint32(10)}}}},
   976  							},
   977  						},
   978  					},
   979  				},
   980  			},
   981  			rbacQueries: []rbacQuery{
   982  				// This incoming RPC Call should match with the certain-remote-ip policy.
   983  				{
   984  					rpcData: &rpcData{
   985  						peerInfo: &peer.Peer{
   986  							Addr: &addr{ipAddress: "0.0.0.0"},
   987  						},
   988  					},
   989  					wantStatusCode: codes.OK,
   990  				},
   991  				// This incoming RPC Call shouldn't match with the certain-remote-ip policy.
   992  				{
   993  					rpcData: &rpcData{
   994  						peerInfo: &peer.Peer{
   995  							Addr: &addr{ipAddress: "10.0.0.0"},
   996  						},
   997  					},
   998  					wantStatusCode: codes.PermissionDenied,
   999  				},
  1000  			},
  1001  		},
  1002  		{
  1003  			name: "DestinationIpMatcher",
  1004  			rbacConfigs: []*v3rbacpb.RBAC{
  1005  				{
  1006  					Policies: map[string]*v3rbacpb.Policy{
  1007  						"certain-destination-ip": {
  1008  							Permissions: []*v3rbacpb.Permission{
  1009  								{Rule: &v3rbacpb.Permission_DestinationIp{DestinationIp: &v3corepb.CidrRange{AddressPrefix: "0.0.0.0", PrefixLen: &wrapperspb.UInt32Value{Value: uint32(10)}}}},
  1010  							},
  1011  							Principals: []*v3rbacpb.Principal{
  1012  								{Identifier: &v3rbacpb.Principal_Any{Any: true}},
  1013  							},
  1014  						},
  1015  					},
  1016  				},
  1017  			},
  1018  			rbacQueries: []rbacQuery{
  1019  				// This incoming RPC Call shouldn't match with the
  1020  				// certain-destination-ip policy, as the test listens on local
  1021  				// host.
  1022  				{
  1023  					rpcData: &rpcData{
  1024  						peerInfo: &peer.Peer{
  1025  							Addr: &addr{ipAddress: "10.0.0.0"},
  1026  						},
  1027  					},
  1028  					wantStatusCode: codes.PermissionDenied,
  1029  				},
  1030  			},
  1031  		},
  1032  		// AllowAndDenyPolicy tests a policy with an allow (on path) and
  1033  		// deny (on port) policy chained together. This represents how a user
  1034  		// configured interceptor would use this, and also is a potential
  1035  		// configuration for a dynamic xds interceptor.
  1036  		{
  1037  			name: "AllowAndDenyPolicy",
  1038  			rbacConfigs: []*v3rbacpb.RBAC{
  1039  				{
  1040  					Policies: map[string]*v3rbacpb.Policy{
  1041  						"certain-source-ip": {
  1042  							Permissions: []*v3rbacpb.Permission{
  1043  								{Rule: &v3rbacpb.Permission_Any{Any: true}},
  1044  							},
  1045  							Principals: []*v3rbacpb.Principal{
  1046  								{Identifier: &v3rbacpb.Principal_DirectRemoteIp{DirectRemoteIp: &v3corepb.CidrRange{AddressPrefix: "0.0.0.0", PrefixLen: &wrapperspb.UInt32Value{Value: uint32(10)}}}},
  1047  							},
  1048  						},
  1049  					},
  1050  					Action: v3rbacpb.RBAC_ALLOW,
  1051  				},
  1052  				{
  1053  					Policies: map[string]*v3rbacpb.Policy{
  1054  						"localhost-fan": {
  1055  							Permissions: []*v3rbacpb.Permission{
  1056  								{Rule: &v3rbacpb.Permission_UrlPath{UrlPath: &v3matcherpb.PathMatcher{Rule: &v3matcherpb.PathMatcher_Path{Path: &v3matcherpb.StringMatcher{MatchPattern: &v3matcherpb.StringMatcher_Exact{Exact: "localhost-fan-page"}}}}}},
  1057  							},
  1058  							Principals: []*v3rbacpb.Principal{
  1059  								{Identifier: &v3rbacpb.Principal_Any{Any: true}},
  1060  							},
  1061  						},
  1062  					},
  1063  					Action: v3rbacpb.RBAC_DENY,
  1064  				},
  1065  			},
  1066  			rbacQueries: []rbacQuery{
  1067  				// This RPC should match with the allow policy, and shouldn't
  1068  				// match with the deny and thus should be allowed to proceed.
  1069  				{
  1070  					rpcData: &rpcData{
  1071  						peerInfo: &peer.Peer{
  1072  							Addr: &addr{ipAddress: "0.0.0.0"},
  1073  						},
  1074  					},
  1075  					wantStatusCode: codes.OK,
  1076  				},
  1077  				// This RPC should match with both the allow policy and deny policy
  1078  				// and thus shouldn't be allowed to proceed as matched with deny.
  1079  				{
  1080  					rpcData: &rpcData{
  1081  						fullMethod: "localhost-fan-page",
  1082  						peerInfo: &peer.Peer{
  1083  							Addr: &addr{ipAddress: "0.0.0.0"},
  1084  						},
  1085  					},
  1086  					wantStatusCode: codes.PermissionDenied,
  1087  				},
  1088  				// This RPC shouldn't match with either policy, and thus
  1089  				// shouldn't be allowed to proceed as didn't match with allow.
  1090  				{
  1091  					rpcData: &rpcData{
  1092  						peerInfo: &peer.Peer{
  1093  							Addr: &addr{ipAddress: "10.0.0.0"},
  1094  						},
  1095  					},
  1096  					wantStatusCode: codes.PermissionDenied,
  1097  				},
  1098  				// This RPC shouldn't match with allow, match with deny, and
  1099  				// thus shouldn't be allowed to proceed.
  1100  				{
  1101  					rpcData: &rpcData{
  1102  						fullMethod: "localhost-fan-page",
  1103  						peerInfo: &peer.Peer{
  1104  							Addr: &addr{ipAddress: "10.0.0.0"},
  1105  						},
  1106  					},
  1107  					wantStatusCode: codes.PermissionDenied,
  1108  				},
  1109  			},
  1110  		},
  1111  		// This test tests that when there are no SANs or Subject's
  1112  		// distinguished name in incoming RPC's, that authenticated matchers
  1113  		// match against the empty string.
  1114  		{
  1115  			name: "default-matching-no-credentials",
  1116  			rbacConfigs: []*v3rbacpb.RBAC{
  1117  				{
  1118  					Policies: map[string]*v3rbacpb.Policy{
  1119  						"service-admin": {
  1120  							Permissions: []*v3rbacpb.Permission{
  1121  								{Rule: &v3rbacpb.Permission_Any{Any: true}},
  1122  							},
  1123  							Principals: []*v3rbacpb.Principal{
  1124  								{Identifier: &v3rbacpb.Principal_Authenticated_{Authenticated: &v3rbacpb.Principal_Authenticated{PrincipalName: &v3matcherpb.StringMatcher{MatchPattern: &v3matcherpb.StringMatcher_Exact{Exact: ""}}}}},
  1125  							},
  1126  						},
  1127  					},
  1128  				},
  1129  			},
  1130  			rbacQueries: []rbacQuery{
  1131  				// This incoming RPC Call should match with the service admin
  1132  				// policy. No authentication info is provided, so the
  1133  				// authenticated matcher should match to the string matcher on
  1134  				// the empty string, matching to the service-admin policy.
  1135  				{
  1136  					rpcData: &rpcData{
  1137  						fullMethod: "some method",
  1138  						peerInfo: &peer.Peer{
  1139  							Addr: &addr{ipAddress: "0.0.0.0"},
  1140  							AuthInfo: credentials.TLSInfo{
  1141  								State: tls.ConnectionState{
  1142  									PeerCertificates: []*x509.Certificate{
  1143  										{
  1144  											URIs: []*url.URL{
  1145  												{
  1146  													Host: "cluster.local",
  1147  													Path: "/ns/default/sa/admin",
  1148  												},
  1149  											},
  1150  										},
  1151  									},
  1152  								},
  1153  							},
  1154  						},
  1155  					},
  1156  					wantStatusCode: codes.OK,
  1157  				},
  1158  			},
  1159  		},
  1160  		// This test tests that an RBAC policy configured with a metadata
  1161  		// matcher as a permission doesn't match with any incoming RPC.
  1162  		{
  1163  			name: "metadata-never-matches",
  1164  			rbacConfigs: []*v3rbacpb.RBAC{
  1165  				{
  1166  					Policies: map[string]*v3rbacpb.Policy{
  1167  						"metadata-never-matches": {
  1168  							Permissions: []*v3rbacpb.Permission{
  1169  								{Rule: &v3rbacpb.Permission_Metadata{
  1170  									Metadata: &v3matcherpb.MetadataMatcher{},
  1171  								}},
  1172  							},
  1173  							Principals: []*v3rbacpb.Principal{
  1174  								{Identifier: &v3rbacpb.Principal_Any{Any: true}},
  1175  							},
  1176  						},
  1177  					},
  1178  				},
  1179  			},
  1180  			rbacQueries: []rbacQuery{
  1181  				{
  1182  					rpcData: &rpcData{
  1183  						fullMethod: "some method",
  1184  						peerInfo: &peer.Peer{
  1185  							Addr: &addr{ipAddress: "0.0.0.0"},
  1186  						},
  1187  					},
  1188  					wantStatusCode: codes.PermissionDenied,
  1189  				},
  1190  			},
  1191  		},
  1192  		// This test tests that an RBAC policy configured with a metadata
  1193  		// matcher with invert set to true as a permission always matches with
  1194  		// any incoming RPC.
  1195  		{
  1196  			name: "metadata-invert-always-matches",
  1197  			rbacConfigs: []*v3rbacpb.RBAC{
  1198  				{
  1199  					Policies: map[string]*v3rbacpb.Policy{
  1200  						"metadata-invert-always-matches": {
  1201  							Permissions: []*v3rbacpb.Permission{
  1202  								{Rule: &v3rbacpb.Permission_Metadata{
  1203  									Metadata: &v3matcherpb.MetadataMatcher{Invert: true},
  1204  								}},
  1205  							},
  1206  							Principals: []*v3rbacpb.Principal{
  1207  								{Identifier: &v3rbacpb.Principal_Any{Any: true}},
  1208  							},
  1209  						},
  1210  					},
  1211  				},
  1212  			},
  1213  			rbacQueries: []rbacQuery{
  1214  				{
  1215  					rpcData: &rpcData{
  1216  						fullMethod: "some method",
  1217  						peerInfo: &peer.Peer{
  1218  							Addr: &addr{ipAddress: "0.0.0.0"},
  1219  						},
  1220  					},
  1221  					wantStatusCode: codes.OK,
  1222  				},
  1223  			},
  1224  		},
  1225  		// AllowAndDenyPolicy tests a policy with an allow (on path) and
  1226  		// deny (on port) policy chained together. This represents how a user
  1227  		// configured interceptor would use this, and also is a potential
  1228  		// configuration for a dynamic xds interceptor.  Further, it tests that
  1229  		// the audit logger works properly in each scenario.
  1230  		{
  1231  			name:       "AuditLoggingAllowAndDenyPolicy_ON_ALLOW",
  1232  			policyName: "test_policy",
  1233  			rbacConfigs: []*v3rbacpb.RBAC{
  1234  				{
  1235  					Policies: map[string]*v3rbacpb.Policy{
  1236  						"localhost-fan": {
  1237  							Permissions: []*v3rbacpb.Permission{
  1238  								{Rule: &v3rbacpb.Permission_UrlPath{UrlPath: &v3matcherpb.PathMatcher{Rule: &v3matcherpb.PathMatcher_Path{Path: &v3matcherpb.StringMatcher{MatchPattern: &v3matcherpb.StringMatcher_Exact{Exact: "localhost-fan-page"}}}}}},
  1239  							},
  1240  							Principals: []*v3rbacpb.Principal{
  1241  								{Identifier: &v3rbacpb.Principal_Any{Any: true}},
  1242  							},
  1243  						},
  1244  					},
  1245  					Action: v3rbacpb.RBAC_DENY,
  1246  					AuditLoggingOptions: &v3rbacpb.RBAC_AuditLoggingOptions{
  1247  						AuditCondition: v3rbacpb.RBAC_AuditLoggingOptions_NONE,
  1248  						LoggerConfigs: []*v3rbacpb.RBAC_AuditLoggingOptions_AuditLoggerConfig{
  1249  							{AuditLogger: &v3corepb.TypedExtensionConfig{
  1250  								Name:        "TestAuditLoggerBuffer",
  1251  								TypedConfig: createUDPATypedStruct(t, map[string]any{}, "AuditLoggingAllowAndDenyPolicy_ON_ALLOW_TestAuditLoggerBuffer")},
  1252  								IsOptional: false,
  1253  							},
  1254  						},
  1255  					},
  1256  				},
  1257  				{
  1258  					Policies: map[string]*v3rbacpb.Policy{
  1259  						"certain-source-ip": {
  1260  							Permissions: []*v3rbacpb.Permission{
  1261  								{Rule: &v3rbacpb.Permission_Any{Any: true}},
  1262  							},
  1263  							Principals: []*v3rbacpb.Principal{
  1264  								{Identifier: &v3rbacpb.Principal_DirectRemoteIp{DirectRemoteIp: &v3corepb.CidrRange{AddressPrefix: "0.0.0.0", PrefixLen: &wrapperspb.UInt32Value{Value: uint32(10)}}}},
  1265  							},
  1266  						},
  1267  					},
  1268  					Action: v3rbacpb.RBAC_ALLOW,
  1269  					AuditLoggingOptions: &v3rbacpb.RBAC_AuditLoggingOptions{
  1270  						AuditCondition: v3rbacpb.RBAC_AuditLoggingOptions_ON_ALLOW,
  1271  						LoggerConfigs: []*v3rbacpb.RBAC_AuditLoggingOptions_AuditLoggerConfig{
  1272  							{AuditLogger: &v3corepb.TypedExtensionConfig{
  1273  								Name:        "TestAuditLoggerBuffer",
  1274  								TypedConfig: createUDPATypedStruct(t, map[string]any{}, "AuditLoggingAllowAndDenyPolicy_ON_ALLOW_TestAuditLoggerBuffer")},
  1275  								IsOptional: false,
  1276  							},
  1277  						},
  1278  					},
  1279  				},
  1280  			},
  1281  			rbacQueries: []rbacQuery{
  1282  				// This RPC should match with the allow policy, and shouldn't
  1283  				// match with the deny and thus should be allowed to proceed.
  1284  				{
  1285  					rpcData: &rpcData{
  1286  						fullMethod: "",
  1287  						peerInfo: &peer.Peer{
  1288  							Addr: &addr{ipAddress: "0.0.0.0"},
  1289  							AuthInfo: credentials.TLSInfo{
  1290  								State: tls.ConnectionState{
  1291  									PeerCertificates: []*x509.Certificate{
  1292  										{
  1293  											URIs: []*url.URL{
  1294  												{
  1295  													Scheme: "spiffe",
  1296  													Host:   "cluster.local",
  1297  													Path:   "/ns/default/sa/admin",
  1298  												},
  1299  											},
  1300  										},
  1301  									},
  1302  								},
  1303  								SPIFFEID: &url.URL{
  1304  									Scheme: "spiffe",
  1305  									Host:   "cluster.local",
  1306  									Path:   "/ns/default/sa/admin",
  1307  								},
  1308  							},
  1309  						},
  1310  					},
  1311  					wantStatusCode: codes.OK,
  1312  					wantAuditEvents: []*audit.Event{
  1313  						{
  1314  							FullMethodName: "",
  1315  							Principal:      "spiffe://cluster.local/ns/default/sa/admin",
  1316  							PolicyName:     "test_policy",
  1317  							MatchedRule:    "certain-source-ip",
  1318  							Authorized:     true,
  1319  						},
  1320  					},
  1321  				},
  1322  				// This RPC should match with both the allow policy and deny policy
  1323  				// and thus shouldn't be allowed to proceed as matched with deny.
  1324  				{
  1325  					rpcData: &rpcData{
  1326  						fullMethod: "localhost-fan-page",
  1327  						peerInfo: &peer.Peer{
  1328  							Addr: &addr{ipAddress: "0.0.0.0"},
  1329  						},
  1330  					},
  1331  					wantStatusCode: codes.PermissionDenied,
  1332  				},
  1333  				// This RPC shouldn't match with either policy, and thus
  1334  				// shouldn't be allowed to proceed as didn't match with allow.
  1335  				{
  1336  					rpcData: &rpcData{
  1337  						peerInfo: &peer.Peer{
  1338  							Addr: &addr{ipAddress: "10.0.0.0"},
  1339  						},
  1340  					},
  1341  					wantStatusCode: codes.PermissionDenied,
  1342  				},
  1343  				// This RPC shouldn't match with allow, match with deny, and
  1344  				// thus shouldn't be allowed to proceed.
  1345  				{
  1346  					rpcData: &rpcData{
  1347  						fullMethod: "localhost-fan-page",
  1348  						peerInfo: &peer.Peer{
  1349  							Addr: &addr{ipAddress: "10.0.0.0"},
  1350  						},
  1351  					},
  1352  					wantStatusCode: codes.PermissionDenied,
  1353  				},
  1354  			},
  1355  		},
  1356  		{
  1357  			name:       "AuditLoggingAllowAndDenyPolicy_ON_DENY",
  1358  			policyName: "test_policy",
  1359  			rbacConfigs: []*v3rbacpb.RBAC{
  1360  				{
  1361  					Policies: map[string]*v3rbacpb.Policy{
  1362  						"localhost-fan": {
  1363  							Permissions: []*v3rbacpb.Permission{
  1364  								{Rule: &v3rbacpb.Permission_UrlPath{UrlPath: &v3matcherpb.PathMatcher{Rule: &v3matcherpb.PathMatcher_Path{Path: &v3matcherpb.StringMatcher{MatchPattern: &v3matcherpb.StringMatcher_Exact{Exact: "localhost-fan-page"}}}}}},
  1365  							},
  1366  							Principals: []*v3rbacpb.Principal{
  1367  								{Identifier: &v3rbacpb.Principal_Any{Any: true}},
  1368  							},
  1369  						},
  1370  					},
  1371  					Action: v3rbacpb.RBAC_DENY,
  1372  					AuditLoggingOptions: &v3rbacpb.RBAC_AuditLoggingOptions{
  1373  						AuditCondition: v3rbacpb.RBAC_AuditLoggingOptions_ON_DENY,
  1374  						LoggerConfigs: []*v3rbacpb.RBAC_AuditLoggingOptions_AuditLoggerConfig{
  1375  							{AuditLogger: &v3corepb.TypedExtensionConfig{
  1376  								Name:        "TestAuditLoggerBuffer",
  1377  								TypedConfig: createUDPATypedStruct(t, map[string]any{}, "AuditLoggingAllowAndDenyPolicy_ON_DENY_TestAuditLoggerBuffer")},
  1378  								IsOptional: false,
  1379  							},
  1380  						},
  1381  					},
  1382  				},
  1383  				{
  1384  					Policies: map[string]*v3rbacpb.Policy{
  1385  						"certain-source-ip": {
  1386  							Permissions: []*v3rbacpb.Permission{
  1387  								{Rule: &v3rbacpb.Permission_Any{Any: true}},
  1388  							},
  1389  							Principals: []*v3rbacpb.Principal{
  1390  								{Identifier: &v3rbacpb.Principal_DirectRemoteIp{DirectRemoteIp: &v3corepb.CidrRange{AddressPrefix: "0.0.0.0", PrefixLen: &wrapperspb.UInt32Value{Value: uint32(10)}}}},
  1391  							},
  1392  						},
  1393  					},
  1394  					Action: v3rbacpb.RBAC_ALLOW,
  1395  					AuditLoggingOptions: &v3rbacpb.RBAC_AuditLoggingOptions{
  1396  						AuditCondition: v3rbacpb.RBAC_AuditLoggingOptions_ON_DENY,
  1397  						LoggerConfigs: []*v3rbacpb.RBAC_AuditLoggingOptions_AuditLoggerConfig{
  1398  							{AuditLogger: &v3corepb.TypedExtensionConfig{
  1399  								Name:        "TestAuditLoggerBuffer",
  1400  								TypedConfig: createUDPATypedStruct(t, map[string]any{}, "AuditLoggingAllowAndDenyPolicy_ON_DENY_TestAuditLoggerBuffer")},
  1401  								IsOptional: false,
  1402  							},
  1403  						},
  1404  					},
  1405  				},
  1406  			},
  1407  			rbacQueries: []rbacQuery{
  1408  				// This RPC should match with the allow policy, and shouldn't
  1409  				// match with the deny and thus should be allowed to proceed.
  1410  				// Audit logging matches with nothing.
  1411  				{
  1412  					rpcData: &rpcData{
  1413  						fullMethod: "",
  1414  						peerInfo: &peer.Peer{
  1415  							Addr: &addr{ipAddress: "0.0.0.0"},
  1416  						},
  1417  					},
  1418  					wantStatusCode: codes.OK,
  1419  				},
  1420  				// This RPC should match with both the allow policy and deny policy
  1421  				// and thus shouldn't be allowed to proceed as matched with deny.
  1422  				// Audit logging matches with deny and short circuits.
  1423  				{
  1424  					rpcData: &rpcData{
  1425  						fullMethod: "localhost-fan-page",
  1426  						peerInfo: &peer.Peer{
  1427  							Addr: &addr{ipAddress: "0.0.0.0"},
  1428  							AuthInfo: credentials.TLSInfo{
  1429  								State: tls.ConnectionState{
  1430  									PeerCertificates: []*x509.Certificate{
  1431  										{
  1432  											URIs: []*url.URL{
  1433  												{
  1434  													Host: "cluster.local",
  1435  													Path: "/ns/default/sa/admin",
  1436  												},
  1437  											},
  1438  										},
  1439  									},
  1440  								},
  1441  							},
  1442  						},
  1443  					},
  1444  					wantStatusCode: codes.PermissionDenied,
  1445  					wantAuditEvents: []*audit.Event{
  1446  						{
  1447  							FullMethodName: "localhost-fan-page",
  1448  							PolicyName:     "test_policy",
  1449  							MatchedRule:    "localhost-fan",
  1450  							Authorized:     false,
  1451  						},
  1452  					},
  1453  				},
  1454  				// This RPC shouldn't match with either policy, and thus
  1455  				// shouldn't be allowed to proceed as didn't match with allow.
  1456  				// Audit logging matches with the allow policy.
  1457  				{
  1458  					rpcData: &rpcData{
  1459  						peerInfo: &peer.Peer{
  1460  							Addr: &addr{ipAddress: "10.0.0.0"},
  1461  						},
  1462  					},
  1463  					wantStatusCode: codes.PermissionDenied,
  1464  					wantAuditEvents: []*audit.Event{
  1465  						{
  1466  							FullMethodName: "",
  1467  							PolicyName:     "test_policy",
  1468  							MatchedRule:    "",
  1469  							Authorized:     false,
  1470  						},
  1471  					},
  1472  				},
  1473  				// This RPC shouldn't match with allow, match with deny, and
  1474  				// thus shouldn't be allowed to proceed.
  1475  				// Audit logging will have the deny logged.
  1476  				{
  1477  					rpcData: &rpcData{
  1478  						fullMethod: "localhost-fan-page",
  1479  						peerInfo: &peer.Peer{
  1480  							Addr: &addr{ipAddress: "10.0.0.0"},
  1481  						},
  1482  					},
  1483  					wantStatusCode: codes.PermissionDenied,
  1484  					wantAuditEvents: []*audit.Event{
  1485  						{
  1486  							FullMethodName: "localhost-fan-page",
  1487  							PolicyName:     "test_policy",
  1488  							MatchedRule:    "localhost-fan",
  1489  							Authorized:     false,
  1490  						},
  1491  					},
  1492  				},
  1493  			},
  1494  		},
  1495  		{
  1496  			name:       "AuditLoggingAllowAndDenyPolicy_NONE",
  1497  			policyName: "test_policy",
  1498  			rbacConfigs: []*v3rbacpb.RBAC{
  1499  				{
  1500  					Policies: map[string]*v3rbacpb.Policy{
  1501  						"localhost-fan": {
  1502  							Permissions: []*v3rbacpb.Permission{
  1503  								{Rule: &v3rbacpb.Permission_UrlPath{UrlPath: &v3matcherpb.PathMatcher{Rule: &v3matcherpb.PathMatcher_Path{Path: &v3matcherpb.StringMatcher{MatchPattern: &v3matcherpb.StringMatcher_Exact{Exact: "localhost-fan-page"}}}}}},
  1504  							},
  1505  							Principals: []*v3rbacpb.Principal{
  1506  								{Identifier: &v3rbacpb.Principal_Any{Any: true}},
  1507  							},
  1508  						},
  1509  					},
  1510  					Action: v3rbacpb.RBAC_DENY,
  1511  					AuditLoggingOptions: &v3rbacpb.RBAC_AuditLoggingOptions{
  1512  						AuditCondition: v3rbacpb.RBAC_AuditLoggingOptions_NONE,
  1513  						LoggerConfigs: []*v3rbacpb.RBAC_AuditLoggingOptions_AuditLoggerConfig{
  1514  							{AuditLogger: &v3corepb.TypedExtensionConfig{
  1515  								Name:        "TestAuditLoggerBuffer",
  1516  								TypedConfig: createUDPATypedStruct(t, map[string]any{}, "AuditLoggingAllowAndDenyPolicy_NONE_TestAuditLoggerBuffer")},
  1517  								IsOptional: false,
  1518  							},
  1519  						},
  1520  					},
  1521  				},
  1522  				{
  1523  					Policies: map[string]*v3rbacpb.Policy{
  1524  						"certain-source-ip": {
  1525  							Permissions: []*v3rbacpb.Permission{
  1526  								{Rule: &v3rbacpb.Permission_Any{Any: true}},
  1527  							},
  1528  							Principals: []*v3rbacpb.Principal{
  1529  								{Identifier: &v3rbacpb.Principal_DirectRemoteIp{DirectRemoteIp: &v3corepb.CidrRange{AddressPrefix: "0.0.0.0", PrefixLen: &wrapperspb.UInt32Value{Value: uint32(10)}}}},
  1530  							},
  1531  						},
  1532  					},
  1533  					Action: v3rbacpb.RBAC_ALLOW,
  1534  					AuditLoggingOptions: &v3rbacpb.RBAC_AuditLoggingOptions{
  1535  						AuditCondition: v3rbacpb.RBAC_AuditLoggingOptions_NONE,
  1536  						LoggerConfigs: []*v3rbacpb.RBAC_AuditLoggingOptions_AuditLoggerConfig{
  1537  							{AuditLogger: &v3corepb.TypedExtensionConfig{
  1538  								Name:        "TestAuditLoggerBuffer",
  1539  								TypedConfig: createUDPATypedStruct(t, map[string]any{}, "AuditLoggingAllowAndDenyPolicy_NONE_TestAuditLoggerBuffer")},
  1540  								IsOptional: false,
  1541  							},
  1542  						},
  1543  					},
  1544  				},
  1545  			},
  1546  			rbacQueries: []rbacQuery{
  1547  				// This RPC should match with the allow policy, and shouldn't
  1548  				// match with the deny and thus should be allowed to proceed.
  1549  				// Audit logging is NONE.
  1550  				{
  1551  					rpcData: &rpcData{
  1552  						fullMethod: "",
  1553  						peerInfo: &peer.Peer{
  1554  							Addr: &addr{ipAddress: "0.0.0.0"},
  1555  						},
  1556  					},
  1557  					wantStatusCode: codes.OK,
  1558  				},
  1559  				// This RPC should match with both the allow policy and deny policy
  1560  				// and thus shouldn't be allowed to proceed as matched with deny.
  1561  				// Audit logging is NONE.
  1562  				{
  1563  					rpcData: &rpcData{
  1564  						fullMethod: "localhost-fan-page",
  1565  						peerInfo: &peer.Peer{
  1566  							Addr: &addr{ipAddress: "0.0.0.0"},
  1567  						},
  1568  					},
  1569  					wantStatusCode: codes.PermissionDenied,
  1570  				},
  1571  				// This RPC shouldn't match with either policy, and thus
  1572  				// shouldn't be allowed to proceed as didn't match with allow.
  1573  				// Audit logging is NONE.
  1574  				{
  1575  					rpcData: &rpcData{
  1576  						peerInfo: &peer.Peer{
  1577  							Addr: &addr{ipAddress: "10.0.0.0"},
  1578  						},
  1579  					},
  1580  					wantStatusCode: codes.PermissionDenied,
  1581  				},
  1582  				// This RPC shouldn't match with allow, match with deny, and
  1583  				// thus shouldn't be allowed to proceed.
  1584  				// Audit logging is NONE.
  1585  				{
  1586  					rpcData: &rpcData{
  1587  						fullMethod: "localhost-fan-page",
  1588  						peerInfo: &peer.Peer{
  1589  							Addr: &addr{ipAddress: "10.0.0.0"},
  1590  						},
  1591  					},
  1592  					wantStatusCode: codes.PermissionDenied,
  1593  				},
  1594  			},
  1595  		},
  1596  		{
  1597  			name:       "AuditLoggingAllowAndDenyPolicy_ON_DENY_AND_ALLOW",
  1598  			policyName: "test_policy",
  1599  			rbacConfigs: []*v3rbacpb.RBAC{
  1600  				{
  1601  					Policies: map[string]*v3rbacpb.Policy{
  1602  						"localhost-fan": {
  1603  							Permissions: []*v3rbacpb.Permission{
  1604  								{Rule: &v3rbacpb.Permission_UrlPath{UrlPath: &v3matcherpb.PathMatcher{Rule: &v3matcherpb.PathMatcher_Path{Path: &v3matcherpb.StringMatcher{MatchPattern: &v3matcherpb.StringMatcher_Exact{Exact: "localhost-fan-page"}}}}}},
  1605  							},
  1606  							Principals: []*v3rbacpb.Principal{
  1607  								{Identifier: &v3rbacpb.Principal_Any{Any: true}},
  1608  							},
  1609  						},
  1610  					},
  1611  					Action: v3rbacpb.RBAC_DENY,
  1612  					AuditLoggingOptions: &v3rbacpb.RBAC_AuditLoggingOptions{
  1613  						AuditCondition: v3rbacpb.RBAC_AuditLoggingOptions_ON_DENY,
  1614  						LoggerConfigs: []*v3rbacpb.RBAC_AuditLoggingOptions_AuditLoggerConfig{
  1615  							{AuditLogger: &v3corepb.TypedExtensionConfig{
  1616  								Name:        "TestAuditLoggerBuffer",
  1617  								TypedConfig: createUDPATypedStruct(t, map[string]any{}, "AuditLoggingAllowAndDenyPolicy_ON_DENY_AND_ALLOW_TestAuditLoggerBuffer")},
  1618  								IsOptional: false,
  1619  							},
  1620  						},
  1621  					},
  1622  				},
  1623  				{
  1624  					Policies: map[string]*v3rbacpb.Policy{
  1625  						"certain-source-ip": {
  1626  							Permissions: []*v3rbacpb.Permission{
  1627  								{Rule: &v3rbacpb.Permission_Any{Any: true}},
  1628  							},
  1629  							Principals: []*v3rbacpb.Principal{
  1630  								{Identifier: &v3rbacpb.Principal_DirectRemoteIp{DirectRemoteIp: &v3corepb.CidrRange{AddressPrefix: "0.0.0.0", PrefixLen: &wrapperspb.UInt32Value{Value: uint32(10)}}}},
  1631  							},
  1632  						},
  1633  					},
  1634  					Action: v3rbacpb.RBAC_ALLOW,
  1635  					AuditLoggingOptions: &v3rbacpb.RBAC_AuditLoggingOptions{
  1636  						AuditCondition: v3rbacpb.RBAC_AuditLoggingOptions_ON_DENY_AND_ALLOW,
  1637  						LoggerConfigs: []*v3rbacpb.RBAC_AuditLoggingOptions_AuditLoggerConfig{
  1638  							{AuditLogger: &v3corepb.TypedExtensionConfig{
  1639  								Name:        "TestAuditLoggerBuffer",
  1640  								TypedConfig: createUDPATypedStruct(t, map[string]any{}, "AuditLoggingAllowAndDenyPolicy_ON_DENY_AND_ALLOW_TestAuditLoggerBuffer")},
  1641  								IsOptional: false,
  1642  							},
  1643  						},
  1644  					},
  1645  				},
  1646  			},
  1647  			rbacQueries: []rbacQuery{
  1648  				// This RPC should match with the allow policy, and shouldn't
  1649  				// match with the deny and thus should be allowed to proceed.
  1650  				// Audit logging matches with nothing.
  1651  				{
  1652  					rpcData: &rpcData{
  1653  						fullMethod: "",
  1654  						peerInfo: &peer.Peer{
  1655  							Addr: &addr{ipAddress: "0.0.0.0"},
  1656  						},
  1657  					},
  1658  					wantStatusCode: codes.OK,
  1659  					wantAuditEvents: []*audit.Event{
  1660  						{
  1661  							FullMethodName: "",
  1662  							PolicyName:     "test_policy",
  1663  							MatchedRule:    "certain-source-ip",
  1664  							Authorized:     true,
  1665  						},
  1666  					},
  1667  				},
  1668  				// This RPC should match with both the allow policy and deny policy
  1669  				// and thus shouldn't be allowed to proceed as matched with deny.
  1670  				// Audit logging matches with deny and short circuits.
  1671  				{
  1672  					rpcData: &rpcData{
  1673  						fullMethod: "localhost-fan-page",
  1674  						peerInfo: &peer.Peer{
  1675  							Addr: &addr{ipAddress: "0.0.0.0"},
  1676  						},
  1677  					},
  1678  					wantStatusCode: codes.PermissionDenied,
  1679  					wantAuditEvents: []*audit.Event{
  1680  						{
  1681  							FullMethodName: "localhost-fan-page",
  1682  							PolicyName:     "test_policy",
  1683  							MatchedRule:    "localhost-fan",
  1684  							Authorized:     false,
  1685  						},
  1686  					},
  1687  				},
  1688  				// This RPC shouldn't match with either policy, and thus
  1689  				// shouldn't be allowed to proceed as didn't match with allow.
  1690  				// Audit logging matches with the allow policy.
  1691  				{
  1692  					rpcData: &rpcData{
  1693  						peerInfo: &peer.Peer{
  1694  							Addr: &addr{ipAddress: "10.0.0.0"},
  1695  						},
  1696  					},
  1697  					wantStatusCode: codes.PermissionDenied,
  1698  					wantAuditEvents: []*audit.Event{
  1699  						{
  1700  							FullMethodName: "",
  1701  							PolicyName:     "test_policy",
  1702  							MatchedRule:    "",
  1703  							Authorized:     false,
  1704  						},
  1705  					},
  1706  				},
  1707  				// This RPC shouldn't match with allow, match with deny, and
  1708  				// thus shouldn't be allowed to proceed.
  1709  				// Audit logging will have the deny logged.
  1710  				{
  1711  					rpcData: &rpcData{
  1712  						fullMethod: "localhost-fan-page",
  1713  						peerInfo: &peer.Peer{
  1714  							Addr: &addr{ipAddress: "10.0.0.0"},
  1715  						},
  1716  					},
  1717  					wantStatusCode: codes.PermissionDenied,
  1718  					wantAuditEvents: []*audit.Event{
  1719  						{
  1720  							FullMethodName: "localhost-fan-page",
  1721  							PolicyName:     "test_policy",
  1722  							MatchedRule:    "localhost-fan",
  1723  							Authorized:     false,
  1724  						},
  1725  					},
  1726  				},
  1727  			},
  1728  		},
  1729  	}
  1730  	for _, test := range tests {
  1731  		t.Run(test.name, func(t *testing.T) {
  1732  			b := TestAuditLoggerBufferBuilder{testName: test.name}
  1733  			audit.RegisterLoggerBuilder(&b)
  1734  			b2 := TestAuditLoggerCustomConfigBuilder{testName: test.name}
  1735  			audit.RegisterLoggerBuilder(&b2)
  1736  
  1737  			// Instantiate the chainedRBACEngine with different configurations that are
  1738  			// interesting to test and to query.
  1739  			cre, err := NewChainEngine(test.rbacConfigs, test.policyName)
  1740  			if err != nil {
  1741  				t.Fatalf("Error constructing RBAC Engine: %v", err)
  1742  			}
  1743  			// Query the created chain of RBAC Engines with different args to see
  1744  			// if the chain of RBAC Engines configured as such works as intended.
  1745  			for _, data := range test.rbacQueries {
  1746  				func() {
  1747  					// Construct the context with three data points that have enough
  1748  					// information to represent incoming RPC's. This will be how a
  1749  					// user uses this API. A user will have to put MD, PeerInfo, and
  1750  					// the connection the RPC is sent on in the context.
  1751  					ctx := metadata.NewIncomingContext(context.Background(), data.rpcData.md)
  1752  
  1753  					// Make a TCP connection with a certain destination port. The
  1754  					// address/port of this connection will be used to populate the
  1755  					// destination ip/port in RPCData struct. This represents what
  1756  					// the user of ChainEngine will have to place into context,
  1757  					// as this is only way to get destination ip and port.
  1758  					lis, err := net.Listen("tcp", "localhost:0")
  1759  					if err != nil {
  1760  						t.Fatalf("Error listening: %v", err)
  1761  					}
  1762  					defer lis.Close()
  1763  					connCh := make(chan net.Conn, 1)
  1764  					go func() {
  1765  						conn, err := lis.Accept()
  1766  						if err != nil {
  1767  							t.Errorf("Error accepting connection: %v", err)
  1768  							return
  1769  						}
  1770  						connCh <- conn
  1771  					}()
  1772  					_, err = net.Dial("tcp", lis.Addr().String())
  1773  					if err != nil {
  1774  						t.Fatalf("Error dialing: %v", err)
  1775  					}
  1776  					conn := <-connCh
  1777  					defer conn.Close()
  1778  					getConnection = func(context.Context) net.Conn {
  1779  						return conn
  1780  					}
  1781  					ctx = peer.NewContext(ctx, data.rpcData.peerInfo)
  1782  					stream := &ServerTransportStreamWithMethod{
  1783  						method: data.rpcData.fullMethod,
  1784  					}
  1785  
  1786  					ctx = grpc.NewContextWithServerTransportStream(ctx, stream)
  1787  					err = cre.IsAuthorized(ctx)
  1788  					if gotCode := status.Code(err); gotCode != data.wantStatusCode {
  1789  						t.Fatalf("IsAuthorized(%+v, %+v) returned (%+v), want(%+v)", ctx, data.rpcData.fullMethod, gotCode, data.wantStatusCode)
  1790  					}
  1791  					if !reflect.DeepEqual(b.auditEvents, data.wantAuditEvents) {
  1792  						t.Fatalf("Unexpected audit event for query:%v", data)
  1793  					}
  1794  
  1795  					// This builder's auditEvents can be shared for several queries, make sure it's empty.
  1796  					b.auditEvents = nil
  1797  				}()
  1798  			}
  1799  		})
  1800  	}
  1801  }
  1802  
  1803  type ServerTransportStreamWithMethod struct {
  1804  	method string
  1805  }
  1806  
  1807  func (sts *ServerTransportStreamWithMethod) Method() string {
  1808  	return sts.method
  1809  }
  1810  
  1811  func (sts *ServerTransportStreamWithMethod) SetHeader(md metadata.MD) error {
  1812  	return nil
  1813  }
  1814  
  1815  func (sts *ServerTransportStreamWithMethod) SendHeader(md metadata.MD) error {
  1816  	return nil
  1817  }
  1818  
  1819  func (sts *ServerTransportStreamWithMethod) SetTrailer(md metadata.MD) error {
  1820  	return nil
  1821  }
  1822  
  1823  // An audit logger that will log to the auditEvents slice.
  1824  type TestAuditLoggerBuffer struct {
  1825  	auditEvents *[]*audit.Event
  1826  }
  1827  
  1828  func (logger *TestAuditLoggerBuffer) Log(e *audit.Event) {
  1829  	*(logger.auditEvents) = append(*(logger.auditEvents), e)
  1830  }
  1831  
  1832  // Builds TestAuditLoggerBuffer.
  1833  type TestAuditLoggerBufferBuilder struct {
  1834  	auditEvents []*audit.Event
  1835  	testName    string
  1836  }
  1837  
  1838  // The required config for TestAuditLoggerBuffer.
  1839  type TestAuditLoggerBufferConfig struct {
  1840  	audit.LoggerConfig
  1841  }
  1842  
  1843  func (b *TestAuditLoggerBufferBuilder) ParseLoggerConfig(configJSON json.RawMessage) (config audit.LoggerConfig, err error) {
  1844  	return TestAuditLoggerBufferConfig{}, nil
  1845  }
  1846  
  1847  func (b *TestAuditLoggerBufferBuilder) Build(config audit.LoggerConfig) audit.Logger {
  1848  	return &TestAuditLoggerBuffer{auditEvents: &b.auditEvents}
  1849  }
  1850  
  1851  func (b *TestAuditLoggerBufferBuilder) Name() string {
  1852  	return b.testName + "_TestAuditLoggerBuffer"
  1853  }
  1854  
  1855  // An audit logger to test using a custom config.
  1856  type TestAuditLoggerCustomConfig struct{}
  1857  
  1858  func (logger *TestAuditLoggerCustomConfig) Log(*audit.Event) {}
  1859  
  1860  // Build TestAuditLoggerCustomConfig. This builds a TestAuditLoggerCustomConfig
  1861  // logger that uses a custom config.
  1862  type TestAuditLoggerCustomConfigBuilder struct {
  1863  	testName string
  1864  }
  1865  
  1866  // The custom config for the TestAuditLoggerCustomConfig logger.
  1867  type TestAuditLoggerCustomConfigConfig struct {
  1868  	audit.LoggerConfig
  1869  	Abc int
  1870  	Xyz string
  1871  }
  1872  
  1873  // Parses TestAuditLoggerCustomConfigConfig. Hard-coded to match with it's test
  1874  // case above.
  1875  func (b TestAuditLoggerCustomConfigBuilder) ParseLoggerConfig(configJSON json.RawMessage) (audit.LoggerConfig, error) {
  1876  	c := TestAuditLoggerCustomConfigConfig{}
  1877  	err := json.Unmarshal(configJSON, &c)
  1878  	if err != nil {
  1879  		return nil, fmt.Errorf("could not parse custom config: %v", err)
  1880  	}
  1881  	return c, nil
  1882  }
  1883  
  1884  func (b *TestAuditLoggerCustomConfigBuilder) Build(config audit.LoggerConfig) audit.Logger {
  1885  	return &TestAuditLoggerCustomConfig{}
  1886  }
  1887  
  1888  func (b *TestAuditLoggerCustomConfigBuilder) Name() string {
  1889  	return b.testName + "_TestAuditLoggerCustomConfig"
  1890  }
  1891  
  1892  // Builds custom configs for audit logger RBAC protos.
  1893  func createUDPATypedStruct(t *testing.T, in map[string]any, name string) *anypb.Any {
  1894  	t.Helper()
  1895  	pb, err := structpb.NewStruct(in)
  1896  	if err != nil {
  1897  		t.Fatalf("createUDPATypedStructFailed during structpb.NewStruct: %v", err)
  1898  	}
  1899  	typedURL := ""
  1900  	if name != "" {
  1901  		typedURL = typeURLPrefix + name
  1902  	}
  1903  	typedStruct := &v1xdsudpatypepb.TypedStruct{
  1904  		TypeUrl: typedURL,
  1905  		Value:   pb,
  1906  	}
  1907  	customConfig, err := anypb.New(typedStruct)
  1908  	if err != nil {
  1909  		t.Fatalf("createUDPATypedStructFailed during anypb.New: %v", err)
  1910  	}
  1911  	return customConfig
  1912  }
  1913  
  1914  // Builds custom configs for audit logger RBAC protos.
  1915  func createXDSTypedStruct(t *testing.T, in map[string]any, name string) *anypb.Any {
  1916  	t.Helper()
  1917  	pb, err := structpb.NewStruct(in)
  1918  	if err != nil {
  1919  		t.Fatalf("createXDSTypedStructFailed during structpb.NewStruct: %v", err)
  1920  	}
  1921  	typedStruct := &v3xdsxdstypepb.TypedStruct{
  1922  		TypeUrl: typeURLPrefix + name,
  1923  		Value:   pb,
  1924  	}
  1925  	customConfig, err := anypb.New(typedStruct)
  1926  	if err != nil {
  1927  		t.Fatalf("createXDSTypedStructFailed during anypb.New: %v", err)
  1928  	}
  1929  	return customConfig
  1930  }