From 29a65eafa461954259d68529b66c52efb0daee29 Mon Sep 17 00:00:00 2001 From: Brian Hill Date: Fri, 3 May 2024 13:34:27 -0400 Subject: [PATCH 1/3] Additional Logging fixed error when Public Key on Lookup Does Not Exist --- PaloAlto/Jobs/Management.cs | 256 ++++++++++++++++++++++++------------ 1 file changed, 169 insertions(+), 87 deletions(-) diff --git a/PaloAlto/Jobs/Management.cs b/PaloAlto/Jobs/Management.cs index 8922de9..4ea8067 100644 --- a/PaloAlto/Jobs/Management.cs +++ b/PaloAlto/Jobs/Management.cs @@ -52,6 +52,8 @@ public class Management : IManagementJobExtension public Management(IPAMSecretResolver resolver) { _resolver = resolver; + _logger = LogHandler.GetClassLogger(); + _logger.LogTrace("Initialized Management with IPAMSecretResolver."); } private string ServerPassword { get; set; } @@ -68,20 +70,27 @@ public Management(IPAMSecretResolver resolver) public JobResult ProcessJob(ManagementJobConfiguration jobConfiguration) { _logger = LogHandler.GetClassLogger(); + _logger.LogTrace($"Processing job with configuration: {JsonConvert.SerializeObject(jobConfiguration)}"); StoreProperties = JsonConvert.DeserializeObject( jobConfiguration.CertificateStoreDetails.Properties, new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Populate }); var json = JsonConvert.SerializeObject(jobConfiguration.JobProperties, Formatting.Indented); + + _logger.LogTrace($"Job Properties: {json}"); JobEntryParams = JsonConvert.DeserializeObject( json, new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Populate }); - + + + + _logger.MethodExit(); return PerformManagement(jobConfiguration); } private string ResolvePamField(string name, string value) { _logger.LogTrace($"Attempting to resolved PAM eligible field {name}"); + return _resolver.Resolve(value); } @@ -93,9 +102,14 @@ private JobResult PerformManagement(ManagementJobConfiguration config) ServerPassword = ResolvePamField("ServerPassword", config.ServerPassword); ServerUserName = ResolvePamField("ServerUserName", config.ServerUsername); + _logger.LogTrace("Validating Store Properties"); + var (valid, result) = Validators.ValidateStoreProperties(StoreProperties, config.CertificateStoreDetails.StorePath, config.CertificateStoreDetails.ClientMachine, config.JobHistoryId, ServerUserName, ServerPassword); + + _logger.LogTrace($"Validated Store Properties and valid={valid}"); + if (!valid) return result; var complete = new JobResult @@ -111,12 +125,14 @@ private JobResult PerformManagement(ManagementJobConfiguration config) _logger.LogTrace("Adding..."); _logger.LogTrace($"Add Config Json {JsonConvert.SerializeObject(config)}"); complete = PerformAddition(config); + _logger.LogTrace("Finished Adding..."); } else if (config.OperationType.ToString() == "Remove") { _logger.LogTrace("Removing..."); _logger.LogTrace($"Remove Config Json {JsonConvert.SerializeObject(config)}"); complete = PerformRemoval(config); + _logger.LogTrace("Finished Removing..."); } return complete; @@ -146,15 +162,16 @@ private JobResult PerformRemoval(ManagementJobConfiguration config) _logger.LogTrace( $"Alias to Remove From Palo Alto: {config.JobCertificate.Alias}"); if (!DeleteCertificate(config, client, warnings, out var deleteResult)) return deleteResult; - + _logger.LogTrace("Committing Changes"); warnings = CommitChanges(config, client, warnings); - + _logger.LogTrace("Committed Changes"); if (warnings.Length > 0) { + _logger.LogTrace("Warnings Found"); deleteResult.FailureMessage = warnings; deleteResult.Result = OrchestratorJobStatusJobResult.Warning; } - + _logger.MethodExit(); return deleteResult; } catch (Exception e) @@ -168,23 +185,26 @@ private JobResult PerformRemoval(ManagementJobConfiguration config) } } - private static bool IsPanoramaDevice(ManagementJobConfiguration config) + private bool IsPanoramaDevice(ManagementJobConfiguration config) { + _logger.MethodEntry(); return config.CertificateStoreDetails.StorePath.Length > 1; } private bool CheckForDuplicate(ManagementJobConfiguration config, PaloAltoClient client, string certificateName) { + _logger.MethodEntry(); try { + _logger.LogTrace("checking for cert list"); var rawCertificatesResult = client.GetCertificateList( $"{config.CertificateStoreDetails.StorePath}/certificate/entry[@name='{certificateName}']") .Result; - - + LogResponse(rawCertificatesResult); + _logger.LogTrace("Checked for cert list"); var certificatesResult = rawCertificatesResult.CertificateResult.Entry.FindAll(c => c.PublicKey != null); - + _logger.MethodExit(); return certificatesResult.Count > 0; } @@ -224,30 +244,35 @@ private JobResult PerformAddition(ManagementJobConfiguration config) if (duplicate && config.Overwrite || !duplicate) { _logger.LogTrace("Either not a duplicate or overwrite was chosen...."); - string certPem; - + _logger.LogTrace($"Found Private Key {config.JobCertificate.PrivateKeyPassword}"); if (string.IsNullOrWhiteSpace(config.JobCertificate.Alias)) _logger.LogTrace("No Alias Found"); - certPem = GetPemFile(config); + var certPem = GetPemFile(config); _logger.LogTrace($"Got certPem {certPem}"); - + _logger.LogTrace("About to check chain info"); //1. Get the chain in a list starting with root first, any intermediate then leaf - var orderedChainList = GetCertificateChain(config.JobCertificate.Contents, config.JobCertificate.PrivateKeyPassword); - var alias = config.JobCertificate.Alias; + var orderedChainList = GetCertificateChain(config.JobCertificate?.Contents, config.JobCertificate?.PrivateKeyPassword); + _logger.LogTrace("Checked chain info"); + var alias = config.JobCertificate.Alias; + _logger.LogTrace($"Alias {alias}"); //1. If the leaf cert is a duplicate then you rename the cert and update it. So you don't have to delete tls profile and cause downtime if (duplicate) { - DateTime currentTime = DateTime.Now; - alias = GenerateName(alias); //fix name length + _logger.LogTrace("Duplicate!"); + alias = GenerateName(alias); //fix name length + _logger.LogTrace($"New Alias {alias}"); } //2. Check palo alto for existing thumbprints of anything in the chain + _logger.LogTrace("Checking for existing thumbprints of anything in the chain"); var rawCertificatesResult = client.GetCertificateList($"{config.CertificateStoreDetails.StorePath}/certificate/entry").Result; + LogResponse(rawCertificatesResult); + _logger.LogTrace("Checked for existing thumbprints of anything in the chain"); List certificates = new List(); ErrorSuccessResponse content = null; string errorMsg = string.Empty; @@ -606,37 +631,53 @@ private Task SetBindings(ManagementJobConfiguration config private List<(X509Certificate2 certificate, string type)> GetCertificateChain(string jobCertificate, string password) { + _logger.MethodEntry(); // Decode the base64-encoded chain to get the bytes byte[] certificateChainBytes = Convert.FromBase64String(jobCertificate); + _logger.LogTrace($"Cert Chain Bytes: {certificateChainBytes}"); // Create a collection to hold the certificates X509Certificate2Collection certificateCollection = new X509Certificate2Collection(); + _logger.LogTrace($"Created certificate collection"); + // Load the certificates from the byte array - certificateCollection.Import(certificateChainBytes, password, X509KeyStorageFlags.Exportable); + certificateCollection.Import(certificateChainBytes, password, X509KeyStorageFlags.Exportable); + + _logger.LogTrace($"Imported collection"); // Identify the root certificate X509Certificate2 rootCertificate = FindRootCertificate(certificateCollection); + _logger.LogTrace("Found Root Certificate"); + // Create a list to hold the ordered certificates - List<(X509Certificate2 certificate, string certType)> orderedCertificates = new List<(X509Certificate2, string)>(); + List<(X509Certificate2 certificate, string certType)> orderedCertificates = new List<(X509Certificate2, string)>(); + + _logger.LogTrace("Created a list to hold the ordered certificates"); // Add the root certificate to the ordered list - if (rootCertificate != null) - orderedCertificates.Add((rootCertificate, "root")); + if (rootCertificate != null) + orderedCertificates.Add((rootCertificate, "root")); + + _logger.LogTrace("Added Root To Collection"); // Add intermediate certificates to the ordered list and mark them as intermediate foreach (X509Certificate2 certificate in certificateCollection) { + _logger.LogTrace("In loop to Add intermediate certificates to the ordered list and mark them as intermediate"); // Exclude root certificate if (!certificate.Equals(rootCertificate)) - { + { + _logger.LogTrace("Excluded root certificate"); // Check if the certificate is not the leaf certificate bool isLeaf = true; foreach (X509Certificate2 potentialIssuer in certificateCollection) { - if (certificate.Subject == potentialIssuer.Issuer && !potentialIssuer.Equals(certificate)) - { + _logger.LogTrace("Check if the certificate is not the leaf certificate"); + if (certificate?.Subject == potentialIssuer?.Issuer && potentialIssuer!=null && !potentialIssuer.Equals(certificate)) + { + _logger.LogTrace("Leaf is false"); isLeaf = false; break; } @@ -645,6 +686,7 @@ private Task SetBindings(ManagementJobConfiguration config // If the certificate is not the leaf certificate, add it as an intermediate certificate if (!isLeaf) { + _logger.LogTrace("If the certificate is not the leaf certificate, add it as an intermediate certificate"); orderedCertificates.Add((certificate, "intermediate")); } } @@ -653,139 +695,179 @@ private Task SetBindings(ManagementJobConfiguration config // Add leaf certificates to the ordered list foreach (X509Certificate2 certificate in certificateCollection) { + _logger.LogTrace("Check for add leaf certificates to the ordered list"); if (!orderedCertificates.Exists(c => c.certificate != null && c.certificate.Equals(certificate))) - { + { + _logger.LogTrace("Added leaf certificates to the ordered list"); orderedCertificates.Add((certificate, "leaf")); } } - + _logger.MethodExit(); return orderedCertificates; } private X509Certificate2 FindRootCertificate(X509Certificate2Collection certificates) { + _logger.MethodEntry(); foreach (X509Certificate2 certificate in certificates) { + _logger.LogTrace("Looping through all the certs to find the root"); + if (IsRootCertificate(certificate, certificates)) - { + { + _logger.LogTrace("Found Root"); return certificate; } } - + _logger.MethodExit(); // Return null if no root certificate is found return null; } private bool IsRootCertificate(X509Certificate2 certificate, X509Certificate2Collection certificates) { + _logger.MethodEntry(); // Check if the certificate is self-signed - if (certificate.Subject == certificate.Issuer) + if (certificate?.Subject == certificate?.Issuer) { + _logger.LogTrace("Subject is equal to issuer"); // Check if there is no issuer in the collection with a matching subject foreach (X509Certificate2 issuerCertificate in certificates) { - if (issuerCertificate.Subject == certificate.Subject && !issuerCertificate.Equals(certificate)) - { + _logger.LogTrace("Checking if there is no issuer in the collection with matching subject"); + if (issuerCertificate.Subject == certificate?.Subject && !issuerCertificate.Equals(certificate)) + { + _logger.LogTrace("Subject equal cert subject and issuer cert not equal to certificate"); + _logger.MethodExit(); return false; } } - + _logger.MethodExit(); return true; } - + _logger.MethodExit(); return false; } - static string[] ExtractCertificateData(string text) - { + private string[] ExtractCertificateData(string text) + { + _logger.MethodEntry(); List certDataList = new List(); int startIndex = 0; - while (startIndex != -1) - { - startIndex = text.IndexOf("-----BEGIN CERTIFICATE-----", startIndex, StringComparison.Ordinal); - if (startIndex != -1) - { - int endIndex = text.IndexOf("-----END CERTIFICATE-----", startIndex, StringComparison.Ordinal); - if (endIndex != -1) - { - int length = endIndex - startIndex - "-----BEGIN CERTIFICATE-----".Length; - if (length >= 0) - { - certDataList.Add(text.Substring(startIndex + "-----BEGIN CERTIFICATE-----".Length, length)); - startIndex = endIndex + "-----END CERTIFICATE-----".Length; - } - else - { - break; - } - } - else - { - break; - } - } - } - + while (startIndex != -1) + { + startIndex = text.IndexOf("-----BEGIN CERTIFICATE-----", startIndex, StringComparison.Ordinal); + if (startIndex != -1) + { + int endIndex = text.IndexOf("-----END CERTIFICATE-----", startIndex, StringComparison.Ordinal); + if (endIndex != -1) + { + int length = endIndex - startIndex - "-----BEGIN CERTIFICATE-----".Length; + if (length >= 0) + { + certDataList.Add(text.Substring(startIndex + "-----BEGIN CERTIFICATE-----".Length, length)); + startIndex = endIndex + "-----END CERTIFICATE-----".Length; + } + else + { + break; + } + } + else + { + break; + } + } + } + _logger.LogTrace($"Cert Data List: {certDataList?.Count}"); + _logger.MethodExit(); return certDataList.ToArray(); } - public static string ExportToPem(X509Certificate2 certificate) + public string ExportToPem(X509Certificate2 certificate) { + _logger.MethodEntry(); StringBuilder builder = new StringBuilder(); builder.AppendLine("-----BEGIN CERTIFICATE-----"); builder.AppendLine(Convert.ToBase64String(certificate.Export(X509ContentType.Cert), Base64FormattingOptions.InsertLineBreaks)); - builder.AppendLine("-----END CERTIFICATE-----"); + builder.AppendLine("-----END CERTIFICATE-----"); + _logger.LogTrace($"String builder results: {builder?.ToString()}"); + _logger.MethodExit(); return builder.ToString(); } - static string RemoveWhitespace(string input) + private string RemoveWhitespace(string input) { + _logger.MethodEntry(); StringBuilder sb = new StringBuilder(); - foreach (char c in input) - { - if (!char.IsWhiteSpace(c)) - { - sb.Append(c); - } - } + foreach (char c in input) + { + if (!char.IsWhiteSpace(c)) + { + sb.Append(c); + } + } + _logger.LogTrace($"String builder results: {sb?.ToString()}"); + _logger.MethodExit(); return sb.ToString(); } private bool ThumbprintFound(string thumbprintToSearch, List certificates, CertificateListResponse rawCertificatesResult) { - foreach (var responseItem in rawCertificatesResult.CertificateResult.Entry) - { - string[] certDataArray = ExtractCertificateData(responseItem.PublicKey); - - // Remove whitespace characters and parse each certificate - foreach (string certData in certDataArray) - { - byte[] rawData = Convert.FromBase64String(RemoveWhitespace(certData)); - X509Certificate2 cert = new X509Certificate2(rawData); - certificates.Add(cert); - } - } - - X509Certificate2 foundCertificate = certificates.FirstOrDefault(cert => cert.Thumbprint != null && cert.Thumbprint.Equals(thumbprintToSearch, StringComparison.OrdinalIgnoreCase)); + _logger.MethodEntry(); + foreach (var responseItem in rawCertificatesResult.CertificateResult.Entry) + { + _logger.LogTrace("Looping through Thumbprints"); + string[] certDataArray = null; + if (responseItem?.PublicKey != null) + { + certDataArray = ExtractCertificateData(responseItem.PublicKey); + } + else + { + // Handle the case where PublicKey is null + _logger.LogTrace("PublicKey is not available."); + } + _logger.LogTrace("Got CertData Array"); + if (certDataArray != null) + { + // Remove whitespace characters and parse each certificate + foreach (string certData in certDataArray) + { + _logger.LogTrace("Inside removing whitespace"); + byte[] rawData = Convert.FromBase64String(RemoveWhitespace(certData)); + _logger.LogTrace("Converted From Base64"); + X509Certificate2 cert = new X509Certificate2(rawData); + _logger.LogTrace("Adding to collection"); + certificates.Add(cert); + _logger.LogTrace("Added to collection"); + } + } + } + _logger.LogTrace("Finding Cert"); + X509Certificate2 foundCertificate = certificates.FirstOrDefault(cert => cert.Thumbprint != null && cert.Thumbprint.Equals(thumbprintToSearch, StringComparison.OrdinalIgnoreCase)); + _logger.LogTrace($"Found cert {foundCertificate}"); + _logger.MethodExit(); if (foundCertificate != null) return true; - return false; } private ErrorSuccessResponse SetTrustedRoot(string jobCertificateAlias, PaloAltoClient client, string templateName) { - _logger.MethodEntry(LogLevel.Debug); + _logger.MethodEntry(); try { - + _logger.LogTrace("Setting Trusted Root"); var result = client.SubmitSetTrustedRoot(jobCertificateAlias, templateName); + _logger.LogTrace("Trusted Root Set"); _logger.LogTrace(result.Result.LineMsg.Line.Count > 0 ? $"Set Trusted Root Response {string.Join(" ,", result.Result.LineMsg.Line)}" : $"Set Trusted Root Response {result.Result.LineMsg.StringMsg}"); + _logger.MethodExit(); return result.Result; } catch (Exception e) From 31dcf9fa4cdbcf877d39def599ca9da73706c3ff Mon Sep 17 00:00:00 2001 From: Brian Hill <76450501+bhillkeyfactor@users.noreply.github.com> Date: Mon, 13 May 2024 10:31:04 -0400 Subject: [PATCH 2/3] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c6ed28..39733b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +2.1.1 +* Bug - Add Renew Failure Object Reference Error when Adding/Renewing a cert. + 2.1.0 * Support for Pan Level Certficates * Support for Pushing Entire Certificate Chain to Panorama From 36c8b6b723f7fbff74f44dfecbdf6a8dfb3f3971 Mon Sep 17 00:00:00 2001 From: Brian Hill <76450501+bhillkeyfactor@users.noreply.github.com> Date: Tue, 25 Jun 2024 14:13:17 -0400 Subject: [PATCH 3/3] Update CHANGELOG.md --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4772cf5..f7053ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +2.2.0 +* Removed support for binding cert to new binding location, can only update certs that are previously bound +* Support for replacing certs on all binding locations both Panorama and Firewalls as long as it was there before +* Support for Virtual Systems on Firewalls, tested with only Azure Virtual Version of Firewall +* Support for Virtual Systems on Panorama Templates + 2.1.0 * Support for Pan Level Certficates * Support for Pushing Entire Certificate Chain to Panorama