Skip to content

Commit bd6640a

Browse files
authored
Merge pull request kubernetes#88769 from deads2k/SNI
Support TLS Server Name overrides in kubeconfig file
2 parents 7624514 + 9dcbc0b commit bd6640a

File tree

9 files changed

+112
-5
lines changed

9 files changed

+112
-5
lines changed

staging/src/k8s.io/cli-runtime/pkg/genericclioptions/config_flags.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ const (
4141
flagContext = "context"
4242
flagNamespace = "namespace"
4343
flagAPIServer = "server"
44+
flagTLSServerName = "tls-server-name"
4445
flagInsecure = "insecure-skip-tls-verify"
4546
flagCertFile = "client-certificate"
4647
flagKeyFile = "client-key"
@@ -84,6 +85,7 @@ type ConfigFlags struct {
8485
Context *string
8586
Namespace *string
8687
APIServer *string
88+
TLSServerName *string
8789
Insecure *bool
8890
CertFile *string
8991
KeyFile *string
@@ -160,6 +162,9 @@ func (f *ConfigFlags) toRawKubeConfigLoader() clientcmd.ClientConfig {
160162
if f.APIServer != nil {
161163
overrides.ClusterInfo.Server = *f.APIServer
162164
}
165+
if f.TLSServerName != nil {
166+
overrides.ClusterInfo.TLSServerName = *f.TLSServerName
167+
}
163168
if f.CAFile != nil {
164169
overrides.ClusterInfo.CertificateAuthority = *f.CAFile
165170
}
@@ -294,6 +299,9 @@ func (f *ConfigFlags) AddFlags(flags *pflag.FlagSet) {
294299
if f.APIServer != nil {
295300
flags.StringVarP(f.APIServer, flagAPIServer, "s", *f.APIServer, "The address and port of the Kubernetes API server")
296301
}
302+
if f.TLSServerName != nil {
303+
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")
304+
}
297305
if f.Insecure != nil {
298306
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")
299307
}
@@ -329,6 +337,7 @@ func NewConfigFlags(usePersistentConfig bool) *ConfigFlags {
329337
Context: stringptr(""),
330338
Namespace: stringptr(""),
331339
APIServer: stringptr(""),
340+
TLSServerName: stringptr(""),
332341
CertFile: stringptr(""),
333342
KeyFile: stringptr(""),
334343
CAFile: stringptr(""),

staging/src/k8s.io/client-go/tools/clientcmd/api/types.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ type Cluster struct {
7070
LocationOfOrigin string
7171
// Server is the address of the kubernetes cluster (https://hostname:port).
7272
Server string `json:"server"`
73+
// TLSServerName is used to check server certificate. If TLSServerName is empty, the hostname used to contact the server is used.
74+
// +optional
75+
TLSServerName string `json:"tls-server-name,omitempty"`
7376
// InsecureSkipTLSVerify skips the validity check for the server's certificate. This will make your HTTPS connections insecure.
7477
// +optional
7578
InsecureSkipTLSVerify bool `json:"insecure-skip-tls-verify,omitempty"`

staging/src/k8s.io/client-go/tools/clientcmd/api/v1/types.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ type Preferences struct {
6363
type Cluster struct {
6464
// Server is the address of the kubernetes cluster (https://hostname:port).
6565
Server string `json:"server"`
66+
// TLSServerName is used to check server certificate. If TLSServerName is empty, the hostname used to contact the server is used.
67+
// +optional
68+
TLSServerName string `json:"tls-server-name,omitempty"`
6669
// InsecureSkipTLSVerify skips the validity check for the server's certificate. This will make your HTTPS connections insecure.
6770
// +optional
6871
InsecureSkipTLSVerify bool `json:"insecure-skip-tls-verify,omitempty"`

staging/src/k8s.io/client-go/tools/clientcmd/api/v1/zz_generated.conversion.go

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

staging/src/k8s.io/client-go/tools/clientcmd/client_config.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ func getServerIdentificationPartialConfig(configAuthInfo clientcmdapi.AuthInfo,
210210
configClientConfig.CAFile = configClusterInfo.CertificateAuthority
211211
configClientConfig.CAData = configClusterInfo.CertificateAuthorityData
212212
configClientConfig.Insecure = configClusterInfo.InsecureSkipTLSVerify
213+
configClientConfig.ServerName = configClusterInfo.TLSServerName
213214
mergo.MergeWithOverwrite(mergedConfig, configClientConfig)
214215

215216
return mergedConfig, nil
@@ -460,6 +461,14 @@ func (config *DirectClientConfig) getCluster() (clientcmdapi.Cluster, error) {
460461
mergedClusterInfo.CertificateAuthorityData = config.overrides.ClusterInfo.CertificateAuthorityData
461462
}
462463

464+
// if the --tls-server-name has been set in overrides, use that value.
465+
// 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
466+
// that setting a --server will effectively clear the KUBECONFIG value of tls-server-name if it is specified on the command line which is
467+
// usually correct.
468+
if config.overrides.ClusterInfo.TLSServerName != "" || config.overrides.ClusterInfo.Server != "" {
469+
mergedClusterInfo.TLSServerName = config.overrides.ClusterInfo.TLSServerName
470+
}
471+
463472
return *mergedClusterInfo, nil
464473
}
465474

staging/src/k8s.io/client-go/tools/clientcmd/client_config_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,42 @@ func TestCAOverridesCAData(t *testing.T) {
180180
matchByteArg(nil, actualCfg.TLSClientConfig.CAData, t)
181181
}
182182

183+
func TestTLSServerName(t *testing.T) {
184+
config := createValidTestConfig()
185+
186+
clientBuilder := NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{
187+
ClusterInfo: clientcmdapi.Cluster{
188+
TLSServerName: "overridden-server-name",
189+
},
190+
}, nil)
191+
192+
actualCfg, err := clientBuilder.ClientConfig()
193+
if err != nil {
194+
t.Errorf("Unexpected error: %v", err)
195+
}
196+
197+
matchStringArg("overridden-server-name", actualCfg.ServerName, t)
198+
matchStringArg("", actualCfg.TLSClientConfig.CAFile, t)
199+
matchByteArg(nil, actualCfg.TLSClientConfig.CAData, t)
200+
}
201+
202+
func TestTLSServerNameClearsWhenServerNameSet(t *testing.T) {
203+
config := createValidTestConfig()
204+
205+
clientBuilder := NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{
206+
ClusterInfo: clientcmdapi.Cluster{
207+
Server: "http://something",
208+
},
209+
}, nil)
210+
211+
actualCfg, err := clientBuilder.ClientConfig()
212+
if err != nil {
213+
t.Errorf("Unexpected error: %v", err)
214+
}
215+
216+
matchStringArg("", actualCfg.ServerName, t)
217+
}
218+
183219
func TestMergeContext(t *testing.T) {
184220
const namespace = "overridden-namespace"
185221

@@ -411,6 +447,7 @@ func TestCreateClean(t *testing.T) {
411447
matchStringArg("", clientConfig.APIPath, t)
412448
matchBoolArg(config.Clusters["clean"].InsecureSkipTLSVerify, clientConfig.Insecure, t)
413449
matchStringArg(config.AuthInfos["clean"].Token, clientConfig.BearerToken, t)
450+
matchStringArg(config.Clusters["clean"].TLSServerName, clientConfig.ServerName, t)
414451
}
415452

416453
func TestCreateCleanWithPrefix(t *testing.T) {
@@ -461,6 +498,7 @@ func TestCreateCleanDefault(t *testing.T) {
461498
}
462499

463500
matchStringArg(config.Clusters["clean"].Server, clientConfig.Host, t)
501+
matchStringArg(config.Clusters["clean"].TLSServerName, clientConfig.ServerName, t)
464502
matchBoolArg(config.Clusters["clean"].InsecureSkipTLSVerify, clientConfig.Insecure, t)
465503
matchStringArg(config.AuthInfos["clean"].Token, clientConfig.BearerToken, t)
466504
}
@@ -477,6 +515,7 @@ func TestCreateCleanDefaultCluster(t *testing.T) {
477515
}
478516

479517
matchStringArg(config.Clusters["clean"].Server, clientConfig.Host, t)
518+
matchStringArg(config.Clusters["clean"].TLSServerName, clientConfig.ServerName, t)
480519
matchBoolArg(config.Clusters["clean"].InsecureSkipTLSVerify, clientConfig.Insecure, t)
481520
matchStringArg(config.AuthInfos["clean"].Token, clientConfig.BearerToken, t)
482521
}

staging/src/k8s.io/client-go/tools/clientcmd/overrides.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ type ClusterOverrideFlags struct {
7171
APIVersion FlagInfo
7272
CertificateAuthority FlagInfo
7373
InsecureSkipTLSVerify FlagInfo
74+
TLSServerName FlagInfo
7475
}
7576

7677
// 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 (
145146
FlagContext = "context"
146147
FlagNamespace = "namespace"
147148
FlagAPIServer = "server"
149+
FlagTLSServerName = "tls-server-name"
148150
FlagInsecure = "insecure-skip-tls-verify"
149151
FlagCertFile = "client-certificate"
150152
FlagKeyFile = "client-key"
@@ -189,6 +191,7 @@ func RecommendedClusterOverrideFlags(prefix string) ClusterOverrideFlags {
189191
APIServer: FlagInfo{prefix + FlagAPIServer, "", "", "The address and port of the Kubernetes API server"},
190192
CertificateAuthority: FlagInfo{prefix + FlagCAFile, "", "", "Path to a cert file for the certificate authority"},
191193
InsecureSkipTLSVerify: FlagInfo{prefix + FlagInsecure, "", "false", "If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure"},
194+
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."},
192195
}
193196
}
194197

@@ -226,6 +229,7 @@ func BindClusterFlags(clusterInfo *clientcmdapi.Cluster, flags *pflag.FlagSet, f
226229
flagNames.APIServer.BindStringFlag(flags, &clusterInfo.Server)
227230
flagNames.CertificateAuthority.BindStringFlag(flags, &clusterInfo.CertificateAuthority)
228231
flagNames.InsecureSkipTLSVerify.BindBoolFlag(flags, &clusterInfo.InsecureSkipTLSVerify)
232+
flagNames.TLSServerName.BindStringFlag(flags, &clusterInfo.TLSServerName)
229233
}
230234

231235
// BindFlags is a convenience method to bind the specified flags to their associated variables

staging/src/k8s.io/kubectl/pkg/cmd/config/create_cluster.go

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import (
2424
"path/filepath"
2525

2626
"github.com/spf13/cobra"
27-
2827
"k8s.io/client-go/tools/clientcmd"
2928
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
3029
cliflag "k8s.io/component-base/cli/flag"
@@ -37,6 +36,7 @@ type createClusterOptions struct {
3736
configAccess clientcmd.ConfigAccess
3837
name string
3938
server cliflag.StringFlag
39+
tlsServerName cliflag.StringFlag
4040
insecureSkipTLSVerify cliflag.Tristate
4141
certificateAuthority cliflag.StringFlag
4242
embedCAData cliflag.Tristate
@@ -56,15 +56,18 @@ var (
5656
kubectl config set-cluster e2e --certificate-authority=~/.kube/e2e/kubernetes.ca.crt
5757
5858
# Disable cert checking for the dev cluster entry
59-
kubectl config set-cluster e2e --insecure-skip-tls-verify=true`)
59+
kubectl config set-cluster e2e --insecure-skip-tls-verify=true
60+
61+
# Set custom TLS server name to use for validation for the e2e cluster entry
62+
kubectl config set-cluster e2e --tls-server-name=my-cluster-name`)
6063
)
6164

6265
// NewCmdConfigSetCluster returns a Command instance for 'config set-cluster' sub command
6366
func NewCmdConfigSetCluster(out io.Writer, configAccess clientcmd.ConfigAccess) *cobra.Command {
6467
options := &createClusterOptions{configAccess: configAccess}
6568

6669
cmd := &cobra.Command{
67-
Use: fmt.Sprintf("set-cluster NAME [--%v=server] [--%v=path/to/certificate/authority] [--%v=true]", clientcmd.FlagAPIServer, clientcmd.FlagCAFile, clientcmd.FlagInsecure),
70+
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),
6871
DisableFlagsInUseLine: true,
6972
Short: i18n.T("Sets a cluster entry in kubeconfig"),
7073
Long: createClusterLong,
@@ -79,6 +82,7 @@ func NewCmdConfigSetCluster(out io.Writer, configAccess clientcmd.ConfigAccess)
7982
options.insecureSkipTLSVerify.Default(false)
8083

8184
cmd.Flags().Var(&options.server, clientcmd.FlagAPIServer, clientcmd.FlagAPIServer+" for the cluster entry in kubeconfig")
85+
cmd.Flags().Var(&options.tlsServerName, clientcmd.FlagTLSServerName, clientcmd.FlagTLSServerName+" for the cluster entry in kubeconfig")
8286
f := cmd.Flags().VarPF(&options.insecureSkipTLSVerify, clientcmd.FlagInsecure, "", clientcmd.FlagInsecure+" for the cluster entry in kubeconfig")
8387
f.NoOptDefVal = "true"
8488
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
120124

121125
if o.server.Provided() {
122126
modifiedCluster.Server = o.server.Value()
127+
// specifying a --server on the command line, overrides the TLSServerName that was specified in the kubeconfig file.
128+
// if both are specified, then the next if block will write the new TLSServerName.
129+
modifiedCluster.TLSServerName = ""
130+
}
131+
if o.tlsServerName.Provided() {
132+
modifiedCluster.TLSServerName = o.tlsServerName.Value()
123133
}
124134
if o.insecureSkipTLSVerify.Provided() {
125135
modifiedCluster.InsecureSkipTLSVerify = o.insecureSkipTLSVerify.Value()

staging/src/k8s.io/kubectl/pkg/cmd/config/create_cluster_test.go

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,12 @@ func TestCreateCluster(t *testing.T) {
4343
args: []string{"my-cluster"},
4444
flags: []string{
4545
"--server=http://192.168.0.1",
46+
"--tls-server-name=my-cluster-name",
4647
},
4748
expected: `Cluster "my-cluster" set.` + "\n",
4849
expectedConfig: clientcmdapi.Config{
4950
Clusters: map[string]*clientcmdapi.Cluster{
50-
"my-cluster": {Server: "http://192.168.0.1"},
51+
"my-cluster": {Server: "http://192.168.0.1", TLSServerName: "my-cluster-name"},
5152
},
5253
},
5354
}
@@ -57,7 +58,7 @@ func TestCreateCluster(t *testing.T) {
5758
func TestModifyCluster(t *testing.T) {
5859
conf := clientcmdapi.Config{
5960
Clusters: map[string]*clientcmdapi.Cluster{
60-
"my-cluster": {Server: "https://192.168.0.1"},
61+
"my-cluster": {Server: "https://192.168.0.1", TLSServerName: "to-be-cleared"},
6162
},
6263
}
6364
test := createClusterTest{
@@ -77,6 +78,30 @@ func TestModifyCluster(t *testing.T) {
7778
test.run(t)
7879
}
7980

81+
func TestModifyClusterServerAndTLS(t *testing.T) {
82+
conf := clientcmdapi.Config{
83+
Clusters: map[string]*clientcmdapi.Cluster{
84+
"my-cluster": {Server: "https://192.168.0.1"},
85+
},
86+
}
87+
test := createClusterTest{
88+
description: "Testing 'kubectl config set-cluster' with an existing cluster",
89+
config: conf,
90+
args: []string{"my-cluster"},
91+
flags: []string{
92+
"--server=https://192.168.0.99",
93+
"--tls-server-name=my-cluster-name",
94+
},
95+
expected: `Cluster "my-cluster" set.` + "\n",
96+
expectedConfig: clientcmdapi.Config{
97+
Clusters: map[string]*clientcmdapi.Cluster{
98+
"my-cluster": {Server: "https://192.168.0.99", TLSServerName: "my-cluster-name"},
99+
},
100+
},
101+
}
102+
test.run(t)
103+
}
104+
80105
func (test createClusterTest) run(t *testing.T) {
81106
fakeKubeFile, err := ioutil.TempFile(os.TempDir(), "")
82107
if err != nil {
@@ -115,5 +140,8 @@ func (test createClusterTest) run(t *testing.T) {
115140
if cluster.Server != test.expectedConfig.Clusters[test.args[0]].Server {
116141
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)
117142
}
143+
if cluster.TLSServerName != test.expectedConfig.Clusters[test.args[0]].TLSServerName {
144+
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)
145+
}
118146
}
119147
}

0 commit comments

Comments
 (0)