gitee.com/ks-custle/core-gm@v0.0.0-20230922171213-b83bdd97b62c/grpc/authz/rbac_translator.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 authz exposes methods to manage authorization within gRPC.
    18  //
    19  // # Experimental
    20  //
    21  // Notice: This package is EXPERIMENTAL and may be changed or removed
    22  // in a later release.
    23  package authz
    24  
    25  import (
    26  	"bytes"
    27  	"encoding/json"
    28  	"fmt"
    29  	"strings"
    30  
    31  	v3rbacpb "gitee.com/ks-custle/core-gm/go-control-plane/envoy/config/rbac/v3"
    32  	v3routepb "gitee.com/ks-custle/core-gm/go-control-plane/envoy/config/route/v3"
    33  	v3matcherpb "gitee.com/ks-custle/core-gm/go-control-plane/envoy/type/matcher/v3"
    34  )
    35  
    36  type header struct {
    37  	Key    string
    38  	Values []string
    39  }
    40  
    41  type peer struct {
    42  	Principals []string
    43  }
    44  
    45  type request struct {
    46  	Paths   []string
    47  	Headers []header
    48  }
    49  
    50  type rule struct {
    51  	Name    string
    52  	Source  peer
    53  	Request request
    54  }
    55  
    56  // Represents the SDK authorization policy provided by user.
    57  type authorizationPolicy struct {
    58  	Name       string
    59  	DenyRules  []rule `json:"deny_rules"`
    60  	AllowRules []rule `json:"allow_rules"`
    61  }
    62  
    63  func principalOr(principals []*v3rbacpb.Principal) *v3rbacpb.Principal {
    64  	return &v3rbacpb.Principal{
    65  		Identifier: &v3rbacpb.Principal_OrIds{
    66  			OrIds: &v3rbacpb.Principal_Set{
    67  				Ids: principals,
    68  			},
    69  		},
    70  	}
    71  }
    72  
    73  func permissionOr(permission []*v3rbacpb.Permission) *v3rbacpb.Permission {
    74  	return &v3rbacpb.Permission{
    75  		Rule: &v3rbacpb.Permission_OrRules{
    76  			OrRules: &v3rbacpb.Permission_Set{
    77  				Rules: permission,
    78  			},
    79  		},
    80  	}
    81  }
    82  
    83  func permissionAnd(permission []*v3rbacpb.Permission) *v3rbacpb.Permission {
    84  	return &v3rbacpb.Permission{
    85  		Rule: &v3rbacpb.Permission_AndRules{
    86  			AndRules: &v3rbacpb.Permission_Set{
    87  				Rules: permission,
    88  			},
    89  		},
    90  	}
    91  }
    92  
    93  func getStringMatcher(value string) *v3matcherpb.StringMatcher {
    94  	switch {
    95  	case value == "*":
    96  		return &v3matcherpb.StringMatcher{
    97  			MatchPattern: &v3matcherpb.StringMatcher_SafeRegex{
    98  				SafeRegex: &v3matcherpb.RegexMatcher{Regex: ".+"}},
    99  		}
   100  	case strings.HasSuffix(value, "*"):
   101  		prefix := strings.TrimSuffix(value, "*")
   102  		return &v3matcherpb.StringMatcher{
   103  			MatchPattern: &v3matcherpb.StringMatcher_Prefix{Prefix: prefix},
   104  		}
   105  	case strings.HasPrefix(value, "*"):
   106  		suffix := strings.TrimPrefix(value, "*")
   107  		return &v3matcherpb.StringMatcher{
   108  			MatchPattern: &v3matcherpb.StringMatcher_Suffix{Suffix: suffix},
   109  		}
   110  	default:
   111  		return &v3matcherpb.StringMatcher{
   112  			MatchPattern: &v3matcherpb.StringMatcher_Exact{Exact: value},
   113  		}
   114  	}
   115  }
   116  
   117  func getHeaderMatcher(key, value string) *v3routepb.HeaderMatcher {
   118  	switch {
   119  	case value == "*":
   120  		return &v3routepb.HeaderMatcher{
   121  			Name: key,
   122  			HeaderMatchSpecifier: &v3routepb.HeaderMatcher_SafeRegexMatch{
   123  				SafeRegexMatch: &v3matcherpb.RegexMatcher{Regex: ".+"}},
   124  		}
   125  	case strings.HasSuffix(value, "*"):
   126  		prefix := strings.TrimSuffix(value, "*")
   127  		return &v3routepb.HeaderMatcher{
   128  			Name:                 key,
   129  			HeaderMatchSpecifier: &v3routepb.HeaderMatcher_PrefixMatch{PrefixMatch: prefix},
   130  		}
   131  	case strings.HasPrefix(value, "*"):
   132  		suffix := strings.TrimPrefix(value, "*")
   133  		return &v3routepb.HeaderMatcher{
   134  			Name:                 key,
   135  			HeaderMatchSpecifier: &v3routepb.HeaderMatcher_SuffixMatch{SuffixMatch: suffix},
   136  		}
   137  	default:
   138  		return &v3routepb.HeaderMatcher{
   139  			Name:                 key,
   140  			HeaderMatchSpecifier: &v3routepb.HeaderMatcher_ExactMatch{ExactMatch: value},
   141  		}
   142  	}
   143  }
   144  
   145  func parsePrincipalNames(principalNames []string) []*v3rbacpb.Principal {
   146  	ps := make([]*v3rbacpb.Principal, 0, len(principalNames))
   147  	for _, principalName := range principalNames {
   148  		newPrincipalName := &v3rbacpb.Principal{
   149  			Identifier: &v3rbacpb.Principal_Authenticated_{
   150  				Authenticated: &v3rbacpb.Principal_Authenticated{
   151  					PrincipalName: getStringMatcher(principalName),
   152  				},
   153  			}}
   154  		ps = append(ps, newPrincipalName)
   155  	}
   156  	return ps
   157  }
   158  
   159  func parsePeer(source peer) *v3rbacpb.Principal {
   160  	if len(source.Principals) == 0 {
   161  		return &v3rbacpb.Principal{
   162  			Identifier: &v3rbacpb.Principal_Any{
   163  				Any: true,
   164  			},
   165  		}
   166  	}
   167  	return principalOr(parsePrincipalNames(source.Principals))
   168  }
   169  
   170  func parsePaths(paths []string) []*v3rbacpb.Permission {
   171  	ps := make([]*v3rbacpb.Permission, 0, len(paths))
   172  	for _, path := range paths {
   173  		newPath := &v3rbacpb.Permission{
   174  			Rule: &v3rbacpb.Permission_UrlPath{
   175  				UrlPath: &v3matcherpb.PathMatcher{
   176  					Rule: &v3matcherpb.PathMatcher_Path{Path: getStringMatcher(path)}}}}
   177  		ps = append(ps, newPath)
   178  	}
   179  	return ps
   180  }
   181  
   182  func parseHeaderValues(key string, values []string) []*v3rbacpb.Permission {
   183  	vs := make([]*v3rbacpb.Permission, 0, len(values))
   184  	for _, value := range values {
   185  		newHeader := &v3rbacpb.Permission{
   186  			Rule: &v3rbacpb.Permission_Header{
   187  				Header: getHeaderMatcher(key, value)}}
   188  		vs = append(vs, newHeader)
   189  	}
   190  	return vs
   191  }
   192  
   193  var unsupportedHeaders = map[string]bool{
   194  	"host":                true,
   195  	"connection":          true,
   196  	"keep-alive":          true,
   197  	"proxy-authenticate":  true,
   198  	"proxy-authorization": true,
   199  	"te":                  true,
   200  	"trailer":             true,
   201  	"transfer-encoding":   true,
   202  	"upgrade":             true,
   203  }
   204  
   205  func unsupportedHeader(key string) bool {
   206  	return key[0] == ':' || strings.HasPrefix(key, "grpc-") || unsupportedHeaders[key]
   207  }
   208  
   209  func parseHeaders(headers []header) ([]*v3rbacpb.Permission, error) {
   210  	hs := make([]*v3rbacpb.Permission, 0, len(headers))
   211  	for i, header := range headers {
   212  		if header.Key == "" {
   213  			return nil, fmt.Errorf(`"headers" %d: "key" is not present`, i)
   214  		}
   215  		header.Key = strings.ToLower(header.Key)
   216  		if unsupportedHeader(header.Key) {
   217  			return nil, fmt.Errorf(`"headers" %d: unsupported "key" %s`, i, header.Key)
   218  		}
   219  		if len(header.Values) == 0 {
   220  			return nil, fmt.Errorf(`"headers" %d: "values" is not present`, i)
   221  		}
   222  		values := parseHeaderValues(header.Key, header.Values)
   223  		hs = append(hs, permissionOr(values))
   224  	}
   225  	return hs, nil
   226  }
   227  
   228  func parseRequest(request request) (*v3rbacpb.Permission, error) {
   229  	var and []*v3rbacpb.Permission
   230  	if len(request.Paths) > 0 {
   231  		and = append(and, permissionOr(parsePaths(request.Paths)))
   232  	}
   233  	if len(request.Headers) > 0 {
   234  		headers, err := parseHeaders(request.Headers)
   235  		if err != nil {
   236  			return nil, err
   237  		}
   238  		and = append(and, permissionAnd(headers))
   239  	}
   240  	if len(and) > 0 {
   241  		return permissionAnd(and), nil
   242  	}
   243  	return &v3rbacpb.Permission{
   244  		Rule: &v3rbacpb.Permission_Any{
   245  			Any: true,
   246  		},
   247  	}, nil
   248  }
   249  
   250  func parseRules(rules []rule, prefixName string) (map[string]*v3rbacpb.Policy, error) {
   251  	policies := make(map[string]*v3rbacpb.Policy)
   252  	for i, rule := range rules {
   253  		if rule.Name == "" {
   254  			return policies, fmt.Errorf(`%d: "name" is not present`, i)
   255  		}
   256  		permission, err := parseRequest(rule.Request)
   257  		if err != nil {
   258  			return nil, fmt.Errorf("%d: %v", i, err)
   259  		}
   260  		policyName := prefixName + "_" + rule.Name
   261  		policies[policyName] = &v3rbacpb.Policy{
   262  			Principals:  []*v3rbacpb.Principal{parsePeer(rule.Source)},
   263  			Permissions: []*v3rbacpb.Permission{permission},
   264  		}
   265  	}
   266  	return policies, nil
   267  }
   268  
   269  // translatePolicy translates SDK authorization policy in JSON format to two
   270  // Envoy RBAC polices (deny followed by allow policy) or only one Envoy RBAC
   271  // allow policy. If the input policy cannot be parsed or is invalid, an error
   272  // will be returned.
   273  func translatePolicy(policyStr string) ([]*v3rbacpb.RBAC, error) {
   274  	policy := &authorizationPolicy{}
   275  	d := json.NewDecoder(bytes.NewReader([]byte(policyStr)))
   276  	d.DisallowUnknownFields()
   277  	if err := d.Decode(policy); err != nil {
   278  		return nil, fmt.Errorf("failed to unmarshal policy: %v", err)
   279  	}
   280  	if policy.Name == "" {
   281  		return nil, fmt.Errorf(`"name" is not present`)
   282  	}
   283  	if len(policy.AllowRules) == 0 {
   284  		return nil, fmt.Errorf(`"allow_rules" is not present`)
   285  	}
   286  	rbacs := make([]*v3rbacpb.RBAC, 0, 2)
   287  	if len(policy.DenyRules) > 0 {
   288  		denyPolicies, err := parseRules(policy.DenyRules, policy.Name)
   289  		if err != nil {
   290  			return nil, fmt.Errorf(`"deny_rules" %v`, err)
   291  		}
   292  		denyRBAC := &v3rbacpb.RBAC{
   293  			Action:   v3rbacpb.RBAC_DENY,
   294  			Policies: denyPolicies,
   295  		}
   296  		rbacs = append(rbacs, denyRBAC)
   297  	}
   298  	allowPolicies, err := parseRules(policy.AllowRules, policy.Name)
   299  	if err != nil {
   300  		return nil, fmt.Errorf(`"allow_rules" %v`, err)
   301  	}
   302  	allowRBAC := &v3rbacpb.RBAC{Action: v3rbacpb.RBAC_ALLOW, Policies: allowPolicies}
   303  	return append(rbacs, allowRBAC), nil
   304  }