Fix PKI syncs to return intermidiate chain and root CA on the json object

This commit is contained in:
Carlos Monastyrski
2025-11-20 10:32:31 -03:00
parent 002969a32d
commit 4ed9ca106e
4 changed files with 70 additions and 3 deletions

View File

@@ -196,3 +196,62 @@ export const convertExtendedKeyUsageArrayToLegacy = (
): CertExtendedKeyUsage[] | undefined => {
return usages?.map(convertToLegacyExtendedKeyUsage);
};
/**
* Parses a PEM-formatted certificate chain and returns individual certificates
* @param certificateChain - PEM-formatted certificate chain
* @returns Array of individual PEM certificates
*/
const parseCertificateChain = (certificateChain: string): string[] => {
if (!certificateChain || typeof certificateChain !== "string") {
return [];
}
const certRegex = new RE2(/-----BEGIN CERTIFICATE-----[\s\S]*?-----END CERTIFICATE-----/g);
const certificates = certificateChain.match(certRegex);
return certificates ? certificates.map((cert) => cert.trim()) : [];
};
/**
* Removes the root CA certificate from a certificate chain, leaving only intermediate certificates.
* If the chain contains only the root CA certificate, returns an empty string.
*
* @param certificateChain - PEM-formatted certificate chain containing leaf + intermediates + root CA
* @returns PEM-formatted certificate chain with only intermediate certificates (no root CA)
*/
export const removeRootCaFromChain = (certificateChain?: string): string => {
if (!certificateChain || typeof certificateChain !== "string") {
return "";
}
const certificates = parseCertificateChain(certificateChain);
if (certificates.length === 0) {
return "";
}
const intermediateCerts = certificates.slice(0, -1);
return intermediateCerts.join("\n");
};
/**
* Extracts the root CA certificate from a certificate chain.
*
* @param certificateChain - PEM-formatted certificate chain containing leaf + intermediates + root CA
* @returns PEM-formatted root CA certificate, or empty string if not found
*/
export const extractRootCaFromChain = (certificateChain?: string): string => {
if (!certificateChain || typeof certificateChain !== "string") {
return "";
}
const certificates = parseCertificateChain(certificateChain);
if (certificates.length === 0) {
return "";
}
return certificates[certificates.length - 1];
};

View File

@@ -17,6 +17,7 @@ import { AWSRegion } from "@app/services/app-connection/app-connection-enums";
import { getAwsConnectionConfig } from "@app/services/app-connection/aws/aws-connection-fns";
import { TAwsConnectionConfig } from "@app/services/app-connection/aws/aws-connection-types";
import { TCertificateDALFactory } from "@app/services/certificate/certificate-dal";
import { removeRootCaFromChain } from "@app/services/certificate-common/certificate-utils";
import { TCertificateSyncDALFactory } from "@app/services/certificate-sync/certificate-sync-dal";
import { CertificateSyncStatus } from "@app/services/certificate-sync/certificate-sync-enums";
import { createConnectionQueue, RateLimitConfig } from "@app/services/connection-queue";
@@ -262,7 +263,10 @@ export const awsSecretsManagerPkiSyncFactory = ({
};
if (certificateChain && certificateChain.trim().length > 0) {
certificateData[fieldMappings.certificateChain] = certificateChain;
const processedCertificateChain = removeRootCaFromChain(certificateChain);
if (processedCertificateChain.trim().length > 0) {
certificateData[fieldMappings.certificateChain] = processedCertificateChain;
}
}
if (caCertificate && typeof caCertificate === "string" && caCertificate.trim().length > 0) {

View File

@@ -10,6 +10,7 @@ import {
import { TChefDataBagItemContent } from "@app/ee/services/secret-sync/chef";
import { logger } from "@app/lib/logger";
import { TCertificateDALFactory } from "@app/services/certificate/certificate-dal";
import { removeRootCaFromChain } from "@app/services/certificate-common/certificate-utils";
import { TCertificateSyncDALFactory } from "@app/services/certificate-sync/certificate-sync-dal";
import { CertificateSyncStatus } from "@app/services/certificate-sync/certificate-sync-enums";
import { createConnectionQueue, RateLimitConfig } from "@app/services/connection-queue";
@@ -264,11 +265,13 @@ export const chefPkiSyncFactory = ({ certificateDAL, certificateSyncDAL }: TChef
} = certificateData;
try {
const processedCertificateChain = certificateChain ? removeRootCaFromChain(certificateChain) : undefined;
const chefDataBagItem: ChefCertificateDataBagItem = {
id: targetItemName,
[fieldMappings.certificate]: cert,
[fieldMappings.privateKey]: certPrivateKey,
...(certificateChain && { [fieldMappings.certificateChain]: certificateChain }),
...(processedCertificateChain && { [fieldMappings.certificateChain]: processedCertificateChain }),
...(caCertificate && { [fieldMappings.caCertificate]: caCertificate })
};

View File

@@ -26,6 +26,7 @@ import { TCertificateSecretDALFactory } from "../certificate/certificate-secret-
import { TCertificateAuthorityCertDALFactory } from "../certificate-authority/certificate-authority-cert-dal";
import { TCertificateAuthorityDALFactory } from "../certificate-authority/certificate-authority-dal";
import { getCaCertChain } from "../certificate-authority/certificate-authority-fns";
import { extractRootCaFromChain } from "../certificate-common/certificate-utils";
import { TCertificateSyncDALFactory } from "../certificate-sync/certificate-sync-dal";
import { CertificateSyncStatus } from "../certificate-sync/certificate-sync-enums";
import { TPkiSyncDALFactory } from "./pki-sync-dal";
@@ -255,7 +256,7 @@ export const pkiSyncQueueFactory = ({
if (!certBody.encryptedCertificateChain) {
certificateChain = `${caCert}\n${caCertChain}`.trim();
}
caCertificate = caCert;
caCertificate = certificateChain ? extractRootCaFromChain(certificateChain) : caCert;
}
} catch (chainError) {
logger.warn(