dubbo.apache.org/dubbo-go/v3@v3.1.1/xds/utils/rbac/matchers.go (about)

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