From 1660b3676beeadbee1ce2b1d93c2d7f1d4fbcdb4 Mon Sep 17 00:00:00 2001 From: Harry Li Date: Tue, 24 Dec 2024 11:53:32 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=E2=9C=A8=20skip=20TLS=20certificat?= =?UTF-8?q?e=20verification=20when=20adding=20kubeconfig?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/add.go | 29 ++++++++++++++++++++++------- cmd/cloud_add.go | 19 ++++++++++--------- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/cmd/add.go b/cmd/add.go index 7044a33a..8d9568de 100644 --- a/cmd/add.go +++ b/cmd/add.go @@ -22,8 +22,9 @@ type AddCommand struct { // KubeConfigOption kubeConfig option type KubeConfigOption struct { - config *clientcmdapi.Config - fileName string + config *clientcmdapi.Config + fileName string + insecureSkipTLSVerify bool } // Init AddCommand @@ -45,6 +46,7 @@ func (ac *AddCommand) Init() { ac.command.Flags().String("context-name", "", "override context name when add kubeconfig context, when context-name is set, context-prefix and context-template parameters will be ignored") ac.command.Flags().StringSlice("context-template", []string{"context"}, "define the attributes used for composing the context name, available values: filename, user, cluster, context, namespace") ac.command.Flags().Bool("select-context", false, "select the context to be added in interactive mode") + ac.command.Flags().Bool("insecure-skip-tls-verify", false, "if true, the server's certificate will not be checked for validity") _ = ac.command.MarkFlagRequired("file") ac.AddCommands(&DocsCommand{}) } @@ -57,6 +59,7 @@ func (ac *AddCommand) runAdd(cmd *cobra.Command, args []string) error { contextName, _ := ac.command.Flags().GetString("context-name") contextTemplate, _ := ac.command.Flags().GetStringSlice("context-template") selectContext, _ := ac.command.Flags().GetBool("select-context") + insecureSkipTLSVerify, _ := ac.command.Flags().GetBool("insecure-skip-tls-verify") var newConfig *clientcmdapi.Config @@ -91,7 +94,7 @@ func (ac *AddCommand) runAdd(cmd *cobra.Command, args []string) error { } } - err = AddToLocal(newConfig, file, contextPrefix, cover, selectContext, contextTemplate, context) + err = AddToLocal(newConfig, file, contextPrefix, cover, selectContext, contextTemplate, context, insecureSkipTLSVerify) if err != nil { return err } @@ -99,14 +102,15 @@ func (ac *AddCommand) runAdd(cmd *cobra.Command, args []string) error { } // AddToLocal add kubeConfig to local -func AddToLocal(newConfig *clientcmdapi.Config, path, contextPrefix string, cover bool, selectContext bool, contextTemplate []string, context []string) error { +func AddToLocal(newConfig *clientcmdapi.Config, path, contextPrefix string, cover bool, selectContext bool, contextTemplate []string, context []string, insecureSkipTLSVerify bool) error { oldConfig, err := clientcmd.LoadFromFile(cfgFile) if err != nil { return err } kco := &KubeConfigOption{ - config: newConfig, - fileName: getFileName(path), + config: newConfig, + fileName: getFileName(path), + insecureSkipTLSVerify: insecureSkipTLSVerify, } // merge context loop outConfig, err := kco.handleContexts(oldConfig, contextPrefix, selectContext, contextTemplate, context) @@ -253,8 +257,17 @@ func (kc *KubeConfigOption) handleContext(oldConfig *clientcmdapi.Config, userName := fmt.Sprintf("%v%v", ctx.AuthInfo, userNameSuffix) clusterName := fmt.Sprintf("%v%v", ctx.Cluster, clusterNameSuffix) newCtx := ctx.DeepCopy() + + // deep copy and clear CA data + cluster := kc.config.Clusters[newCtx.Cluster].DeepCopy() + if kc.insecureSkipTLSVerify { + cluster.InsecureSkipTLSVerify = true + cluster.CertificateAuthority = "" + cluster.CertificateAuthorityData = nil + } + newConfig.AuthInfos[userName] = kc.config.AuthInfos[newCtx.AuthInfo] - newConfig.Clusters[clusterName] = kc.config.Clusters[newCtx.Cluster] + newConfig.Clusters[clusterName] = cluster newConfig.Contexts[name] = newCtx newConfig.Contexts[name].AuthInfo = userName newConfig.Contexts[name].Cluster = clusterName @@ -280,5 +293,7 @@ kubecm add -f test.yaml --select-context kubecm add -f test.yaml --context context1,context2 # Add kubeconfig from stdin cat /etc/kubernetes/admin.conf | kubecm add -f - +# Merge test.yaml with $HOME/.kube/config and skip TLS certificate verification +kubecm add -f test.yaml --insecure-skip-tls-verify ` } diff --git a/cmd/cloud_add.go b/cmd/cloud_add.go index 687e0f46..ec59e689 100644 --- a/cmd/cloud_add.go +++ b/cmd/cloud_add.go @@ -39,6 +39,7 @@ func (ca *CloudAddCommand) runCloudAdd(cmd *cobra.Command, args []string) error context, _ := ca.command.Flags().GetStringSlice("context") selectContext, _ := ca.command.Flags().GetBool("select-context") contextTemplate, _ := ca.command.Flags().GetStringSlice("context-template") + insecureSkipTLSVerify, _ := ca.command.Flags().GetBool("insecure-skip-tls-verify") var num int if provider == "" { num = selectCloud(Clouds, "Select Cloud") @@ -77,7 +78,7 @@ func (ca *CloudAddCommand) runCloudAdd(cmd *cobra.Command, args []string) error if err != nil { return err } - err = AddToLocal(newConfig, clusters[clusterNum].Name, "", cover, selectContext, contextTemplate, context) + err = AddToLocal(newConfig, clusters[clusterNum].Name, "", cover, selectContext, contextTemplate, context, insecureSkipTLSVerify) if err != nil { return err } @@ -90,7 +91,7 @@ func (ca *CloudAddCommand) runCloudAdd(cmd *cobra.Command, args []string) error if err != nil { return err } - err = AddToLocal(newConfig, fmt.Sprintf("alicloud-%s", clusterID), "", cover, selectContext, contextTemplate, context) + err = AddToLocal(newConfig, fmt.Sprintf("alicloud-%s", clusterID), "", cover, selectContext, contextTemplate, context, insecureSkipTLSVerify) if err != nil { return err } @@ -130,7 +131,7 @@ func (ca *CloudAddCommand) runCloudAdd(cmd *cobra.Command, args []string) error if err != nil { return err } - err = AddToLocal(newConfig, clusters[clusterNum].Name, "", cover, selectContext, contextTemplate, context) + err = AddToLocal(newConfig, clusters[clusterNum].Name, "", cover, selectContext, contextTemplate, context, insecureSkipTLSVerify) if err != nil { return err } @@ -143,7 +144,7 @@ func (ca *CloudAddCommand) runCloudAdd(cmd *cobra.Command, args []string) error if err != nil { return err } - err = AddToLocal(newConfig, fmt.Sprintf("tencent-%s", clusterID), "", cover, selectContext, contextTemplate, context) + err = AddToLocal(newConfig, fmt.Sprintf("tencent-%s", clusterID), "", cover, selectContext, contextTemplate, context, insecureSkipTLSVerify) if err != nil { return err } @@ -172,7 +173,7 @@ func (ca *CloudAddCommand) runCloudAdd(cmd *cobra.Command, args []string) error if err != nil { return err } - err = AddToLocal(newConfig, clusters[clusterNum].Name, "", cover, selectContext, contextTemplate, context) + err = AddToLocal(newConfig, clusters[clusterNum].Name, "", cover, selectContext, contextTemplate, context, insecureSkipTLSVerify) if err != nil { return err } @@ -185,7 +186,7 @@ func (ca *CloudAddCommand) runCloudAdd(cmd *cobra.Command, args []string) error if err != nil { return err } - err = AddToLocal(newConfig, fmt.Sprintf("rancher-%s", clusterID), "", cover, selectContext, contextTemplate, context) + err = AddToLocal(newConfig, fmt.Sprintf("rancher-%s", clusterID), "", cover, selectContext, contextTemplate, context, insecureSkipTLSVerify) if err != nil { return err } @@ -222,7 +223,7 @@ func (ca *CloudAddCommand) runCloudAdd(cmd *cobra.Command, args []string) error if err != nil { return err } - err = AddToLocal(newConfig, fmt.Sprintf("aws-%s", clusterID), "", cover, selectContext, contextTemplate, context) + err = AddToLocal(newConfig, fmt.Sprintf("aws-%s", clusterID), "", cover, selectContext, contextTemplate, context, insecureSkipTLSVerify) if err != nil { return err } @@ -282,7 +283,7 @@ func (ca *CloudAddCommand) runCloudAdd(cmd *cobra.Command, args []string) error if err != nil { return err } - return AddToLocal(newConfig, fmt.Sprintf("azure-%s", clusterID), "", cover, selectContext, contextTemplate, context) + return AddToLocal(newConfig, fmt.Sprintf("azure-%s", clusterID), "", cover, selectContext, contextTemplate, context, insecureSkipTLSVerify) } subscriptionList, err := azure.ListSubscriptions() @@ -335,7 +336,7 @@ func (ca *CloudAddCommand) runCloudAdd(cmd *cobra.Command, args []string) error if err != nil { return err } - return AddToLocal(newConfig, fmt.Sprintf("azure-%s", clusterID), "", cover, selectContext, contextTemplate, context) + return AddToLocal(newConfig, fmt.Sprintf("azure-%s", clusterID), "", cover, selectContext, contextTemplate, context, insecureSkipTLSVerify) } return nil From ac71185147d47c2a4cca1e31abe0bcf7fe3a3a04 Mon Sep 17 00:00:00 2001 From: Harry Li Date: Tue, 24 Dec 2024 16:15:55 +0800 Subject: [PATCH 2/2] test: Add skip TLS certificate verification case --- cmd/add_test.go | 122 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 121 insertions(+), 1 deletion(-) diff --git a/cmd/add_test.go b/cmd/add_test.go index e6ddc91f..b0b79053 100644 --- a/cmd/add_test.go +++ b/cmd/add_test.go @@ -351,7 +351,7 @@ func TestAddToLocal(t *testing.T) { } // Test AddToLocal function - err = AddToLocal(newConfig, tempFile.Name(), "", true, false, []string{"context"}, []string{}) + err = AddToLocal(newConfig, tempFile.Name(), "", true, false, []string{"context"}, []string{}, false) if err != nil { t.Fatalf("Failed to add to local: %v", err) } @@ -447,3 +447,123 @@ func TestGenerateContextName(t *testing.T) { }) } } + +func TestAddToLocal_InsecureSkipTLSVerify(t *testing.T) { + oldCfg := clientcmdapi.Config{ + Contexts: map[string]*clientcmdapi.Context{ + "old-context": {AuthInfo: "old-user", Cluster: "old-cluster"}, + }, + AuthInfos: map[string]*clientcmdapi.AuthInfo{ + "old-user": {}, + }, + Clusters: map[string]*clientcmdapi.Cluster{ + "old-cluster": {Server: "https://old.example.org"}, + }, + CurrentContext: "old-context", + } + + oldFile, err := os.CreateTemp("", "old-kubeconfig-*.yaml") + if err != nil { + t.Fatalf("failed to create temp file for old config: %v", err) + } + defer os.Remove(oldFile.Name()) + defer oldFile.Close() + + if err := clientcmd.WriteToFile(oldCfg, oldFile.Name()); err != nil { + t.Fatalf("failed to write old config to file: %v", err) + } + + cfgFile = oldFile.Name() + + newCfg := &clientcmdapi.Config{ + Clusters: map[string]*clientcmdapi.Cluster{ + "test-cluster": { + Server: "https://test.example.org", + CertificateAuthority: "/fake/ca/path", + CertificateAuthorityData: []byte("fake-ca-data"), + InsecureSkipTLSVerify: false, + }, + }, + AuthInfos: map[string]*clientcmdapi.AuthInfo{ + "test-authinfo": {Token: "test-token"}, + }, + Contexts: map[string]*clientcmdapi.Context{ + "test-context": { + AuthInfo: "test-authinfo", + Cluster: "test-cluster", + Namespace: "test-namespace", + }, + }, + CurrentContext: "test-context", + } + + tests := []struct { + name string + insecureSkipTLSVerify bool + wantInsecureSkipTLS bool + wantCertificateAuthNil bool + }{ + { + name: "InsecureSkipTLSVerify=false", + insecureSkipTLSVerify: false, + wantInsecureSkipTLS: false, + wantCertificateAuthNil: false, // dont clear CA + }, + { + name: "InsecureSkipTLSVerify=true", + insecureSkipTLSVerify: true, + wantInsecureSkipTLS: true, + wantCertificateAuthNil: true, // will clear CA + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := clientcmd.WriteToFile(oldCfg, oldFile.Name()); err != nil { + t.Fatalf("failed to re-write old config to file: %v", err) + } + + err = AddToLocal( + newCfg.DeepCopy(), + "fake-path", + "", + true, + false, + []string{"context"}, + []string{}, + tt.insecureSkipTLSVerify, + ) + if err != nil { + t.Fatalf("AddToLocal() failed: %v", err) + } + + merged, err := clientcmd.LoadFromFile(oldFile.Name()) + if err != nil { + t.Fatalf("failed to load config from file: %v", err) + } + + cluster, ok := merged.Clusters["test-cluster"] + if !ok { + t.Fatalf("cluster 'test-cluster' not found in merged config") + } + + if cluster.InsecureSkipTLSVerify != tt.wantInsecureSkipTLS { + t.Errorf("got InsecureSkipTLSVerify=%v, want %v", + cluster.InsecureSkipTLSVerify, tt.wantInsecureSkipTLS) + } + + if tt.wantCertificateAuthNil { + if cluster.CertificateAuthority != "" || len(cluster.CertificateAuthorityData) != 0 { + t.Errorf("CertificateAuthority/CertificateAuthorityData not cleared, got path=%q data=%q", + cluster.CertificateAuthority, string(cluster.CertificateAuthorityData)) + } + } else { + if cluster.CertificateAuthority != "/fake/ca/path" || + string(cluster.CertificateAuthorityData) != "fake-ca-data" { + t.Errorf("CertificateAuthority/CertificateAuthorityData changed unexpectedly, got path=%q data=%q", + cluster.CertificateAuthority, string(cluster.CertificateAuthorityData)) + } + } + }) + } +}