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