Revert "Revert "Root encrypted data to kms encryption""

This commit is contained in:
Maidul Islam
2025-02-11 22:41:32 -05:00
committed by GitHub
parent d2cf296250
commit f0b6382f92
77 changed files with 2205 additions and 1295 deletions
@@ -1,10 +1,24 @@
import { TDbClient } from "@app/db";
import { TableName } from "@app/db/schemas";
import { DatabaseError } from "@app/lib/errors";
import { ormify } from "@app/lib/knex";
import { Knex } from "knex";
export type TKmsRootConfigDALFactory = ReturnType<typeof kmsRootConfigDALFactory>;
export const kmsRootConfigDALFactory = (db: TDbClient) => {
const kmsOrm = ormify(db, TableName.KmsServerRootConfig);
return kmsOrm;
const findById = async (id: string, tx?: Knex) => {
try {
const result = await (tx || db)(TableName.KmsServerRootConfig)
.where({ id } as never)
.first("*");
return result;
} catch (error) {
throw new DatabaseError({ error, name: "Find by id" });
}
};
return { ...kmsOrm, findById };
};
+30 -53
View File
@@ -12,8 +12,8 @@ import {
TExternalKmsProviderFns
} from "@app/ee/services/external-kms/providers/model";
import { THsmServiceFactory } from "@app/ee/services/hsm/hsm-service";
import { KeyStorePrefixes, TKeyStoreFactory } from "@app/keystore/keystore";
import { getConfig } from "@app/lib/config/env";
import { KeyStorePrefixes, PgSqlLock, TKeyStoreFactory } from "@app/keystore/keystore";
import { TEnvConfig } 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";
@@ -44,23 +44,22 @@ type TKmsServiceFactoryDep = {
kmsDAL: TKmsKeyDALFactory;
projectDAL: Pick<TProjectDALFactory, "findById" | "updateById" | "transaction">;
orgDAL: Pick<TOrgDALFactory, "findById" | "updateById" | "transaction">;
kmsRootConfigDAL: Pick<TKmsRootConfigDALFactory, "findById" | "create" | "updateById">;
kmsRootConfigDAL: Pick<TKmsRootConfigDALFactory, "findById" | "create" | "updateById" | "transaction">;
keyStore: Pick<TKeyStoreFactory, "acquireLock" | "waitTillReady" | "setItemWithExpiry">;
internalKmsDAL: Pick<TInternalKmsDALFactory, "create">;
hsmService: THsmServiceFactory;
envConfig: Pick<TEnvConfig, "ENCRYPTION_KEY" | "ROOT_ENCRYPTION_KEY">;
};
export type TKmsServiceFactory = ReturnType<typeof kmsServiceFactory>;
const KMS_ROOT_CREATION_WAIT_KEY = "wait_till_ready_kms_root_key";
const KMS_ROOT_CREATION_WAIT_TIME = 10;
// akhilmhdh: Don't edit this value. This is measured for blob concatination in kms
const KMS_VERSION = "v01";
const KMS_VERSION_BLOB_LENGTH = 3;
const KmsSanitizedSchema = KmsKeysSchema.extend({ isExternal: z.boolean() });
export const kmsServiceFactory = ({
envConfig,
kmsDAL,
kmsRootConfigDAL,
keyStore,
@@ -473,7 +472,8 @@ export const kmsServiceFactory = ({
}
const kmsDecryptor = await decryptWithKmsKey({
kmsId: kmsKeyId
kmsId: kmsKeyId,
tx: trx
});
return kmsDecryptor({
@@ -635,10 +635,8 @@ export const kmsServiceFactory = ({
};
const $getBasicEncryptionKey = () => {
const appCfg = getConfig();
const encryptionKey = appCfg.ENCRYPTION_KEY || appCfg.ROOT_ENCRYPTION_KEY;
const isBase64 = !appCfg.ENCRYPTION_KEY;
const encryptionKey = envConfig.ENCRYPTION_KEY || envConfig.ROOT_ENCRYPTION_KEY;
const isBase64 = !envConfig.ENCRYPTION_KEY;
if (!encryptionKey)
throw new Error(
"Root encryption key not found for KMS service. Did you set the ENCRYPTION_KEY or ROOT_ENCRYPTION_KEY environment variables?"
@@ -874,54 +872,33 @@ export const kmsServiceFactory = ({
return { id, name, orgId, isExternal };
};
// akhilmhdh: a copy of this is made in migrations/utils/kms
const startService = async () => {
const lock = await keyStore.acquireLock([`KMS_ROOT_CFG_LOCK`], 3000, { retryCount: 3 }).catch(() => null);
if (!lock) {
await keyStore.waitTillReady({
key: KMS_ROOT_CREATION_WAIT_KEY,
keyCheckCb: (val) => val === "true",
waitingCb: () => logger.info("KMS. Waiting for leader to finish creation of KMS Root Key")
const kmsRootConfig = await kmsRootConfigDAL.transaction(async (tx) => {
await tx.raw("SELECT pg_advisory_xact_lock(?)", [PgSqlLock.KmsRootKeyInit]);
// check if KMS root key was already generated and saved in DB
const existingRootConfig = await kmsRootConfigDAL.findById(KMS_ROOT_CONFIG_UUID);
if (existingRootConfig) return existingRootConfig;
logger.info("KMS: Generating new ROOT Key");
const newRootKey = randomSecureBytes(32);
const encryptedRootKey = await $encryptRootKey(newRootKey, RootKeyEncryptionStrategy.Software).catch((err) => {
logger.error({ hsmEnabled: hsmService.isActive() }, "KMS: Failed to encrypt ROOT Key");
throw err;
});
}
// check if KMS root key was already generated and saved in DB
const kmsRootConfig = await kmsRootConfigDAL.findById(KMS_ROOT_CONFIG_UUID);
// case 1: a root key already exists in the DB
if (kmsRootConfig) {
if (lock) await lock.release();
logger.info(`KMS: Encrypted ROOT Key found from DB. Decrypting. [strategy=${kmsRootConfig.encryptionStrategy}]`);
const decryptedRootKey = await $decryptRootKey(kmsRootConfig);
// set the flag so that other instance nodes can start
await keyStore.setItemWithExpiry(KMS_ROOT_CREATION_WAIT_KEY, KMS_ROOT_CREATION_WAIT_TIME, "true");
logger.info("KMS: Loading ROOT Key into Memory.");
ROOT_ENCRYPTION_KEY = decryptedRootKey;
return;
}
// case 2: no config is found, so we create a new root key with basic encryption
logger.info("KMS: Generating new ROOT Key");
const newRootKey = randomSecureBytes(32);
const encryptedRootKey = await $encryptRootKey(newRootKey, RootKeyEncryptionStrategy.Software).catch((err) => {
logger.error({ hsmEnabled: hsmService.isActive() }, "KMS: Failed to encrypt ROOT Key");
throw err;
const newRootConfig = await kmsRootConfigDAL.create({
// @ts-expect-error id is kept as fixed for idempotence and to avoid race condition
id: KMS_ROOT_CONFIG_UUID,
encryptedRootKey,
encryptionStrategy: RootKeyEncryptionStrategy.Software
});
return newRootConfig;
});
await kmsRootConfigDAL.create({
// @ts-expect-error id is kept as fixed for idempotence and to avoid race condition
id: KMS_ROOT_CONFIG_UUID,
encryptedRootKey,
encryptionStrategy: RootKeyEncryptionStrategy.Software
});
const decryptedRootKey = await $decryptRootKey(kmsRootConfig);
// set the flag so that other instance nodes can start
await keyStore.setItemWithExpiry(KMS_ROOT_CREATION_WAIT_KEY, KMS_ROOT_CREATION_WAIT_TIME, "true");
logger.info("KMS: Saved and loaded ROOT Key into memory");
if (lock) await lock.release();
ROOT_ENCRYPTION_KEY = newRootKey;
logger.info("KMS: Loading ROOT Key into Memory.");
ROOT_ENCRYPTION_KEY = decryptedRootKey;
};
const updateEncryptionStrategy = async (strategy: RootKeyEncryptionStrategy) => {