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