google.golang.org/grpc@v1.72.2/internal/xds/rbac/matchers.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 "errors" 21 "fmt" 22 "net" 23 "net/netip" 24 "regexp" 25 26 v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" 27 v3rbacpb "github.com/envoyproxy/go-control-plane/envoy/config/rbac/v3" 28 v3route_componentspb "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" 29 v3matcherpb "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" 30 internalmatcher "google.golang.org/grpc/internal/xds/matcher" 31 ) 32 33 // matcher is an interface that takes data about incoming RPC's and returns 34 // whether it matches with whatever matcher implements this interface. 35 type matcher interface { 36 match(data *rpcData) bool 37 } 38 39 // policyMatcher helps determine whether an incoming RPC call matches a policy. 40 // A policy is a logical role (e.g. Service Admin), which is comprised of 41 // permissions and principals. A principal is an identity (or identities) for a 42 // downstream subject which are assigned the policy (role), and a permission is 43 // an action(s) that a principal(s) can take. A policy matches if both a 44 // permission and a principal match, which will be determined by the child or 45 // permissions and principal matchers. policyMatcher implements the matcher 46 // interface. 47 type policyMatcher struct { 48 permissions *orMatcher 49 principals *orMatcher 50 } 51 52 func newPolicyMatcher(policy *v3rbacpb.Policy) (*policyMatcher, error) { 53 permissions, err := matchersFromPermissions(policy.Permissions) 54 if err != nil { 55 return nil, err 56 } 57 principals, err := matchersFromPrincipals(policy.Principals) 58 if err != nil { 59 return nil, err 60 } 61 return &policyMatcher{ 62 permissions: &orMatcher{matchers: permissions}, 63 principals: &orMatcher{matchers: principals}, 64 }, nil 65 } 66 67 func (pm *policyMatcher) match(data *rpcData) bool { 68 // A policy matches if and only if at least one of its permissions match the 69 // action taking place AND at least one if its principals match the 70 // downstream peer. 71 return pm.permissions.match(data) && pm.principals.match(data) 72 } 73 74 // matchersFromPermissions takes a list of permissions (can also be 75 // a single permission, e.g. from a not matcher which is logically !permission) 76 // and returns a list of matchers which correspond to that permission. This will 77 // be called in many instances throughout the initial construction of the RBAC 78 // engine from the AND and OR matchers and also from the NOT matcher. 79 func matchersFromPermissions(permissions []*v3rbacpb.Permission) ([]matcher, error) { 80 var matchers []matcher 81 for _, permission := range permissions { 82 switch permission.GetRule().(type) { 83 case *v3rbacpb.Permission_AndRules: 84 mList, err := matchersFromPermissions(permission.GetAndRules().Rules) 85 if err != nil { 86 return nil, err 87 } 88 matchers = append(matchers, &andMatcher{matchers: mList}) 89 case *v3rbacpb.Permission_OrRules: 90 mList, err := matchersFromPermissions(permission.GetOrRules().Rules) 91 if err != nil { 92 return nil, err 93 } 94 matchers = append(matchers, &orMatcher{matchers: mList}) 95 case *v3rbacpb.Permission_Any: 96 matchers = append(matchers, &alwaysMatcher{}) 97 case *v3rbacpb.Permission_Header: 98 m, err := newHeaderMatcher(permission.GetHeader()) 99 if err != nil { 100 return nil, err 101 } 102 matchers = append(matchers, m) 103 case *v3rbacpb.Permission_UrlPath: 104 m, err := newURLPathMatcher(permission.GetUrlPath()) 105 if err != nil { 106 return nil, err 107 } 108 matchers = append(matchers, m) 109 case *v3rbacpb.Permission_DestinationIp: 110 // Due to this being on server side, the destination IP is the local 111 // IP. 112 m, err := newLocalIPMatcher(permission.GetDestinationIp()) 113 if err != nil { 114 return nil, err 115 } 116 matchers = append(matchers, m) 117 case *v3rbacpb.Permission_DestinationPort: 118 matchers = append(matchers, newPortMatcher(permission.GetDestinationPort())) 119 case *v3rbacpb.Permission_NotRule: 120 mList, err := matchersFromPermissions([]*v3rbacpb.Permission{{Rule: permission.GetNotRule().Rule}}) 121 if err != nil { 122 return nil, err 123 } 124 matchers = append(matchers, ¬Matcher{matcherToNot: mList[0]}) 125 case *v3rbacpb.Permission_Metadata: 126 // Never matches - so no-op if not inverted, always match if 127 // inverted. 128 if permission.GetMetadata().GetInvert() { // Test metadata being no-op and also metadata with invert always matching 129 matchers = append(matchers, &alwaysMatcher{}) 130 } 131 case *v3rbacpb.Permission_RequestedServerName: 132 // Not supported in gRPC RBAC currently - a permission typed as 133 // requested server name in the initial config will be a no-op. 134 } 135 } 136 return matchers, nil 137 } 138 139 func matchersFromPrincipals(principals []*v3rbacpb.Principal) ([]matcher, error) { 140 var matchers []matcher 141 for _, principal := range principals { 142 switch principal.GetIdentifier().(type) { 143 case *v3rbacpb.Principal_AndIds: 144 mList, err := matchersFromPrincipals(principal.GetAndIds().Ids) 145 if err != nil { 146 return nil, err 147 } 148 matchers = append(matchers, &andMatcher{matchers: mList}) 149 case *v3rbacpb.Principal_OrIds: 150 mList, err := matchersFromPrincipals(principal.GetOrIds().Ids) 151 if err != nil { 152 return nil, err 153 } 154 matchers = append(matchers, &orMatcher{matchers: mList}) 155 case *v3rbacpb.Principal_Any: 156 matchers = append(matchers, &alwaysMatcher{}) 157 case *v3rbacpb.Principal_Authenticated_: 158 authenticatedMatcher, err := newAuthenticatedMatcher(principal.GetAuthenticated()) 159 if err != nil { 160 return nil, err 161 } 162 matchers = append(matchers, authenticatedMatcher) 163 case *v3rbacpb.Principal_DirectRemoteIp: 164 m, err := newRemoteIPMatcher(principal.GetDirectRemoteIp()) 165 if err != nil { 166 return nil, err 167 } 168 matchers = append(matchers, m) 169 case *v3rbacpb.Principal_Header: 170 // Do we need an error here? 171 m, err := newHeaderMatcher(principal.GetHeader()) 172 if err != nil { 173 return nil, err 174 } 175 matchers = append(matchers, m) 176 case *v3rbacpb.Principal_UrlPath: 177 m, err := newURLPathMatcher(principal.GetUrlPath()) 178 if err != nil { 179 return nil, err 180 } 181 matchers = append(matchers, m) 182 case *v3rbacpb.Principal_NotId: 183 mList, err := matchersFromPrincipals([]*v3rbacpb.Principal{{Identifier: principal.GetNotId().Identifier}}) 184 if err != nil { 185 return nil, err 186 } 187 matchers = append(matchers, ¬Matcher{matcherToNot: mList[0]}) 188 case *v3rbacpb.Principal_SourceIp: 189 // The source ip principal identifier is deprecated. Thus, a 190 // principal typed as a source ip in the identifier will be a no-op. 191 // The config should use DirectRemoteIp instead. 192 case *v3rbacpb.Principal_RemoteIp: 193 // RBAC in gRPC treats direct_remote_ip and remote_ip as logically 194 // equivalent, as per A41. 195 m, err := newRemoteIPMatcher(principal.GetRemoteIp()) 196 if err != nil { 197 return nil, err 198 } 199 matchers = append(matchers, m) 200 case *v3rbacpb.Principal_Metadata: 201 // Not supported in gRPC RBAC currently - a principal typed as 202 // Metadata in the initial config will be a no-op. 203 } 204 } 205 return matchers, nil 206 } 207 208 // orMatcher is a matcher where it successfully matches if one of it's 209 // children successfully match. It also logically represents a principal or 210 // permission, but can also be it's own entity further down the tree of 211 // matchers. orMatcher implements the matcher interface. 212 type orMatcher struct { 213 matchers []matcher 214 } 215 216 func (om *orMatcher) match(data *rpcData) bool { 217 // Range through child matchers and pass in data about incoming RPC, and 218 // only one child matcher has to match to be logically successful. 219 for _, m := range om.matchers { 220 if m.match(data) { 221 return true 222 } 223 } 224 return false 225 } 226 227 // andMatcher is a matcher that is successful if every child matcher 228 // matches. andMatcher implements the matcher interface. 229 type andMatcher struct { 230 matchers []matcher 231 } 232 233 func (am *andMatcher) match(data *rpcData) bool { 234 for _, m := range am.matchers { 235 if !m.match(data) { 236 return false 237 } 238 } 239 return true 240 } 241 242 // alwaysMatcher is a matcher that will always match. This logically 243 // represents an any rule for a permission or a principal. alwaysMatcher 244 // implements the matcher interface. 245 type alwaysMatcher struct { 246 } 247 248 func (am *alwaysMatcher) match(*rpcData) bool { 249 return true 250 } 251 252 // notMatcher is a matcher that nots an underlying matcher. notMatcher 253 // implements the matcher interface. 254 type notMatcher struct { 255 matcherToNot matcher 256 } 257 258 func (nm *notMatcher) match(data *rpcData) bool { 259 return !nm.matcherToNot.match(data) 260 } 261 262 // headerMatcher is a matcher that matches on incoming HTTP Headers present 263 // in the incoming RPC. headerMatcher implements the matcher interface. 264 type headerMatcher struct { 265 matcher internalmatcher.HeaderMatcher 266 } 267 268 func newHeaderMatcher(headerMatcherConfig *v3route_componentspb.HeaderMatcher) (*headerMatcher, error) { 269 var m internalmatcher.HeaderMatcher 270 switch headerMatcherConfig.HeaderMatchSpecifier.(type) { 271 case *v3route_componentspb.HeaderMatcher_ExactMatch: 272 m = internalmatcher.NewHeaderExactMatcher(headerMatcherConfig.Name, headerMatcherConfig.GetExactMatch(), headerMatcherConfig.InvertMatch) 273 case *v3route_componentspb.HeaderMatcher_SafeRegexMatch: 274 regex, err := regexp.Compile(headerMatcherConfig.GetSafeRegexMatch().Regex) 275 if err != nil { 276 return nil, err 277 } 278 m = internalmatcher.NewHeaderRegexMatcher(headerMatcherConfig.Name, regex, headerMatcherConfig.InvertMatch) 279 case *v3route_componentspb.HeaderMatcher_RangeMatch: 280 m = internalmatcher.NewHeaderRangeMatcher(headerMatcherConfig.Name, headerMatcherConfig.GetRangeMatch().Start, headerMatcherConfig.GetRangeMatch().End, headerMatcherConfig.InvertMatch) 281 case *v3route_componentspb.HeaderMatcher_PresentMatch: 282 m = internalmatcher.NewHeaderPresentMatcher(headerMatcherConfig.Name, headerMatcherConfig.GetPresentMatch(), headerMatcherConfig.InvertMatch) 283 case *v3route_componentspb.HeaderMatcher_PrefixMatch: 284 m = internalmatcher.NewHeaderPrefixMatcher(headerMatcherConfig.Name, headerMatcherConfig.GetPrefixMatch(), headerMatcherConfig.InvertMatch) 285 case *v3route_componentspb.HeaderMatcher_SuffixMatch: 286 m = internalmatcher.NewHeaderSuffixMatcher(headerMatcherConfig.Name, headerMatcherConfig.GetSuffixMatch(), headerMatcherConfig.InvertMatch) 287 case *v3route_componentspb.HeaderMatcher_ContainsMatch: 288 m = internalmatcher.NewHeaderContainsMatcher(headerMatcherConfig.Name, headerMatcherConfig.GetContainsMatch(), headerMatcherConfig.InvertMatch) 289 case *v3route_componentspb.HeaderMatcher_StringMatch: 290 sm, err := internalmatcher.StringMatcherFromProto(headerMatcherConfig.GetStringMatch()) 291 if err != nil { 292 return nil, fmt.Errorf("invalid string matcher %+v: %v", headerMatcherConfig.GetStringMatch(), err) 293 } 294 m = internalmatcher.NewHeaderStringMatcher(headerMatcherConfig.Name, sm, headerMatcherConfig.InvertMatch) 295 default: 296 return nil, errors.New("unknown header matcher type") 297 } 298 return &headerMatcher{matcher: m}, nil 299 } 300 301 func (hm *headerMatcher) match(data *rpcData) bool { 302 return hm.matcher.Match(data.md) 303 } 304 305 // urlPathMatcher matches on the URL Path of the incoming RPC. In gRPC, this 306 // logically maps to the full method name the RPC is calling on the server side. 307 // urlPathMatcher implements the matcher interface. 308 type urlPathMatcher struct { 309 stringMatcher internalmatcher.StringMatcher 310 } 311 312 func newURLPathMatcher(pathMatcher *v3matcherpb.PathMatcher) (*urlPathMatcher, error) { 313 stringMatcher, err := internalmatcher.StringMatcherFromProto(pathMatcher.GetPath()) 314 if err != nil { 315 return nil, err 316 } 317 return &urlPathMatcher{stringMatcher: stringMatcher}, nil 318 } 319 320 func (upm *urlPathMatcher) match(data *rpcData) bool { 321 return upm.stringMatcher.Match(data.fullMethod) 322 } 323 324 // remoteIPMatcher and localIPMatcher both are matchers that match against 325 // a CIDR Range. Two different matchers are needed as the remote and destination 326 // ip addresses come from different parts of the data about incoming RPC's 327 // passed in. Matching a CIDR Range means to determine whether the IP Address 328 // falls within the CIDR Range or not. They both implement the matcher 329 // interface. 330 type remoteIPMatcher struct { 331 // ipNet represents the CidrRange that this matcher was configured with. 332 // This is what will remote and destination IP's will be matched against. 333 ipNet *net.IPNet 334 } 335 336 func newRemoteIPMatcher(cidrRange *v3corepb.CidrRange) (*remoteIPMatcher, error) { 337 // Convert configuration to a cidrRangeString, as Go standard library has 338 // methods that parse cidr string. 339 cidrRangeString := fmt.Sprintf("%s/%d", cidrRange.AddressPrefix, cidrRange.PrefixLen.Value) 340 _, ipNet, err := net.ParseCIDR(cidrRangeString) 341 if err != nil { 342 return nil, err 343 } 344 return &remoteIPMatcher{ipNet: ipNet}, nil 345 } 346 347 func (sim *remoteIPMatcher) match(data *rpcData) bool { 348 ip, _ := netip.ParseAddr(data.peerInfo.Addr.String()) 349 return sim.ipNet.Contains(net.IP(ip.AsSlice())) 350 } 351 352 type localIPMatcher struct { 353 ipNet *net.IPNet 354 } 355 356 func newLocalIPMatcher(cidrRange *v3corepb.CidrRange) (*localIPMatcher, error) { 357 cidrRangeString := fmt.Sprintf("%s/%d", cidrRange.AddressPrefix, cidrRange.PrefixLen.Value) 358 _, ipNet, err := net.ParseCIDR(cidrRangeString) 359 if err != nil { 360 return nil, err 361 } 362 return &localIPMatcher{ipNet: ipNet}, nil 363 } 364 365 func (dim *localIPMatcher) match(data *rpcData) bool { 366 ip, _ := netip.ParseAddr(data.localAddr.String()) 367 return dim.ipNet.Contains(net.IP(ip.AsSlice())) 368 } 369 370 // portMatcher matches on whether the destination port of the RPC matches the 371 // destination port this matcher was instantiated with. portMatcher 372 // implements the matcher interface. 373 type portMatcher struct { 374 destinationPort uint32 375 } 376 377 func newPortMatcher(destinationPort uint32) *portMatcher { 378 return &portMatcher{destinationPort: destinationPort} 379 } 380 381 func (pm *portMatcher) match(data *rpcData) bool { 382 return data.destinationPort == pm.destinationPort 383 } 384 385 // authenticatedMatcher matches on the name of the Principal. If set, the URI 386 // SAN or DNS SAN in that order is used from the certificate, otherwise the 387 // subject field is used. If unset, it applies to any user that is 388 // authenticated. authenticatedMatcher implements the matcher interface. 389 type authenticatedMatcher struct { 390 stringMatcher *internalmatcher.StringMatcher 391 } 392 393 func newAuthenticatedMatcher(authenticatedMatcherConfig *v3rbacpb.Principal_Authenticated) (*authenticatedMatcher, error) { 394 // Represents this line in the RBAC documentation = "If unset, it applies to 395 // any user that is authenticated" (see package-level comments). 396 if authenticatedMatcherConfig.PrincipalName == nil { 397 return &authenticatedMatcher{}, nil 398 } 399 stringMatcher, err := internalmatcher.StringMatcherFromProto(authenticatedMatcherConfig.PrincipalName) 400 if err != nil { 401 return nil, err 402 } 403 return &authenticatedMatcher{stringMatcher: &stringMatcher}, nil 404 } 405 406 func (am *authenticatedMatcher) match(data *rpcData) bool { 407 if data.authType != "tls" { 408 // Connection is not authenticated. 409 return false 410 } 411 if am.stringMatcher == nil { 412 // Allows any authenticated user. 413 return true 414 } 415 // "If there is no client certificate (thus no SAN nor Subject), check if "" 416 // (empty string) matches. If it matches, the principal_name is said to 417 // match" - A41 418 if len(data.certs) == 0 { 419 return am.stringMatcher.Match("") 420 } 421 cert := data.certs[0] 422 // The order of matching as per the RBAC documentation (see package-level comments) 423 // is as follows: URI SANs, DNS SANs, and then subject name. 424 for _, uriSAN := range cert.URIs { 425 if am.stringMatcher.Match(uriSAN.String()) { 426 return true 427 } 428 } 429 for _, dnsSAN := range cert.DNSNames { 430 if am.stringMatcher.Match(dnsSAN) { 431 return true 432 } 433 } 434 return am.stringMatcher.Match(cert.Subject.String()) 435 }