github.com/oam-dev/cluster-gateway@v1.9.0/pkg/apis/cluster/v1alpha1/clustergateway_proxy_configuration.go (about)

     1  /*
     2  Copyright 2022 The KubeVela 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 v1alpha1
    18  
    19  import (
    20  	"fmt"
    21  	"os"
    22  	"regexp"
    23  
    24  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    25  	"k8s.io/apimachinery/pkg/util/yaml"
    26  	"k8s.io/apiserver/pkg/authentication/user"
    27  	"k8s.io/client-go/rest"
    28  	"k8s.io/utils/strings/slices"
    29  
    30  	"github.com/oam-dev/cluster-gateway/pkg/config"
    31  )
    32  
    33  const (
    34  	AnnotationClusterGatewayProxyConfiguration = "cluster.core.oam.dev/cluster-gateway-proxy-configuration"
    35  )
    36  
    37  type ClusterGatewayProxyConfiguration struct {
    38  	metav1.TypeMeta `json:",inline"`
    39  	Spec            ClusterGatewayProxyConfigurationSpec `json:"spec"`
    40  }
    41  
    42  type ClusterGatewayProxyConfigurationSpec struct {
    43  	ClientIdentityExchanger `json:"clientIdentityExchanger"`
    44  }
    45  
    46  type ClientIdentityExchanger struct {
    47  	Rules []ClientIdentityExchangeRule `json:"rules,omitempty"`
    48  }
    49  
    50  type ClientIdentityExchangeType string
    51  
    52  const (
    53  	PrivilegedIdentityExchanger    ClientIdentityExchangeType = "PrivilegedIdentityExchanger"
    54  	StaticMappingIdentityExchanger ClientIdentityExchangeType = "StaticMappingIdentityExchanger"
    55  	ExternalIdentityExchanger      ClientIdentityExchangeType = "ExternalIdentityExchanger"
    56  )
    57  
    58  type ClientIdentityExchangeRule struct {
    59  	Name   string                     `json:"name"`
    60  	Type   ClientIdentityExchangeType `json:"type"`
    61  	Source *IdentityExchangerSource   `json:"source"`
    62  
    63  	Target *IdentityExchangerTarget `json:"target,omitempty"`
    64  	URL    *string                  `json:"url,omitempty"`
    65  }
    66  
    67  type IdentityExchangerTarget struct {
    68  	User   string   `json:"user,omitempty"`
    69  	Groups []string `json:"groups,omitempty"`
    70  	UID    string   `json:"uid,omitempty"`
    71  }
    72  
    73  type IdentityExchangerSource struct {
    74  	User    *string `json:"user,omitempty"`
    75  	Group   *string `json:"group,omitempty"`
    76  	UID     *string `json:"uid,omitempty"`
    77  	Cluster *string `json:"cluster,omitempty"`
    78  
    79  	UserPattern    *string `json:"userPattern,omitempty"`
    80  	GroupPattern   *string `json:"groupPattern,omitempty"`
    81  	ClusterPattern *string `json:"clusterPattern,omitempty"`
    82  }
    83  
    84  var GlobalClusterGatewayProxyConfiguration = &ClusterGatewayProxyConfiguration{}
    85  
    86  func LoadGlobalClusterGatewayProxyConfig() error {
    87  	if config.ClusterGatewayProxyConfigPath == "" {
    88  		return nil
    89  	}
    90  	bs, err := os.ReadFile(config.ClusterGatewayProxyConfigPath)
    91  	if err != nil {
    92  		return err
    93  	}
    94  	return yaml.Unmarshal(bs, GlobalClusterGatewayProxyConfiguration)
    95  }
    96  
    97  func ExchangeIdentity(exchanger *ClientIdentityExchanger, userInfo user.Info, cluster string) (matched bool, ruleName string, projected *rest.ImpersonationConfig, err error) {
    98  	for _, rule := range exchanger.Rules {
    99  		if matched, projected, err = exchangeIdentity(&rule, userInfo, cluster); matched {
   100  			return matched, rule.Name, projected, err
   101  		}
   102  	}
   103  	return false, "", nil, nil
   104  }
   105  
   106  func exchangeIdentity(rule *ClientIdentityExchangeRule, userInfo user.Info, cluster string) (matched bool, projected *rest.ImpersonationConfig, err error) {
   107  	if !matchIdentity(rule.Source, userInfo, cluster) {
   108  		return false, nil, nil
   109  	}
   110  	switch rule.Type {
   111  	case PrivilegedIdentityExchanger:
   112  		return true, &rest.ImpersonationConfig{}, nil
   113  	case StaticMappingIdentityExchanger:
   114  		return true, &rest.ImpersonationConfig{
   115  			UserName: rule.Target.User,
   116  			Groups:   rule.Target.Groups,
   117  			UID:      rule.Target.UID,
   118  		}, nil
   119  	case ExternalIdentityExchanger:
   120  		return true, nil, fmt.Errorf("ExternalIdentityExchanger is not implemented")
   121  	}
   122  	return true, nil, fmt.Errorf("unknown exchanger type: %s", rule.Type)
   123  }
   124  
   125  // denyQuery return true when the pattern is valid and could be used as regular expression,
   126  // and the given query does not match the pattern, otherwise return false
   127  func (in *IdentityExchangerSource) denyQuery(pattern *string, query string) bool {
   128  	if pattern == nil {
   129  		return false
   130  	}
   131  	matched, err := regexp.Match(*pattern, []byte(query))
   132  	if err != nil {
   133  		return false
   134  	}
   135  	return !matched
   136  }
   137  
   138  // denyGroups return true if none of the group matches the given pattern
   139  func (in *IdentityExchangerSource) denyGroups(groupPattern *string, groups []string) bool {
   140  	if groupPattern == nil {
   141  		return false
   142  	}
   143  	for _, group := range groups {
   144  		if !in.denyQuery(groupPattern, group) {
   145  			return false
   146  		}
   147  	}
   148  	return true
   149  }
   150  
   151  func matchIdentity(in *IdentityExchangerSource, userInfo user.Info, cluster string) bool {
   152  	if in == nil {
   153  		return false
   154  	}
   155  	switch {
   156  	case in.User != nil && userInfo.GetName() != *in.User:
   157  		return false
   158  	case in.Group != nil && !slices.Contains(userInfo.GetGroups(), *in.Group):
   159  		return false
   160  	case in.UID != nil && userInfo.GetUID() != *in.UID:
   161  		return false
   162  	case in.Cluster != nil && cluster != *in.Cluster:
   163  		return false
   164  	case in.denyQuery(in.UserPattern, userInfo.GetName()):
   165  		return false
   166  	case in.denyGroups(in.GroupPattern, userInfo.GetGroups()):
   167  		return false
   168  	case in.denyQuery(in.ClusterPattern, cluster):
   169  		return false
   170  	}
   171  	return true
   172  }