mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-08 10:28:00 +00:00
Encrypt CRL
This commit is contained in:
@@ -68,7 +68,7 @@ export async function up(knex: Knex): Promise<void> {
|
|||||||
t.timestamps(true, true, true);
|
t.timestamps(true, true, true);
|
||||||
t.uuid("caId").notNullable().unique();
|
t.uuid("caId").notNullable().unique();
|
||||||
t.foreign("caId").references("id").inTable(TableName.CertificateAuthority).onDelete("CASCADE");
|
t.foreign("caId").references("id").inTable(TableName.CertificateAuthority).onDelete("CASCADE");
|
||||||
t.text("crl").notNullable(); // TODO: encrypt
|
t.binary("encryptedCrl").notNullable(); // TODO: encrypt
|
||||||
t.integer("ttl").notNullable(); // in minutes
|
t.integer("ttl").notNullable(); // in minutes
|
||||||
// TODO: consider type (crl or delta)
|
// TODO: consider type (crl or delta)
|
||||||
// TODO: rebuild interval
|
// TODO: rebuild interval
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { zodBuffer } from "@app/lib/zod";
|
||||||
|
|
||||||
import { TImmutableDBKeys } from "./models";
|
import { TImmutableDBKeys } from "./models";
|
||||||
|
|
||||||
export const CertificateAuthorityCrlSchema = z.object({
|
export const CertificateAuthorityCrlSchema = z.object({
|
||||||
@@ -12,7 +14,7 @@ export const CertificateAuthorityCrlSchema = z.object({
|
|||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date(),
|
updatedAt: z.date(),
|
||||||
caId: z.string().uuid(),
|
caId: z.string().uuid(),
|
||||||
crl: z.string(),
|
encryptedCrl: zodBuffer,
|
||||||
ttl: z.number()
|
ttl: z.number()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -537,6 +537,8 @@ export const registerRoutes = async (
|
|||||||
certificateAuthorityDAL,
|
certificateAuthorityDAL,
|
||||||
certificateAuthoritySecretDAL,
|
certificateAuthoritySecretDAL,
|
||||||
certificateDAL,
|
certificateDAL,
|
||||||
|
projectDAL,
|
||||||
|
kmsService,
|
||||||
queueService
|
queueService
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -13,17 +13,17 @@ export const certificateAuthorityDALFactory = (db: TDbClient) => {
|
|||||||
const result: {
|
const result: {
|
||||||
caId: string;
|
caId: string;
|
||||||
parentCaId?: string;
|
parentCaId?: string;
|
||||||
certificate: Buffer;
|
encryptedCertificate: Buffer;
|
||||||
}[] = await db
|
}[] = await db
|
||||||
.withRecursive("cte", (cte) => {
|
.withRecursive("cte", (cte) => {
|
||||||
void cte
|
void cte
|
||||||
.select("ca.id as caId", "ca.parentCaId", "cert.certificate")
|
.select("ca.id as caId", "ca.parentCaId", "cert.encryptedCertificate")
|
||||||
.from({ ca: TableName.CertificateAuthority })
|
.from({ ca: TableName.CertificateAuthority })
|
||||||
.leftJoin({ cert: TableName.CertificateAuthorityCert }, "ca.id", "cert.caId")
|
.leftJoin({ cert: TableName.CertificateAuthorityCert }, "ca.id", "cert.caId")
|
||||||
.where("ca.id", caId)
|
.where("ca.id", caId)
|
||||||
.unionAll((builder) => {
|
.unionAll((builder) => {
|
||||||
void builder
|
void builder
|
||||||
.select("ca.id as caId", "ca.parentCaId", "cert.certificate")
|
.select("ca.id as caId", "ca.parentCaId", "cert.encryptedCertificate")
|
||||||
.from({ ca: TableName.CertificateAuthority })
|
.from({ ca: TableName.CertificateAuthority })
|
||||||
.leftJoin({ cert: TableName.CertificateAuthorityCert }, "ca.id", "cert.caId")
|
.leftJoin({ cert: TableName.CertificateAuthorityCert }, "ca.id", "cert.caId")
|
||||||
.innerJoin("cte", "cte.parentCaId", "ca.id");
|
.innerJoin("cte", "cte.parentCaId", "ca.id");
|
||||||
@@ -33,7 +33,7 @@ export const certificateAuthorityDALFactory = (db: TDbClient) => {
|
|||||||
.from("cte");
|
.from("cte");
|
||||||
|
|
||||||
// Extract certificates and reverse the order to have the root CA at the end
|
// Extract certificates and reverse the order to have the root CA at the end
|
||||||
const certChain: Buffer[] = result.map((row) => row.certificate);
|
const certChain: Buffer[] = result.map((row) => row.encryptedCertificate);
|
||||||
return certChain;
|
return certChain;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new DatabaseError({ error, name: "BuildCertificateChain" });
|
throw new DatabaseError({ error, name: "BuildCertificateChain" });
|
||||||
|
|||||||
@@ -1,17 +1,20 @@
|
|||||||
// import * as x509 from "@peculiar/x509";
|
import * as x509 from "@peculiar/x509";
|
||||||
// import crypto from "crypto";
|
import crypto from "crypto";
|
||||||
|
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { daysToMillisecond, secondsToMillis } from "@app/lib/dates";
|
import { daysToMillisecond, secondsToMillis } from "@app/lib/dates";
|
||||||
// import { BadRequestError } from "@app/lib/errors";
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
import { logger } from "@app/lib/logger";
|
import { logger } from "@app/lib/logger";
|
||||||
import { QueueJobs, QueueName, TQueueServiceFactory } from "@app/queue";
|
import { QueueJobs, QueueName, TQueueServiceFactory } from "@app/queue";
|
||||||
import { TCertificateDALFactory } from "@app/services/certificate/certificate-dal";
|
import { TCertificateDALFactory } from "@app/services/certificate/certificate-dal";
|
||||||
|
import { CertKeyAlgorithm, CertStatus } from "@app/services/certificate/certificate-types";
|
||||||
|
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||||
|
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||||
|
import { getProjectKmsCertificateKeyId } from "@app/services/project/project-fns";
|
||||||
|
|
||||||
// import { CertKeyAlgorithm, CertStatus } from "@app/services/certificate/certificate-types";
|
|
||||||
import { TCertificateAuthorityCrlDALFactory } from "./certificate-authority-crl-dal";
|
import { TCertificateAuthorityCrlDALFactory } from "./certificate-authority-crl-dal";
|
||||||
import { TCertificateAuthorityDALFactory } from "./certificate-authority-dal";
|
import { TCertificateAuthorityDALFactory } from "./certificate-authority-dal";
|
||||||
// import { keyAlgorithmToAlgCfg } from "./certificate-authority-fns";
|
import { keyAlgorithmToAlgCfg } from "./certificate-authority-fns";
|
||||||
import { TCertificateAuthoritySecretDALFactory } from "./certificate-authority-secret-dal";
|
import { TCertificateAuthoritySecretDALFactory } from "./certificate-authority-secret-dal";
|
||||||
import { TRotateCaCrlTriggerDTO } from "./certificate-authority-types";
|
import { TRotateCaCrlTriggerDTO } from "./certificate-authority-types";
|
||||||
|
|
||||||
@@ -21,15 +24,19 @@ type TCertificateAuthorityQueueFactoryDep = {
|
|||||||
certificateAuthorityCrlDAL: TCertificateAuthorityCrlDALFactory;
|
certificateAuthorityCrlDAL: TCertificateAuthorityCrlDALFactory;
|
||||||
certificateAuthoritySecretDAL: TCertificateAuthoritySecretDALFactory;
|
certificateAuthoritySecretDAL: TCertificateAuthoritySecretDALFactory;
|
||||||
certificateDAL: TCertificateDALFactory;
|
certificateDAL: TCertificateDALFactory;
|
||||||
|
projectDAL: Pick<TProjectDALFactory, "findProjectBySlug" | "findOne" | "updateById" | "findById" | "transaction">;
|
||||||
|
kmsService: Pick<TKmsServiceFactory, "generateKmsKey" | "encrypt" | "decrypt">;
|
||||||
queueService: TQueueServiceFactory;
|
queueService: TQueueServiceFactory;
|
||||||
};
|
};
|
||||||
export type TCertificateAuthorityQueueFactory = ReturnType<typeof certificateAuthorityQueueFactory>;
|
export type TCertificateAuthorityQueueFactory = ReturnType<typeof certificateAuthorityQueueFactory>;
|
||||||
|
|
||||||
export const certificateAuthorityQueueFactory = ({
|
export const certificateAuthorityQueueFactory = ({
|
||||||
// certificateAuthorityCrlDAL,
|
certificateAuthorityCrlDAL,
|
||||||
// certificateAuthorityDAL,
|
certificateAuthorityDAL,
|
||||||
// certificateAuthoritySecretDAL,
|
certificateAuthoritySecretDAL,
|
||||||
// certificateDAL,
|
certificateDAL,
|
||||||
|
projectDAL,
|
||||||
|
kmsService,
|
||||||
queueService
|
queueService
|
||||||
}: TCertificateAuthorityQueueFactoryDep) => {
|
}: TCertificateAuthorityQueueFactoryDep) => {
|
||||||
// TODO 1: auto-periodic rotation
|
// TODO 1: auto-periodic rotation
|
||||||
@@ -64,55 +71,69 @@ export const certificateAuthorityQueueFactory = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// queueService.start(QueueName.CaCrlRotation, async (job) => {
|
queueService.start(QueueName.CaCrlRotation, async (job) => {
|
||||||
// const { caId } = job.data;
|
const { caId } = job.data;
|
||||||
// logger.info(`secretReminderQueue.process: [secretDocument=${caId}]`);
|
logger.info(`secretReminderQueue.process: [secretDocument=${caId}]`);
|
||||||
|
|
||||||
// const ca = await certificateAuthorityDAL.findById(caId);
|
const ca = await certificateAuthorityDAL.findById(caId);
|
||||||
// if (!ca) throw new BadRequestError({ message: "CA not found" });
|
if (!ca) throw new BadRequestError({ message: "CA not found" });
|
||||||
|
|
||||||
// const caKeys = await certificateAuthoritySecretDAL.findOne({ caId: ca.id });
|
const caKeys = await certificateAuthoritySecretDAL.findOne({ caId: ca.id });
|
||||||
|
|
||||||
// const alg = keyAlgorithmToAlgCfg(ca.keyAlgorithm as CertKeyAlgorithm);
|
const alg = keyAlgorithmToAlgCfg(ca.keyAlgorithm as CertKeyAlgorithm);
|
||||||
// const skObj = crypto.createPrivateKey({ key: caKeys.sk, format: "pem", type: "pkcs8" });
|
|
||||||
// const sk = await crypto.subtle.importKey("pkcs8", skObj.export({ format: "der", type: "pkcs8" }), alg, true, [
|
|
||||||
// "sign"
|
|
||||||
// ]);
|
|
||||||
|
|
||||||
// const revokedCerts = await certificateDAL.find({
|
const keyId = await getProjectKmsCertificateKeyId({
|
||||||
// caId: ca.id,
|
projectId: ca.projectId,
|
||||||
// status: CertStatus.REVOKED
|
projectDAL,
|
||||||
// });
|
kmsService
|
||||||
|
});
|
||||||
|
|
||||||
// const crl = await x509.X509CrlGenerator.create({
|
const privateKey = await kmsService.decrypt({
|
||||||
// issuer: ca.dn,
|
kmsId: keyId,
|
||||||
// thisUpdate: new Date(),
|
cipherTextBlob: caKeys.encryptedPrivateKey
|
||||||
// nextUpdate: new Date("2025/12/12"), // TODO: depends on configured rebuild interval
|
});
|
||||||
// entries: revokedCerts.map((revokedCert) => {
|
|
||||||
// return {
|
|
||||||
// serialNumber: revokedCert.serialNumber,
|
|
||||||
// revocationDate: new Date(revokedCert.revokedAt as Date),
|
|
||||||
// reason: revokedCert.revocationReason as number,
|
|
||||||
// invalidity: new Date("2022/01/01"),
|
|
||||||
// issuer: ca.dn
|
|
||||||
// };
|
|
||||||
// }),
|
|
||||||
// signingAlgorithm: alg,
|
|
||||||
// signingKey: sk
|
|
||||||
// });
|
|
||||||
|
|
||||||
// const base64crl = crl.toString("base64");
|
const skObj = crypto.createPrivateKey({ key: privateKey, format: "der", type: "pkcs8" });
|
||||||
// const crlPem = `-----BEGIN X509 CRL-----\n${base64crl.match(/.{1,64}/g)?.join("\n")}\n-----END X509 CRL-----`;
|
const sk = await crypto.subtle.importKey("pkcs8", skObj.export({ format: "der", type: "pkcs8" }), alg, true, [
|
||||||
|
"sign"
|
||||||
|
]);
|
||||||
|
|
||||||
// await certificateAuthorityCrlDAL.update(
|
const revokedCerts = await certificateDAL.find({
|
||||||
// {
|
caId: ca.id,
|
||||||
// caId: ca.id
|
status: CertStatus.REVOKED
|
||||||
// },
|
});
|
||||||
// {
|
|
||||||
// crl: crlPem // TODO: encrypt
|
const crl = await x509.X509CrlGenerator.create({
|
||||||
// }
|
issuer: ca.dn,
|
||||||
// );
|
thisUpdate: new Date(),
|
||||||
// });
|
nextUpdate: new Date("2025/12/12"), // TODO: depends on configured rebuild interval
|
||||||
|
entries: revokedCerts.map((revokedCert) => {
|
||||||
|
return {
|
||||||
|
serialNumber: revokedCert.serialNumber,
|
||||||
|
revocationDate: new Date(revokedCert.revokedAt as Date),
|
||||||
|
reason: revokedCert.revocationReason as number,
|
||||||
|
invalidity: new Date("2022/01/01"),
|
||||||
|
issuer: ca.dn
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
signingAlgorithm: alg,
|
||||||
|
signingKey: sk
|
||||||
|
});
|
||||||
|
|
||||||
|
const { cipherTextBlob: encryptedCrl } = await kmsService.encrypt({
|
||||||
|
kmsId: keyId,
|
||||||
|
plainText: Buffer.from(new Uint8Array(crl.rawData))
|
||||||
|
});
|
||||||
|
|
||||||
|
await certificateAuthorityCrlDAL.update(
|
||||||
|
{
|
||||||
|
caId: ca.id
|
||||||
|
},
|
||||||
|
{
|
||||||
|
encryptedCrl
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
queueService.listen(QueueName.CaCrlRotation, "failed", (job, err) => {
|
queueService.listen(QueueName.CaCrlRotation, "failed", (job, err) => {
|
||||||
logger.error(err, "Failed to rotate CA CRL %s", job?.id);
|
logger.error(err, "Failed to rotate CA CRL %s", job?.id);
|
||||||
|
|||||||
@@ -193,10 +193,15 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { cipherTextBlob: encryptedCrl } = await kmsService.encrypt({
|
||||||
|
kmsId: keyId,
|
||||||
|
plainText: Buffer.alloc(0)
|
||||||
|
});
|
||||||
|
|
||||||
await certificateAuthorityCrlDAL.create(
|
await certificateAuthorityCrlDAL.create(
|
||||||
{
|
{
|
||||||
caId: ca.id,
|
caId: ca.id,
|
||||||
crl: "", // TODO: encrypt
|
encryptedCrl,
|
||||||
ttl: 60 // in minutes
|
ttl: 60 // in minutes
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
@@ -944,18 +949,23 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
signingKey: sk
|
signingKey: sk
|
||||||
});
|
});
|
||||||
|
|
||||||
const base64crl = crl.toString("base64");
|
const { cipherTextBlob: encryptedCrl } = await kmsService.encrypt({
|
||||||
const crlPem = `-----BEGIN X509 CRL-----\n${base64crl.match(/.{1,64}/g)?.join("\n")}\n-----END X509 CRL-----`;
|
kmsId: keyId,
|
||||||
|
plainText: Buffer.from(new Uint8Array(crl.rawData))
|
||||||
|
});
|
||||||
|
|
||||||
await certificateAuthorityCrlDAL.update(
|
await certificateAuthorityCrlDAL.update(
|
||||||
{
|
{
|
||||||
caId: ca.id
|
caId: ca.id
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
crl: crlPem // TODO: encrypt
|
encryptedCrl
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const base64crl = crl.toString("base64");
|
||||||
|
const crlPem = `-----BEGIN X509 CRL-----\n${base64crl.match(/.{1,64}/g)?.join("\n")}\n-----END X509 CRL-----`;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
crl: crlPem
|
crl: crlPem
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user