mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-15 10:35:17 +00:00
feat: load project kms backup
This commit is contained in:
@@ -11,6 +11,7 @@ import { KeyStorePrefixes, TKeyStoreFactory } from "@app/keystore/keystore";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { randomSecureBytes } from "@app/lib/crypto";
|
||||
import { symmetricCipherService, SymmetricEncryption } from "@app/lib/crypto/cipher";
|
||||
import { generateHash } from "@app/lib/crypto/encryption";
|
||||
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||
import { logger } from "@app/lib/logger";
|
||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||
@@ -598,16 +599,73 @@ export const kmsServiceFactory = ({
|
||||
const kmsEncryptor = await encryptWithKmsKey({ kmsId: kmsKeyIdForEncrypt });
|
||||
const { cipherTextBlob: encryptedSecretManagerDataKey } = await kmsEncryptor({ plainText: secretManagerDataKey });
|
||||
|
||||
// format: version.projectId.kmsFunction.kmsId.Base64(encryptedDataKey)
|
||||
const secretManagerBackup = `v1.${projectId}.secretManager.${kmsKeyIdForEncrypt}.${encryptedSecretManagerDataKey.toString(
|
||||
// backup format: version.projectId.kmsFunction.kmsId.Base64(encryptedDataKey).verificationHash
|
||||
let secretManagerBackup = `v1.${projectId}.secretManager.${kmsKeyIdForEncrypt}.${encryptedSecretManagerDataKey.toString(
|
||||
"base64"
|
||||
)}`;
|
||||
|
||||
const verificationHash = generateHash(secretManagerBackup);
|
||||
secretManagerBackup = `${secretManagerBackup}.${verificationHash}`;
|
||||
|
||||
return {
|
||||
secretManager: secretManagerBackup
|
||||
};
|
||||
};
|
||||
|
||||
const loadProjectKeyBackup = async (projectId: string, backup: string) => {
|
||||
const project = await projectDAL.findById(projectId);
|
||||
if (!project) {
|
||||
throw new NotFoundError({
|
||||
message: "Project not found"
|
||||
});
|
||||
}
|
||||
|
||||
const [, backupProjectId, , backupKmsKeyId, backupBase64EncryptedDataKey, backupHash] = backup.split(".");
|
||||
const computedHash = generateHash(backup.substring(0, backup.lastIndexOf(".")));
|
||||
if (computedHash !== backupHash) {
|
||||
throw new BadRequestError({
|
||||
message: "Invalid backup"
|
||||
});
|
||||
}
|
||||
|
||||
if (backupProjectId !== projectId) {
|
||||
throw new BadRequestError({
|
||||
message: "Invalid backup for project"
|
||||
});
|
||||
}
|
||||
|
||||
const kmsDecryptor = await decryptWithKmsKey({ kmsId: backupKmsKeyId });
|
||||
const dataKey = await kmsDecryptor({
|
||||
cipherTextBlob: Buffer.from(backupBase64EncryptedDataKey, "base64")
|
||||
});
|
||||
|
||||
const newKms = await kmsDAL.transaction(async (tx) => {
|
||||
const key = await generateKmsKey({
|
||||
isReserved: true,
|
||||
orgId: project.orgId,
|
||||
tx
|
||||
});
|
||||
|
||||
const kmsEncryptor = await encryptWithKmsKey({ kmsId: key.id }, tx);
|
||||
const { cipherTextBlob } = await kmsEncryptor({ plainText: dataKey });
|
||||
|
||||
await projectDAL.updateById(
|
||||
projectId,
|
||||
{
|
||||
kmsSecretManagerKeyId: key.id,
|
||||
kmsSecretManagerEncryptedDataKey: cipherTextBlob
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
||||
return kmsDAL.findByIdWithAssociatedKms(key.id, tx);
|
||||
});
|
||||
|
||||
return {
|
||||
secretManagerKmsKey: newKms
|
||||
};
|
||||
};
|
||||
|
||||
const startService = async () => {
|
||||
const appCfg = getConfig();
|
||||
// This will switch to a seal process and HMS flow in future
|
||||
@@ -666,6 +724,7 @@ export const kmsServiceFactory = ({
|
||||
getProjectSecretManagerKmsDataKey,
|
||||
getProjectSecretManagerKmsKey,
|
||||
updateProjectSecretManagerKmsKey,
|
||||
getProjectKeyBackup
|
||||
getProjectKeyBackup,
|
||||
loadProjectKeyBackup
|
||||
};
|
||||
};
|
||||
|
||||
@@ -41,6 +41,7 @@ import {
|
||||
TGetProjectDTO,
|
||||
TListProjectCasDTO,
|
||||
TListProjectCertsDTO,
|
||||
TLoadProjectKmsBackupDTO,
|
||||
TToggleProjectAutoCapitalizationDTO,
|
||||
TUpdateAuditLogsRetentionDTO,
|
||||
TUpdateProjectDTO,
|
||||
@@ -78,7 +79,10 @@ type TProjectServiceFactoryDep = {
|
||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||
orgDAL: Pick<TOrgDALFactory, "findOne">;
|
||||
keyStore: Pick<TKeyStoreFactory, "deleteItem">;
|
||||
kmsService: Pick<TKmsServiceFactory, "updateProjectSecretManagerKmsKey" | "getProjectKeyBackup">;
|
||||
kmsService: Pick<
|
||||
TKmsServiceFactory,
|
||||
"updateProjectSecretManagerKmsKey" | "getProjectKeyBackup" | "loadProjectKeyBackup"
|
||||
>;
|
||||
};
|
||||
|
||||
export type TProjectServiceFactory = ReturnType<typeof projectServiceFactory>;
|
||||
@@ -721,6 +725,35 @@ export const projectServiceFactory = ({
|
||||
return kmsBackup;
|
||||
};
|
||||
|
||||
const loadProjectKmsBackup = async ({
|
||||
projectId,
|
||||
actor,
|
||||
actorId,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
backup
|
||||
}: TLoadProjectKmsBackupDTO) => {
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Kms);
|
||||
|
||||
const plan = await licenseService.getPlan(actorOrgId);
|
||||
if (!plan.externalKms) {
|
||||
throw new BadRequestError({
|
||||
message: "Failed to load KMS backup due to plan restriction. Upgrade to the enterprise plan."
|
||||
});
|
||||
}
|
||||
|
||||
const kmsBackup = await kmsService.loadProjectKeyBackup(projectId, backup);
|
||||
return kmsBackup;
|
||||
};
|
||||
|
||||
return {
|
||||
createProject,
|
||||
deleteProject,
|
||||
@@ -736,6 +769,7 @@ export const projectServiceFactory = ({
|
||||
updateVersionLimit,
|
||||
updateAuditLogsRetention,
|
||||
updateProjectKmsKey,
|
||||
getProjectKmsBackup
|
||||
getProjectKmsBackup,
|
||||
loadProjectKmsBackup
|
||||
};
|
||||
};
|
||||
|
||||
@@ -107,3 +107,7 @@ export type TListProjectCertsDTO = {
|
||||
export type TUpdateProjectKmsDTO = {
|
||||
secretManagerKmsKeyId: string;
|
||||
} & TProjectPermission;
|
||||
|
||||
export type TLoadProjectKmsBackupDTO = {
|
||||
backup: string;
|
||||
} & TProjectPermission;
|
||||
|
||||
Reference in New Issue
Block a user