mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-04 06:26:16 +00:00
Improve deprecated certs invalidation logic
This commit is contained in:
@@ -51,6 +51,7 @@ export enum QueueName {
|
||||
AuditLogPrune = "audit-log-prune",
|
||||
DailyResourceCleanUp = "daily-resource-cleanup",
|
||||
DailyExpiringPkiItemAlert = "daily-expiring-pki-item-alert",
|
||||
PkiSyncCleanup = "pki-sync-cleanup",
|
||||
PkiSubscriber = "pki-subscriber",
|
||||
TelemetryInstanceStats = "telemtry-self-hosted-stats",
|
||||
IntegrationSync = "sync-integrations",
|
||||
@@ -86,6 +87,7 @@ export enum QueueJobs {
|
||||
AuditLogPrune = "audit-log-prune-job",
|
||||
DailyResourceCleanUp = "daily-resource-cleanup-job",
|
||||
DailyExpiringPkiItemAlert = "daily-expiring-pki-item-alert",
|
||||
PkiSyncCleanup = "pki-sync-cleanup-job",
|
||||
SecWebhook = "secret-webhook-trigger",
|
||||
TelemetryInstanceStats = "telemetry-self-hosted-stats",
|
||||
IntegrationSync = "secret-integration-pull",
|
||||
@@ -151,6 +153,10 @@ export type TQueueJobTypes = {
|
||||
name: QueueJobs.DailyExpiringPkiItemAlert;
|
||||
payload: undefined;
|
||||
};
|
||||
[QueueName.PkiSyncCleanup]: {
|
||||
name: QueueJobs.PkiSyncCleanup;
|
||||
payload: undefined;
|
||||
};
|
||||
[QueueName.AuditLogPrune]: {
|
||||
name: QueueJobs.AuditLogPrune;
|
||||
payload: undefined;
|
||||
|
||||
@@ -248,6 +248,7 @@ import { pkiCollectionServiceFactory } from "@app/services/pki-collection/pki-co
|
||||
import { pkiSubscriberDALFactory } from "@app/services/pki-subscriber/pki-subscriber-dal";
|
||||
import { pkiSubscriberQueueServiceFactory } from "@app/services/pki-subscriber/pki-subscriber-queue";
|
||||
import { pkiSubscriberServiceFactory } from "@app/services/pki-subscriber/pki-subscriber-service";
|
||||
import { pkiSyncCleanupQueueServiceFactory } from "@app/services/pki-sync/pki-sync-cleanup-queue";
|
||||
import { pkiSyncDALFactory } from "@app/services/pki-sync/pki-sync-dal";
|
||||
import { pkiSyncQueueFactory } from "@app/services/pki-sync/pki-sync-queue";
|
||||
import { pkiSyncServiceFactory } from "@app/services/pki-sync/pki-sync-service";
|
||||
@@ -1906,7 +1907,15 @@ export const registerRoutes = async (
|
||||
licenseService,
|
||||
certificateDAL,
|
||||
certificateBodyDAL,
|
||||
certificateSecretDAL
|
||||
certificateSecretDAL,
|
||||
certificateAuthorityDAL,
|
||||
certificateAuthorityCertDAL
|
||||
});
|
||||
|
||||
const pkiSyncCleanup = pkiSyncCleanupQueueServiceFactory({
|
||||
queueService,
|
||||
pkiSyncDAL,
|
||||
pkiSyncQueue
|
||||
});
|
||||
|
||||
const internalCaFns = InternalCertificateAuthorityFns({
|
||||
@@ -2104,6 +2113,7 @@ export const registerRoutes = async (
|
||||
await telemetryQueue.startTelemetryCheck();
|
||||
await telemetryQueue.startAggregatedEventsJob();
|
||||
await dailyResourceCleanUp.init();
|
||||
await pkiSyncCleanup.init();
|
||||
await dailyReminderQueueService.startDailyRemindersJob();
|
||||
await dailyReminderQueueService.startSecretReminderMigrationJob();
|
||||
await dailyExpiringPkiItemAlert.startSendingAlerts();
|
||||
|
||||
@@ -53,7 +53,14 @@ const PkiSyncSchema = z.object({
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date(),
|
||||
isPlatformManagedCredentials: z.boolean().nullable().optional()
|
||||
})
|
||||
}),
|
||||
subscriber: z
|
||||
.object({
|
||||
id: z.string(),
|
||||
name: z.string()
|
||||
})
|
||||
.nullable()
|
||||
.optional()
|
||||
});
|
||||
|
||||
const PkiSyncOptionsSchema = z.object({
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { TDbClient } from "@app/db";
|
||||
import { TableName } from "@app/db/schemas";
|
||||
import { TableName, TCertificates } from "@app/db/schemas";
|
||||
import { DatabaseError } from "@app/lib/errors";
|
||||
import { ormify } from "@app/lib/knex";
|
||||
|
||||
@@ -93,11 +93,33 @@ export const certificateDALFactory = (db: TDbClient) => {
|
||||
}
|
||||
};
|
||||
|
||||
const findExpiredSyncedCertificates = async (): Promise<TCertificates[]> => {
|
||||
try {
|
||||
const yesterday = new Date();
|
||||
yesterday.setDate(yesterday.getDate() - 1);
|
||||
yesterday.setHours(0, 0, 0, 0);
|
||||
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
|
||||
const certs = await db
|
||||
.replicaNode()(TableName.Certificate)
|
||||
.where("notAfter", ">=", yesterday)
|
||||
.where("notAfter", "<", today)
|
||||
.whereNotNull("pkiSubscriberId");
|
||||
|
||||
return certs;
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "Find expired synced certificates" });
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
...certificateOrm,
|
||||
countCertificatesInProject,
|
||||
countCertificatesForPkiSubscriber,
|
||||
findLatestActiveCertForSubscriber,
|
||||
findAllActiveCertsForSubscriber
|
||||
findAllActiveCertsForSubscriber,
|
||||
findExpiredSyncedCertificates
|
||||
};
|
||||
};
|
||||
|
||||
@@ -22,8 +22,8 @@ import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||
import { TPkiCollectionDALFactory } from "@app/services/pki-collection/pki-collection-dal";
|
||||
import { TPkiCollectionItemDALFactory } from "@app/services/pki-collection/pki-collection-item-dal";
|
||||
import { TPkiSyncDALFactory } from "@app/services/pki-sync/pki-sync-dal";
|
||||
import { triggerAutoSyncForSubscriber } from "@app/services/pki-sync/pki-sync-utils";
|
||||
import { TPkiSyncQueueFactory } from "@app/services/pki-sync/pki-sync-queue";
|
||||
import { triggerAutoSyncForSubscriber } from "@app/services/pki-sync/pki-sync-utils";
|
||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||
import { getProjectKmsCertificateKeyId } from "@app/services/project/project-fns";
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ export const AZURE_KEY_VAULT_PKI_SYNC_LIST_OPTION = {
|
||||
connection: AppConnection.AzureKeyVault,
|
||||
destination: PkiSync.AzureKeyVault,
|
||||
canImportCertificates: false,
|
||||
canRemoveCertificates: false,
|
||||
canRemoveCertificates: true,
|
||||
defaultCertificateNameSchema: "Infisical-PKI-Sync-{{certificateId}}",
|
||||
forbiddenCharacters: AZURE_KEY_VAULT_CERTIFICATE_NAMING.FORBIDDEN_CHARACTERS,
|
||||
allowedCharacterPattern: AZURE_KEY_VAULT_CERTIFICATE_NAMING.ALLOWED_CHARACTER_PATTERN,
|
||||
|
||||
@@ -329,13 +329,14 @@ export const azureKeyVaultPkiSyncFactory = ({ kmsService, appConnectionDAL }: TA
|
||||
key: string;
|
||||
cert: string;
|
||||
privateKey: string;
|
||||
certificateChain?: string;
|
||||
}[] = [];
|
||||
|
||||
// Track which certificates should exist in Azure Key Vault
|
||||
const activeCertificateNames = Object.keys(certificateMap);
|
||||
|
||||
// Iterate through certificates to sync to Azure Key Vault
|
||||
Object.entries(certificateMap).forEach(([certName, { cert, privateKey }]) => {
|
||||
Object.entries(certificateMap).forEach(([certName, { cert, privateKey, certificateChain }]) => {
|
||||
if (disabledAzureKeyVaultCertificateKeys.includes(certName)) {
|
||||
return;
|
||||
}
|
||||
@@ -347,7 +348,8 @@ export const azureKeyVaultPkiSyncFactory = ({ kmsService, appConnectionDAL }: TA
|
||||
setCertificates.push({
|
||||
key: certName,
|
||||
cert,
|
||||
privateKey
|
||||
privateKey,
|
||||
certificateChain
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -364,13 +366,26 @@ export const azureKeyVaultPkiSyncFactory = ({ kmsService, appConnectionDAL }: TA
|
||||
// Upload certificates to Azure Key Vault with rate limiting
|
||||
const uploadResults = await executeWithConcurrencyLimit(
|
||||
setCertificates,
|
||||
async ({ key, cert, privateKey }) => {
|
||||
async ({ key, cert, privateKey, certificateChain }) => {
|
||||
try {
|
||||
// Combine certificate and private key in PEM format for Azure Key Vault
|
||||
// Azure Key Vault accepts PEM format with both cert and private key
|
||||
let combinedPem = cert;
|
||||
// Combine private key, certificate, and certificate chain in PEM format for Azure Key Vault
|
||||
let combinedPem = "";
|
||||
|
||||
if (privateKey) {
|
||||
combinedPem = `${privateKey}\n${cert}`;
|
||||
combinedPem = privateKey.trim();
|
||||
}
|
||||
|
||||
if (combinedPem) {
|
||||
combinedPem = `${combinedPem}\n${cert.trim()}`;
|
||||
} else {
|
||||
combinedPem = cert.trim();
|
||||
}
|
||||
|
||||
if (certificateChain) {
|
||||
const trimmedChain = certificateChain.trim();
|
||||
if (trimmedChain) {
|
||||
combinedPem = `${combinedPem}\n${trimmedChain}`;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert to base64 for Azure Key Vault import
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { logger } from "@app/lib/logger";
|
||||
import { QueueJobs, QueueName, TQueueServiceFactory } from "@app/queue";
|
||||
|
||||
import { TPkiSyncDALFactory } from "./pki-sync-dal";
|
||||
import { TPkiSyncQueueFactory } from "./pki-sync-queue";
|
||||
|
||||
type TPkiSyncCleanupQueueServiceFactoryDep = {
|
||||
queueService: TQueueServiceFactory;
|
||||
pkiSyncDAL: Pick<TPkiSyncDALFactory, "findPkiSyncsWithExpiredCertificates">;
|
||||
pkiSyncQueue: Pick<TPkiSyncQueueFactory, "queuePkiSyncSyncCertificatesById">;
|
||||
};
|
||||
|
||||
export type TPkiSyncCleanupQueueServiceFactory = ReturnType<typeof pkiSyncCleanupQueueServiceFactory>;
|
||||
|
||||
export const pkiSyncCleanupQueueServiceFactory = ({
|
||||
queueService,
|
||||
pkiSyncDAL,
|
||||
pkiSyncQueue
|
||||
}: TPkiSyncCleanupQueueServiceFactoryDep) => {
|
||||
const appCfg = getConfig();
|
||||
|
||||
const syncExpiredCertificatesForPkiSyncs = async () => {
|
||||
try {
|
||||
const pkiSyncsWithExpiredCerts = await pkiSyncDAL.findPkiSyncsWithExpiredCertificates();
|
||||
|
||||
if (pkiSyncsWithExpiredCerts.length === 0) {
|
||||
logger.info("No PKI syncs found with certificates that expired the previous day");
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`Found ${pkiSyncsWithExpiredCerts.length} PKI sync(s) with certificates that expired the previous day`
|
||||
);
|
||||
|
||||
// Trigger sync for each PKI sync that has expired certificates
|
||||
for (const { id: syncId, subscriberId } of pkiSyncsWithExpiredCerts) {
|
||||
try {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await pkiSyncQueue.queuePkiSyncSyncCertificatesById({
|
||||
syncId
|
||||
});
|
||||
logger.info(
|
||||
`Successfully queued PKI sync ${syncId} for subscriber ${subscriberId} due to expired certificates`
|
||||
);
|
||||
} catch (error) {
|
||||
logger.error(error, `Failed to queue PKI sync ${syncId} for subscriber ${subscriberId}`);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(error, "Failed to sync expired certificates for PKI syncs");
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const init = async () => {
|
||||
if (appCfg.isSecondaryInstance) {
|
||||
return;
|
||||
}
|
||||
|
||||
await queueService.stopRepeatableJob(
|
||||
QueueName.PkiSyncCleanup,
|
||||
QueueJobs.PkiSyncCleanup,
|
||||
{ pattern: "0 0 * * *", utc: true },
|
||||
QueueName.PkiSyncCleanup // just a job id
|
||||
);
|
||||
|
||||
await queueService.startPg<QueueName.PkiSyncCleanup>(
|
||||
QueueJobs.PkiSyncCleanup,
|
||||
async () => {
|
||||
try {
|
||||
logger.info(`${QueueName.PkiSyncCleanup}: queue task started`);
|
||||
await syncExpiredCertificatesForPkiSyncs();
|
||||
logger.info(`${QueueName.PkiSyncCleanup}: queue task completed`);
|
||||
} catch (error) {
|
||||
logger.error(error, `${QueueName.PkiSyncCleanup}: PKI sync cleanup failed`);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
{
|
||||
batchSize: 1,
|
||||
workerCount: 1,
|
||||
pollingIntervalSeconds: 1
|
||||
}
|
||||
);
|
||||
|
||||
await queueService.schedulePg(QueueJobs.PkiSyncCleanup, "0 0 * * *", undefined, { tz: "UTC" });
|
||||
};
|
||||
|
||||
return {
|
||||
init,
|
||||
syncExpiredCertificatesForPkiSyncs
|
||||
};
|
||||
};
|
||||
@@ -42,6 +42,49 @@ const basePkiSyncQuery = ({ filter, db, tx }: { db: TDbClient; filter?: PkiSyncF
|
||||
return query;
|
||||
};
|
||||
|
||||
const basePkiSyncWithSubscriberQuery = ({
|
||||
filter,
|
||||
db,
|
||||
tx
|
||||
}: {
|
||||
db: TDbClient;
|
||||
filter?: PkiSyncFindFilter;
|
||||
tx?: Knex;
|
||||
}) => {
|
||||
const query = (tx || db.replicaNode())(TableName.PkiSync)
|
||||
.leftJoin(TableName.AppConnection, `${TableName.PkiSync}.connectionId`, `${TableName.AppConnection}.id`)
|
||||
.leftJoin(TableName.PkiSubscriber, `${TableName.PkiSync}.subscriberId`, `${TableName.PkiSubscriber}.id`)
|
||||
.select(selectAllTableCols(TableName.PkiSync))
|
||||
.select(
|
||||
// app connection fields
|
||||
db.ref("name").withSchema(TableName.AppConnection).as("appConnectionName"),
|
||||
db.ref("app").withSchema(TableName.AppConnection).as("appConnectionApp"),
|
||||
db.ref("encryptedCredentials").withSchema(TableName.AppConnection).as("appConnectionEncryptedCredentials"),
|
||||
db.ref("orgId").withSchema(TableName.AppConnection).as("appConnectionOrgId"),
|
||||
db.ref("projectId").withSchema(TableName.AppConnection).as("appConnectionProjectId"),
|
||||
db.ref("method").withSchema(TableName.AppConnection).as("appConnectionMethod"),
|
||||
db.ref("description").withSchema(TableName.AppConnection).as("appConnectionDescription"),
|
||||
db.ref("version").withSchema(TableName.AppConnection).as("appConnectionVersion"),
|
||||
db.ref("gatewayId").withSchema(TableName.AppConnection).as("appConnectionGatewayId"),
|
||||
db.ref("createdAt").withSchema(TableName.AppConnection).as("appConnectionCreatedAt"),
|
||||
db.ref("updatedAt").withSchema(TableName.AppConnection).as("appConnectionUpdatedAt"),
|
||||
db
|
||||
.ref("isPlatformManagedCredentials")
|
||||
.withSchema(TableName.AppConnection)
|
||||
.as("appConnectionIsPlatformManagedCredentials"),
|
||||
// pki subscriber fields
|
||||
db.ref("id").withSchema(TableName.PkiSubscriber).as("pkiSubscriberId"),
|
||||
db.ref("name").withSchema(TableName.PkiSubscriber).as("subscriberName")
|
||||
);
|
||||
|
||||
if (filter) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
void query.where(buildFindFilter(prependTableNameToFindFilter(TableName.PkiSync, filter)));
|
||||
}
|
||||
|
||||
return query;
|
||||
};
|
||||
|
||||
const expandPkiSync = (pkiSync: Awaited<ReturnType<typeof basePkiSyncQuery>>[number]) => {
|
||||
const {
|
||||
appConnectionName,
|
||||
@@ -84,6 +127,51 @@ const expandPkiSync = (pkiSync: Awaited<ReturnType<typeof basePkiSyncQuery>>[num
|
||||
};
|
||||
};
|
||||
|
||||
const expandPkiSyncWithSubscriber = (pkiSync: Awaited<ReturnType<typeof basePkiSyncWithSubscriberQuery>>[number]) => {
|
||||
const {
|
||||
appConnectionName,
|
||||
appConnectionApp,
|
||||
appConnectionEncryptedCredentials,
|
||||
appConnectionOrgId,
|
||||
appConnectionProjectId,
|
||||
appConnectionMethod,
|
||||
appConnectionDescription,
|
||||
appConnectionVersion,
|
||||
appConnectionGatewayId,
|
||||
appConnectionCreatedAt,
|
||||
appConnectionUpdatedAt,
|
||||
appConnectionIsPlatformManagedCredentials,
|
||||
pkiSubscriberId,
|
||||
subscriberName,
|
||||
...el
|
||||
} = pkiSync;
|
||||
|
||||
return {
|
||||
...el,
|
||||
destination: el.destination as PkiSync,
|
||||
destinationConfig: el.destinationConfig as Record<string, unknown>,
|
||||
syncOptions: el.syncOptions as Record<string, unknown>,
|
||||
appConnectionName,
|
||||
appConnectionApp,
|
||||
connection: {
|
||||
id: el.connectionId,
|
||||
name: appConnectionName,
|
||||
app: appConnectionApp,
|
||||
encryptedCredentials: appConnectionEncryptedCredentials,
|
||||
orgId: appConnectionOrgId,
|
||||
projectId: appConnectionProjectId,
|
||||
method: appConnectionMethod,
|
||||
description: appConnectionDescription,
|
||||
version: appConnectionVersion,
|
||||
gatewayId: appConnectionGatewayId,
|
||||
createdAt: appConnectionCreatedAt,
|
||||
updatedAt: appConnectionUpdatedAt,
|
||||
isPlatformManagedCredentials: appConnectionIsPlatformManagedCredentials
|
||||
},
|
||||
subscriber: pkiSubscriberId && subscriberName ? { id: pkiSubscriberId, name: subscriberName } : null
|
||||
};
|
||||
};
|
||||
|
||||
export const pkiSyncDALFactory = (db: TDbClient) => {
|
||||
const pkiSyncOrm = ormify(db, TableName.PkiSync);
|
||||
|
||||
@@ -96,6 +184,15 @@ export const pkiSyncDALFactory = (db: TDbClient) => {
|
||||
}
|
||||
};
|
||||
|
||||
const findByProjectIdWithSubscribers = async (projectId: string, tx?: Knex) => {
|
||||
try {
|
||||
const pkiSyncs = await basePkiSyncWithSubscriberQuery({ filter: { projectId }, db, tx });
|
||||
return pkiSyncs.map(expandPkiSyncWithSubscriber);
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "Find By Project ID With Subscribers - PKI Sync" });
|
||||
}
|
||||
};
|
||||
|
||||
const findBySubscriberId = async (subscriberId: string, tx?: Knex) => {
|
||||
try {
|
||||
const pkiSyncs = await basePkiSyncQuery({ filter: { subscriberId }, db, tx });
|
||||
@@ -168,9 +265,42 @@ export const pkiSyncDALFactory = (db: TDbClient) => {
|
||||
return expandPkiSync(pkiSync);
|
||||
};
|
||||
|
||||
const findPkiSyncsWithExpiredCertificates = async (): Promise<Array<{ id: string; subscriberId: string }>> => {
|
||||
try {
|
||||
const yesterday = new Date();
|
||||
yesterday.setDate(yesterday.getDate() - 1);
|
||||
yesterday.setHours(0, 0, 0, 0);
|
||||
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
|
||||
const pkiSyncs = (await db
|
||||
.replicaNode()(TableName.PkiSync)
|
||||
.select(`${TableName.PkiSync}.id`, `${TableName.PkiSync}.subscriberId`)
|
||||
.innerJoin(
|
||||
TableName.Certificate,
|
||||
`${TableName.PkiSync}.subscriberId`,
|
||||
`${TableName.Certificate}.pkiSubscriberId`
|
||||
)
|
||||
.where(`${TableName.Certificate}.notAfter`, ">=", yesterday)
|
||||
.where(`${TableName.Certificate}.notAfter`, "<", today)
|
||||
.whereNotNull(`${TableName.Certificate}.pkiSubscriberId`)
|
||||
.whereNotNull(`${TableName.PkiSync}.subscriberId`)
|
||||
.groupBy(`${TableName.PkiSync}.id`, `${TableName.PkiSync}.subscriberId`)) as Array<{
|
||||
id: string;
|
||||
subscriberId: string;
|
||||
}>;
|
||||
|
||||
return pkiSyncs;
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "Find PKI syncs with expired certificates" });
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
...pkiSyncOrm,
|
||||
findByProjectId,
|
||||
findByProjectIdWithSubscribers,
|
||||
findBySubscriberId,
|
||||
findByIdAndProjectId,
|
||||
findByNameAndProjectId,
|
||||
@@ -178,6 +308,7 @@ export const pkiSyncDALFactory = (db: TDbClient) => {
|
||||
findOne,
|
||||
find,
|
||||
create,
|
||||
updateById
|
||||
updateById,
|
||||
findPkiSyncsWithExpiredCertificates
|
||||
};
|
||||
};
|
||||
|
||||
@@ -22,6 +22,9 @@ import { TCertificateBodyDALFactory } from "../certificate/certificate-body-dal"
|
||||
import { TCertificateDALFactory } from "../certificate/certificate-dal";
|
||||
import { getCertificateCredentials } from "../certificate/certificate-fns";
|
||||
import { TCertificateSecretDALFactory } from "../certificate/certificate-secret-dal";
|
||||
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 { TPkiSyncDALFactory } from "./pki-sync-dal";
|
||||
import { PkiSyncStatus } from "./pki-sync-enums";
|
||||
import { PkiSyncError } from "./pki-sync-errors";
|
||||
@@ -58,6 +61,8 @@ type TPkiSyncQueueFactoryDep = {
|
||||
>;
|
||||
certificateBodyDAL: Pick<TCertificateBodyDALFactory, "findOne" | "create">;
|
||||
certificateSecretDAL: Pick<TCertificateSecretDALFactory, "findOne" | "create">;
|
||||
certificateAuthorityDAL: Pick<TCertificateAuthorityDALFactory, "findById">;
|
||||
certificateAuthorityCertDAL: Pick<TCertificateAuthorityCertDALFactory, "findById">;
|
||||
};
|
||||
|
||||
type PkiSyncActionJob = Job<
|
||||
@@ -86,7 +91,9 @@ export const pkiSyncQueueFactory = ({
|
||||
licenseService,
|
||||
certificateDAL,
|
||||
certificateBodyDAL,
|
||||
certificateSecretDAL
|
||||
certificateSecretDAL,
|
||||
certificateAuthorityDAL,
|
||||
certificateAuthorityCertDAL
|
||||
}: TPkiSyncQueueFactoryDep) => {
|
||||
const appCfg = getConfig();
|
||||
|
||||
@@ -207,6 +214,32 @@ export const pkiSyncQueueFactory = ({
|
||||
certPrivateKey = undefined;
|
||||
}
|
||||
|
||||
let certificateChain: string | undefined;
|
||||
try {
|
||||
if (certBody.encryptedCertificateChain) {
|
||||
const decryptedCertChain = await kmsDecryptor({
|
||||
cipherTextBlob: certBody.encryptedCertificateChain
|
||||
});
|
||||
certificateChain = decryptedCertChain.toString();
|
||||
} else if (certificate.caCertId) {
|
||||
const { caCert, caCertChain } = await getCaCertChain({
|
||||
caCertId: certificate.caCertId,
|
||||
certificateAuthorityDAL,
|
||||
certificateAuthorityCertDAL,
|
||||
projectDAL,
|
||||
kmsService
|
||||
});
|
||||
certificateChain = `${caCert}\n${caCertChain}`.trim();
|
||||
}
|
||||
} catch (chainError) {
|
||||
logger.warn(
|
||||
{ certificateId: certificate.id, subscriberId, error: chainError },
|
||||
"Certificate chain not found or could not be decrypted - certificate may be imported or chain was not stored"
|
||||
);
|
||||
// Continue without certificate chain
|
||||
certificateChain = undefined;
|
||||
}
|
||||
|
||||
let certificateName: string;
|
||||
const syncOptions = pkiSync.syncOptions as { certificateNameSchema?: string } | undefined;
|
||||
const certificateNameSchema = syncOptions?.certificateNameSchema;
|
||||
@@ -223,7 +256,8 @@ export const pkiSyncQueueFactory = ({
|
||||
|
||||
certificateMap[certificateName] = {
|
||||
cert: certificatePem,
|
||||
privateKey: certPrivateKey || ""
|
||||
privateKey: certPrivateKey || "",
|
||||
certificateChain
|
||||
};
|
||||
} else {
|
||||
logger.warn({ certificateId: certificate.id, subscriberId }, "Certificate body not found for certificate");
|
||||
@@ -649,8 +683,7 @@ export const pkiSyncQueueFactory = ({
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
||||
throw new Error(`Unhandled PKI Sync Job ${job.name}`);
|
||||
throw new Error(`Unhandled PKI Sync Job ${String(job.name)}`);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -701,8 +734,7 @@ export const pkiSyncQueueFactory = ({
|
||||
await $handleRemoveCertificatesJob(job as TPkiSyncRemoveCertificatesDTO, pkiSync);
|
||||
break;
|
||||
default:
|
||||
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
||||
throw new Error(`Unhandled PKI Sync Job ${job.name}`);
|
||||
throw new Error(`Unhandled PKI Sync Job ${String(job.name)}`);
|
||||
}
|
||||
} finally {
|
||||
if (job.name === QueueJobs.PkiSyncSyncCertificates) {
|
||||
|
||||
@@ -38,7 +38,10 @@ const getDestinationAppType = (destination: PkiSync): AppConnection => {
|
||||
};
|
||||
|
||||
type TPkiSyncServiceFactoryDep = {
|
||||
pkiSyncDAL: TPkiSyncDALFactory;
|
||||
pkiSyncDAL: Pick<
|
||||
TPkiSyncDALFactory,
|
||||
"findById" | "findByProjectIdWithSubscribers" | "findByNameAndProjectId" | "create" | "updateById" | "deleteById"
|
||||
>;
|
||||
pkiSubscriberDAL: Pick<TPkiSubscriberDALFactory, "findById">;
|
||||
appConnectionService: Pick<TAppConnectionServiceFactory, "connectAppConnectionById">;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
|
||||
@@ -262,7 +265,10 @@ export const pkiSyncServiceFactory = ({
|
||||
return pkiSyncDAL.deleteById(id);
|
||||
};
|
||||
|
||||
const listPkiSyncsByProjectId = async ({ projectId }: TListPkiSyncsByProjectId, actor: OrgServiceActor) => {
|
||||
const listPkiSyncsByProjectId = async (
|
||||
{ projectId }: TListPkiSyncsByProjectId,
|
||||
actor: OrgServiceActor
|
||||
): Promise<TPkiSync[]> => {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actor: actor.type,
|
||||
actorId: actor.id,
|
||||
@@ -274,8 +280,9 @@ export const pkiSyncServiceFactory = ({
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionPkiSyncActions.Read, ProjectPermissionSub.PkiSyncs);
|
||||
|
||||
const pkiSyncs = await pkiSyncDAL.findByProjectId(projectId);
|
||||
return pkiSyncs as TPkiSync[];
|
||||
const pkiSyncsWithSubscribers = await pkiSyncDAL.findByProjectIdWithSubscribers(projectId);
|
||||
|
||||
return pkiSyncsWithSubscribers as TPkiSync[];
|
||||
};
|
||||
|
||||
const findPkiSyncById = async ({ id, projectId }: TFindPkiSyncByIdDTO, actor: OrgServiceActor) => {
|
||||
@@ -307,7 +314,12 @@ export const pkiSyncServiceFactory = ({
|
||||
: ProjectPermissionSub.PkiSyncs
|
||||
);
|
||||
|
||||
return pkiSync as TPkiSync;
|
||||
const result = {
|
||||
...pkiSync,
|
||||
subscriber: findSubscriber ? { id: findSubscriber.id, name: findSubscriber.name } : null
|
||||
} as TPkiSync;
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
const triggerPkiSyncSyncCertificatesById = async (
|
||||
|
||||
@@ -49,6 +49,10 @@ export type TPkiSync = {
|
||||
updatedAt: Date;
|
||||
isPlatformManagedCredentials?: boolean;
|
||||
};
|
||||
subscriber?: {
|
||||
id: string;
|
||||
name: string;
|
||||
} | null;
|
||||
};
|
||||
|
||||
export type TPkiSyncWithCredentials = TPkiSync & {
|
||||
@@ -66,7 +70,7 @@ export type TPkiSyncListItem = TPkiSync & {
|
||||
appConnectionApp: string;
|
||||
};
|
||||
|
||||
export type TCertificateMap = Record<string, { cert: string; privateKey: string }>;
|
||||
export type TCertificateMap = Record<string, { cert: string; privateKey: string; certificateChain?: string }>;
|
||||
|
||||
export type TCreatePkiSyncDTO = {
|
||||
name: string;
|
||||
|
||||
Reference in New Issue
Block a user