diff --git a/README.md b/README.md index 4e8b88ee..976825e9 100644 --- a/README.md +++ b/README.md @@ -119,3 +119,18 @@ to the hosting cluster which is basically responsible for: The addon-manager can be installed via simple helm commands, please refer to the installation guide [here](https://open-cluster-management.io/scenarios/pushing-kube-api-requests/#installation). + +### Identity Passing + +When feature flag `ClientIdentityPenetration` is enabled, cluster-gateway will +recognize the identity in the incoming requests and use the [impersonation mechanism](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#user-impersonation) +to send requests to managed clusters with identity impersonated. By default, +the impersonated identity is consistent with the identity in the incoming requests. + +In the cases that the identity in different clusters are not aligned, the [ClientIdentityExchanger](https://github.com/oam-dev/cluster-gateway/issues/120) +feature would be helpful to make projections. You can use either the global configuration +or the cluster configuration for declaring the identity exchange rules, like the given +[example](https://github.com/oam-dev/cluster-gateway/tree/master/examples/client-identity-exchanger/config.yaml). +For global configuration, you need to set up the `--cluster-gateway-proxy-config=` +to enable it. For cluster configuration, you can set the annotation `cluster.core.oam.dev/cluster-gateway-proxy-configuration` +value to enable the configuration for the requests to the attached cluster. \ No newline at end of file diff --git a/cmd/apiserver/main.go b/cmd/apiserver/main.go index caeb13d5..23792c40 100644 --- a/cmd/apiserver/main.go +++ b/cmd/apiserver/main.go @@ -64,6 +64,9 @@ func main() { if err := config.ValidateClusterProxy(); err != nil { klog.Fatal(err) } + if err := clusterv1alpha1.LoadGlobalClusterGatewayProxyConfig(); err != nil { + klog.Fatal(err) + } return options }). WithPostStartHook("init-master-loopback-client", singleton.InitLoopbackClient). @@ -76,6 +79,7 @@ func main() { config.AddClusterProxyFlags(cmd.Flags()) config.AddProxyAuthorizationFlags(cmd.Flags()) config.AddUserAgentFlags(cmd.Flags()) + config.AddClusterGatewayProxyConfig(cmd.Flags()) cmd.Flags().BoolVarP(&options.OCMIntegration, "ocm-integration", "", false, "Enabling OCM integration, reading cluster CA and api endpoint from managed "+ "cluster.") diff --git a/e2e/benchmark/configmap.go b/e2e/benchmark/configmap.go index 67ae9581..0283b4f1 100644 --- a/e2e/benchmark/configmap.go +++ b/e2e/benchmark/configmap.go @@ -81,7 +81,6 @@ var _ = Describe("Basic RoundTrip Test", "shouldn't take too long.") }, 100) - Measure("get namespace kube-system from managed cluster", func(b Benchmarker) { runtime := b.Time("runtime", func() { _, err = multiClusterClient.CoreV1().Namespaces().Get( diff --git a/examples/client-identity-exchanger/config.yaml b/examples/client-identity-exchanger/config.yaml new file mode 100644 index 00000000..27c04d5c --- /dev/null +++ b/examples/client-identity-exchanger/config.yaml @@ -0,0 +1,16 @@ +apiVersion: cluster.core.oam.dev/v1alpha1 +kind: ClusterGatewayProxyConfiguration +spec: + clientIdentityExchanger: + rules: + - name: super-user + source: + group: sudoer + type: PrivilegedIdentityExchanger + - name: mapping + source: + user: user-12345 + cluster: cluster-34567 + target: + user: user-34567 + type: StaticMappingIdentityExchanger \ No newline at end of file diff --git a/pkg/apis/cluster/v1alpha1/clustergateway_proxy.go b/pkg/apis/cluster/v1alpha1/clustergateway_proxy.go index c4ebb10e..6803a6aa 100644 --- a/pkg/apis/cluster/v1alpha1/clustergateway_proxy.go +++ b/pkg/apis/cluster/v1alpha1/clustergateway_proxy.go @@ -26,6 +26,7 @@ import ( "time" utilfeature "k8s.io/apiserver/pkg/util/feature" + "k8s.io/klog/v2" "github.com/oam-dev/cluster-gateway/pkg/config" "github.com/oam-dev/cluster-gateway/pkg/featuregates" @@ -224,7 +225,7 @@ func (p *proxyHandler) ServeHTTP(writer http.ResponseWriter, request *http.Reque return } if p.impersonate || utilfeature.DefaultFeatureGate.Enabled(featuregates.ClientIdentityPenetration) { - cfg.Impersonate = getImpersonationConfig(request) + cfg.Impersonate = p.getImpersonationConfig(request) } rt, err := restclient.TransportFor(cfg) if err != nil { @@ -305,8 +306,26 @@ func (e ErrorResponderFunc) Error(w http.ResponseWriter, req *http.Request, err e(w, req, err) } -func getImpersonationConfig(req *http.Request) restclient.ImpersonationConfig { +func (p *proxyHandler) getImpersonationConfig(req *http.Request) restclient.ImpersonationConfig { user, _ := request.UserFrom(req.Context()) + if p.clusterGateway.Spec.ProxyConfig != nil { + matched, ruleName, projected, err := ExchangeIdentity(&p.clusterGateway.Spec.ProxyConfig.Spec.ClientIdentityExchanger, user, p.parentName) + if err != nil { + klog.Errorf("exchange identity with cluster config error: %w", err) + } + if matched { + klog.Infof("identity exchanged with rule `%s` in the proxy config from cluster `%s`", ruleName, p.clusterGateway.Name) + return *projected + } + } + matched, ruleName, projected, err := ExchangeIdentity(&GlobalClusterGatewayProxyConfiguration.Spec.ClientIdentityExchanger, user, p.parentName) + if err != nil { + klog.Errorf("exchange identity with global config error: %w", err) + } + if matched { + klog.Infof("identity exchanged with rule `%s` in the proxy config from global config", ruleName) + return *projected + } return restclient.ImpersonationConfig{ UserName: user.GetName(), Groups: user.GetGroups(), diff --git a/pkg/apis/cluster/v1alpha1/clustergateway_proxy_configuration.go b/pkg/apis/cluster/v1alpha1/clustergateway_proxy_configuration.go new file mode 100644 index 00000000..c1ad27e9 --- /dev/null +++ b/pkg/apis/cluster/v1alpha1/clustergateway_proxy_configuration.go @@ -0,0 +1,172 @@ +/* +Copyright 2022 The KubeVela Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "fmt" + "os" + "regexp" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/yaml" + "k8s.io/apiserver/pkg/authentication/user" + "k8s.io/client-go/rest" + "k8s.io/utils/strings/slices" + + "github.com/oam-dev/cluster-gateway/pkg/config" +) + +const ( + AnnotationClusterGatewayProxyConfiguration = "cluster.core.oam.dev/cluster-gateway-proxy-configuration" +) + +type ClusterGatewayProxyConfiguration struct { + metav1.TypeMeta `json:",inline"` + Spec ClusterGatewayProxyConfigurationSpec `json:"spec"` +} + +type ClusterGatewayProxyConfigurationSpec struct { + ClientIdentityExchanger `json:"clientIdentityExchanger"` +} + +type ClientIdentityExchanger struct { + Rules []ClientIdentityExchangeRule `json:"rules,omitempty"` +} + +type ClientIdentityExchangeType string + +const ( + PrivilegedIdentityExchanger ClientIdentityExchangeType = "PrivilegedIdentityExchanger" + StaticMappingIdentityExchanger ClientIdentityExchangeType = "StaticMappingIdentityExchanger" + ExternalIdentityExchanger ClientIdentityExchangeType = "ExternalIdentityExchanger" +) + +type ClientIdentityExchangeRule struct { + Name string `json:"name"` + Type ClientIdentityExchangeType `json:"type"` + Source *IdentityExchangerSource `json:"source"` + + Target *IdentityExchangerTarget `json:"target,omitempty"` + URL *string `json:"url,omitempty"` +} + +type IdentityExchangerTarget struct { + User string `json:"user,omitempty"` + Groups []string `json:"groups,omitempty"` + UID string `json:"uid,omitempty"` +} + +type IdentityExchangerSource struct { + User *string `json:"user,omitempty"` + Group *string `json:"group,omitempty"` + UID *string `json:"uid,omitempty"` + Cluster *string `json:"cluster,omitempty"` + + UserPattern *string `json:"userPattern,omitempty"` + GroupPattern *string `json:"groupPattern,omitempty"` + ClusterPattern *string `json:"clusterPattern,omitempty"` +} + +var GlobalClusterGatewayProxyConfiguration = &ClusterGatewayProxyConfiguration{} + +func LoadGlobalClusterGatewayProxyConfig() error { + if config.ClusterGatewayProxyConfigPath == "" { + return nil + } + bs, err := os.ReadFile(config.ClusterGatewayProxyConfigPath) + if err != nil { + return err + } + return yaml.Unmarshal(bs, GlobalClusterGatewayProxyConfiguration) +} + +func ExchangeIdentity(exchanger *ClientIdentityExchanger, userInfo user.Info, cluster string) (matched bool, ruleName string, projected *rest.ImpersonationConfig, err error) { + for _, rule := range exchanger.Rules { + if matched, projected, err = exchangeIdentity(&rule, userInfo, cluster); matched { + return matched, rule.Name, projected, err + } + } + return false, "", nil, nil +} + +func exchangeIdentity(rule *ClientIdentityExchangeRule, userInfo user.Info, cluster string) (matched bool, projected *rest.ImpersonationConfig, err error) { + if !matchIdentity(rule.Source, userInfo, cluster) { + return false, nil, nil + } + switch rule.Type { + case PrivilegedIdentityExchanger: + return true, &rest.ImpersonationConfig{}, nil + case StaticMappingIdentityExchanger: + return true, &rest.ImpersonationConfig{ + UserName: rule.Target.User, + Groups: rule.Target.Groups, + UID: rule.Target.UID, + }, nil + case ExternalIdentityExchanger: + return true, nil, fmt.Errorf("ExternalIdentityExchanger is not implemented") + } + return true, nil, fmt.Errorf("unknown exchanger type: %s", rule.Type) +} + +// denyQuery return true when the pattern is valid and could be used as regular expression, +// and the given query does not match the pattern, otherwise return false +func (in *IdentityExchangerSource) denyQuery(pattern *string, query string) bool { + if pattern == nil { + return false + } + matched, err := regexp.Match(*pattern, []byte(query)) + if err != nil { + return false + } + return !matched +} + +// denyGroups return true if none of the group matches the given pattern +func (in *IdentityExchangerSource) denyGroups(groupPattern *string, groups []string) bool { + if groupPattern == nil { + return false + } + for _, group := range groups { + if !in.denyQuery(groupPattern, group) { + return false + } + } + return true +} + +func matchIdentity(in *IdentityExchangerSource, userInfo user.Info, cluster string) bool { + if in == nil { + return false + } + switch { + case in.User != nil && userInfo.GetName() != *in.User: + return false + case in.Group != nil && !slices.Contains(userInfo.GetGroups(), *in.Group): + return false + case in.UID != nil && userInfo.GetUID() != *in.UID: + return false + case in.Cluster != nil && cluster != *in.Cluster: + return false + case in.denyQuery(in.UserPattern, userInfo.GetName()): + return false + case in.denyGroups(in.GroupPattern, userInfo.GetGroups()): + return false + case in.denyQuery(in.ClusterPattern, cluster): + return false + } + return true +} diff --git a/pkg/apis/cluster/v1alpha1/clustergateway_proxy_configuration_test.go b/pkg/apis/cluster/v1alpha1/clustergateway_proxy_configuration_test.go new file mode 100644 index 00000000..c145a4a6 --- /dev/null +++ b/pkg/apis/cluster/v1alpha1/clustergateway_proxy_configuration_test.go @@ -0,0 +1,172 @@ +/* +Copyright 2022 The KubeVela Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" + "k8s.io/apiserver/pkg/authentication/user" + "k8s.io/client-go/rest" + "k8s.io/utils/pointer" +) + +func TestExchangeIdentity(t *testing.T) { + testcases := map[string]struct { + Exchanger *ClientIdentityExchanger + UserInfo user.Info + Cluster string + Matched bool + RuleName string + Projected *rest.ImpersonationConfig + Error error + }{ + "match-user": { + Exchanger: &ClientIdentityExchanger{Rules: []ClientIdentityExchangeRule{{ + Name: "user-match", + Type: PrivilegedIdentityExchanger, + Source: &IdentityExchangerSource{User: pointer.String("test")}, + }}}, + UserInfo: &user.DefaultInfo{Name: "test"}, + Matched: true, + RuleName: "user-match", + Projected: &rest.ImpersonationConfig{}, + Error: nil, + }, + "match-group": { + Exchanger: &ClientIdentityExchanger{Rules: []ClientIdentityExchangeRule{{ + Name: "group-match", + Type: StaticMappingIdentityExchanger, + Source: &IdentityExchangerSource{Group: pointer.String("test-group")}, + Target: &IdentityExchangerTarget{Groups: []string{"projected"}}, + }}}, + UserInfo: &user.DefaultInfo{Name: "test", Groups: []string{"group", "test-group"}}, + Matched: true, + RuleName: "group-match", + Projected: &rest.ImpersonationConfig{Groups: []string{"projected"}}, + Error: nil, + }, + "match-uid": { + Exchanger: &ClientIdentityExchanger{Rules: []ClientIdentityExchangeRule{{ + Name: "uid-match", + Type: PrivilegedIdentityExchanger, + Source: &IdentityExchangerSource{UID: pointer.String("12345")}, + }}}, + UserInfo: &user.DefaultInfo{Name: "abc", UID: "12345"}, + Matched: true, + RuleName: "uid-match", + Projected: &rest.ImpersonationConfig{}, + Error: nil, + }, + "match-cluster": { + Exchanger: &ClientIdentityExchanger{Rules: []ClientIdentityExchangeRule{{ + Name: "name-match", + Type: PrivilegedIdentityExchanger, + Source: &IdentityExchangerSource{User: pointer.String("test"), Cluster: pointer.String("c1")}, + }, { + Name: "group-match", + Type: StaticMappingIdentityExchanger, + Source: &IdentityExchangerSource{Group: pointer.String("test-group"), Cluster: pointer.String("c2")}, + Target: &IdentityExchangerTarget{Groups: []string{"projected"}}, + }}}, + UserInfo: &user.DefaultInfo{Name: "test", Groups: []string{"group", "test-group"}}, + Cluster: "c2", + Matched: true, + RuleName: "group-match", + Projected: &rest.ImpersonationConfig{Groups: []string{"projected"}}, + Error: nil, + }, + "match-user-pattern": { + Exchanger: &ClientIdentityExchanger{Rules: []ClientIdentityExchangeRule{{ + Name: "user-pattern-match", + Type: PrivilegedIdentityExchanger, + Source: &IdentityExchangerSource{UserPattern: pointer.String("test-.*")}, + }}}, + UserInfo: &user.DefaultInfo{Name: "test-1234"}, + Matched: true, + RuleName: "user-pattern-match", + Projected: &rest.ImpersonationConfig{}, + Error: nil, + }, + "match-group-pattern": { + Exchanger: &ClientIdentityExchanger{Rules: []ClientIdentityExchangeRule{{ + Name: "group-pattern-match", + Type: StaticMappingIdentityExchanger, + Source: &IdentityExchangerSource{GroupPattern: pointer.String("test-group:.+")}, + Target: &IdentityExchangerTarget{Groups: []string{"projected"}}, + }}}, + UserInfo: &user.DefaultInfo{Name: "test", Groups: []string{"group:1", "test-group:2"}}, + Matched: true, + RuleName: "group-pattern-match", + Projected: &rest.ImpersonationConfig{Groups: []string{"projected"}}, + Error: nil, + }, + "match-cluster-pattern": { + Exchanger: &ClientIdentityExchanger{Rules: []ClientIdentityExchangeRule{{ + Name: "cluster-pattern-match", + Type: StaticMappingIdentityExchanger, + Source: &IdentityExchangerSource{ClusterPattern: pointer.String("cluster-\\d+")}, + Target: &IdentityExchangerTarget{User: "special"}, + }}}, + UserInfo: &user.DefaultInfo{Name: "test"}, + Cluster: "cluster-1", + Matched: true, + RuleName: "cluster-pattern-match", + Projected: &rest.ImpersonationConfig{UserName: "special"}, + Error: nil, + }, + "not-implemented": { + Exchanger: &ClientIdentityExchanger{Rules: []ClientIdentityExchangeRule{{ + Name: "external-identity-exchange", + Type: ExternalIdentityExchanger, + Source: &IdentityExchangerSource{ClusterPattern: pointer.String("cluster-\\d+")}, + URL: pointer.String("http://1.2.3.4:5000"), + }}}, + UserInfo: &user.DefaultInfo{Name: "test"}, + Cluster: "cluster-1", + Matched: true, + RuleName: "external-identity-exchange", + Projected: nil, + Error: fmt.Errorf("ExternalIdentityExchanger is not implemented"), + }, + "no-match": { + Exchanger: &ClientIdentityExchanger{Rules: []ClientIdentityExchangeRule{{ + Name: "cluster-pattern-match", + Type: StaticMappingIdentityExchanger, + Source: &IdentityExchangerSource{ClusterPattern: pointer.String("cluster-\\d+")}, + Target: &IdentityExchangerTarget{User: "special"}, + }}}, + UserInfo: &user.DefaultInfo{Name: "test"}, + Cluster: "cluster-other", + Matched: false, + }, + } + for name, tt := range testcases { + t.Run(name, func(t *testing.T) { + matched, ruleName, projected, err := ExchangeIdentity(tt.Exchanger, tt.UserInfo, tt.Cluster) + if tt.Error != nil { + require.Error(t, tt.Error, err) + return + } + require.NoError(t, err) + require.Equal(t, tt.Matched, matched) + require.Equal(t, tt.RuleName, ruleName) + require.Equal(t, tt.Projected, projected) + }) + } +} diff --git a/pkg/apis/cluster/v1alpha1/clustergateway_proxy_test.go b/pkg/apis/cluster/v1alpha1/clustergateway_proxy_test.go index 5e485dcd..0e3c9eb7 100644 --- a/pkg/apis/cluster/v1alpha1/clustergateway_proxy_test.go +++ b/pkg/apis/cluster/v1alpha1/clustergateway_proxy_test.go @@ -15,9 +15,11 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apiserver/pkg/authentication/user" "k8s.io/apiserver/pkg/endpoints/request" "k8s.io/apiserver/pkg/registry/rest" "k8s.io/apiserver/pkg/util/feature" + clientgorest "k8s.io/client-go/rest" "k8s.io/component-base/featuregate" k8stesting "k8s.io/component-base/featuregate/testing" "k8s.io/utils/pointer" @@ -191,3 +193,40 @@ func (f *fakeResponder) Object(statusCode int, obj runtime.Object) { func (f *fakeResponder) Error(err error) { f.receivingErr = err } + +func TestGetImpersonationConfig(t *testing.T) { + baseReq, err := http.NewRequest(http.MethodGet, "", nil) + require.NoError(t, err) + base := context.Background() + + GlobalClusterGatewayProxyConfiguration = &ClusterGatewayProxyConfiguration{ + Spec: ClusterGatewayProxyConfigurationSpec{ + ClientIdentityExchanger: ClientIdentityExchanger{Rules: []ClientIdentityExchangeRule{{ + Name: "name-matcher", + Type: StaticMappingIdentityExchanger, + Source: &IdentityExchangerSource{User: pointer.String("test")}, + Target: &IdentityExchangerTarget{User: "global"}, + }}}, + }, + } + + h := &proxyHandler{clusterGateway: &ClusterGateway{Spec: ClusterGatewaySpec{ProxyConfig: &ClusterGatewayProxyConfiguration{ + Spec: ClusterGatewayProxyConfigurationSpec{ + ClientIdentityExchanger: ClientIdentityExchanger{Rules: []ClientIdentityExchangeRule{{ + Name: "group-matcher", + Type: StaticMappingIdentityExchanger, + Source: &IdentityExchangerSource{Group: pointer.String("group")}, + Target: &IdentityExchangerTarget{User: "local"}, + }}}, + }, + }}}} + + ctx := request.WithUser(base, &user.DefaultInfo{Name: "test", Groups: []string{"group"}}) + require.Equal(t, clientgorest.ImpersonationConfig{UserName: "local"}, h.getImpersonationConfig(baseReq.WithContext(ctx))) + + ctx = request.WithUser(base, &user.DefaultInfo{Name: "test", Groups: []string{"group-test"}}) + require.Equal(t, clientgorest.ImpersonationConfig{UserName: "global"}, h.getImpersonationConfig(baseReq.WithContext(ctx))) + + ctx = request.WithUser(base, &user.DefaultInfo{Name: "tester", Groups: []string{"group-test"}}) + require.Equal(t, clientgorest.ImpersonationConfig{UserName: "tester", Groups: []string{"group-test"}}, h.getImpersonationConfig(baseReq.WithContext(ctx))) +} diff --git a/pkg/apis/cluster/v1alpha1/clustergateway_types.go b/pkg/apis/cluster/v1alpha1/clustergateway_types.go index defb536e..e3357c75 100644 --- a/pkg/apis/cluster/v1alpha1/clustergateway_types.go +++ b/pkg/apis/cluster/v1alpha1/clustergateway_types.go @@ -56,8 +56,9 @@ type ClusterGatewayList struct { // ClusterGatewaySpec defines the desired state of ClusterGateway type ClusterGatewaySpec struct { - Provider string `json:"provider"` - Access ClusterAccess `json:"access"` + Provider string `json:"provider"` + Access ClusterAccess `json:"access"` + ProxyConfig *ClusterGatewayProxyConfiguration `json:"-"` } type ClusterAccess struct { diff --git a/pkg/apis/cluster/v1alpha1/clustergateway_types_secret.go b/pkg/apis/cluster/v1alpha1/clustergateway_types_secret.go index c4ad8a4b..0e359794 100644 --- a/pkg/apis/cluster/v1alpha1/clustergateway_types_secret.go +++ b/pkg/apis/cluster/v1alpha1/clustergateway_types_secret.go @@ -6,6 +6,9 @@ import ( "strconv" "strings" + "k8s.io/apimachinery/pkg/util/yaml" + "k8s.io/utils/pointer" + "github.com/oam-dev/cluster-gateway/pkg/common" "github.com/oam-dev/cluster-gateway/pkg/config" "github.com/oam-dev/cluster-gateway/pkg/featuregates" @@ -255,5 +258,17 @@ func convert(caData []byte, apiServerEndpoint string, insecure bool, secret *v1. } } + if utilfeature.DefaultMutableFeatureGate.Enabled(featuregates.ClientIdentityPenetration) { + if proxyConfigRaw, ok := secret.Annotations[AnnotationClusterGatewayProxyConfiguration]; ok { + proxyConfig := &ClusterGatewayProxyConfiguration{} + if err := yaml.Unmarshal([]byte(proxyConfigRaw), proxyConfig); err == nil { + for _, rule := range proxyConfig.Spec.Rules { + rule.Source.Cluster = pointer.String(c.Name) + } + c.Spec.ProxyConfig = proxyConfig + } + } + } + return c, nil } diff --git a/pkg/apis/proxy/v1alpha1/groupversion_info.go b/pkg/apis/proxy/v1alpha1/groupversion_info.go index 1f4de00c..99dca250 100644 --- a/pkg/apis/proxy/v1alpha1/groupversion_info.go +++ b/pkg/apis/proxy/v1alpha1/groupversion_info.go @@ -15,8 +15,8 @@ limitations under the License. */ // Package v1alpha1 contains API Schema definitions for the proxy v1alpha1 API group -//+kubebuilder:object:generate=true -//+groupName=proxy.open-cluster-management.io +// +kubebuilder:object:generate=true +// +groupName=proxy.open-cluster-management.io package v1alpha1 import ( diff --git a/pkg/config/args_proxyconfig.go b/pkg/config/args_proxyconfig.go new file mode 100644 index 00000000..bd4a57a6 --- /dev/null +++ b/pkg/config/args_proxyconfig.go @@ -0,0 +1,28 @@ +/* +Copyright 2022 The KubeVela Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package config + +import ( + "github.com/spf13/pflag" +) + +var ClusterGatewayProxyConfigPath string + +func AddClusterGatewayProxyConfig(set *pflag.FlagSet) { + set.StringVarP(&ClusterGatewayProxyConfigPath, "cluster-gateway-proxy-config", "", "", + "the path for cluster-gateway proxy configuration") +} diff --git a/pkg/generated/clientset/versioned/scheme/register.go b/pkg/generated/clientset/versioned/scheme/register.go index 9495c4ac..d14aa4eb 100644 --- a/pkg/generated/clientset/versioned/scheme/register.go +++ b/pkg/generated/clientset/versioned/scheme/register.go @@ -34,14 +34,14 @@ var localSchemeBuilder = runtime.SchemeBuilder{ // AddToScheme adds all types of this clientset into the given scheme. This allows composition // of clientsets, like in: // -// import ( -// "k8s.io/client-go/kubernetes" -// clientsetscheme "k8s.io/client-go/kubernetes/scheme" -// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" -// ) +// import ( +// "k8s.io/client-go/kubernetes" +// clientsetscheme "k8s.io/client-go/kubernetes/scheme" +// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" +// ) // -// kclientset, _ := kubernetes.NewForConfig(c) -// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) +// kclientset, _ := kubernetes.NewForConfig(c) +// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) // // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types // correctly. diff --git a/pkg/util/singleton/loopback.go b/pkg/util/singleton/loopback.go index 681382f5..fe284264 100644 --- a/pkg/util/singleton/loopback.go +++ b/pkg/util/singleton/loopback.go @@ -3,6 +3,8 @@ package singleton import ( "time" + controllerruntimeconfig "sigs.k8s.io/controller-runtime/pkg/client/config" + "github.com/oam-dev/cluster-gateway/pkg/config" "github.com/oam-dev/cluster-gateway/pkg/featuregates" "github.com/oam-dev/cluster-gateway/pkg/util/cert" @@ -48,7 +50,13 @@ func GetKubeClient() kubernetes.Interface { func InitLoopbackClient(ctx server.PostStartHookContext) error { var err error - copiedCfg := clientgorest.CopyConfig(loopback.GetLoopbackMasterClientConfig()) + cfg := loopback.GetLoopbackMasterClientConfig() + if cfg == nil { + if cfg, err = controllerruntimeconfig.GetConfig(); err != nil { + return err + } + } + copiedCfg := clientgorest.CopyConfig(cfg) copiedCfg.RateLimiter = nil kubeClient, err = kubernetes.NewForConfig(copiedCfg) if err != nil {