From d8da94337534b63d31667635f6c2b9d082568f8b Mon Sep 17 00:00:00 2001 From: cb <chriswbell@gmail.com> Date: Fri, 28 Apr 2023 16:50:16 -0700 Subject: [PATCH] Support TLS Server Name overrides in kubeconfig (#1282) The client should support tls-server-name just like client-go and kubectl. See https://github.com/kubernetes/kubernetes/pull/88769 --- .../KubeConfigModels/ClusterEndpoint.cs | 6 +++++ src/KubernetesClient/Kubernetes.ConfigInit.cs | 3 +++ src/KubernetesClient/Kubernetes.cs | 5 +++++ ...ubernetesClientConfiguration.ConfigFile.cs | 1 + .../KubernetesClientConfiguration.cs | 5 +++++ .../KubernetesClientConfigurationTests.cs | 11 ++++++++++ .../assets/kubeconfig.tls-servername.yml | 22 +++++++++++++++++++ 7 files changed, 53 insertions(+) create mode 100644 tests/KubernetesClient.Tests/assets/kubeconfig.tls-servername.yml diff --git a/src/KubernetesClient.Models/KubeConfigModels/ClusterEndpoint.cs b/src/KubernetesClient.Models/KubeConfigModels/ClusterEndpoint.cs index 4838d0083..cdd791b4a 100644 --- a/src/KubernetesClient.Models/KubeConfigModels/ClusterEndpoint.cs +++ b/src/KubernetesClient.Models/KubeConfigModels/ClusterEndpoint.cs @@ -25,6 +25,12 @@ public class ClusterEndpoint [YamlMember(Alias = "server")] public string Server { get; set; } + /// <summary> + /// Gets or sets a value to override the TLS server name. + /// </summary> + [YamlMember(Alias = "tls-server-name", ApplyNamingConventions = false)] + public string TlsServerName { get; set; } + /// <summary> /// Gets or sets a value indicating whether to skip the validity check for the server's certificate. /// This will make your HTTPS connections insecure. diff --git a/src/KubernetesClient/Kubernetes.ConfigInit.cs b/src/KubernetesClient/Kubernetes.ConfigInit.cs index 7fc808b9f..ac81fbd93 100644 --- a/src/KubernetesClient/Kubernetes.ConfigInit.cs +++ b/src/KubernetesClient/Kubernetes.ConfigInit.cs @@ -26,6 +26,7 @@ public Kubernetes(KubernetesClientConfiguration config, params DelegatingHandler ValidateConfig(config); CaCerts = config.SslCaCerts; SkipTlsVerify = config.SkipTlsVerify; + TlsServerName = config.TlsServerName; CreateHttpClient(handlers, config); InitializeFromConfig(config); HttpClientTimeout = config.HttpClientTimeout; @@ -115,6 +116,8 @@ private void InitializeFromConfig(KubernetesClientConfiguration config) private bool SkipTlsVerify { get; } + private string TlsServerName { get; } + // NOTE: this method replicates the logic that the base ServiceClient uses except that it doesn't insert the RetryDelegatingHandler // and it does insert the WatcherDelegatingHandler. we don't want the RetryDelegatingHandler because it has a very broad definition // of what requests have failed. it considers everything outside 2xx to be failed, including 1xx (e.g. 101 Switching Protocols) and diff --git a/src/KubernetesClient/Kubernetes.cs b/src/KubernetesClient/Kubernetes.cs index bbbe70ee8..6b9972485 100644 --- a/src/KubernetesClient/Kubernetes.cs +++ b/src/KubernetesClient/Kubernetes.cs @@ -149,6 +149,11 @@ protected virtual async Task<HttpResponseMessage> SendRequestRaw(string requestC await Credentials.ProcessHttpRequestAsync(httpRequest, cancellationToken).ConfigureAwait(false); } + if (!string.IsNullOrWhiteSpace(TlsServerName)) + { + httpRequest.Headers.Host = TlsServerName; + } + // Send Request cancellationToken.ThrowIfCancellationRequested(); var httpResponse = await HttpClient.SendAsync(httpRequest, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); diff --git a/src/KubernetesClient/KubernetesClientConfiguration.ConfigFile.cs b/src/KubernetesClient/KubernetesClientConfiguration.ConfigFile.cs index a1cf39740..9d439ad78 100644 --- a/src/KubernetesClient/KubernetesClientConfiguration.ConfigFile.cs +++ b/src/KubernetesClient/KubernetesClientConfiguration.ConfigFile.cs @@ -267,6 +267,7 @@ private void SetClusterDetails(K8SConfiguration k8SConfig, Context activeContext Host = clusterDetails.ClusterEndpoint.Server; SkipTlsVerify = clusterDetails.ClusterEndpoint.SkipTlsVerify; + TlsServerName = clusterDetails.ClusterEndpoint.TlsServerName; if (!Uri.TryCreate(Host, UriKind.Absolute, out var uri)) { diff --git a/src/KubernetesClient/KubernetesClientConfiguration.cs b/src/KubernetesClient/KubernetesClientConfiguration.cs index 61e0d061b..ac4a66719 100644 --- a/src/KubernetesClient/KubernetesClientConfiguration.cs +++ b/src/KubernetesClient/KubernetesClientConfiguration.cs @@ -56,6 +56,11 @@ public partial class KubernetesClientConfiguration /// </summary> public bool SkipTlsVerify { get; set; } + /// <summary> + /// Option to override the TLS server name + /// </summary> + public string TlsServerName { get; set; } + /// <summary> /// Gets or sets the HTTP user agent. /// </summary> diff --git a/tests/KubernetesClient.Tests/KubernetesClientConfigurationTests.cs b/tests/KubernetesClient.Tests/KubernetesClientConfigurationTests.cs index 976844836..8f491a8ed 100644 --- a/tests/KubernetesClient.Tests/KubernetesClientConfigurationTests.cs +++ b/tests/KubernetesClient.Tests/KubernetesClientConfigurationTests.cs @@ -341,6 +341,17 @@ public void SmartSkipTlsVerify() Assert.Equal("http://horse.org", cfg.Host); } + /// <summary> + /// Make sure that TlsServerName is present + /// </summary> + [Fact] + public void TlsServerName() + { + var fi = new FileInfo("assets/kubeconfig.tls-servername.yml"); + var cfg = KubernetesClientConfiguration.BuildConfigFromConfigFile(fi); + Assert.Equal("pony", cfg.TlsServerName); + } + /// <summary> /// Checks config could work well when current-context is not set but masterUrl is set. #issue 24 /// </summary> diff --git a/tests/KubernetesClient.Tests/assets/kubeconfig.tls-servername.yml b/tests/KubernetesClient.Tests/assets/kubeconfig.tls-servername.yml new file mode 100644 index 000000000..bf5ea1d3c --- /dev/null +++ b/tests/KubernetesClient.Tests/assets/kubeconfig.tls-servername.yml @@ -0,0 +1,22 @@ +# Sample file based on https://kubernetes.io/docs/tasks/access-application-cluster/authenticate-across-clusters-kubeconfig/ +# WARNING: File includes minor fixes +--- +current-context: federal-context +apiVersion: v1 +clusters: +- cluster: + server: https://horse.org:443 + tls-server-name: pony + name: horse-cluster +contexts: +- context: + cluster: horse-cluster + namespace: chisel-ns + user: green-user + name: federal-context +kind: Config +users: +- name: green-user + user: + password: secret + username: admin