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 }