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