Merge pull request #88769 from deads2k/SNI

Support TLS Server Name overrides in kubeconfig file
This commit is contained in:
Kubernetes Prow Robot 2020-03-03 23:18:00 -08:00 committed by GitHub
commit bd6640a8e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 112 additions and 5 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

@ -233,6 +233,7 @@ func Convert_api_AuthProviderConfig_To_v1_AuthProviderConfig(in *api.AuthProvide
func autoConvert_v1_Cluster_To_api_Cluster(in *Cluster, out *api.Cluster, s conversion.Scope) error {
out.Server = in.Server
out.TLSServerName = in.TLSServerName
out.InsecureSkipTLSVerify = in.InsecureSkipTLSVerify
out.CertificateAuthority = in.CertificateAuthority
out.CertificateAuthorityData = *(*[]byte)(unsafe.Pointer(&in.CertificateAuthorityData))
@ -250,6 +251,7 @@ func Convert_v1_Cluster_To_api_Cluster(in *Cluster, out *api.Cluster, s conversi
func autoConvert_api_Cluster_To_v1_Cluster(in *api.Cluster, out *Cluster, s conversion.Scope) error {
// INFO: in.LocationOfOrigin opted out of conversion generation
out.Server = in.Server
out.TLSServerName = in.TLSServerName
out.InsecureSkipTLSVerify = in.InsecureSkipTLSVerify
out.CertificateAuthority = in.CertificateAuthority
out.CertificateAuthorityData = *(*[]byte)(unsafe.Pointer(&in.CertificateAuthorityData))

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,14 @@ func (config *DirectClientConfig) getCluster() (clientcmdapi.Cluster, error) {
mergedClusterInfo.CertificateAuthorityData = config.overrides.ClusterInfo.CertificateAuthorityData
}
// if the --tls-server-name has been set in overrides, use that value.
// if the --server has been set in overrides, then use the value of --tls-server-name specified on the CLI too. This gives the property
// that setting a --server will effectively clear the KUBECONFIG value of tls-server-name if it is specified on the command line which is
// usually correct.
if config.overrides.ClusterInfo.TLSServerName != "" || config.overrides.ClusterInfo.Server != "" {
mergedClusterInfo.TLSServerName = config.overrides.ClusterInfo.TLSServerName
}
return *mergedClusterInfo, nil
}

View File

@ -180,6 +180,42 @@ 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 TestTLSServerNameClearsWhenServerNameSet(t *testing.T) {
config := createValidTestConfig()
clientBuilder := NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{
ClusterInfo: clientcmdapi.Cluster{
Server: "http://something",
},
}, nil)
actualCfg, err := clientBuilder.ClientConfig()
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
matchStringArg("", actualCfg.ServerName, t)
}
func TestMergeContext(t *testing.T) {
const namespace = "overridden-namespace"
@ -411,6 +447,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 +498,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 +515,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")
@ -120,6 +124,12 @@ func (o *createClusterOptions) modifyCluster(existingCluster clientcmdapi.Cluste
if o.server.Provided() {
modifiedCluster.Server = o.server.Value()
// specifying a --server on the command line, overrides the TLSServerName that was specified in the kubeconfig file.
// if both are specified, then the next if block will write the new TLSServerName.
modifiedCluster.TLSServerName = ""
}
if o.tlsServerName.Provided() {
modifiedCluster.TLSServerName = o.tlsServerName.Value()
}
if o.insecureSkipTLSVerify.Provided() {
modifiedCluster.InsecureSkipTLSVerify = o.insecureSkipTLSVerify.Value()

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"},
},
},
}
@ -57,7 +58,7 @@ func TestCreateCluster(t *testing.T) {
func TestModifyCluster(t *testing.T) {
conf := clientcmdapi.Config{
Clusters: map[string]*clientcmdapi.Cluster{
"my-cluster": {Server: "https://192.168.0.1"},
"my-cluster": {Server: "https://192.168.0.1", TLSServerName: "to-be-cleared"},
},
}
test := createClusterTest{
@ -77,6 +78,30 @@ func TestModifyCluster(t *testing.T) {
test.run(t)
}
func TestModifyClusterServerAndTLS(t *testing.T) {
conf := clientcmdapi.Config{
Clusters: map[string]*clientcmdapi.Cluster{
"my-cluster": {Server: "https://192.168.0.1"},
},
}
test := createClusterTest{
description: "Testing 'kubectl config set-cluster' with an existing cluster",
config: conf,
args: []string{"my-cluster"},
flags: []string{
"--server=https://192.168.0.99",
"--tls-server-name=my-cluster-name",
},
expected: `Cluster "my-cluster" set.` + "\n",
expectedConfig: clientcmdapi.Config{
Clusters: map[string]*clientcmdapi.Cluster{
"my-cluster": {Server: "https://192.168.0.99", TLSServerName: "my-cluster-name"},
},
},
}
test.run(t)
}
func (test createClusterTest) run(t *testing.T) {
fakeKubeFile, err := ioutil.TempFile(os.TempDir(), "")
if err != nil {
@ -115,5 +140,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 %q\n but got %q\n ", test.description, test.expectedConfig.Clusters[test.args[0]].TLSServerName, cluster.TLSServerName)
}
}
}