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