Support TLS Server Name overrides in kubeconfig file

Signed-off-by: Suresh Kumar Ponnusamy <suresh.ponnusamy@freshworks.com>
This commit is contained in:
Suresh Kumar Ponnusamy 2019-08-28 10:51:14 +05:30 committed by David Eads
parent 06b798781a
commit 37c81ed79a
8 changed files with 61 additions and 4 deletions

View File

@ -41,6 +41,7 @@ const (
flagContext = "context"
flagNamespace = "namespace"
flagAPIServer = "server"
flagTLSServerName = "tls-server-name"
flagInsecure = "insecure-skip-tls-verify"
flagCertFile = "client-certificate"
flagKeyFile = "client-key"
@ -84,6 +85,7 @@ type ConfigFlags struct {
Context *string
Namespace *string
APIServer *string
TLSServerName *string
Insecure *bool
CertFile *string
KeyFile *string
@ -160,6 +162,9 @@ func (f *ConfigFlags) toRawKubeConfigLoader() clientcmd.ClientConfig {
if f.APIServer != nil {
overrides.ClusterInfo.Server = *f.APIServer
}
if f.TLSServerName != nil {
overrides.ClusterInfo.TLSServerName = *f.TLSServerName
}
if f.CAFile != nil {
overrides.ClusterInfo.CertificateAuthority = *f.CAFile
}
@ -294,6 +299,9 @@ func (f *ConfigFlags) AddFlags(flags *pflag.FlagSet) {
if f.APIServer != nil {
flags.StringVarP(f.APIServer, flagAPIServer, "s", *f.APIServer, "The address and port of the Kubernetes API server")
}
if f.TLSServerName != nil {
flags.StringVar(f.TLSServerName, flagTLSServerName, *f.TLSServerName, "Server name to use for server certificate validation. If it is not provided, the hostname used to contact the server is used")
}
if f.Insecure != nil {
flags.BoolVar(f.Insecure, flagInsecure, *f.Insecure, "If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure")
}
@ -329,6 +337,7 @@ func NewConfigFlags(usePersistentConfig bool) *ConfigFlags {
Context: stringptr(""),
Namespace: stringptr(""),
APIServer: stringptr(""),
TLSServerName: stringptr(""),
CertFile: stringptr(""),
KeyFile: stringptr(""),
CAFile: stringptr(""),

View File

@ -70,6 +70,9 @@ type Cluster struct {
LocationOfOrigin string
// Server is the address of the kubernetes cluster (https://hostname:port).
Server string `json:"server"`
// TLSServerName is used to check server certificate. If TLSServerName is empty, the hostname used to contact the server is used.
// +optional
TLSServerName string `json:"tls-server-name,omitempty"`
// InsecureSkipTLSVerify skips the validity check for the server's certificate. This will make your HTTPS connections insecure.
// +optional
InsecureSkipTLSVerify bool `json:"insecure-skip-tls-verify,omitempty"`

View File

@ -63,6 +63,9 @@ type Preferences struct {
type Cluster struct {
// Server is the address of the kubernetes cluster (https://hostname:port).
Server string `json:"server"`
// TLSServerName is used to check server certificate. If TLSServerName is empty, the hostname used to contact the server is used.
// +optional
TLSServerName string `json:"tls-server-name,omitempty"`
// InsecureSkipTLSVerify skips the validity check for the server's certificate. This will make your HTTPS connections insecure.
// +optional
InsecureSkipTLSVerify bool `json:"insecure-skip-tls-verify,omitempty"`

View File

@ -210,6 +210,7 @@ func getServerIdentificationPartialConfig(configAuthInfo clientcmdapi.AuthInfo,
configClientConfig.CAFile = configClusterInfo.CertificateAuthority
configClientConfig.CAData = configClusterInfo.CertificateAuthorityData
configClientConfig.Insecure = configClusterInfo.InsecureSkipTLSVerify
configClientConfig.ServerName = configClusterInfo.TLSServerName
mergo.MergeWithOverwrite(mergedConfig, configClientConfig)
return mergedConfig, nil
@ -460,6 +461,10 @@ func (config *DirectClientConfig) getCluster() (clientcmdapi.Cluster, error) {
mergedClusterInfo.CertificateAuthorityData = config.overrides.ClusterInfo.CertificateAuthorityData
}
if config.overrides.ClusterInfo.TLSServerName != "" {
mergedClusterInfo.TLSServerName = config.overrides.ClusterInfo.TLSServerName
}
return *mergedClusterInfo, nil
}

View File

@ -180,6 +180,25 @@ func TestCAOverridesCAData(t *testing.T) {
matchByteArg(nil, actualCfg.TLSClientConfig.CAData, t)
}
func TestTLSServerName(t *testing.T) {
config := createValidTestConfig()
clientBuilder := NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{
ClusterInfo: clientcmdapi.Cluster{
TLSServerName: "overridden-server-name",
},
}, nil)
actualCfg, err := clientBuilder.ClientConfig()
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
matchStringArg("overridden-server-name", actualCfg.ServerName, t)
matchStringArg("", actualCfg.TLSClientConfig.CAFile, t)
matchByteArg(nil, actualCfg.TLSClientConfig.CAData, t)
}
func TestMergeContext(t *testing.T) {
const namespace = "overridden-namespace"
@ -411,6 +430,7 @@ func TestCreateClean(t *testing.T) {
matchStringArg("", clientConfig.APIPath, t)
matchBoolArg(config.Clusters["clean"].InsecureSkipTLSVerify, clientConfig.Insecure, t)
matchStringArg(config.AuthInfos["clean"].Token, clientConfig.BearerToken, t)
matchStringArg(config.Clusters["clean"].TLSServerName, clientConfig.ServerName, t)
}
func TestCreateCleanWithPrefix(t *testing.T) {
@ -461,6 +481,7 @@ func TestCreateCleanDefault(t *testing.T) {
}
matchStringArg(config.Clusters["clean"].Server, clientConfig.Host, t)
matchStringArg(config.Clusters["clean"].TLSServerName, clientConfig.ServerName, t)
matchBoolArg(config.Clusters["clean"].InsecureSkipTLSVerify, clientConfig.Insecure, t)
matchStringArg(config.AuthInfos["clean"].Token, clientConfig.BearerToken, t)
}
@ -477,6 +498,7 @@ func TestCreateCleanDefaultCluster(t *testing.T) {
}
matchStringArg(config.Clusters["clean"].Server, clientConfig.Host, t)
matchStringArg(config.Clusters["clean"].TLSServerName, clientConfig.ServerName, t)
matchBoolArg(config.Clusters["clean"].InsecureSkipTLSVerify, clientConfig.Insecure, t)
matchStringArg(config.AuthInfos["clean"].Token, clientConfig.BearerToken, t)
}

View File

@ -71,6 +71,7 @@ type ClusterOverrideFlags struct {
APIVersion FlagInfo
CertificateAuthority FlagInfo
InsecureSkipTLSVerify FlagInfo
TLSServerName FlagInfo
}
// FlagInfo contains information about how to register a flag. This struct is useful if you want to provide a way for an extender to
@ -145,6 +146,7 @@ const (
FlagContext = "context"
FlagNamespace = "namespace"
FlagAPIServer = "server"
FlagTLSServerName = "tls-server-name"
FlagInsecure = "insecure-skip-tls-verify"
FlagCertFile = "client-certificate"
FlagKeyFile = "client-key"
@ -189,6 +191,7 @@ func RecommendedClusterOverrideFlags(prefix string) ClusterOverrideFlags {
APIServer: FlagInfo{prefix + FlagAPIServer, "", "", "The address and port of the Kubernetes API server"},
CertificateAuthority: FlagInfo{prefix + FlagCAFile, "", "", "Path to a cert file for the certificate authority"},
InsecureSkipTLSVerify: FlagInfo{prefix + FlagInsecure, "", "false", "If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure"},
TLSServerName: FlagInfo{prefix + FlagTLSServerName, "", "", "If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used."},
}
}
@ -226,6 +229,7 @@ func BindClusterFlags(clusterInfo *clientcmdapi.Cluster, flags *pflag.FlagSet, f
flagNames.APIServer.BindStringFlag(flags, &clusterInfo.Server)
flagNames.CertificateAuthority.BindStringFlag(flags, &clusterInfo.CertificateAuthority)
flagNames.InsecureSkipTLSVerify.BindBoolFlag(flags, &clusterInfo.InsecureSkipTLSVerify)
flagNames.TLSServerName.BindStringFlag(flags, &clusterInfo.TLSServerName)
}
// BindFlags is a convenience method to bind the specified flags to their associated variables

View File

@ -24,7 +24,6 @@ import (
"path/filepath"
"github.com/spf13/cobra"
"k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
cliflag "k8s.io/component-base/cli/flag"
@ -37,6 +36,7 @@ type createClusterOptions struct {
configAccess clientcmd.ConfigAccess
name string
server cliflag.StringFlag
tlsServerName cliflag.StringFlag
insecureSkipTLSVerify cliflag.Tristate
certificateAuthority cliflag.StringFlag
embedCAData cliflag.Tristate
@ -56,7 +56,10 @@ var (
kubectl config set-cluster e2e --certificate-authority=~/.kube/e2e/kubernetes.ca.crt
# Disable cert checking for the dev cluster entry
kubectl config set-cluster e2e --insecure-skip-tls-verify=true`)
kubectl config set-cluster e2e --insecure-skip-tls-verify=true
# Set custom TLS server name to use for validation for the e2e cluster entry
kubectl config set-cluster e2e --tls-server-name=my-cluster-name`)
)
// NewCmdConfigSetCluster returns a Command instance for 'config set-cluster' sub command
@ -64,7 +67,7 @@ func NewCmdConfigSetCluster(out io.Writer, configAccess clientcmd.ConfigAccess)
options := &createClusterOptions{configAccess: configAccess}
cmd := &cobra.Command{
Use: fmt.Sprintf("set-cluster NAME [--%v=server] [--%v=path/to/certificate/authority] [--%v=true]", clientcmd.FlagAPIServer, clientcmd.FlagCAFile, clientcmd.FlagInsecure),
Use: fmt.Sprintf("set-cluster NAME [--%v=server] [--%v=path/to/certificate/authority] [--%v=true] [--%v=example.com]", clientcmd.FlagAPIServer, clientcmd.FlagCAFile, clientcmd.FlagInsecure, clientcmd.FlagTLSServerName),
DisableFlagsInUseLine: true,
Short: i18n.T("Sets a cluster entry in kubeconfig"),
Long: createClusterLong,
@ -79,6 +82,7 @@ func NewCmdConfigSetCluster(out io.Writer, configAccess clientcmd.ConfigAccess)
options.insecureSkipTLSVerify.Default(false)
cmd.Flags().Var(&options.server, clientcmd.FlagAPIServer, clientcmd.FlagAPIServer+" for the cluster entry in kubeconfig")
cmd.Flags().Var(&options.tlsServerName, clientcmd.FlagTLSServerName, clientcmd.FlagTLSServerName+" for the cluster entry in kubeconfig")
f := cmd.Flags().VarPF(&options.insecureSkipTLSVerify, clientcmd.FlagInsecure, "", clientcmd.FlagInsecure+" for the cluster entry in kubeconfig")
f.NoOptDefVal = "true"
cmd.Flags().Var(&options.certificateAuthority, clientcmd.FlagCAFile, "Path to "+clientcmd.FlagCAFile+" file for the cluster entry in kubeconfig")
@ -121,6 +125,9 @@ func (o *createClusterOptions) modifyCluster(existingCluster clientcmdapi.Cluste
if o.server.Provided() {
modifiedCluster.Server = o.server.Value()
}
if o.tlsServerName.Provided() {
modifiedCluster.TLSServerName = o.tlsServerName.Value()
}
if o.insecureSkipTLSVerify.Provided() {
modifiedCluster.InsecureSkipTLSVerify = o.insecureSkipTLSVerify.Value()
// Specifying insecure mode clears any certificate authority

View File

@ -43,11 +43,12 @@ func TestCreateCluster(t *testing.T) {
args: []string{"my-cluster"},
flags: []string{
"--server=http://192.168.0.1",
"--tls-server-name=my-cluster-name",
},
expected: `Cluster "my-cluster" set.` + "\n",
expectedConfig: clientcmdapi.Config{
Clusters: map[string]*clientcmdapi.Cluster{
"my-cluster": {Server: "http://192.168.0.1"},
"my-cluster": {Server: "http://192.168.0.1", TLSServerName: "my-cluster-name"},
},
},
}
@ -115,5 +116,8 @@ func (test createClusterTest) run(t *testing.T) {
if cluster.Server != test.expectedConfig.Clusters[test.args[0]].Server {
t.Errorf("Fail in %q\n expected cluster server %v\n but got %v\n ", test.description, test.expectedConfig.Clusters[test.args[0]].Server, cluster.Server)
}
if cluster.TLSServerName != test.expectedConfig.Clusters[test.args[0]].TLSServerName {
t.Errorf("Fail in %q\n expected cluster TLS server name %v\n but got %v\n ", test.description, test.expectedConfig.Clusters[test.args[0]].TLSServerName, cluster.TLSServerName)
}
}
}