diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..fa3ed22 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,12 @@ +# See GitHub's documentation for more information on this file: +# https://docs.github.com/en/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically/configuration-options-for-dependency-updates +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" + - package-ecosystem: "gomod" + directory: "/" + schedule: + interval: "daily" \ No newline at end of file diff --git a/TestConsole/TestConsole.csproj b/TestConsole/TestConsole.csproj index 718969a..cf64fd0 100644 --- a/TestConsole/TestConsole.csproj +++ b/TestConsole/TestConsole.csproj @@ -11,7 +11,7 @@ - + diff --git a/TestConsole/tests.json b/TestConsole/tests.json new file mode 100644 index 0000000..e69de29 diff --git a/TestConsole/tests.yml b/TestConsole/tests.yml new file mode 100644 index 0000000..e69de29 diff --git a/dev_k8s_cluster/keyfactor_command/enrollments.tf b/dev_k8s_cluster/keyfactor_command/enrollments.tf index fd08ccc..a5e24d2 100644 --- a/dev_k8s_cluster/keyfactor_command/enrollments.tf +++ b/dev_k8s_cluster/keyfactor_command/enrollments.tf @@ -1,11 +1,91 @@ +resource "keyfactor_certificate" "pfx_enrollment_00" { + common_name = "K8S PFX Enrollment Certificate 01" + country = "US" + state = "Ohio" + locality = "Cleveland" + organization = "Keyfactor" + organizational_unit = "Engineering" + dns_sans = ["K8S PFX Enrollment Certificate 00"] + // Please don't use this password in production pass in an environmental variable or something + certificate_authority = "${var.default_ca_domain}\\${var.default_cert_ca}" + certificate_template = var.webserver_template + metadata = { + "Email-Contact" = "terraform@example.com" + } +} + resource "keyfactor_certificate" "pfx_enrollment_01" { + common_name = "K8S PFX Enrollment Certificate 01" + country = "US" + state = "Ohio" + locality = "Cleveland" + organization = "Keyfactor" + organizational_unit = "Engineering" + dns_sans = ["K8S PFX Enrollment Certificate 01"] + // Please don't use this password in production pass in an environmental variable or something + certificate_authority = "${var.default_ca_domain}\\${var.default_cert_ca}" + certificate_template = var.webserver_template + metadata = { + "Email-Contact" = "terraform@example.com" + } +} + +resource "keyfactor_certificate" "pfx_enrollment_02" { + common_name = "K8S PFX Enrollment Certificate 02" + country = "US" + state = "Ohio" + locality = "Cleveland" + organization = "Keyfactor" + organizational_unit = "Engineering" + dns_sans = ["K8S PFX Enrollment Certificate 02"] + // Please don't use this password in production pass in an environmental variable or something + certificate_authority = "${var.default_ca_domain}\\${var.default_cert_ca}" + certificate_template = var.webserver_template + metadata = { + "Email-Contact" = "terraform@example.com" + } +} + +resource "keyfactor_certificate" "pfx_enrollment_03" { + common_name = "K8S PFX Enrollment Certificate 03" + country = "US" + state = "Ohio" + locality = "Cleveland" + organization = "Keyfactor" + organizational_unit = "Engineering" + dns_sans = ["K8S PFX Enrollment Certificate 03"] + // Please don't use this password in production pass in an environmental variable or something + certificate_authority = "${var.default_ca_domain}\\${var.default_cert_ca}" + certificate_template = var.webserver_template + metadata = { + "Email-Contact" = "terraform@example.com" + } +} + +resource "keyfactor_certificate" "pfx_enrollment_04" { + common_name = "K8S PFX Enrollment Certificate 04" + country = "US" + state = "Ohio" + locality = "Cleveland" + organization = "Keyfactor" + organizational_unit = "Engineering" + dns_sans = ["K8S PFX Enrollment Certificate 04"] + // Please don't use this password in production pass in an environmental variable or something + certificate_authority = "${var.default_ca_domain}\\${var.default_cert_ca}" + certificate_template = var.webserver_template + metadata = { + "Email-Contact" = "terraform@example.com" + } +} + +resource "keyfactor_certificate" "pfx_enrollment_05" { common_name = "K8S PFX Enrollment Certificate" country = "US" state = "Ohio" locality = "Cleveland" organization = "Keyfactor" organizational_unit = "Engineering" - dns_sans = ["K8S PFX Enrollment Certificate"] + dns_sans = ["K8S PFX Enrollment Certificate 05"] // Please don't use this password in production pass in an environmental variable or something certificate_authority = "${var.default_ca_domain}\\${var.default_cert_ca}" certificate_template = var.webserver_template diff --git a/dev_k8s_cluster/keyfactor_command/kfc_stores.tf b/dev_k8s_cluster/keyfactor_command/kfc_stores.tf deleted file mode 100644 index c6c808d..0000000 --- a/dev_k8s_cluster/keyfactor_command/kfc_stores.tf +++ /dev/null @@ -1,31 +0,0 @@ -data keyfactor_agent "k8s" { - agent_identifier = var.client_machine_name -} - - - -resource "keyfactor_certificate_store" "tls_store" { - client_machine = data.keyfactor_agent.k8s.client_machine - # Orchestrator client name - store_path = "${var.kube_cluster_name}/${var.kube_namespace}/${var.kube_tlssecr_name}" # Varies based on store type - agent_identifier = data.keyfactor_agent.k8s.agent_identifier - # Orchestrator GUID or Orchestrator ClientMachine - store_type = "K8STLSSecr" # Must exist in KeyFactor - server_username = "kubeconfig" - server_password = file(var.kubeconfig_file) - server_use_ssl = true - inventory_schedule = "5m" - properties = { - KubeSecretType = "tls_secret" -# KubeNamespace = var.kube_namespace # this SHOULD take precedence over the store_path -# KubeSecretName = var.k8stlssecr_name # this SHOULD take precedence over the store_path -# KubeSvcCreds = file(var.kubeconfig_file) # todo: invalid property -# SeparateChain = true # todo: invalid property -# IncludeCertChain = true # todo: invalid property - } -} - -resource "keyfactor_certificate_deployment" "k8stlssecr" { - certificate_id = keyfactor_certificate.pfx_enrollment_01.certificate_id - certificate_store_id = keyfactor_certificate_store.tls_store.id -} \ No newline at end of file diff --git a/dev_k8s_cluster/keyfactor_command/kfc_tlssecr_stores.tf b/dev_k8s_cluster/keyfactor_command/kfc_tlssecr_stores.tf new file mode 100644 index 0000000..de89535 --- /dev/null +++ b/dev_k8s_cluster/keyfactor_command/kfc_tlssecr_stores.tf @@ -0,0 +1,137 @@ +data keyfactor_agent "k8s" { + agent_identifier = var.client_machine_name +} + + + +resource "keyfactor_certificate_store" "tls_store_00" { + client_machine = data.keyfactor_agent.k8s.client_machine + # Orchestrator client name + store_path = "${var.kube_cluster_name}/${var.kube_namespace}/${var.kube_tlssecr_name}00" # Varies based on store type + agent_identifier = data.keyfactor_agent.k8s.agent_identifier + # Orchestrator GUID or Orchestrator ClientMachine + store_type = "K8STLSSecr" # Must exist in KeyFactor + server_username = "kubeconfig" + server_password = file(var.kubeconfig_file) + server_use_ssl = true + inventory_schedule = "5m" + properties = { + KubeSecretType = "tls_secret" +# KubeNamespace = var.kube_namespace # this SHOULD take precedence over the store_path +# KubeSecretName = var.k8stlssecr_name # this SHOULD take precedence over the store_path +# KubeSvcCreds = file(var.kubeconfig_file) # todo: invalid property +# SeparateChain = true # todo: invalid property +# IncludeCertChain = true # todo: invalid property + } +} + +resource "keyfactor_certificate_deployment" "k8stlssecr_00" { + certificate_id = keyfactor_certificate.pfx_enrollment_00.certificate_id + certificate_store_id = keyfactor_certificate_store.tls_store_00.id +} + + + +resource "keyfactor_certificate_store" "tls_store_01" { + client_machine = data.keyfactor_agent.k8s.client_machine + # Orchestrator client name + store_path = "${var.kube_cluster_name}/${var.kube_namespace}/${var.kube_tlssecr_name}01" # Varies based on store type + agent_identifier = data.keyfactor_agent.k8s.agent_identifier + # Orchestrator GUID or Orchestrator ClientMachine + store_type = "K8STLSSecr" # Must exist in KeyFactor + server_username = "kubeconfig" + server_password = file(var.kubeconfig_file) + server_use_ssl = true + inventory_schedule = "5m" + properties = { + KubeSecretType = "tls_secret" + # KubeNamespace = var.kube_namespace # this SHOULD take precedence over the store_path + # KubeSecretName = var.k8stlssecr_name # this SHOULD take precedence over the store_path + # KubeSvcCreds = file(var.kubeconfig_file) # todo: invalid property + # SeparateChain = true # todo: invalid property + # IncludeCertChain = true # todo: invalid property + } +} + +resource "keyfactor_certificate_deployment" "k8stlssecr_01" { + certificate_id = keyfactor_certificate.pfx_enrollment_01.certificate_id + certificate_store_id = keyfactor_certificate_store.tls_store_01.id +} + +resource "keyfactor_certificate_store" "tls_store_02" { + client_machine = data.keyfactor_agent.k8s.client_machine + # Orchestrator client name + store_path = "${var.kube_cluster_name}/${var.kube_namespace}/${var.kube_tlssecr_name}02" # Varies based on store type + agent_identifier = data.keyfactor_agent.k8s.agent_identifier + # Orchestrator GUID or Orchestrator ClientMachine + store_type = "K8STLSSecr" # Must exist in KeyFactor + server_username = "kubeconfig" + server_password = file(var.kubeconfig_file) + server_use_ssl = true + inventory_schedule = "5m" + properties = { + KubeSecretType = "tls_secret" + # KubeNamespace = var.kube_namespace # this SHOULD take precedence over the store_path + # KubeSecretName = var.k8stlssecr_name # this SHOULD take precedence over the store_path + # KubeSvcCreds = file(var.kubeconfig_file) # todo: invalid property + # SeparateChain = true # todo: invalid property + # IncludeCertChain = true # todo: invalid property + } +} + +resource "keyfactor_certificate_deployment" "k8stlssecr_02" { + certificate_id = keyfactor_certificate.pfx_enrollment_02.certificate_id + certificate_store_id = keyfactor_certificate_store.tls_store_02.id +} + +resource "keyfactor_certificate_store" "tls_store_03" { + client_machine = data.keyfactor_agent.k8s.client_machine + # Orchestrator client name + store_path = "${var.kube_cluster_name}/${var.kube_namespace}/${var.kube_tlssecr_name}03" # Varies based on store type + agent_identifier = data.keyfactor_agent.k8s.agent_identifier + # Orchestrator GUID or Orchestrator ClientMachine + store_type = "K8STLSSecr" # Must exist in KeyFactor + server_username = "kubeconfig" + server_password = file(var.kubeconfig_file) + server_use_ssl = true + inventory_schedule = "5m" + properties = { + KubeSecretType = "tls_secret" + # KubeNamespace = var.kube_namespace # this SHOULD take precedence over the store_path + # KubeSecretName = var.k8stlssecr_name # this SHOULD take precedence over the store_path + # KubeSvcCreds = file(var.kubeconfig_file) # todo: invalid property + # SeparateChain = true # todo: invalid property + # IncludeCertChain = true # todo: invalid property + } +} + +resource "keyfactor_certificate_deployment" "k8stlssecr_03" { + certificate_id = keyfactor_certificate.pfx_enrollment_03.certificate_id + certificate_store_id = keyfactor_certificate_store.tls_store_03.id +} + +resource "keyfactor_certificate_store" "tls_store_04" { + client_machine = data.keyfactor_agent.k8s.client_machine + # Orchestrator client name + store_path = "${var.kube_cluster_name}/${var.kube_namespace}/${var.kube_tlssecr_name}04" # Varies based on store type + agent_identifier = data.keyfactor_agent.k8s.agent_identifier + # Orchestrator GUID or Orchestrator ClientMachine + store_type = "K8STLSSecr" # Must exist in KeyFactor + server_username = "kubeconfig" + server_password = file(var.kubeconfig_file) + server_use_ssl = true + inventory_schedule = "5m" + properties = { + KubeSecretType = "tls_secret" + # KubeNamespace = var.kube_namespace # this SHOULD take precedence over the store_path + # KubeSecretName = var.k8stlssecr_name # this SHOULD take precedence over the store_path + # KubeSvcCreds = file(var.kubeconfig_file) # todo: invalid property + # SeparateChain = true # todo: invalid property + # IncludeCertChain = true # todo: invalid property + } +} + +resource "keyfactor_certificate_deployment" "k8stlssecr_04" { + certificate_id = keyfactor_certificate.pfx_enrollment_04.certificate_id + certificate_store_id = keyfactor_certificate_store.tls_store_04.id +} \ No newline at end of file diff --git a/kubernetes-orchestrator-extension/Clients/KubeClient.cs b/kubernetes-orchestrator-extension/Clients/KubeClient.cs index 2edda13..817ba14 100644 --- a/kubernetes-orchestrator-extension/Clients/KubeClient.cs +++ b/kubernetes-orchestrator-extension/Clients/KubeClient.cs @@ -16,6 +16,7 @@ using System.Text; using k8s; using k8s.Autorest; +using k8s.Exceptions; using k8s.KubeConfigModels; using k8s.Models; using Keyfactor.Extensions.Orchestrator.K8S.Jobs; @@ -37,14 +38,14 @@ public class KubeCertificateManagerClient { private readonly ILogger _logger; - public KubeCertificateManagerClient(string kubeconfig) + public KubeCertificateManagerClient(string kubeconfig, bool useSSL = true) { _logger = LogHandler.GetClassLogger(MethodBase.GetCurrentMethod()?.DeclaringType); Client = GetKubeClient(kubeconfig); ConfigJson = kubeconfig; try { - ConfigObj = ParseKubeConfig(kubeconfig); + ConfigObj = ParseKubeConfig(kubeconfig, !useSSL); // invert useSSL to skip TLS verification } catch (Exception) { @@ -63,10 +64,12 @@ public string GetClusterName() _logger.LogTrace("Entered GetClusterName()"); try { + _logger.LogTrace("Returning cluster name from ConfigObj"); return ConfigObj.Clusters.FirstOrDefault()?.Name; } catch (Exception) { + _logger.LogWarning("Error getting cluster name from ConfigObj attempting to return client base uri"); return GetHost(); } } @@ -77,33 +80,48 @@ public string GetHost() return Client.BaseUri.ToString(); } - private K8SConfiguration ParseKubeConfig(string kubeconfig) + private K8SConfiguration ParseKubeConfig(string kubeconfig, bool skipTLSVerify = false) { _logger.LogTrace("Entered ParseKubeConfig()"); var k8SConfiguration = new K8SConfiguration(); - // test if kubeconfig is base64 encoded - _logger.LogDebug("Testing if kubeconfig is base64 encoded"); + + _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"); + } + try { + // test if kubeconfig is base64 encoded + _logger.LogDebug("Testing if kubeconfig is base64 encoded"); var decodedKubeconfig = Encoding.UTF8.GetString(Convert.FromBase64String(kubeconfig)); kubeconfig = decodedKubeconfig; _logger.LogDebug("Successfully decoded kubeconfig from base64"); } catch { - // not base64 encoded so do nothing + _logger.LogTrace("Kubeconfig is not base64 encoded"); } - // check if json is escaped + _logger.LogTrace("Checking if kubeconfig is escaped JSON"); if (kubeconfig.StartsWith("\\")) { - _logger.LogDebug("Unescaping kubeconfig JSON"); + _logger.LogDebug("Un-escaping kubeconfig JSON"); kubeconfig = kubeconfig.Replace("\\", ""); kubeconfig = kubeconfig.Replace("\\n", "\n"); + _logger.LogDebug("Successfully un-escaped kubeconfig JSON"); } // parse kubeconfig as a dictionary of string, string - if (!kubeconfig.StartsWith("{")) return k8SConfiguration; + 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"); + // return k8SConfiguration; + } + _logger.LogDebug("Parsing kubeconfig as a dictionary of string, string"); @@ -130,6 +148,21 @@ private K8SConfiguration ParseKubeConfig(string kubeconfig) _logger.LogTrace("Entering foreach loop to parse clusters..."); foreach (var clusterMetadata in JsonConvert.DeserializeObject(cl.ToString() ?? string.Empty)) { + _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")) + { + 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"); + skipTLSVerify = true; + } + } + var clusterObj = new Cluster { Name = clusterMetadata["name"]?.ToString(), @@ -137,11 +170,10 @@ private K8SConfiguration ParseKubeConfig(string kubeconfig) { Server = clusterMetadata["cluster"]?["server"]?.ToString(), CertificateAuthorityData = clusterMetadata["cluster"]?["certificate-authority-data"]?.ToString(), - SkipTlsVerify = false + 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 { clusterObj }; } @@ -188,6 +220,7 @@ private K8SConfiguration ParseKubeConfig(string kubeconfig) _logger.LogTrace("Finished parsing contexts"); _logger.LogDebug("Finished parsing kubeconfig"); + return k8SConfiguration; } @@ -207,36 +240,34 @@ 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 { - _logger.LogDebug( - "Config defined in store parameters takes highest precedence - calling BuildConfigFromConfigObject()"); try { + _logger.LogDebug( + "Config defined in store parameters takes highest precedence - calling BuildConfigFromConfigObject()"); config = KubernetesClientConfiguration.BuildConfigFromConfigObject(k8SConfiguration); _logger.LogDebug("Finished calling BuildConfigFromConfigObject()"); } catch (Exception e) { - _logger.LogError("Error building config from config object: " + e.Message); + _logger.LogError("Error building config from config object: {Error}", e.Message); config = KubernetesClientConfiguration.BuildDefaultConfig(); } } - else if - (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 though"); + "No config defined in store parameters, using default config. This should never happen!"); config = KubernetesClientConfiguration.BuildDefaultConfig(); _logger.LogDebug("Finished calling BuildDefaultConfig()"); } else { - // Logger.LogDebug($"Attempting to load config from file {credentialFileName}"); + _logger.LogDebug("Calling BuildConfigFromConfigFile()"); config = KubernetesClientConfiguration.BuildConfigFromConfigFile( strWorkPath != null && !credentialFileName.Contains(strWorkPath) ? Path.Join(strWorkPath, credentialFileName) @@ -277,15 +308,15 @@ public X509Certificate2 FindCertificateByAlias(X509Certificate2Collection certif { var foundCertificate = certificates .OfType() - .FirstOrDefault(cert => cert.SubjectName.Name.Contains(alias)); + .FirstOrDefault(cert => cert.SubjectName.Name != null && cert.SubjectName.Name.Contains(alias)); return foundCertificate; } public V1Secret RemoveFromPKCS12SecretStore(K8SJobCertificate jobCertificate, string secretName, - string namespaceName, string secretType, string certdataFieldName, + string namespaceName, string secretType, string certDataFieldName, string storePasswd, V1Secret k8SSecretData, - bool append = false, bool overwrite = true, bool passwdIsK8sSecret = false, string passwordSecretPath = "", + bool append = false, bool overwrite = true, bool passwdIsK8SSecret = false, string passwordSecretPath = "", string passwordFieldName = "password", string[] certdataFieldNames = null) { @@ -308,14 +339,12 @@ public V1Secret RemoveFromPKCS12SecretStore(K8SJobCertificate jobCertificate, st { _logger.LogTrace("existingPkcs12DataObj.Data is not null"); - // KeyValuePair updated_data = new KeyValuePair(); - foreach (var fieldName in existingPkcs12DataObj?.Data.Keys) { //check if key is in certdataFieldNames //if fieldname contains a . then split it and use the last part var searchFieldName = fieldName; - certdataFieldName = fieldName; + certDataFieldName = fieldName; if (fieldName.Contains(".")) { var splitFieldName = fieldName.Split("."); @@ -400,13 +429,13 @@ public V1Secret RemoveFromPKCS12SecretStore(K8SJobCertificate jobCertificate, st Type = "Opaque", Data = new Dictionary { - { certdataFieldName, p12bytes } + { certDataFieldName, p12bytes } } }; switch (string.IsNullOrEmpty(storePasswd)) { case false - when string.IsNullOrEmpty(passwordSecretPath) && passwdIsK8sSecret + when string.IsNullOrEmpty(passwordSecretPath) && passwdIsK8SSecret : // password is not empty and passwordSecretPath is empty { _logger.LogDebug("Adding password to secret..."); @@ -415,7 +444,7 @@ when string.IsNullOrEmpty(passwordSecretPath) && passwdIsK8sSecret break; } case false - when !string.IsNullOrEmpty(passwordSecretPath) && passwdIsK8sSecret + when !string.IsNullOrEmpty(passwordSecretPath) && passwdIsK8SSecret : // password is not empty and passwordSecretPath is not empty { _logger.LogDebug("Adding password secret path to secret..."); @@ -1745,6 +1774,7 @@ public List DiscoverSecrets(string[] allowedKeys, string secType, string _logger.LogTrace("Entered DiscoverSecrets()"); V1NamespaceList namespaces; var clusterName = GetClusterName() ?? GetHost(); + _logger.LogTrace("clusterName: {ClusterName}", clusterName); var nsList = new string[] { }; @@ -1759,22 +1789,25 @@ public List DiscoverSecrets(string[] allowedKeys, string secType, string } - _logger.LogDebug("Attempting to list k8s namespaces from " + clusterName); + _logger.LogDebug("Attempting to list k8s namespaces from {ClusterName}", clusterName); + _logger.LogDebug("Calling CoreV1.ListNamespace()"); namespaces = Client.CoreV1.ListNamespace(); - _logger.LogTrace("namespaces.Items.Count: " + namespaces.Items.Count); - _logger.LogTrace("namespaces.Items: " + namespaces.Items); + _logger.LogDebug("returned from CoreV1.ListNamespace()"); + _logger.LogTrace("namespaces.Items.Count: {Count}", namespaces.Items.Count); + _logger.LogTrace("namespaces.Items: {Items}", namespaces.Items.ToString()); - nsList = ns.Contains(",") ? ns.Split(",") : new[] { ns }; - foreach (var nsLI in nsList) + nsList = ns.Contains(',') ? ns.Split(",") : new[] { ns }; + foreach (var nsLi in nsList) { + _logger.LogTrace("Iterating through namespace list {NamespaceList}", nsLi); var secretsList = new List(); - _logger.LogTrace("Entering foreach loop to list all secrets in each returned namespace."); + _logger.LogTrace("Entering foreach loop to list all secrets in each returned namespace"); foreach (var nsObj in namespaces.Items) { - if (nsLI != "all" && nsLI != nsObj.Metadata.Name) + if (nsLi != "all" && nsLi != nsObj.Metadata.Name) { - _logger.LogWarning("Skipping namespace " + nsObj.Metadata.Name + - " because it does not match the namespace filter."); + _logger.LogWarning( + "Skipping namespace '{Namespace}' because it does not match the namespace filter", nsObj.Metadata.Name); continue; } diff --git a/kubernetes-orchestrator-extension/Jobs/Discovery.cs b/kubernetes-orchestrator-extension/Jobs/Discovery.cs index 14bc38c..5867be5 100644 --- a/kubernetes-orchestrator-extension/Jobs/Discovery.cs +++ b/kubernetes-orchestrator-extension/Jobs/Discovery.cs @@ -11,6 +11,7 @@ using Keyfactor.Extensions.Orchestrator.K8S.Clients; using Keyfactor.Orchestrators.Common.Enums; using Keyfactor.Orchestrators.Extensions; +using Keyfactor.Logging; using Keyfactor.Orchestrators.Extensions.Interfaces; using Microsoft.Extensions.Logging; @@ -39,29 +40,48 @@ public JobResult ProcessJob(DiscoveryJobConfiguration config, SubmitDiscoveryUpd //NLog Logging to c:\CMS\Logs\CMS_Agent_Log.txt - InitializeStore(config); - Logger.LogInformation("Begin Discovery for K8S Orchestrator Extension for job " + config.JobId); - Logger.LogInformation($"Discovery for store type: {config.Capability}"); + Logger = LogHandler.GetClassLogger(GetType()); + Logger.LogInformation("Begin Discovery for K8S Orchestrator Extension for job {JobID}", config.JobId); + Logger.LogInformation("Discovery for store type: {Capability}", config.Capability); + try + { + Logger.LogDebug("Calling InitializeStore()"); + InitializeStore(config); + Logger.LogDebug("Store initialized successfully"); + } catch (Exception ex) + { + Logger.LogError("Failed to initialize store: {Error}", ex.Message); + return FailJob("Failed to initialize store: " + ex.Message, config.JobHistoryId); + } + + var locations = new List(); KubeSvcCreds = ServerPassword; - KubeClient = new KubeCertificateManagerClient(KubeSvcCreds); + Logger.LogDebug("Calling KubeCertificateManagerClient()"); + KubeClient = new KubeCertificateManagerClient(KubeSvcCreds, config.UseSSL); + if (KubeClient == null) + { + Logger.LogError("Failed to create KubeCertificateManagerClient"); + return FailJob("Failed to create KubeCertificateManagerClient", config.JobHistoryId); + } - var namespaces = config.JobProperties["dirs"].ToString()?.Split(','); + var namespaces = config.JobProperties["dirs"].ToString()?.Split(',') ?? Array.Empty(); if (namespaces is { Length: 0 }) { + Logger.LogDebug("No namespaces provided, using `default` namespace"); namespaces = new[] { "default" }; } - Logger.LogDebug("Namespaces: " + string.Join(",", namespaces)); + Logger.LogDebug("Namespaces: {Namespaces}", string.Join(",", namespaces)); - var ignoreNamespace = config.JobProperties["ignoreddirs"].ToString()?.Split(','); - Logger.LogDebug("Ignored Namespaces: " + string.Join(",", ignoreNamespace)); + var ignoreNamespace = config.JobProperties["ignoreddirs"].ToString()?.Split(',') ?? Array.Empty(); + Logger.LogDebug("Ignored Namespaces: {Namespaces}", string.Join(",", ignoreNamespace)); - var secretAllowedKeys = config.JobProperties["patterns"].ToString()?.Split(','); - Logger.LogDebug("Secret Allowed Keys: " + string.Join(",", secretAllowedKeys)); + var secretAllowedKeys = config.JobProperties["patterns"].ToString()?.Split(',') ?? Array.Empty(); + Logger.LogDebug("Secret Allowed Keys: {AllowedKeys}",string.Join(",", secretAllowedKeys)); - Logger.LogTrace("Discovery entering switch block based on capability: " + config.Capability); + Logger.LogTrace("Discovery entering switch block based on capability {Capability}", config.Capability); try { //Code logic to: diff --git a/kubernetes-orchestrator-extension/Jobs/JobBase.cs b/kubernetes-orchestrator-extension/Jobs/JobBase.cs index e02bf40..1da7a35 100644 --- a/kubernetes-orchestrator-extension/Jobs/JobBase.cs +++ b/kubernetes-orchestrator-extension/Jobs/JobBase.cs @@ -24,9 +24,11 @@ using Microsoft.Extensions.Logging; using Newtonsoft.Json; using Org.BouncyCastle.Pkcs; +using Org.BouncyCastle.Security; using Org.BouncyCastle.Utilities.IO.Pem; using Org.BouncyCastle.X509; using PemWriter = Org.BouncyCastle.OpenSsl.PemWriter; +using X509Certificate = Org.BouncyCastle.X509.X509Certificate; namespace Keyfactor.Extensions.Orchestrator.K8S.Jobs; @@ -90,7 +92,8 @@ public class K8SJobCertificate public bool HasPrivateKey { get; set; } = false; public bool HasPassword { get; set; } = false; - + + public X509CertificateEntry CertificateEntry { get; set; } public X509CertificateEntry[] CertificateEntryChain { get; set; } @@ -104,28 +107,31 @@ public class K8SJobCertificate public abstract class JobBase { - static protected readonly string[] SupportedKubeStoreTypes; + protected static readonly string[] SupportedKubeStoreTypes; - static protected readonly string[] RequiredProperties; + protected static readonly string[] RequiredProperties; - static protected readonly string[] TLSAllowedKeys; - static protected readonly string[] OpaqueAllowedKeys; - static protected readonly string[] CertAllowedKeys; - static protected readonly string[] Pkcs12AllowedKeys; - static protected readonly string[] JksAllowedKeys; - static protected readonly string DefaultPFXSecretFieldName = "pfx"; - static protected readonly string DefaultJKSSecretFieldName = "jks"; - static protected readonly string DefaultPFXPasswordSecretFieldName = "password"; + protected static readonly string[] TLSAllowedKeys; + protected static readonly string[] OpaqueAllowedKeys; + protected static readonly string[] CertAllowedKeys; + protected static readonly string[] Pkcs12AllowedKeys; + protected static readonly string[] JksAllowedKeys; + protected static readonly string DefaultPFXSecretFieldName = "pfx"; + protected static readonly string DefaultJKSSecretFieldName = "jks"; + protected static readonly string DefaultPFXPasswordSecretFieldName = "password"; - internal protected bool SeparateChain { get; set; } = false; //Don't arbitrarily change this to true without specifying BREAKING CHANGE in the release notes. - internal protected bool IncludeCertChain { get; set; } = true; //Don't arbitrarily change this to false without specifying BREAKING CHANGE in the release notes. + protected internal bool SeparateChain { get; set; } = false; //Don't arbitrarily change this to true without specifying BREAKING CHANGE in the release notes. + protected internal bool IncludeCertChain { get; set; } = true; //Don't arbitrarily change this to false without specifying BREAKING CHANGE in the release notes. - internal protected string OperationType { get; set; } + protected internal string OperationType { get; set; } + protected internal bool SkipTlsValidation { get; set; } = false; - static protected string CertChainSeparator = ","; - internal protected KubeCertificateManagerClient KubeClient; + protected static string CertChainSeparator = ","; + + + protected internal KubeCertificateManagerClient KubeClient; - internal protected ILogger Logger; + protected internal ILogger Logger; static JobBase() { CertAllowedKeys = new[] { "cert", "csr" }; @@ -188,7 +194,7 @@ protected void InitializeStore(InventoryJobConfiguration config) InventoryConfig = config; Capability = config.Capability; Logger = LogHandler.GetClassLogger(GetType()); - Logger.LogTrace("Entered InitializeStore() for INVENTORY."); + Logger.LogTrace("Entered InitializeStore() for INVENTORY"); var props = JsonConvert.DeserializeObject(config.CertificateStoreDetails.Properties); //var props = Jsonconfig.CertificateStoreDetails.Properties; ServerUsername = config?.ServerUsername; @@ -208,13 +214,24 @@ protected void InitializeStore(DiscoveryJobConfiguration config) { DiscoveryConfig = config; Logger = LogHandler.GetClassLogger(GetType()); - Logger.LogTrace("Entered InitializeStore() for DISCOVERY."); + Logger.LogTrace("Entered InitializeStore() for DISCOVERY"); var props = config.JobProperties; Capability = config?.Capability; ServerUsername = config?.ServerUsername; ServerPassword = config?.ServerPassword; + // check that config has UseSSL bool set + if (config.UseSSL) + { + Logger.LogInformation("UseSSL is set to true, setting SkipTlsValidation to false"); + SkipTlsValidation = false; + } + else + { + Logger.LogInformation("UseSSL is set to false, setting SkipTlsValidation to true"); + SkipTlsValidation = true; + } - Logger.LogTrace($"ServerUsername: {ServerUsername}"); + Logger.LogTrace("ServerUsername: {ServerUsername}", ServerUsername); Logger.LogTrace("Calling InitializeProperties()"); InitializeProperties(props); @@ -224,7 +241,7 @@ protected void InitializeStore(ManagementJobConfiguration config) { ManagementConfig = config; Logger = LogHandler.GetClassLogger(GetType()); - Logger.LogTrace("Entered InitializeStore() for MANAGEMENT."); + Logger.LogTrace("Entered InitializeStore() for MANAGEMENT"); var props = JsonConvert.DeserializeObject(config.CertificateStoreDetails.Properties); Capability = config?.Capability; ServerUsername = config?.ServerUsername; @@ -390,48 +407,48 @@ public bool isNamespaceStore(string capability) public string resolveStorePath(string spath) { Logger.LogTrace("Entered resolveStorePath()"); - Logger.LogTrace("Passed Store Path: " + spath); + Logger.LogTrace("Passed Store Path: {Path}", spath); - Logger.LogTrace("Attempting to split storepath by '/'"); + Logger.LogTrace("Attempting to split store path by '/'"); var sPathParts = spath.Split("/"); - Logger.LogTrace("Split count: " + sPathParts.Length); + Logger.LogTrace("Split count: {Count}", sPathParts.Length); var isNsStore = isNamespaceStore(Capability); switch (sPathParts.Length) { case 1 when Capability.Contains("NS"): - Logger.LogInformation("Store path is 1 part and capability is namespace. Assuming that storepath is namespace and setting KubeSecretName equal empty."); - Logger.LogWarning($"Store is of type namespace. Setting KubeSecretName equal empty and namespace to storepath."); + Logger.LogInformation("Store path is 1 part and capability is namespace, assuming that store path is namespace and setting 'KubeSecretName' equal empty"); + Logger.LogWarning("Store is of type namespace. Setting KubeSecretName equal empty and namespace to store path"); KubeSecretName = ""; KubeNamespace = sPathParts[0]; break; case 1 when Capability.Contains("Cluster"): Logger.LogTrace( - "Store path is 1 part and capability is cluster. Assuming that storepath is the cluster name and setting KubeSecretName and KubeNamespace equal empty."); - Logger.LogWarning($"Store is of type cluster. Setting KubeSecretName and KubeNamespace equal empty."); + "Store path is 1 part and capability is cluster, assuming that store path is the cluster name and setting 'KubeSecretName' and 'KubeNamespace' equal empty"); + Logger.LogWarning("Store is of type cluster, setting 'KubeSecretName' and 'KubeNamespace' equal empty"); KubeSecretName = ""; KubeNamespace = ""; break; case 1: - Logger.LogTrace("Store path is 1 part assuming that it is the secret name"); + Logger.LogTrace("Store path is 1 part assuming that it is the 'KubeSecretName'"); if (string.IsNullOrEmpty(KubeSecretName)) { - Logger.LogTrace("No KubeSecretName set. Setting KubeSecretName to store path."); + Logger.LogTrace("No 'KubeSecretName' set, setting 'KubeSecretName' to store path"); KubeSecretName = sPathParts[0]; } break; case 2 when Capability.Contains("Cluster"): - Logger.LogError("Store path is 2 parts and capability is cluster. This is not a valid combination."); + Logger.LogError("Store path is 2 parts and capability is cluster, this is not a valid combination"); break; case 2 when Capability.Contains("NS"): var nsPrefix = sPathParts[0]; var nsName = sPathParts[1]; Logger.LogTrace( - "Store path is 2 parts and capability is namespace. Assuming that storepath pattern is either cluster/namespacename or namespace/namespacename"); - Logger.LogDebug("Discarding namespace prefix and setting namespace to storepath"); + "Store path is 2 parts and capability is namespace, assuming that store path pattern is either 'cluster/namespacename' or 'namespace/namespacename'"); + Logger.LogDebug("Discarding namespace prefix and setting namespace to store path"); if (string.IsNullOrEmpty(KubeNamespace)) { - Logger.LogTrace("No KubeNamespace set. Setting KubeNamespace to store path."); + Logger.LogTrace("No 'KubeNamespace' set, setting 'KubeNamespace' to store path"); KubeNamespace = nsName; } break; @@ -441,33 +458,33 @@ public string resolveStorePath(string spath) var kSn = sPathParts[1]; if (string.IsNullOrEmpty(KubeNamespace)) { - Logger.LogTrace("No KubeNamespace set. Setting KubeNamespace to store path."); + Logger.LogTrace("No 'KubeNamespace' set, setting 'KubeNamespace' to store path"); KubeNamespace = kNs; } if (string.IsNullOrEmpty(KubeSecretName)) { - Logger.LogTrace("No KubeSecretName set. Setting KubeSecretName to store path."); + Logger.LogTrace("No 'KubeSecretName' set, setting 'KubeSecretName' to store path"); KubeSecretName = kSn; } break; case 3 when Capability.Contains("Cluster"): - Logger.LogError("Store path is 2 parts and capability is cluster. This is not a valid combination."); + Logger.LogError("Store path is 2 parts and capability is cluster, this is not a valid combination"); break; case 3 when Capability.Contains("NS"): - Logger.LogTrace("Store path is 3 parts assuming that storepath pattern is the cluster/namespace/namespacename"); + Logger.LogTrace("Store path is 3 parts assuming that store path pattern is the 'cluster/namespace/namespacename'"); var nsCluster = sPathParts[0]; var nsClarifier = sPathParts[1]; var nsName3 = sPathParts[2]; if (string.IsNullOrEmpty(KubeNamespace)) { - Logger.LogTrace("No KubeNamespace set. Setting KubeNamespace to store path."); + Logger.LogTrace("No 'KubeNamespace' set, setting 'KubeNamespace' to store path"); KubeNamespace = nsName3; } KubeSecretName = ""; break; case 3: - Logger.LogTrace("Store path is 3 parts assuming that it is the cluster/namespace/secret name"); + Logger.LogTrace("Store path is 3 parts assuming that it is the 'cluster/namespace/secret' name"); var kH = sPathParts[0]; var kN = sPathParts[1]; var kS = sPathParts[2]; @@ -479,17 +496,17 @@ public string resolveStorePath(string spath) } if (string.IsNullOrEmpty(KubeNamespace)) { - Logger.LogTrace("No KubeNamespace set. Setting KubeNamespace to store path."); + Logger.LogTrace("No 'KubeNamespace' set, setting 'KubeNamespace' to store path"); KubeNamespace = kN; } if (string.IsNullOrEmpty(KubeSecretName)) { - Logger.LogTrace("No KubeSecretName set. Setting KubeSecretName to store path."); + Logger.LogTrace("No 'KubeSecretName' set, setting 'KubeSecretName' to store path"); KubeSecretName = kS; } break; case 4 when Capability.Contains("Cluster") || Capability.Contains("NS"): - Logger.LogError($"Store path is 4 parts and capability is {Capability}. This is not a valid combination."); + Logger.LogError("Store path is 4 parts and capability is {Capability}. This is not a valid combination", Capability); break; case 4: Logger.LogTrace("Store path is 4 parts assuming that it is the cluster/namespace/secret type/secret name"); @@ -499,17 +516,17 @@ public string resolveStorePath(string spath) var kSN = sPathParts[3]; if (string.IsNullOrEmpty(KubeNamespace)) { - Logger.LogTrace("No KubeNamespace set. Setting KubeNamespace to store path."); + Logger.LogTrace("No 'KubeNamespace' set, setting 'KubeNamespace' to store path"); KubeNamespace = kNN; } if (string.IsNullOrEmpty(KubeSecretName)) { - Logger.LogTrace("No KubeSecretName set. Setting KubeSecretName to store path."); + Logger.LogTrace("No 'KubeSecretName' set, setting 'KubeSecretName' to store path"); KubeSecretName = kSN; } break; default: - Logger.LogWarning("Unable to resolve store path. Please check the store path and try again."); + Logger.LogWarning("Unable to resolve store path, please check the store path and try again"); break; } return GetStorePath(); @@ -520,13 +537,12 @@ private void InitializeProperties(dynamic storeProperties) Logger.LogTrace("Entered InitializeProperties()"); if (storeProperties == null) throw new ConfigurationException( - $"Invalid configuration. Please provide {RequiredProperties}. Or review the documentation at https://github.com/Keyfactor/kubernetes-orchestrator#custom-fields-tab."); + $"Invalid configuration. Please provide {RequiredProperties}. Or review the documentation at https://github.com/Keyfactor/kubernetes-orchestrator#custom-fields-tab"); // check if key is present and set values if not - try { - Logger.LogDebug("Setting K8S values from store properties."); + Logger.LogDebug("Setting K8S values from store properties"); KubeNamespace = storeProperties["KubeNamespace"]; KubeSecretName = storeProperties["KubeSecretName"]; KubeSecretType = storeProperties["KubeSecretType"]; @@ -539,7 +555,7 @@ private void InitializeProperties(dynamic storeProperties) } else { - Logger.LogDebug("PasswordIsSeparateSecret not found in store properties."); + Logger.LogDebug("PasswordIsSeparateSecret not found in store properties"); PasswordIsSeparateSecret = false; } @@ -550,7 +566,7 @@ private void InitializeProperties(dynamic storeProperties) } else { - Logger.LogDebug("PasswordFieldName not found in store properties."); + Logger.LogDebug("PasswordFieldName not found in store properties"); PasswordFieldName = ""; } @@ -561,7 +577,7 @@ private void InitializeProperties(dynamic storeProperties) } else { - Logger.LogDebug("StorePasswordPath not found in store properties."); + Logger.LogDebug("StorePasswordPath not found in store properties"); StorePasswordPath = ""; } @@ -572,66 +588,63 @@ private void InitializeProperties(dynamic storeProperties) } else { - Logger.LogDebug("KubeSecretKey not found in store properties."); + Logger.LogDebug("KubeSecretKey not found in store properties"); CertificateDataFieldName = ""; } } catch (Exception) { - Logger.LogError("Unknown error while parsing store properties."); - Logger.LogWarning("Setting KubeSecretType and KubeSvcCreds to empty strings."); + Logger.LogError("Unknown error while parsing store properties"); + Logger.LogWarning("Setting KubeSecretType and KubeSvcCreds to empty strings"); KubeSecretType = ""; KubeSvcCreds = ""; } //check if storeProperties contains ServerUsername key - - - Logger.LogInformation("Attempting to resolve ServerUsername from store properties or PAM provider."); + Logger.LogInformation("Attempting to resolve 'ServerUsername' from store properties or PAM provider"); var pamServerUsername = (string)PAMUtilities.ResolvePAMField(_resolver, Logger, "ServerUsername", ServerUsername); if (!string.IsNullOrEmpty(pamServerUsername)) { - Logger.LogInformation("ServerUsername resolved from PAM provider. Setting ServerUsername to resolved value."); - Logger.LogTrace("PAMServerUsername: " + pamServerUsername); + Logger.LogInformation("ServerUsername resolved from PAM provider, setting 'ServerUsername' to resolved value"); + Logger.LogTrace("PAMServerUsername: {Username}", pamServerUsername); ServerUsername = pamServerUsername; } else { - Logger.LogInformation("ServerUsername not resolved from PAM provider. Attempting to resolve Server Username from store properties."); + Logger.LogInformation("ServerUsername not resolved from PAM provider, attempting to resolve 'Server Username' from store properties"); pamServerUsername = (string)PAMUtilities.ResolvePAMField(_resolver, Logger, "Server Username", ServerUsername); if (!string.IsNullOrEmpty(pamServerUsername)) { - Logger.LogInformation("ServerUsername resolved from store properties. Setting ServerUsername to resolved value."); - Logger.LogTrace("PAMServerUsername: " + pamServerUsername); + Logger.LogInformation("ServerUsername resolved from store properties. Setting ServerUsername to resolved value"); + Logger.LogTrace("PAMServerUsername: {Username}", pamServerUsername); ServerUsername = pamServerUsername; } } if (string.IsNullOrEmpty(ServerUsername)) { - Logger.LogInformation("ServerUsername is empty. Setting ServerUsername to default value."); + Logger.LogInformation("ServerUsername is empty, setting 'ServerUsername' to default value: 'kubeconfig'"); ServerUsername = "kubeconfig"; } // Check if ServerPassword is empty and resolve from store properties or PAM provider - try { - Logger.LogInformation("Attempting to resolve ServerPassword from store properties or PAM provider."); + Logger.LogInformation("Attempting to resolve 'ServerPassword' from store properties or PAM provider"); var pamServerPassword = (string)PAMUtilities.ResolvePAMField(_resolver, Logger, "ServerPassword", ServerPassword); if (!string.IsNullOrEmpty(pamServerPassword)) { - Logger.LogInformation("ServerPassword resolved from PAM provider. Setting ServerPassword to resolved value."); + Logger.LogInformation("ServerPassword resolved from PAM provider, setting 'ServerPassword' to resolved value"); // Logger.LogTrace("PAMServerPassword: " + pamServerPassword); ServerPassword = pamServerPassword; } else { - Logger.LogInformation("ServerPassword not resolved from PAM provider. Attempting to resolve Server Password from store properties."); + Logger.LogInformation("ServerPassword not resolved from PAM provider, attempting to resolve 'Server Password' from store properties"); pamServerPassword = (string)PAMUtilities.ResolvePAMField(_resolver, Logger, "Server Password", ServerPassword); if (!string.IsNullOrEmpty(pamServerPassword)) { - Logger.LogInformation("ServerPassword resolved from store properties. Setting ServerPassword to resolved value."); + Logger.LogInformation("ServerPassword resolved from store properties, setting 'ServerPassword' to resolved value"); // Logger.LogTrace("PAMServerPassword: " + pamServerPassword); ServerPassword = pamServerPassword; } @@ -639,42 +652,42 @@ private void InitializeProperties(dynamic storeProperties) } catch (Exception e) { - Logger.LogError("Unable to resolve ServerPassword from store properties or PAM provider, defaulting to empty string."); + Logger.LogError("Unable to resolve 'ServerPassword' from store properties or PAM provider, defaulting to empty string"); ServerPassword = ""; - Logger.LogError(e.Message); - Logger.LogTrace(e.ToString()); - Logger.LogTrace(e.StackTrace); - // throw new ConfigurationException("Invalid configuration. ServerPassword not provided or is invalid."); + Logger.LogError("{Message}", e.Message); + Logger.LogTrace("{Message}",e.ToString()); + Logger.LogTrace("{Trace}", e.StackTrace); + // throw new ConfigurationException("Invalid configuration. ServerPassword not provided or is invalid"); } - // } else { - // Logger.LogError("Unable to resolve ServerPassword from store properties or PAM provider, defaulting to empty string."); - // throw new ConfigurationException("Invalid configuration. ServerPassword not provided or is invalid."); - // } try { + Logger.LogInformation("Attempting to resolve 'StorePassword' from store properties or PAM provider"); var pamStorePassword = (string)PAMUtilities.ResolvePAMField(_resolver, Logger, "StorePassword", StorePassword); if (!string.IsNullOrEmpty(pamStorePassword)) { + Logger.LogInformation("StorePassword resolved from PAM provider, setting 'StorePassword' to resolved value"); StorePassword = pamStorePassword; } else { + Logger.LogInformation("StorePassword not resolved from PAM provider, attempting to resolve 'Store Password' from store properties"); pamStorePassword = (string)PAMUtilities.ResolvePAMField(_resolver, Logger, "Store Password", StorePassword); if (!string.IsNullOrEmpty(pamStorePassword)) { + Logger.LogInformation("StorePassword resolved from store properties, setting 'StorePassword' to resolved value"); StorePassword = pamStorePassword; } } } catch (Exception e) { - Logger.LogError("Unable to resolve StorePassword from store properties or PAM provider, defaulting to empty string."); + Logger.LogError("Unable to resolve 'StorePassword' from store properties or PAM provider, defaulting to empty string"); StorePassword = ""; - Logger.LogError(e.Message); - Logger.LogTrace(e.ToString()); - Logger.LogTrace(e.StackTrace); - // throw new ConfigurationException("Invalid configuration. StorePassword not provided or is invalid."); + Logger.LogError("{Message}", e.Message); + Logger.LogTrace("{Message}",e.ToString()); + Logger.LogTrace("{Trace}", e.StackTrace); + // throw new ConfigurationException("Invalid configuration. StorePassword not provided or is invalid"); } if (ServerUsername == "kubeconfig" || string.IsNullOrEmpty(ServerUsername)) @@ -689,6 +702,7 @@ private void InitializeProperties(dynamic storeProperties) case "pfx": case "p12": case "pkcs12": + Logger.LogInformation("Kubernetes certificate store type is 'pfx'. Setting default values for 'PasswordFieldName' and 'CertificateDataFieldName'"); PasswordFieldName = storeProperties.ContainsKey("PasswordFieldName") ? storeProperties["PasswordFieldName"] : DefaultPFXPasswordSecretFieldName; PasswordIsSeparateSecret = storeProperties.ContainsKey("PasswordIsSeparateSecret") ? storeProperties["PasswordIsSeparateSecret"] : false; StorePasswordPath = storeProperties.ContainsKey("StorePasswordPath") ? storeProperties["StorePasswordPath"] : ""; @@ -697,6 +711,7 @@ private void InitializeProperties(dynamic storeProperties) CertificateDataFieldName = storeProperties.ContainsKey("CertificateDataFieldName") ? storeProperties["CertificateDataFieldName"] : DefaultPFXSecretFieldName; break; case "jks": + Logger.LogInformation("Kubernetes certificate store type is 'jks'. Setting default values for 'PasswordFieldName' and 'CertificateDataFieldName'"); PasswordFieldName = storeProperties.ContainsKey("PasswordFieldName") ? storeProperties["PasswordFieldName"] : DefaultPFXPasswordSecretFieldName; PasswordIsSeparateSecret = storeProperties.ContainsKey("PasswordIsSeparateSecret") ? bool.Parse(storeProperties["PasswordIsSeparateSecret"]) : false; StorePasswordPath = storeProperties.ContainsKey("StorePasswordPath") ? storeProperties["StorePasswordPath"] : ""; @@ -706,38 +721,44 @@ private void InitializeProperties(dynamic storeProperties) break; } - KubeClient = new KubeCertificateManagerClient(KubeSvcCreds); - - KubeHost = KubeClient.GetHost(); - KubeCluster = KubeClient.GetClusterName(); + Logger.LogTrace("Creating new KubeCertificateManagerClient object"); + // KubeClient = new KubeCertificateManagerClient(KubeSvcCreds); + // + // Logger.LogTrace("Getting KubeHost and KubeCluster from KubeClient"); + // KubeHost = KubeClient.GetHost(); + // Logger.LogTrace("KubeHost: {KubeHost}", KubeHost); + // + // Logger.LogTrace("Getting cluster name from KubeClient"); + // KubeCluster = KubeClient.GetClusterName(); + // Logger.LogTrace("KubeCluster: {KubeCluster}", KubeCluster); if (string.IsNullOrEmpty(KubeSecretName) && !string.IsNullOrEmpty(StorePath) && !Capability.Contains("NS") && !Capability.Contains("Cluster")) { - Logger.LogDebug("KubeSecretName is empty. Attempting to set KubeSecretName from StorePath."); + Logger.LogDebug("KubeSecretName is empty, attempting to set 'KubeSecretName' from StorePath"); resolveStorePath(StorePath); } if (string.IsNullOrEmpty(KubeNamespace) && !string.IsNullOrEmpty(StorePath)) { - Logger.LogDebug("KubeNamespace is empty. Attempting to set KubeNamespace from StorePath."); + Logger.LogDebug("KubeNamespace is empty, attempting to set 'KubeNamespace' from StorePath"); resolveStorePath(StorePath); } if (string.IsNullOrEmpty(KubeNamespace)) { - Logger.LogDebug("KubeNamespace is empty. Setting KubeNamespace to 'default'."); + Logger.LogDebug("KubeNamespace is empty, setting 'KubeNamespace' to 'default'"); KubeNamespace = "default"; } - Logger.LogDebug($"KubeNamespace: {KubeNamespace}"); - Logger.LogDebug($"KubeSecretName: {KubeSecretName}"); - Logger.LogDebug($"KubeSecretType: {KubeSecretType}"); + Logger.LogDebug("KubeNamespace: {KubeNamespace}", KubeNamespace); + Logger.LogDebug("KubeSecretName: {KubeSecretName}", KubeSecretName); + Logger.LogDebug("KubeSecretType: {KubeSecretType}", KubeSecretName); if (!string.IsNullOrEmpty(KubeSecretName)) return; // KubeSecretName = StorePath.Split("/").Last(); - Logger.LogWarning("KubeSecretName is empty. Setting KubeSecretName to StorePath."); + Logger.LogWarning("KubeSecretName is empty, setting 'KubeSecretName' to StorePath"); KubeSecretName = StorePath; - Logger.LogTrace("KubeSecretName: " + KubeSecretName); + Logger.LogTrace("KubeSecretName: {KubeSecretName}", KubeSecretName); } @@ -767,45 +788,51 @@ public string GetStorePath() secretType = KubeSecretType.ToLower(); } - Logger.LogTrace("secretType: " + secretType); - Logger.LogTrace("Entered switch statement based on secretType."); + Logger.LogTrace("secretType: {SecretType}", secretType); + Logger.LogTrace("Entered switch statement based on secretType"); switch (secretType) { case "secret": case "opaque": case "tls": case "tls_secret": - Logger.LogDebug("Kubernetes secret resource type. Setting secretType to 'secret'."); + Logger.LogDebug("Kubernetes secret resource type, setting secretType to 'secret'"); secretType = "secret"; break; case "cert": case "certs": case "certificate": case "certificates": - Logger.LogDebug("Kubernetes certificate resource type. Setting secretType to 'certificate'."); + Logger.LogDebug("Kubernetes certificate resource type, setting secretType to 'certificate'"); secretType = "certificate"; break; case "namespace": - storePath = $"{KubeClient.GetClusterName()}/namespace/{KubeNamespace}"; + Logger.LogDebug("Kubernetes namespace resource type, setting secretType to 'namespace'"); KubeSecretType = "namespace"; + + Logger.LogDebug("Setting store path to 'cluster/namespace/namespacename' for 'namespace' secret type"); + storePath = $"{KubeClient.GetClusterName()}/namespace/{KubeNamespace}"; + Logger.LogDebug("Returning storePath: {StorePath}", storePath); return storePath; case "cluster": + Logger.LogDebug("Kubernetes cluster resource type, setting secretType to 'cluster'"); KubeSecretType = "cluster"; + Logger.LogDebug("Returning storePath: {StorePath}", storePath); return storePath; default: - Logger.LogWarning("Unknown secret type. Will use value provided."); - Logger.LogTrace($"secretType: {secretType}"); + Logger.LogWarning("Unknown secret type '{SecretType}' will use value provided", secretType); + Logger.LogTrace("secretType: {SecretType}", secretType); break; } - Logger.LogTrace("Building StorePath."); + Logger.LogDebug("Building StorePath"); storePath = $"{KubeClient.GetClusterName()}/{KubeNamespace}/{secretType}/{KubeSecretName}"; - Logger.LogDebug("Returning StorePath: " + storePath); + Logger.LogDebug("Returning storePath: {StorePath}", storePath); return storePath; } catch (Exception e) { - Logger.LogError("Unknown error constructing canonical store path."); + Logger.LogError("Unknown error constructing canonical store path {Error}", e.Message); return StorePath; } @@ -813,10 +840,10 @@ public string GetStorePath() protected byte[] GetKeyBytes(X509Certificate2 certObj, string certPassword = null) { - Logger.LogTrace("Entered GetKeyBytes()"); - Logger.LogTrace("Key algo: " + certObj.GetKeyAlgorithm()); - Logger.LogTrace("Has private key: " + certObj.HasPrivateKey); - Logger.LogTrace("Pub key: " + certObj.GetPublicKey()); + Logger.LogDebug("Entered GetKeyBytes()"); + Logger.LogTrace("Key algo: {KeyAlgo}", certObj.GetKeyAlgorithm()); + Logger.LogTrace("Has private key: {HasPrivateKey}",certObj.HasPrivateKey); + Logger.LogTrace("Pub key: {PublicKey}", certObj.GetPublicKey()); byte[] keyBytes; @@ -843,7 +870,7 @@ protected byte[] GetKeyBytes(X509Certificate2 certObj, string certPassword = nul Logger.LogTrace("GetDSAPrivateKey().ExportPkcs8PrivateKey(): completed"); break; default: - Logger.LogWarning("Unknown key algorithm. Attempting to export as PKCS12."); + Logger.LogWarning("Unknown key algorithm, attempting to export as PKCS12"); Logger.LogTrace("Export(X509ContentType.Pkcs12, certPassword)"); keyBytes = certObj.Export(X509ContentType.Pkcs12, certPassword); Logger.LogTrace("Export(X509ContentType.Pkcs12, certPassword) complete"); @@ -851,24 +878,23 @@ protected byte[] GetKeyBytes(X509Certificate2 certObj, string certPassword = nul } if (keyBytes != null) return keyBytes; - Logger.LogError("Key bytes are null. This is unexpected."); - throw new Exception("Key bytes are null. This is unexpected."); - - return keyBytes; + Logger.LogError("Unable to parse private key"); + + throw new InvalidKeyException($"Unable to parse private key from certificate '{certObj.Thumbprint}'"); } catch (Exception e) { - Logger.LogError("Unknown error getting key bytes, but we're going to try a different method."); - Logger.LogError(e.Message); - Logger.LogTrace(e.ToString()); - Logger.LogTrace(e.StackTrace); + Logger.LogError("Unknown error getting key bytes, but we're going to try a different method"); + Logger.LogError("{Message}", e.Message); + Logger.LogTrace("{Message}",e.ToString()); + Logger.LogTrace("{Trace}", e.StackTrace); try { if (certObj.HasPrivateKey) { try { - Logger.LogDebug("Attempting to export private key as PKCS8."); + Logger.LogDebug("Attempting to export private key as PKCS8"); Logger.LogTrace("ExportPkcs8PrivateKey()"); keyBytes = certObj.PrivateKey.ExportPkcs8PrivateKey(); Logger.LogTrace("ExportPkcs8PrivateKey() complete"); @@ -878,12 +904,12 @@ protected byte[] GetKeyBytes(X509Certificate2 certObj, string certPassword = nul } catch (Exception e2) { - Logger.LogError("Unknown error exporting private key as PKCS8, but we're going to try a a final method ."); + Logger.LogError("Unknown error exporting private key as PKCS8, but we're going to try a a final method "); Logger.LogError(e2.Message); Logger.LogTrace(e2.ToString()); Logger.LogTrace(e2.StackTrace); //attempt to export encrypted pkcs8 - Logger.LogDebug("Attempting to export encrypted PKCS8 private key."); + Logger.LogDebug("Attempting to export encrypted PKCS8 private key"); Logger.LogTrace("ExportEncryptedPkcs8PrivateKey()"); keyBytes = certObj.PrivateKey.ExportEncryptedPkcs8PrivateKey(certPassword, new PbeParameters( @@ -898,16 +924,16 @@ protected byte[] GetKeyBytes(X509Certificate2 certObj, string certPassword = nul } catch (Exception ie) { - Logger.LogError("Unknown error exporting private key as PKCS8, returning null."); - Logger.LogError(ie.Message); - Logger.LogTrace(ie.ToString()); - Logger.LogTrace(ie.StackTrace); + Logger.LogError("Unknown error exporting private key as PKCS8, returning null"); + Logger.LogError("{Message}", ie.Message); + Logger.LogTrace("{Message}",ie.ToString()); + Logger.LogTrace("{Trace}", ie.StackTrace); } - return new byte[] { }; + return Array.Empty(); } } - static protected JobResult FailJob(string message, long jobHistoryId) + protected static JobResult FailJob(string message, long jobHistoryId) { return new JobResult { @@ -917,7 +943,7 @@ static protected JobResult FailJob(string message, long jobHistoryId) }; } - static protected JobResult SuccessJob(long jobHistoryId, string jobMessage = null) + protected static JobResult SuccessJob(long jobHistoryId, string jobMessage = null) { var result = new JobResult { @@ -940,72 +966,71 @@ protected string ParseJobPrivateKey(ManagementJobConfiguration config) if (string.IsNullOrWhiteSpace(config.JobCertificate.Alias)) Logger.LogTrace("No Alias Found"); // Load PFX - Logger.LogTrace("Loading PFX..."); + Logger.LogTrace("Loading PFX from job contents"); var pfxBytes = Convert.FromBase64String(config.JobCertificate.Contents); - Logger.LogTrace("Loaded PFX..."); - Pkcs12Store p; - string alias = config.JobCertificate.Alias; + Logger.LogTrace("PFX loaded successfully"); + + var alias = config.JobCertificate.Alias; + Logger.LogTrace("Alias: {Alias}", alias); - Logger.LogTrace("Creating Pkcs12Store..."); + Logger.LogTrace("Creating Pkcs12Store object"); // Load the PKCS12 bytes into a Pkcs12Store object - using (MemoryStream pkcs12Stream = new MemoryStream(pfxBytes)) - { - Pkcs12Store store = new Pkcs12StoreBuilder().Build(); + using var pkcs12Stream = new MemoryStream(pfxBytes); + var store = new Pkcs12StoreBuilder().Build(); - store.Load(pkcs12Stream, config.JobCertificate.PrivateKeyPassword.ToCharArray()); + Logger.LogDebug("Attempting to load PFX into store using password"); + store.Load(pkcs12Stream, config.JobCertificate.PrivateKeyPassword.ToCharArray()); - // Find the private key entry with the given alias - foreach (string aliasName in store.Aliases) - { - if (aliasName.Equals(alias) && store.IsKeyEntry(aliasName)) - { - AsymmetricKeyEntry keyEntry = store.GetKey(aliasName); + // Find the private key entry with the given alias + Logger.LogDebug("Attempting to get private key entry with alias"); + foreach (var aliasName in store.Aliases) + { + Logger.LogTrace("Alias: {Alias}", aliasName); + if (!aliasName.Equals(alias) || !store.IsKeyEntry(aliasName)) continue; + Logger.LogDebug("Alias found, attempting to get private key"); + var keyEntry = store.GetKey(aliasName); - // Convert the private key to unencrypted PEM format - using (StringWriter stringWriter = new StringWriter()) - { - PemWriter pemWriter = new PemWriter(stringWriter); - pemWriter.WriteObject(keyEntry.Key); - pemWriter.Writer.Flush(); + // Convert the private key to unencrypted PEM format + using var stringWriter = new StringWriter(); + var pemWriter = new PemWriter(stringWriter); + pemWriter.WriteObject(keyEntry.Key); + pemWriter.Writer.Flush(); - return stringWriter.ToString(); - } - } - } + Logger.LogDebug("Private key found for alias {Alias}, returning private key", alias); + return stringWriter.ToString(); } + Logger.LogDebug("Alias '{Alias}' not found, returning null private key", alias); return null; // Private key with the given alias not found - - } protected string getK8SStorePassword(V1Secret certData) { Logger.LogDebug("Entered getK8SStorePassword()"); - Logger.LogDebug("Attempting to get store password from K8S secret."); - var storePasswordBytes = new byte[] { }; + Logger.LogDebug("Attempting to get store password from K8S secret"); + var storePasswordBytes = Array.Empty(); // if secret is a buddy pass if (!string.IsNullOrEmpty(StorePassword)) { - Logger.LogDebug("StorePassword is not null or empty, using StorePassword"); - var passwordHash = GetSHA256Hash(StorePassword); - Logger.LogTrace("Password hash: " + passwordHash); + Logger.LogDebug("Using provided 'StorePassword'"); + // var passwordHash = GetSHA256Hash(StorePassword); + // Logger.LogTrace("Password hash: " + passwordHash); storePasswordBytes = Encoding.UTF8.GetBytes(StorePassword); } else if (!string.IsNullOrEmpty(StorePasswordPath)) { // Split password path into namespace and secret name - Logger.LogDebug("Store password is null or empty, using StorePasswordPath"); + Logger.LogDebug("Store password is null or empty and StorePasswordPath is set, attempting to read password from K8S buddy secret"); Logger.LogTrace("Password path: {Path}", StorePasswordPath); - Logger.LogDebug("Splitting password path by /"); + Logger.LogTrace("Splitting password path by /"); var passwordPath = StorePasswordPath.Split("/"); Logger.LogDebug("Password path length: {Len}", passwordPath.Length.ToString()); var passwordNamespace = ""; var passwordSecretName = ""; if (passwordPath.Length == 1) { - Logger.LogDebug("Password path length is 1, using KubeNamespace."); + Logger.LogDebug("Password path length is 1, using KubeNamespace"); passwordNamespace = KubeNamespace; Logger.LogTrace("Password namespace: {Namespace}", passwordNamespace); passwordSecretName = passwordPath[0]; @@ -1026,17 +1051,27 @@ protected string getK8SStorePassword(V1Secret certData) Logger.LogDebug("Attempting to read K8S buddy secret"); var k8sPasswordObj = KubeClient.ReadBuddyPass(passwordSecretName, passwordNamespace); storePasswordBytes = k8sPasswordObj.Data[PasswordFieldName]; - var passwordHash = GetSHA256Hash(Encoding.UTF8.GetString(storePasswordBytes)); - Logger.LogTrace("Password hash: {Pwd}", passwordHash); + // var passwordHash = GetSHA256Hash(Encoding.UTF8.GetString(storePasswordBytes)); + // Logger.LogTrace("Password hash: {Pwd}", passwordHash); + if (storePasswordBytes == null) + { + Logger.LogError("Password not found in K8S buddy secret"); + throw new InvalidK8SSecretException("Password not found in K8S buddy secret"); // todo: should this be thrown? + } Logger.LogDebug("K8S buddy secret read successfully"); } else if (certData != null && certData.Data.TryGetValue(PasswordFieldName, out var value1)) { Logger.LogDebug("Attempting to read password from PasswordFieldName"); storePasswordBytes = value1; - var passwordHash = GetSHA256Hash(Encoding.UTF8.GetString(storePasswordBytes)); - Logger.LogTrace("Password hash: {Pwd}", passwordHash); - Logger.LogDebug("Password read successfully."); + // var passwordHash = GetSHA256Hash(Encoding.UTF8.GetString(storePasswordBytes)); + // Logger.LogTrace("Password hash: {Pwd}", passwordHash); + if (storePasswordBytes == null) + { + Logger.LogError("Password not found in K8S secret"); + throw new InvalidK8SSecretException("Password not found in K8S secret"); // todo: should this be thrown? + } + Logger.LogDebug("Password read successfully"); } else { @@ -1044,12 +1079,12 @@ protected string getK8SStorePassword(V1Secret certData) var passwdEx = ""; if (!string.IsNullOrEmpty(StorePasswordPath)) { - passwdEx = "Store secret '" + StorePasswordPath + "'did not contain key '" + CertificateDataFieldName + "' or '" + PasswordFieldName + "'." + - " Please provide a valid store password and try again."; + passwdEx = "Store secret '" + StorePasswordPath + "'did not contain key '" + CertificateDataFieldName + "' or '" + PasswordFieldName + "'" + + " Please provide a valid store password and try again"; } else { - passwdEx = "Invalid store password. Please provide a valid store password and try again."; + passwdEx = "Invalid store password. Please provide a valid store password and try again"; } Logger.LogError("{Msg}", passwdEx); throw new Exception(passwdEx); @@ -1058,63 +1093,88 @@ protected string getK8SStorePassword(V1Secret certData) //convert password to string var storePassword = Encoding.UTF8.GetString(storePasswordBytes); // Logger.LogTrace("Store password: {Pwd}", storePassword); - var passwordHash2 = GetSHA256Hash(storePassword); - Logger.LogTrace("Password hash: {Pwd}", passwordHash2); + // var passwordHash2 = GetSHA256Hash(storePassword); + // Logger.LogTrace("Password hash: {Pwd}", passwordHash2); + Logger.LogDebug("Returning store password"); return storePassword; } protected Pkcs12Store LoadPkcs12Store(byte[] pkcs12Data, string password) { + Logger.LogDebug("Entered LoadPkcs12Store()"); var storeBuilder = new Pkcs12StoreBuilder(); var store = storeBuilder.Build(); + Logger.LogDebug("Attempting to load PKCS12 store"); using var pkcs12Stream = new MemoryStream(pkcs12Data); if (password != null) store.Load(pkcs12Stream, password.ToCharArray()); + Logger.LogDebug("PKCS12 store loaded successfully"); return store; } - protected string getCertificatePem(Pkcs12Store store, string password, string alias = "") + protected string GetCertificatePem(Pkcs12Store store, string password, string alias = "") { + Logger.LogDebug("Entered GetCertificatePem()"); if (string.IsNullOrEmpty(alias)) { alias = store.Aliases.Cast().FirstOrDefault(store.IsKeyEntry); } + + Logger.LogDebug("Attempting to get certificate with alias {Alias}", alias); var cert = store.GetCertificate(alias).Certificate; using var stringWriter = new StringWriter(); var pemWriter = new PemWriter(stringWriter); + + Logger.LogDebug("Attempting to write certificate to PEM format"); pemWriter.WriteObject(cert); pemWriter.Writer.Flush(); + Logger.LogTrace("certificate:\n{Cert}", stringWriter.ToString()); + + Logger.LogDebug("Returning certificate in PEM format"); return stringWriter.ToString(); } protected string getPrivateKeyPem(Pkcs12Store store, string password, string alias = "") { + Logger.LogDebug("Entered getPrivateKeyPem()"); if (string.IsNullOrEmpty(alias)) { - alias = store.Aliases.Cast().FirstOrDefault(store.IsKeyEntry); + Logger.LogDebug("Alias is empty, attempting to get key entry alias"); + alias = store.Aliases.FirstOrDefault(store.IsKeyEntry); } + + Logger.LogDebug("Attempting to get private key with alias {Alias}", alias); var privateKey = store.GetKey(alias).Key; using var stringWriter = new StringWriter(); var pemWriter = new PemWriter(stringWriter); + + Logger.LogDebug("Attempting to write private key to PEM format"); pemWriter.WriteObject(privateKey); pemWriter.Writer.Flush(); + // Logger.LogTrace("private key:\n{Key}", stringWriter.ToString()); + Logger.LogDebug("Returning private key in PEM format for alias '{Alias}'", alias); return stringWriter.ToString(); } protected List getCertChain(Pkcs12Store store, string password, string alias = "") { + Logger.LogDebug("Entered getCertChain()"); if (string.IsNullOrEmpty(alias)) { + Logger.LogDebug("Alias is empty, attempting to get key entry alias"); alias = store.Aliases.Cast().FirstOrDefault(store.IsKeyEntry); } + var chain = new List(); + Logger.LogDebug("Attempting to get certificate chain with alias {Alias}", alias); var chainCerts = store.GetCertificateChain(alias); foreach (var chainCert in chainCerts) { + Logger.LogTrace("Adding certificate to chain"); using var stringWriter = new StringWriter(); var pemWriter = new PemWriter(stringWriter); pemWriter.WriteObject(chainCert.Certificate); @@ -1122,6 +1182,8 @@ protected List getCertChain(Pkcs12Store store, string password, string a chain.Add(stringWriter.ToString()); } + Logger.LogTrace("Certificate chain:\n{Chain}", string.Join("\n", chain)); + Logger.LogDebug("Returning certificate chain"); return chain; } @@ -1141,16 +1203,14 @@ public static bool IsDerFormat(byte[] data) public static string ConvertDerToPem(byte[] data) { var pemObject = new PemObject("CERTIFICATE", data); - using (var stringWriter = new StringWriter()) - { - var pemWriter = new PemWriter(stringWriter); - pemWriter.WriteObject(pemObject); - pemWriter.Writer.Flush(); - return stringWriter.ToString(); - } + using var stringWriter = new StringWriter(); + var pemWriter = new PemWriter(stringWriter); + pemWriter.WriteObject(pemObject); + pemWriter.Writer.Flush(); + return stringWriter.ToString(); } - public string GetSHA256Hash(string input) + protected static string GetSHA256Hash(string input) { var passwordHashBytes = SHA256.Create().ComputeHash(Encoding.UTF8.GetBytes(input)); var passwordHash = BitConverter.ToString(passwordHashBytes).Replace("-", "").ToLower(); diff --git a/kubernetes-orchestrator-extension/Keyfactor.Orchestrators.K8S.csproj b/kubernetes-orchestrator-extension/Keyfactor.Orchestrators.K8S.csproj index 5f3befe..ce85ab7 100644 --- a/kubernetes-orchestrator-extension/Keyfactor.Orchestrators.K8S.csproj +++ b/kubernetes-orchestrator-extension/Keyfactor.Orchestrators.K8S.csproj @@ -21,10 +21,10 @@ - - + + - +