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, &notMatcher{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, &notMatcher{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  }