Skip to content

Commit

Permalink
fix(client): Add logic to force TLSv1.3 connection.
Browse files Browse the repository at this point in the history
  • Loading branch information
spbsoluble committed Jun 6, 2024
1 parent 63c8298 commit 446d3f9
Show file tree
Hide file tree
Showing 3 changed files with 380 additions and 23 deletions.
115 changes: 92 additions & 23 deletions kubernetes-orchestrator-extension/Clients/KubeClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,14 @@
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Reflection;
using System.Security.Authentication;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using k8s;
using k8s.Autorest;
using k8s.Exceptions;
Expand All @@ -34,6 +38,21 @@

namespace Keyfactor.Extensions.Orchestrator.K8S.Clients;

public class CustomHttpClientHandler : DelegatingHandler
{
public CustomHttpClientHandler(HttpMessageHandler innerHandler)
: base(innerHandler)
{
}

protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
// You can add any custom logic here if needed
return await base.SendAsync(request, cancellationToken);
}
}

public class KubeCertificateManagerClient
{
private readonly ILogger _logger;
Expand All @@ -56,9 +75,37 @@ public KubeCertificateManagerClient(string kubeconfig, bool useSSL = true)
private string ConfigJson { get; set; }

private K8SConfiguration ConfigObj { get; }
private KubernetesClientConfiguration ClientConfig;

private IKubernetes Client { get; set; }

private void ClientTest()
{
_logger.LogTrace("Entered clientTest()");
_logger.LogDebug("Testing client connection by listing k8s namespaces");
var namespaces = Client.CoreV1.ListNamespace();
_logger.LogTrace("k8s namespaces: {Namespaces}", namespaces?.Items.ToString());
}

public void EnforceTLS_1_3()
{
_logger.LogDebug("Entered EnforceTLS_1_3()");
_logger.LogDebug("Creating HttpClientHandler with SslProtocols.Tls13");
var handler = new HttpClientHandler
{
SslProtocols = SslProtocols.Tls13
};

// Create the custom delegating handler
_logger.LogDebug("Creating CustomHttpClientHandler with HttpClientHandler");
var customHandler = new CustomHttpClientHandler(handler);

_logger.LogDebug("Recreating Kubernetes client with custom handler for TLS 1.3 enforcement");
Client = new Kubernetes(ClientConfig, customHandler);

ClientTest();
}

public string GetClusterName()
{
_logger.LogTrace("Entered GetClusterName()");
Expand All @@ -84,14 +131,15 @@ private K8SConfiguration ParseKubeConfig(string kubeconfig, bool skipTLSVerify =
{
_logger.LogTrace("Entered ParseKubeConfig()");
var k8SConfiguration = new K8SConfiguration();

_logger.LogTrace("Checking if kubeconfig is null or empty");
if (string.IsNullOrEmpty(kubeconfig))
{
_logger.LogError("kubeconfig is null or empty");
throw new KubeConfigException("kubeconfig is null or empty, please provide a valid kubeconfig in JSON format. For more information on how to create a kubeconfig file, please visit https://github.com/Keyfactor/k8s-orchestrator/tree/main/scripts/kubernetes#example-service-account-json");
throw new KubeConfigException(
"kubeconfig is null or empty, please provide a valid kubeconfig in JSON format. For more information on how to create a kubeconfig file, please visit https://github.com/Keyfactor/k8s-orchestrator/tree/main/scripts/kubernetes#example-service-account-json");
}

try
{
// test if kubeconfig is base64 encoded
Expand All @@ -118,10 +166,11 @@ private K8SConfiguration ParseKubeConfig(string kubeconfig, bool skipTLSVerify =
if (!kubeconfig.StartsWith("{"))
{
_logger.LogError("kubeconfig is not a JSON object");
throw new KubeConfigException("kubeconfig is not a JSON object, please provide a valid kubeconfig in JSON format. For more information on how to create a kubeconfig file, please visit: https://github.com/Keyfactor/k8s-orchestrator/tree/main/scripts/kubernetes#get_service_account_credssh");
throw new KubeConfigException(
"kubeconfig is not a JSON object, please provide a valid kubeconfig in JSON format. For more information on how to create a kubeconfig file, please visit: https://github.com/Keyfactor/k8s-orchestrator/tree/main/scripts/kubernetes#get_service_account_credssh");
// return k8SConfiguration;
}
}


_logger.LogDebug("Parsing kubeconfig as a dictionary of string, string");

Expand Down Expand Up @@ -151,18 +200,21 @@ private K8SConfiguration ParseKubeConfig(string kubeconfig, bool skipTLSVerify =
_logger.LogTrace("Creating Cluster object for cluster '{Name}'", clusterMetadata["name"]?.ToString());
// get environment variable for skip tls verify and convert to bool
var skipTlsEnvStr = Environment.GetEnvironmentVariable("KEYFACTOR_ORCHESTRATOR_SKIP_TLS_VERIFY");
_logger.LogTrace("KEYFACTOR_ORCHESTRATOR_SKIP_TLS_VERIFY environment variable: {SkipTlsVerify}", skipTlsEnvStr);
if (!string.IsNullOrEmpty(skipTlsEnvStr) && (bool.TryParse(skipTlsEnvStr, out var skipTlsVerifyEnv) || skipTlsEnvStr == "1"))
_logger.LogTrace("KEYFACTOR_ORCHESTRATOR_SKIP_TLS_VERIFY environment variable: {SkipTlsVerify}",
skipTlsEnvStr);
if (!string.IsNullOrEmpty(skipTlsEnvStr) &&
(bool.TryParse(skipTlsEnvStr, out var skipTlsVerifyEnv) || skipTlsEnvStr == "1"))
{
if (skipTlsEnvStr == "1") skipTlsVerifyEnv = true;
_logger.LogDebug("Setting skip-tls-verify to {SkipTlsVerify}", skipTlsVerifyEnv);
if (skipTlsVerifyEnv && !skipTLSVerify)
{
_logger.LogWarning("Skipping TLS verification is enabled in environment variable KEYFACTOR_ORCHESTRATOR_SKIP_TLS_VERIFY this takes the highest precedence and verification will be skipped. To disable this, set the environment variable to 'false' or remove it");
_logger.LogWarning(
"Skipping TLS verification is enabled in environment variable KEYFACTOR_ORCHESTRATOR_SKIP_TLS_VERIFY this takes the highest precedence and verification will be skipped. To disable this, set the environment variable to 'false' or remove it");
skipTLSVerify = true;
}
}

var clusterObj = new Cluster
{
Name = clusterMetadata["name"]?.ToString(),
Expand All @@ -173,7 +225,8 @@ private K8SConfiguration ParseKubeConfig(string kubeconfig, bool skipTLSVerify =
SkipTlsVerify = skipTLSVerify
}
};
_logger.LogTrace("Adding cluster '{Name}'({@Endpoint}) to K8SConfiguration", clusterObj.Name, clusterObj.ClusterEndpoint);
_logger.LogTrace("Adding cluster '{Name}'({@Endpoint}) to K8SConfiguration", clusterObj.Name,
clusterObj.ClusterEndpoint);
k8SConfiguration.Clusters = new List<Cluster> { clusterObj };
}

Expand Down Expand Up @@ -220,7 +273,7 @@ private K8SConfiguration ParseKubeConfig(string kubeconfig, bool skipTLSVerify =

_logger.LogTrace("Finished parsing contexts");
_logger.LogDebug("Finished parsing kubeconfig");

return k8SConfiguration;
}

Expand All @@ -240,35 +293,36 @@ private IKubernetes GetKubeClient(string kubeconfig)
_logger.LogDebug("Calling ParseKubeConfig()");
var k8SConfiguration = ParseKubeConfig(kubeconfig);
_logger.LogDebug("Finished calling ParseKubeConfig()");

// use k8sConfiguration over credentialFileName
KubernetesClientConfiguration config;
if (k8SConfiguration != null) // Config defined in store parameters takes highest precedence
{
try
{
_logger.LogDebug(
"Config defined in store parameters takes highest precedence - calling BuildConfigFromConfigObject()");
config = KubernetesClientConfiguration.BuildConfigFromConfigObject(k8SConfiguration);
ClientConfig = KubernetesClientConfiguration.BuildConfigFromConfigObject(k8SConfiguration);
_logger.LogDebug("Finished calling BuildConfigFromConfigObject()");
}
catch (Exception e)
{
_logger.LogError("Error building config from config object: {Error}", e.Message);
config = KubernetesClientConfiguration.BuildDefaultConfig();
ClientConfig = KubernetesClientConfiguration.BuildDefaultConfig();
}
}
else if (string.IsNullOrEmpty(credentialFileName)) // If no config defined in store parameters, use default config. This should never happen though.
else if
(string.IsNullOrEmpty(
credentialFileName)) // If no config defined in store parameters, use default config. This should never happen though.
{
_logger.LogWarning(
"No config defined in store parameters, using default config. This should never happen!");
config = KubernetesClientConfiguration.BuildDefaultConfig();
ClientConfig = KubernetesClientConfiguration.BuildDefaultConfig();
_logger.LogDebug("Finished calling BuildDefaultConfig()");
}
else
{
_logger.LogDebug("Calling BuildConfigFromConfigFile()");
config = KubernetesClientConfiguration.BuildConfigFromConfigFile(
ClientConfig = KubernetesClientConfiguration.BuildConfigFromConfigFile(
strWorkPath != null && !credentialFileName.Contains(strWorkPath)
? Path.Join(strWorkPath, credentialFileName)
: // Else attempt to load config from file
Expand All @@ -277,11 +331,25 @@ private IKubernetes GetKubeClient(string kubeconfig)
}

_logger.LogDebug("Creating Kubernetes client");
IKubernetes client = new Kubernetes(config);
_logger.LogDebug("Finished creating Kubernetes client");
// Configure the HTTP client handler to use only TLS 1.3


var client = new Kubernetes(ClientConfig);
_logger.LogDebug("Finished creating Kubernetes client");
_logger.LogTrace("Setting Client property");
Client = client;

try
{
ClientTest();
}
catch (System.ComponentModel.Win32Exception ex)
{
_logger.LogError("Error testing client connection: {Error}", ex.Message);
_logger.LogTrace("{StackTrace}", ex.ToString());
EnforceTLS_1_3();
}

_logger.LogTrace("Exiting GetKubeClient()");
return client;
}
Expand Down Expand Up @@ -1793,7 +1861,7 @@ public List<string> DiscoverSecrets(string[] allowedKeys, string secType, string
_logger.LogTrace("Client BaseUrl: {BaseUrl}", Client.BaseUri);
_logger.LogDebug("Calling CoreV1.ListNamespace()");
namespaces = Client.CoreV1.ListNamespace();

_logger.LogDebug("returned from CoreV1.ListNamespace()");
_logger.LogTrace("namespaces.Items.Count: {Count}", namespaces.Items.Count);
_logger.LogTrace("namespaces.Items: {Items}", namespaces.Items.ToString());
Expand All @@ -1809,7 +1877,8 @@ public List<string> DiscoverSecrets(string[] allowedKeys, string secType, string
if (nsLi != "all" && nsLi != nsObj.Metadata.Name)
{
_logger.LogWarning(
"Skipping namespace '{Namespace}' because it does not match the namespace filter", nsObj.Metadata.Name);
"Skipping namespace '{Namespace}' because it does not match the namespace filter",
nsObj.Metadata.Name);
continue;
}

Expand Down
Loading

0 comments on commit 446d3f9

Please sign in to comment.