google.golang.org/grpc@v1.62.1/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 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" 26 v3rbacpb "github.com/envoyproxy/go-control-plane/envoy/config/rbac/v3" 27 v3route_componentspb "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" 28 v3matcherpb "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" 29 internalmatcher "google.golang.org/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 // Never matches - so no-op if not inverted, always match if 126 // inverted. 127 if permission.GetMetadata().GetInvert() { // Test metadata being no-op and also metadata with invert always matching 128 matchers = append(matchers, &alwaysMatcher{}) 129 } 130 case *v3rbacpb.Permission_RequestedServerName: 131 // Not supported in gRPC RBAC currently - a permission typed as 132 // requested server name in the initial config will be a no-op. 133 } 134 } 135 return matchers, nil 136 } 137 138 func matchersFromPrincipals(principals []*v3rbacpb.Principal) ([]matcher, error) { 139 var matchers []matcher 140 for _, principal := range principals { 141 switch principal.GetIdentifier().(type) { 142 case *v3rbacpb.Principal_AndIds: 143 mList, err := matchersFromPrincipals(principal.GetAndIds().Ids) 144 if err != nil { 145 return nil, err 146 } 147 matchers = append(matchers, &andMatcher{matchers: mList}) 148 case *v3rbacpb.Principal_OrIds: 149 mList, err := matchersFromPrincipals(principal.GetOrIds().Ids) 150 if err != nil { 151 return nil, err 152 } 153 matchers = append(matchers, &orMatcher{matchers: mList}) 154 case *v3rbacpb.Principal_Any: 155 matchers = append(matchers, &alwaysMatcher{}) 156 case *v3rbacpb.Principal_Authenticated_: 157 authenticatedMatcher, err := newAuthenticatedMatcher(principal.GetAuthenticated()) 158 if err != nil { 159 return nil, err 160 } 161 matchers = append(matchers, authenticatedMatcher) 162 case *v3rbacpb.Principal_DirectRemoteIp: 163 m, err := newRemoteIPMatcher(principal.GetDirectRemoteIp()) 164 if err != nil { 165 return nil, err 166 } 167 matchers = append(matchers, m) 168 case *v3rbacpb.Principal_Header: 169 // Do we need an error here? 170 m, err := newHeaderMatcher(principal.GetHeader()) 171 if err != nil { 172 return nil, err 173 } 174 matchers = append(matchers, m) 175 case *v3rbacpb.Principal_UrlPath: 176 m, err := newURLPathMatcher(principal.GetUrlPath()) 177 if err != nil { 178 return nil, err 179 } 180 matchers = append(matchers, m) 181 case *v3rbacpb.Principal_NotId: 182 mList, err := matchersFromPrincipals([]*v3rbacpb.Principal{{Identifier: principal.GetNotId().Identifier}}) 183 if err != nil { 184 return nil, err 185 } 186 matchers = append(matchers, ¬Matcher{matcherToNot: mList[0]}) 187 case *v3rbacpb.Principal_SourceIp: 188 // The source ip principal identifier is deprecated. Thus, a 189 // principal typed as a source ip in the identifier will be a no-op. 190 // The config should use DirectRemoteIp instead. 191 case *v3rbacpb.Principal_RemoteIp: 192 // RBAC in gRPC treats direct_remote_ip and remote_ip as logically 193 // equivalent, as per A41. 194 m, err := newRemoteIPMatcher(principal.GetRemoteIp()) 195 if err != nil { 196 return nil, err 197 } 198 matchers = append(matchers, m) 199 case *v3rbacpb.Principal_Metadata: 200 // Not supported in gRPC RBAC currently - a principal typed as 201 // Metadata in the initial config will be a no-op. 202 } 203 } 204 return matchers, nil 205 } 206 207 // orMatcher is a matcher where it successfully matches if one of it's 208 // children successfully match. It also logically represents a principal or 209 // permission, but can also be it's own entity further down the tree of 210 // matchers. orMatcher implements the matcher interface. 211 type orMatcher struct { 212 matchers []matcher 213 } 214 215 func (om *orMatcher) match(data *rpcData) bool { 216 // Range through child matchers and pass in data about incoming RPC, and 217 // only one child matcher has to match to be logically successful. 218 for _, m := range om.matchers { 219 if m.match(data) { 220 return true 221 } 222 } 223 return false 224 } 225 226 // andMatcher is a matcher that is successful if every child matcher 227 // matches. andMatcher implements the matcher interface. 228 type andMatcher struct { 229 matchers []matcher 230 } 231 232 func (am *andMatcher) match(data *rpcData) bool { 233 for _, m := range am.matchers { 234 if !m.match(data) { 235 return false 236 } 237 } 238 return true 239 } 240 241 // alwaysMatcher is a matcher that will always match. This logically 242 // represents an any rule for a permission or a principal. alwaysMatcher 243 // implements the matcher interface. 244 type alwaysMatcher struct { 245 } 246 247 func (am *alwaysMatcher) match(data *rpcData) bool { 248 return true 249 } 250 251 // notMatcher is a matcher that nots an underlying matcher. notMatcher 252 // implements the matcher interface. 253 type notMatcher struct { 254 matcherToNot matcher 255 } 256 257 func (nm *notMatcher) match(data *rpcData) bool { 258 return !nm.matcherToNot.match(data) 259 } 260 261 // headerMatcher is a matcher that matches on incoming HTTP Headers present 262 // in the incoming RPC. headerMatcher implements the matcher interface. 263 type headerMatcher struct { 264 matcher internalmatcher.HeaderMatcher 265 } 266 267 func newHeaderMatcher(headerMatcherConfig *v3route_componentspb.HeaderMatcher) (*headerMatcher, error) { 268 var m internalmatcher.HeaderMatcher 269 switch headerMatcherConfig.HeaderMatchSpecifier.(type) { 270 case *v3route_componentspb.HeaderMatcher_ExactMatch: 271 m = internalmatcher.NewHeaderExactMatcher(headerMatcherConfig.Name, headerMatcherConfig.GetExactMatch(), headerMatcherConfig.InvertMatch) 272 case *v3route_componentspb.HeaderMatcher_SafeRegexMatch: 273 regex, err := regexp.Compile(headerMatcherConfig.GetSafeRegexMatch().Regex) 274 if err != nil { 275 return nil, err 276 } 277 m = internalmatcher.NewHeaderRegexMatcher(headerMatcherConfig.Name, regex, headerMatcherConfig.InvertMatch) 278 case *v3route_componentspb.HeaderMatcher_RangeMatch: 279 m = internalmatcher.NewHeaderRangeMatcher(headerMatcherConfig.Name, headerMatcherConfig.GetRangeMatch().Start, headerMatcherConfig.GetRangeMatch().End, headerMatcherConfig.InvertMatch) 280 case *v3route_componentspb.HeaderMatcher_PresentMatch: 281 m = internalmatcher.NewHeaderPresentMatcher(headerMatcherConfig.Name, headerMatcherConfig.GetPresentMatch(), headerMatcherConfig.InvertMatch) 282 case *v3route_componentspb.HeaderMatcher_PrefixMatch: 283 m = internalmatcher.NewHeaderPrefixMatcher(headerMatcherConfig.Name, headerMatcherConfig.GetPrefixMatch(), headerMatcherConfig.InvertMatch) 284 case *v3route_componentspb.HeaderMatcher_SuffixMatch: 285 m = internalmatcher.NewHeaderSuffixMatcher(headerMatcherConfig.Name, headerMatcherConfig.GetSuffixMatch(), headerMatcherConfig.InvertMatch) 286 case *v3route_componentspb.HeaderMatcher_ContainsMatch: 287 m = internalmatcher.NewHeaderContainsMatcher(headerMatcherConfig.Name, headerMatcherConfig.GetContainsMatch(), headerMatcherConfig.InvertMatch) 288 case *v3route_componentspb.HeaderMatcher_StringMatch: 289 sm, err := internalmatcher.StringMatcherFromProto(headerMatcherConfig.GetStringMatch()) 290 if err != nil { 291 return nil, fmt.Errorf("invalid string matcher %+v: %v", headerMatcherConfig.GetStringMatch(), err) 292 } 293 m = internalmatcher.NewHeaderStringMatcher(headerMatcherConfig.Name, sm, headerMatcherConfig.InvertMatch) 294 default: 295 return nil, errors.New("unknown header matcher type") 296 } 297 return &headerMatcher{matcher: m}, nil 298 } 299 300 func (hm *headerMatcher) match(data *rpcData) bool { 301 return hm.matcher.Match(data.md) 302 } 303 304 // urlPathMatcher matches on the URL Path of the incoming RPC. In gRPC, this 305 // logically maps to the full method name the RPC is calling on the server side. 306 // urlPathMatcher implements the matcher interface. 307 type urlPathMatcher struct { 308 stringMatcher internalmatcher.StringMatcher 309 } 310 311 func newURLPathMatcher(pathMatcher *v3matcherpb.PathMatcher) (*urlPathMatcher, error) { 312 stringMatcher, err := internalmatcher.StringMatcherFromProto(pathMatcher.GetPath()) 313 if err != nil { 314 return nil, err 315 } 316 return &urlPathMatcher{stringMatcher: stringMatcher}, nil 317 } 318 319 func (upm *urlPathMatcher) match(data *rpcData) bool { 320 return upm.stringMatcher.Match(data.fullMethod) 321 } 322 323 // remoteIPMatcher and localIPMatcher both are matchers that match against 324 // a CIDR Range. Two different matchers are needed as the remote and destination 325 // ip addresses come from different parts of the data about incoming RPC's 326 // passed in. Matching a CIDR Range means to determine whether the IP Address 327 // falls within the CIDR Range or not. They both implement the matcher 328 // interface. 329 type remoteIPMatcher struct { 330 // ipNet represents the CidrRange that this matcher was configured with. 331 // This is what will remote and destination IP's will be matched against. 332 ipNet *net.IPNet 333 } 334 335 func newRemoteIPMatcher(cidrRange *v3corepb.CidrRange) (*remoteIPMatcher, error) { 336 // Convert configuration to a cidrRangeString, as Go standard library has 337 // methods that parse cidr string. 338 cidrRangeString := fmt.Sprintf("%s/%d", cidrRange.AddressPrefix, cidrRange.PrefixLen.Value) 339 _, ipNet, err := net.ParseCIDR(cidrRangeString) 340 if err != nil { 341 return nil, err 342 } 343 return &remoteIPMatcher{ipNet: ipNet}, nil 344 } 345 346 func (sim *remoteIPMatcher) match(data *rpcData) bool { 347 return sim.ipNet.Contains(net.IP(net.ParseIP(data.peerInfo.Addr.String()))) 348 } 349 350 type localIPMatcher struct { 351 ipNet *net.IPNet 352 } 353 354 func newLocalIPMatcher(cidrRange *v3corepb.CidrRange) (*localIPMatcher, error) { 355 cidrRangeString := fmt.Sprintf("%s/%d", cidrRange.AddressPrefix, cidrRange.PrefixLen.Value) 356 _, ipNet, err := net.ParseCIDR(cidrRangeString) 357 if err != nil { 358 return nil, err 359 } 360 return &localIPMatcher{ipNet: ipNet}, nil 361 } 362 363 func (dim *localIPMatcher) match(data *rpcData) bool { 364 return dim.ipNet.Contains(net.IP(net.ParseIP(data.localAddr.String()))) 365 } 366 367 // portMatcher matches on whether the destination port of the RPC matches the 368 // destination port this matcher was instantiated with. portMatcher 369 // implements the matcher interface. 370 type portMatcher struct { 371 destinationPort uint32 372 } 373 374 func newPortMatcher(destinationPort uint32) *portMatcher { 375 return &portMatcher{destinationPort: destinationPort} 376 } 377 378 func (pm *portMatcher) match(data *rpcData) bool { 379 return data.destinationPort == pm.destinationPort 380 } 381 382 // authenticatedMatcher matches on the name of the Principal. If set, the URI 383 // SAN or DNS SAN in that order is used from the certificate, otherwise the 384 // subject field is used. If unset, it applies to any user that is 385 // authenticated. authenticatedMatcher implements the matcher interface. 386 type authenticatedMatcher struct { 387 stringMatcher *internalmatcher.StringMatcher 388 } 389 390 func newAuthenticatedMatcher(authenticatedMatcherConfig *v3rbacpb.Principal_Authenticated) (*authenticatedMatcher, error) { 391 // Represents this line in the RBAC documentation = "If unset, it applies to 392 // any user that is authenticated" (see package-level comments). 393 if authenticatedMatcherConfig.PrincipalName == nil { 394 return &authenticatedMatcher{}, nil 395 } 396 stringMatcher, err := internalmatcher.StringMatcherFromProto(authenticatedMatcherConfig.PrincipalName) 397 if err != nil { 398 return nil, err 399 } 400 return &authenticatedMatcher{stringMatcher: &stringMatcher}, nil 401 } 402 403 func (am *authenticatedMatcher) match(data *rpcData) bool { 404 if data.authType != "tls" { 405 // Connection is not authenticated. 406 return false 407 } 408 if am.stringMatcher == nil { 409 // Allows any authenticated user. 410 return true 411 } 412 // "If there is no client certificate (thus no SAN nor Subject), check if "" 413 // (empty string) matches. If it matches, the principal_name is said to 414 // match" - A41 415 if len(data.certs) == 0 { 416 return am.stringMatcher.Match("") 417 } 418 cert := data.certs[0] 419 // The order of matching as per the RBAC documentation (see package-level comments) 420 // is as follows: URI SANs, DNS SANs, and then subject name. 421 for _, uriSAN := range cert.URIs { 422 if am.stringMatcher.Match(uriSAN.String()) { 423 return true 424 } 425 } 426 for _, dnsSAN := range cert.DNSNames { 427 if am.stringMatcher.Match(dnsSAN) { 428 return true 429 } 430 } 431 return am.stringMatcher.Match(cert.Subject.String()) 432 }