From 9f8e99a7e9e43ee506bfe5eb3d50da1095db06b9 Mon Sep 17 00:00:00 2001 From: Daniel Hougaard Date: Wed, 22 Oct 2025 00:24:48 +0400 Subject: [PATCH] fix: fips + hsm mode, key requirements --- .../e2e-test/routes/v2/service-token.spec.ts | 3 +- backend/e2e-test/routes/v3/secrets.spec.ts | 2 +- backend/e2e-test/vitest-environment-knex.ts | 29 +++++++++--- backend/src/@types/fastify-zod.d.ts | 4 ++ .../20250210101840_webhook-to-kms.ts | 11 +++-- ...250210101841_dynamic-secret-root-to-kms.ts | 11 +++-- .../20250210101841_secret-rotation-to-kms.ts | 10 +++-- .../20250210101842_identity-k8-auth-to-kms.ts | 11 +++-- ...0250210101842_identity-oidc-auth-to-kms.ts | 10 +++-- .../20250210101845_directory-config-to-kms.ts | 39 ++++++++-------- ...50513081738_remove-gateway-project-link.ts | 9 ++-- ...0_github-app-connection-to-environments.ts | 10 +++-- .../20250903191434_audit-log-stream-v2.ts | 14 ++++-- backend/src/db/migrations/utils/env-config.ts | 26 ++++++++++- backend/src/db/migrations/utils/services.ts | 30 ++++++++----- backend/src/db/seeds/1-user.ts | 21 ++++++++- backend/src/db/seeds/3-project.ts | 21 ++++++++- backend/src/db/seeds/5-machine-identity.ts | 20 ++++++++- backend/src/ee/services/hsm/hsm-fns.ts | 8 +++- backend/src/ee/services/hsm/hsm-service.ts | 8 +++- backend/src/lib/config/env.ts | 27 ++++++++++- backend/src/lib/crypto/cryptography/crypto.ts | 45 +++++++++++++++---- backend/src/main.ts | 25 ++++++++--- backend/src/server/app.ts | 16 ++++--- backend/src/server/routes/index.ts | 19 +++----- 25 files changed, 317 insertions(+), 112 deletions(-) diff --git a/backend/e2e-test/routes/v2/service-token.spec.ts b/backend/e2e-test/routes/v2/service-token.spec.ts index 4f72987cb0..d3a8b0f676 100644 --- a/backend/e2e-test/routes/v2/service-token.spec.ts +++ b/backend/e2e-test/routes/v2/service-token.spec.ts @@ -146,7 +146,8 @@ describe("Service token secret ops", async () => { let folderId = ""; beforeAll(async () => { initLogger(); - await initEnvConfig(testSuperAdminDAL, logger); + + await initEnvConfig(testHsmService, testKmsRootConfigDAL, testSuperAdminDAL, logger); serviceToken = await createServiceToken( [{ secretPath: "/**", environment: seedData1.environment.slug }], diff --git a/backend/e2e-test/routes/v3/secrets.spec.ts b/backend/e2e-test/routes/v3/secrets.spec.ts index 1e58c7f4a6..db5953f291 100644 --- a/backend/e2e-test/routes/v3/secrets.spec.ts +++ b/backend/e2e-test/routes/v3/secrets.spec.ts @@ -158,7 +158,7 @@ describe("Secret V3 Router", async () => { let folderId = ""; beforeAll(async () => { initLogger(); - await initEnvConfig(testSuperAdminDAL, logger); + await initEnvConfig(testHsmService, testKmsRootConfigDAL, testSuperAdminDAL, logger); const projectKeyRes = await testServer.inject({ method: "GET", diff --git a/backend/e2e-test/vitest-environment-knex.ts b/backend/e2e-test/vitest-environment-knex.ts index 085b8fe305..0f84dbee23 100644 --- a/backend/e2e-test/vitest-environment-knex.ts +++ b/backend/e2e-test/vitest-environment-knex.ts @@ -6,7 +6,7 @@ import { crypto } from "@app/lib/crypto/cryptography"; import path from "path"; import { seedData1 } from "@app/db/seed-data"; -import { getDatabaseCredentials, initEnvConfig } from "@app/lib/config/env"; +import { getDatabaseCredentials, getHsmConfig, initEnvConfig } from "@app/lib/config/env"; import { initLogger } from "@app/lib/logger"; import { main } from "@app/server/app"; import { AuthMethod, AuthTokenType } from "@app/services/auth/auth-type"; @@ -20,6 +20,8 @@ import { initializeHsmModule } from "@app/ee/services/hsm/hsm-fns"; import { buildRedisFromConfig } from "@app/lib/config/redis"; import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal"; import { bootstrapCheck } from "@app/server/boot-strap-check"; +import { hsmServiceFactory } from "@app/ee/services/hsm/hsm-service"; +import { kmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal"; dotenv.config({ path: path.join(__dirname, "../../.env.test"), debug: true }); export default { @@ -28,6 +30,7 @@ export default { async setup() { const logger = initLogger(); const databaseCredentials = getDatabaseCredentials(logger); + const hsmConfig = getHsmConfig(logger); const db = initDbConnection({ dbConnectionUri: databaseCredentials.dbConnectionUri, @@ -35,7 +38,19 @@ export default { }); const superAdminDAL = superAdminDALFactory(db); - const envCfg = await initEnvConfig(superAdminDAL, logger); + const kmsRootConfigDAL = kmsRootConfigDALFactory(db); + + const hsmModule = initializeHsmModule(hsmConfig); + hsmModule.initialize(); + + const hsmService = hsmServiceFactory({ + hsmModule: hsmModule.getModule(), + envConfig: hsmConfig + }); + + await hsmService.startService(); + + const envCfg = await initEnvConfig(hsmService, kmsRootConfigDAL, superAdminDAL, logger); const redis = buildRedisFromConfig(envCfg); await redis.flushdb("SYNC"); @@ -68,16 +83,14 @@ export default { await queue.initialize(); - const hsmModule = initializeHsmModule(envCfg); - hsmModule.initialize(); - const server = await main({ db, smtp, logger, queue, keyStore, - hsmModule: hsmModule.getModule(), + hsmService, + kmsRootConfigDAL, superAdminDAL, redis, envConfig: envCfg @@ -92,6 +105,10 @@ export default { // @ts-expect-error type globalThis.testSuperAdminDAL = superAdminDAL; // @ts-expect-error type + globalThis.testKmsRootConfigDAL = kmsRootConfigDAL; + // @ts-expect-error type + globalThis.testHsmService = hsmService; + // @ts-expect-error type globalThis.jwtAuthToken = crypto.jwt().sign( { authTokenType: AuthTokenType.ACCESS_TOKEN, diff --git a/backend/src/@types/fastify-zod.d.ts b/backend/src/@types/fastify-zod.d.ts index f0240d1a09..91cd006059 100644 --- a/backend/src/@types/fastify-zod.d.ts +++ b/backend/src/@types/fastify-zod.d.ts @@ -1,7 +1,9 @@ import { FastifyInstance, RawReplyDefaultExpression, RawRequestDefaultExpression, RawServerDefault } from "fastify"; +import { THsmServiceFactory } from "@app/ee/services/hsm/hsm-service"; import { CustomLogger } from "@app/lib/logger/logger"; import { ZodTypeProvider } from "@app/server/plugins/fastify-zod"; +import { TKmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal"; import { TSuperAdminDALFactory } from "@app/services/super-admin/super-admin-dal"; declare global { @@ -16,5 +18,7 @@ declare global { // used only for testing const testServer: FastifyZodProvider; const testSuperAdminDAL: TSuperAdminDALFactory; + const testKmsRootConfigDAL: TKmsRootConfigDALFactory; + const testHsmService: THsmServiceFactory; const jwtAuthToken: string; } diff --git a/backend/src/db/migrations/20250210101840_webhook-to-kms.ts b/backend/src/db/migrations/20250210101840_webhook-to-kms.ts index 09a346abba..2fbf681284 100644 --- a/backend/src/db/migrations/20250210101840_webhook-to-kms.ts +++ b/backend/src/db/migrations/20250210101840_webhook-to-kms.ts @@ -3,13 +3,14 @@ import { Knex } from "knex"; import { inMemoryKeyStore } from "@app/keystore/memory"; import { crypto } from "@app/lib/crypto/cryptography"; import { initLogger } from "@app/lib/logger"; +import { kmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal"; import { KmsDataKey } from "@app/services/kms/kms-types"; import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal"; import { SecretKeyEncoding, TableName } from "../schemas"; -import { getMigrationEnvConfig } from "./utils/env-config"; +import { getMigrationEnvConfig, getMigrationHsmConfig } from "./utils/env-config"; import { createCircularCache } from "./utils/ring-buffer"; -import { getMigrationEncryptionServices } from "./utils/services"; +import { getMigrationEncryptionServices, getMigrationHsmService } from "./utils/services"; const BATCH_SIZE = 500; export async function up(knex: Knex): Promise { @@ -25,10 +26,12 @@ export async function up(knex: Knex): Promise { if (hasUrl) t.string("url").nullable().alter(); }); } - initLogger(); + + const { hsmService } = await getMigrationHsmService({ envConfig: getMigrationHsmConfig() }); const superAdminDAL = superAdminDALFactory(knex); - const envConfig = await getMigrationEnvConfig(superAdminDAL); + const kmsRootConfigDAL = kmsRootConfigDALFactory(knex); + const envConfig = await getMigrationEnvConfig(superAdminDAL, hsmService, kmsRootConfigDAL); const keyStore = inMemoryKeyStore(); const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex }); diff --git a/backend/src/db/migrations/20250210101841_dynamic-secret-root-to-kms.ts b/backend/src/db/migrations/20250210101841_dynamic-secret-root-to-kms.ts index 94e30a7b87..179cb9bd69 100644 --- a/backend/src/db/migrations/20250210101841_dynamic-secret-root-to-kms.ts +++ b/backend/src/db/migrations/20250210101841_dynamic-secret-root-to-kms.ts @@ -4,13 +4,14 @@ import { inMemoryKeyStore } from "@app/keystore/memory"; import { crypto } from "@app/lib/crypto/cryptography"; import { selectAllTableCols } from "@app/lib/knex"; import { initLogger } from "@app/lib/logger"; +import { kmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal"; import { KmsDataKey } from "@app/services/kms/kms-types"; import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal"; import { SecretKeyEncoding, TableName } from "../schemas"; -import { getMigrationEnvConfig } from "./utils/env-config"; +import { getMigrationEnvConfig, getMigrationHsmConfig } from "./utils/env-config"; import { createCircularCache } from "./utils/ring-buffer"; -import { getMigrationEncryptionServices } from "./utils/services"; +import { getMigrationEncryptionServices, getMigrationHsmService } from "./utils/services"; const BATCH_SIZE = 500; export async function up(knex: Knex): Promise { @@ -30,8 +31,12 @@ export async function up(knex: Knex): Promise { } initLogger(); + + const { hsmService } = await getMigrationHsmService({ envConfig: getMigrationHsmConfig() }); + const superAdminDAL = superAdminDALFactory(knex); - const envConfig = await getMigrationEnvConfig(superAdminDAL); + const kmsRootConfigDAL = kmsRootConfigDALFactory(knex); + const envConfig = await getMigrationEnvConfig(superAdminDAL, hsmService, kmsRootConfigDAL); const keyStore = inMemoryKeyStore(); const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex }); diff --git a/backend/src/db/migrations/20250210101841_secret-rotation-to-kms.ts b/backend/src/db/migrations/20250210101841_secret-rotation-to-kms.ts index bbda48dac5..aef429ab98 100644 --- a/backend/src/db/migrations/20250210101841_secret-rotation-to-kms.ts +++ b/backend/src/db/migrations/20250210101841_secret-rotation-to-kms.ts @@ -4,13 +4,14 @@ import { inMemoryKeyStore } from "@app/keystore/memory"; import { crypto } from "@app/lib/crypto/cryptography"; import { selectAllTableCols } from "@app/lib/knex"; import { initLogger } from "@app/lib/logger"; +import { kmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal"; import { KmsDataKey } from "@app/services/kms/kms-types"; import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal"; import { SecretKeyEncoding, TableName } from "../schemas"; -import { getMigrationEnvConfig } from "./utils/env-config"; +import { getMigrationEnvConfig, getMigrationHsmConfig } from "./utils/env-config"; import { createCircularCache } from "./utils/ring-buffer"; -import { getMigrationEncryptionServices } from "./utils/services"; +import { getMigrationEncryptionServices, getMigrationHsmService } from "./utils/services"; const BATCH_SIZE = 500; export async function up(knex: Knex): Promise { @@ -24,8 +25,11 @@ export async function up(knex: Knex): Promise { } initLogger(); + const { hsmService } = await getMigrationHsmService({ envConfig: getMigrationHsmConfig() }); + const superAdminDAL = superAdminDALFactory(knex); - const envConfig = await getMigrationEnvConfig(superAdminDAL); + const kmsRootConfigDAL = kmsRootConfigDALFactory(knex); + const envConfig = await getMigrationEnvConfig(superAdminDAL, hsmService, kmsRootConfigDAL); const keyStore = inMemoryKeyStore(); const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex }); diff --git a/backend/src/db/migrations/20250210101842_identity-k8-auth-to-kms.ts b/backend/src/db/migrations/20250210101842_identity-k8-auth-to-kms.ts index a24bfdf0c2..f3fa630281 100644 --- a/backend/src/db/migrations/20250210101842_identity-k8-auth-to-kms.ts +++ b/backend/src/db/migrations/20250210101842_identity-k8-auth-to-kms.ts @@ -4,13 +4,14 @@ import { inMemoryKeyStore } from "@app/keystore/memory"; import { crypto, SymmetricKeySize } from "@app/lib/crypto/cryptography"; import { selectAllTableCols } from "@app/lib/knex"; import { initLogger } from "@app/lib/logger"; +import { kmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal"; import { KmsDataKey } from "@app/services/kms/kms-types"; import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal"; import { SecretKeyEncoding, TableName, TOrgBots } from "../schemas"; -import { getMigrationEnvConfig } from "./utils/env-config"; +import { getMigrationEnvConfig, getMigrationHsmConfig } from "./utils/env-config"; import { createCircularCache } from "./utils/ring-buffer"; -import { getMigrationEncryptionServices } from "./utils/services"; +import { getMigrationEncryptionServices, getMigrationHsmService } from "./utils/services"; const BATCH_SIZE = 500; const reencryptIdentityK8sAuth = async (knex: Knex) => { @@ -55,9 +56,11 @@ const reencryptIdentityK8sAuth = async (knex: Knex) => { } initLogger(); - const superAdminDAL = superAdminDALFactory(knex); - const envConfig = await getMigrationEnvConfig(superAdminDAL); + const { hsmService } = await getMigrationHsmService({ envConfig: getMigrationHsmConfig() }); + const superAdminDAL = superAdminDALFactory(knex); + const kmsRootConfigDAL = kmsRootConfigDALFactory(knex); + const envConfig = await getMigrationEnvConfig(superAdminDAL, hsmService, kmsRootConfigDAL); const keyStore = inMemoryKeyStore(); const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex }); const orgEncryptionRingBuffer = diff --git a/backend/src/db/migrations/20250210101842_identity-oidc-auth-to-kms.ts b/backend/src/db/migrations/20250210101842_identity-oidc-auth-to-kms.ts index 25db615fab..f970043f05 100644 --- a/backend/src/db/migrations/20250210101842_identity-oidc-auth-to-kms.ts +++ b/backend/src/db/migrations/20250210101842_identity-oidc-auth-to-kms.ts @@ -4,13 +4,14 @@ import { inMemoryKeyStore } from "@app/keystore/memory"; import { crypto, SymmetricKeySize } from "@app/lib/crypto/cryptography"; import { selectAllTableCols } from "@app/lib/knex"; import { initLogger } from "@app/lib/logger"; +import { kmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal"; import { KmsDataKey } from "@app/services/kms/kms-types"; import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal"; import { SecretKeyEncoding, TableName, TOrgBots } from "../schemas"; -import { getMigrationEnvConfig } from "./utils/env-config"; +import { getMigrationEnvConfig, getMigrationHsmConfig } from "./utils/env-config"; import { createCircularCache } from "./utils/ring-buffer"; -import { getMigrationEncryptionServices } from "./utils/services"; +import { getMigrationEncryptionServices, getMigrationHsmService } from "./utils/services"; const BATCH_SIZE = 500; const reencryptIdentityOidcAuth = async (knex: Knex) => { @@ -35,8 +36,11 @@ const reencryptIdentityOidcAuth = async (knex: Knex) => { } initLogger(); + const { hsmService } = await getMigrationHsmService({ envConfig: getMigrationHsmConfig() }); + const superAdminDAL = superAdminDALFactory(knex); - const envConfig = await getMigrationEnvConfig(superAdminDAL); + const kmsRootConfigDAL = kmsRootConfigDALFactory(knex); + const envConfig = await getMigrationEnvConfig(superAdminDAL, hsmService, kmsRootConfigDAL); const keyStore = inMemoryKeyStore(); const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex }); diff --git a/backend/src/db/migrations/20250210101845_directory-config-to-kms.ts b/backend/src/db/migrations/20250210101845_directory-config-to-kms.ts index 783693da65..62b4e85566 100644 --- a/backend/src/db/migrations/20250210101845_directory-config-to-kms.ts +++ b/backend/src/db/migrations/20250210101845_directory-config-to-kms.ts @@ -4,16 +4,18 @@ import { inMemoryKeyStore } from "@app/keystore/memory"; import { crypto, SymmetricKeySize } from "@app/lib/crypto/cryptography"; import { selectAllTableCols } from "@app/lib/knex"; import { initLogger } from "@app/lib/logger"; +import { kmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal"; +import { TKmsServiceFactory } from "@app/services/kms/kms-service"; import { KmsDataKey } from "@app/services/kms/kms-types"; import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal"; import { SecretKeyEncoding, TableName } from "../schemas"; -import { getMigrationEnvConfig } from "./utils/env-config"; +import { getMigrationEnvConfig, getMigrationHsmConfig } from "./utils/env-config"; import { createCircularCache } from "./utils/ring-buffer"; -import { getMigrationEncryptionServices } from "./utils/services"; +import { getMigrationEncryptionServices, getMigrationHsmService } from "./utils/services"; const BATCH_SIZE = 500; -const reencryptSamlConfig = async (knex: Knex) => { +const reencryptSamlConfig = async (knex: Knex, kmsService: TKmsServiceFactory) => { const hasEncryptedEntrypointColumn = await knex.schema.hasColumn(TableName.SamlConfig, "encryptedSamlEntryPoint"); const hasEncryptedIssuerColumn = await knex.schema.hasColumn(TableName.SamlConfig, "encryptedSamlIssuer"); const hasEncryptedCertificateColumn = await knex.schema.hasColumn(TableName.SamlConfig, "encryptedSamlCertificate"); @@ -28,10 +30,6 @@ const reencryptSamlConfig = async (knex: Knex) => { } initLogger(); - const superAdminDAL = superAdminDALFactory(knex); - const envConfig = await getMigrationEnvConfig(superAdminDAL); - const keyStore = inMemoryKeyStore(); - const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex }); const orgEncryptionRingBuffer = createCircularCache>>(25); @@ -159,7 +157,7 @@ const reencryptSamlConfig = async (knex: Knex) => { } }; -const reencryptLdapConfig = async (knex: Knex) => { +const reencryptLdapConfig = async (knex: Knex, kmsService: TKmsServiceFactory) => { const hasEncryptedLdapBindDNColum = await knex.schema.hasColumn(TableName.LdapConfig, "encryptedLdapBindDN"); const hasEncryptedLdapBindPassColumn = await knex.schema.hasColumn(TableName.LdapConfig, "encryptedLdapBindPass"); const hasEncryptedCertificateColumn = await knex.schema.hasColumn(TableName.LdapConfig, "encryptedLdapCaCertificate"); @@ -194,10 +192,6 @@ const reencryptLdapConfig = async (knex: Knex) => { } initLogger(); - const superAdminDAL = superAdminDALFactory(knex); - const envConfig = await getMigrationEnvConfig(superAdminDAL); - const keyStore = inMemoryKeyStore(); - const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex }); const orgEncryptionRingBuffer = createCircularCache>>(25); @@ -323,7 +317,7 @@ const reencryptLdapConfig = async (knex: Knex) => { } }; -const reencryptOidcConfig = async (knex: Knex) => { +const reencryptOidcConfig = async (knex: Knex, kmsService: TKmsServiceFactory) => { const hasEncryptedOidcClientIdColumn = await knex.schema.hasColumn(TableName.OidcConfig, "encryptedOidcClientId"); const hasEncryptedOidcClientSecretColumn = await knex.schema.hasColumn( TableName.OidcConfig, @@ -354,10 +348,6 @@ const reencryptOidcConfig = async (knex: Knex) => { } initLogger(); - const superAdminDAL = superAdminDALFactory(knex); - const envConfig = await getMigrationEnvConfig(superAdminDAL); - const keyStore = inMemoryKeyStore(); - const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex }); const orgEncryptionRingBuffer = createCircularCache>>(25); @@ -462,9 +452,18 @@ const reencryptOidcConfig = async (knex: Knex) => { }; export async function up(knex: Knex): Promise { - await reencryptSamlConfig(knex); - await reencryptLdapConfig(knex); - await reencryptOidcConfig(knex); + initLogger(); + + const { hsmService } = await getMigrationHsmService({ envConfig: getMigrationHsmConfig() }); + const superAdminDAL = superAdminDALFactory(knex); + const kmsRootConfigDAL = kmsRootConfigDALFactory(knex); + const envConfig = await getMigrationEnvConfig(superAdminDAL, hsmService, kmsRootConfigDAL); + const keyStore = inMemoryKeyStore(); + const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex }); + + await reencryptSamlConfig(knex, kmsService); + await reencryptLdapConfig(knex, kmsService); + await reencryptOidcConfig(knex, kmsService); } const dropSamlConfigColumns = async (knex: Knex) => { diff --git a/backend/src/db/migrations/20250513081738_remove-gateway-project-link.ts b/backend/src/db/migrations/20250513081738_remove-gateway-project-link.ts index a0985471f3..dd9ff2d6a4 100644 --- a/backend/src/db/migrations/20250513081738_remove-gateway-project-link.ts +++ b/backend/src/db/migrations/20250513081738_remove-gateway-project-link.ts @@ -3,12 +3,13 @@ import { Knex } from "knex"; import { inMemoryKeyStore } from "@app/keystore/memory"; import { selectAllTableCols } from "@app/lib/knex"; import { initLogger } from "@app/lib/logger"; +import { kmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal"; import { KmsDataKey } from "@app/services/kms/kms-types"; import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal"; import { TableName } from "../schemas"; -import { getMigrationEnvConfig } from "./utils/env-config"; -import { getMigrationEncryptionServices } from "./utils/services"; +import { getMigrationEnvConfig, getMigrationHsmConfig } from "./utils/env-config"; +import { getMigrationEncryptionServices, getMigrationHsmService } from "./utils/services"; // Note(daniel): We aren't dropping tables or columns in this migrations so we can easily rollback if needed. // In the future we need to drop the projectGatewayId on the dynamic secrets table, and drop the project_gateways table entirely. @@ -40,8 +41,10 @@ export async function up(knex: Knex): Promise { ); initLogger(); + const { hsmService } = await getMigrationHsmService({ envConfig: getMigrationHsmConfig() }); const superAdminDAL = superAdminDALFactory(knex); - const envConfig = await getMigrationEnvConfig(superAdminDAL); + const kmsRootConfigDAL = kmsRootConfigDALFactory(knex); + const envConfig = await getMigrationEnvConfig(superAdminDAL, hsmService, kmsRootConfigDAL); const keyStore = inMemoryKeyStore(); const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex }); diff --git a/backend/src/db/migrations/20250711005900_github-app-connection-to-environments.ts b/backend/src/db/migrations/20250711005900_github-app-connection-to-environments.ts index 548d6207a8..f2bc0a96a9 100644 --- a/backend/src/db/migrations/20250711005900_github-app-connection-to-environments.ts +++ b/backend/src/db/migrations/20250711005900_github-app-connection-to-environments.ts @@ -2,19 +2,23 @@ import { Knex } from "knex"; import { inMemoryKeyStore } from "@app/keystore/memory"; import { selectAllTableCols } from "@app/lib/knex"; +import { kmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal"; import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal"; import { TableName } from "../schemas"; -import { getMigrationEnvConfig } from "./utils/env-config"; -import { getMigrationEncryptionServices } from "./utils/services"; +import { getMigrationEnvConfig, getMigrationHsmConfig } from "./utils/env-config"; +import { getMigrationEncryptionServices, getMigrationHsmService } from "./utils/services"; export async function up(knex: Knex) { const existingSuperAdminsWithGithubConnection = await knex(TableName.SuperAdmin) .select(selectAllTableCols(TableName.SuperAdmin)) .whereNotNull(`${TableName.SuperAdmin}.encryptedGitHubAppConnectionClientId`); + const { hsmService } = await getMigrationHsmService({ envConfig: getMigrationHsmConfig() }); + const superAdminDAL = superAdminDALFactory(knex); - const envConfig = await getMigrationEnvConfig(superAdminDAL); + const kmsRootConfigDAL = kmsRootConfigDALFactory(knex); + const envConfig = await getMigrationEnvConfig(superAdminDAL, hsmService, kmsRootConfigDAL); const keyStore = inMemoryKeyStore(); const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex }); diff --git a/backend/src/db/migrations/20250903191434_audit-log-stream-v2.ts b/backend/src/db/migrations/20250903191434_audit-log-stream-v2.ts index a70dcb8b97..82fa4a0392 100644 --- a/backend/src/db/migrations/20250903191434_audit-log-stream-v2.ts +++ b/backend/src/db/migrations/20250903191434_audit-log-stream-v2.ts @@ -2,13 +2,14 @@ import { Knex } from "knex"; import { inMemoryKeyStore } from "@app/keystore/memory"; import { crypto } from "@app/lib/crypto/cryptography"; +import { kmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal"; import { KmsDataKey } from "@app/services/kms/kms-types"; import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal"; import { SecretKeyEncoding, TableName } from "../schemas"; -import { getMigrationEnvConfig } from "./utils/env-config"; +import { getMigrationEnvConfig, getMigrationHsmConfig } from "./utils/env-config"; import { createCircularCache } from "./utils/ring-buffer"; -import { getMigrationEncryptionServices } from "./utils/services"; +import { getMigrationEncryptionServices, getMigrationHsmService } from "./utils/services"; const BATCH_SIZE = 500; export async function up(knex: Knex): Promise { @@ -25,8 +26,10 @@ export async function up(knex: Knex): Promise { }); if (!hasEncryptedCredentials) { + const { hsmService } = await getMigrationHsmService({ envConfig: getMigrationHsmConfig() }); const superAdminDAL = superAdminDALFactory(knex); - const envConfig = await getMigrationEnvConfig(superAdminDAL); + const kmsRootConfigDAL = kmsRootConfigDALFactory(knex); + const envConfig = await getMigrationEnvConfig(superAdminDAL, hsmService, kmsRootConfigDAL); const keyStore = inMemoryKeyStore(); const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex }); @@ -131,8 +134,11 @@ export async function down(knex: Knex): Promise { const hasEncryptedCredentials = await knex.schema.hasColumn(TableName.AuditLogStream, "encryptedCredentials"); if (hasEncryptedCredentials) { + const { hsmService } = await getMigrationHsmService({ envConfig: getMigrationHsmConfig() }); + const superAdminDAL = superAdminDALFactory(knex); - const envConfig = await getMigrationEnvConfig(superAdminDAL); + const kmsRootConfigDAL = kmsRootConfigDALFactory(knex); + const envConfig = await getMigrationEnvConfig(superAdminDAL, hsmService, kmsRootConfigDAL); const keyStore = inMemoryKeyStore(); const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex }); diff --git a/backend/src/db/migrations/utils/env-config.ts b/backend/src/db/migrations/utils/env-config.ts index 6da7044aa6..3a08f0123a 100644 --- a/backend/src/db/migrations/utils/env-config.ts +++ b/backend/src/db/migrations/utils/env-config.ts @@ -1,8 +1,10 @@ import { z } from "zod"; +import { THsmServiceFactory } from "@app/ee/services/hsm/hsm-service"; import { crypto } from "@app/lib/crypto/cryptography"; import { removeTrailingSlash } from "@app/lib/fn"; import { zpStr } from "@app/lib/zod"; +import { TKmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal"; import { TSuperAdminDALFactory } from "@app/services/super-admin/super-admin-dal"; const envSchema = z @@ -42,7 +44,27 @@ const envSchema = z export type TMigrationEnvConfig = z.infer; -export const getMigrationEnvConfig = async (superAdminDAL: TSuperAdminDALFactory) => { +export const getMigrationHsmConfig = () => { + const parsedEnv = envSchema.safeParse(process.env); + if (!parsedEnv.success) { + console.error("Invalid environment variables. Check the error below"); + console.error(parsedEnv.error.issues); + process.exit(-1); + } + return { + isHsmConfigured: parsedEnv.data.isHsmConfigured, + HSM_PIN: parsedEnv.data.HSM_PIN, + HSM_SLOT: parsedEnv.data.HSM_SLOT, + HSM_LIB_PATH: parsedEnv.data.HSM_LIB_PATH, + HSM_KEY_LABEL: parsedEnv.data.HSM_KEY_LABEL + }; +}; + +export const getMigrationEnvConfig = async ( + superAdminDAL: TSuperAdminDALFactory, + hsmService: THsmServiceFactory, + kmsRootConfigDAL: TKmsRootConfigDALFactory +) => { const parsedEnv = envSchema.safeParse(process.env); if (!parsedEnv.success) { // eslint-disable-next-line no-console @@ -58,7 +80,7 @@ export const getMigrationEnvConfig = async (superAdminDAL: TSuperAdminDALFactory let envCfg = Object.freeze(parsedEnv.data); - const fipsEnabled = await crypto.initialize(superAdminDAL, envCfg); + const fipsEnabled = await crypto.initialize(superAdminDAL, hsmService, kmsRootConfigDAL, envCfg); // Fix for 128-bit entropy encryption key expansion issue: // In FIPS it is not ideal to expand a 128-bit key into 256-bit. We solved this issue in the past by creating the ROOT_ENCRYPTION_KEY. diff --git a/backend/src/db/migrations/utils/services.ts b/backend/src/db/migrations/utils/services.ts index e4b675f7e0..67db7cd9d2 100644 --- a/backend/src/db/migrations/utils/services.ts +++ b/backend/src/db/migrations/utils/services.ts @@ -29,6 +29,24 @@ type TDependencies = { keyStore: TKeyStoreFactory; }; +type THsmServiceDependencies = { + envConfig: Pick; +}; + +export const getMigrationHsmService = async ({ envConfig }: THsmServiceDependencies) => { + const hsmModule = initializeHsmModule(envConfig); + hsmModule.initialize(); + + const hsmService = hsmServiceFactory({ + hsmModule: hsmModule.getModule(), + envConfig + }); + + await hsmService.startService(); + + return { hsmService }; +}; + export const getMigrationEncryptionServices = async ({ envConfig, db, keyStore }: TDependencies) => { // ----- DAL dependencies ----- const orgDAL = orgDALFactory(db); @@ -67,15 +85,7 @@ export const getMigrationEncryptionServices = async ({ envConfig, db, keyStore } // ----- HSM startup ----- - const hsmModule = initializeHsmModule(envConfig); - hsmModule.initialize(); - - const hsmService = hsmServiceFactory({ - hsmModule: hsmModule.getModule(), - envConfig - }); - - await hsmService.startService(); + const { hsmService } = await getMigrationHsmService({ envConfig }); const hsmStatus = await isHsmActiveAndEnabled({ hsmService, @@ -113,5 +123,5 @@ export const getMigrationEncryptionServices = async ({ envConfig, db, keyStore } await kmsService.startService(hsmStatus); - return { kmsService }; + return { kmsService, hsmService }; }; diff --git a/backend/src/db/seeds/1-user.ts b/backend/src/db/seeds/1-user.ts index 43ce4dadfc..9f42ef12b6 100644 --- a/backend/src/db/seeds/1-user.ts +++ b/backend/src/db/seeds/1-user.ts @@ -1,7 +1,10 @@ import { Knex } from "knex"; -import { initEnvConfig } from "@app/lib/config/env"; +import { initializeHsmModule } from "@app/ee/services/hsm/hsm-fns"; +import { hsmServiceFactory } from "@app/ee/services/hsm/hsm-service"; +import { getHsmConfig, initEnvConfig } from "@app/lib/config/env"; import { initLogger, logger } from "@app/lib/logger"; +import { kmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal"; import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal"; import { AuthMethod } from "../../services/auth/auth-type"; @@ -17,7 +20,21 @@ export async function seed(knex: Knex): Promise { initLogger(); const superAdminDAL = superAdminDALFactory(knex); - await initEnvConfig(superAdminDAL, logger); + const kmsRootConfigDAL = kmsRootConfigDALFactory(knex); + + const hsmConfig = getHsmConfig(logger); + + const hsmModule = initializeHsmModule(hsmConfig); + hsmModule.initialize(); + + const hsmService = hsmServiceFactory({ + hsmModule: hsmModule.getModule(), + envConfig: hsmConfig + }); + + await hsmService.startService(); + + await initEnvConfig(hsmService, kmsRootConfigDAL, superAdminDAL, logger); await knex(TableName.SuperAdmin).insert([ // eslint-disable-next-line diff --git a/backend/src/db/seeds/3-project.ts b/backend/src/db/seeds/3-project.ts index d0294022f2..99083ab943 100644 --- a/backend/src/db/seeds/3-project.ts +++ b/backend/src/db/seeds/3-project.ts @@ -1,11 +1,14 @@ import { Knex } from "knex"; -import { initEnvConfig } from "@app/lib/config/env"; +import { initializeHsmModule } from "@app/ee/services/hsm/hsm-fns"; +import { hsmServiceFactory } from "@app/ee/services/hsm/hsm-service"; +import { getHsmConfig, initEnvConfig } from "@app/lib/config/env"; import { crypto, SymmetricKeySize } from "@app/lib/crypto/cryptography"; import { generateUserSrpKeys } from "@app/lib/crypto/srp"; import { initLogger, logger } from "@app/lib/logger"; import { alphaNumericNanoId } from "@app/lib/nanoid"; import { AuthMethod } from "@app/services/auth/auth-type"; +import { kmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal"; import { membershipRoleDALFactory } from "@app/services/membership/membership-role-dal"; import { membershipUserDALFactory } from "@app/services/membership-user/membership-user-dal"; import { assignWorkspaceKeysToMembers, createProjectKey } from "@app/services/project/project-fns"; @@ -192,7 +195,21 @@ export async function seed(knex: Knex): Promise { initLogger(); const superAdminDAL = superAdminDALFactory(knex); - await initEnvConfig(superAdminDAL, logger); + const kmsRootConfigDAL = kmsRootConfigDALFactory(knex); + + const hsmConfig = getHsmConfig(logger); + + const hsmModule = initializeHsmModule(hsmConfig); + hsmModule.initialize(); + + const hsmService = hsmServiceFactory({ + hsmModule: hsmModule.getModule(), + envConfig: hsmConfig + }); + + await hsmService.startService(); + + await initEnvConfig(hsmService, kmsRootConfigDAL, superAdminDAL, logger); const [project] = await knex(TableName.Project) .insert({ diff --git a/backend/src/db/seeds/5-machine-identity.ts b/backend/src/db/seeds/5-machine-identity.ts index 333fc7e3a6..5314d12e2f 100644 --- a/backend/src/db/seeds/5-machine-identity.ts +++ b/backend/src/db/seeds/5-machine-identity.ts @@ -1,8 +1,11 @@ import { Knex } from "knex"; -import { initEnvConfig } from "@app/lib/config/env"; +import { initializeHsmModule } from "@app/ee/services/hsm/hsm-fns"; +import { hsmServiceFactory } from "@app/ee/services/hsm/hsm-service"; +import { getHsmConfig, initEnvConfig } from "@app/lib/config/env"; import { crypto } from "@app/lib/crypto/cryptography"; import { initLogger, logger } from "@app/lib/logger"; +import { kmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal"; import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal"; import { AccessScope, IdentityAuthMethod, OrgMembershipRole, ProjectMembershipRole, TableName } from "../schemas"; @@ -15,7 +18,20 @@ export async function seed(knex: Knex): Promise { initLogger(); const superAdminDAL = superAdminDALFactory(knex); - await initEnvConfig(superAdminDAL, logger); + const kmsRootConfigDAL = kmsRootConfigDALFactory(knex); + const hsmConfig = getHsmConfig(logger); + + const hsmModule = initializeHsmModule(hsmConfig); + hsmModule.initialize(); + + const hsmService = hsmServiceFactory({ + hsmModule: hsmModule.getModule(), + envConfig: hsmConfig + }); + + await hsmService.startService(); + + await initEnvConfig(hsmService, kmsRootConfigDAL, superAdminDAL, logger); // Inserts seed entries await knex(TableName.Identity).insert([ diff --git a/backend/src/ee/services/hsm/hsm-fns.ts b/backend/src/ee/services/hsm/hsm-fns.ts index f4d0b88011..400fa31e9c 100644 --- a/backend/src/ee/services/hsm/hsm-fns.ts +++ b/backend/src/ee/services/hsm/hsm-fns.ts @@ -73,7 +73,7 @@ export const isHsmActiveAndEnabled = async ({ }: { hsmService: Pick; kmsRootConfigDAL: Pick; - licenseService: Pick; + licenseService?: Pick; }) => { const isHsmConfigured = await hsmService.isActive(); @@ -83,7 +83,11 @@ export const isHsmActiveAndEnabled = async ({ const rootKmsConfig = await kmsRootConfigDAL.findById(KMS_ROOT_CONFIG_UUID).catch(() => null); rootKmsConfigEncryptionStrategy = (rootKmsConfig?.encryptionStrategy || null) as RootKeyEncryptionStrategy | null; - if (rootKmsConfigEncryptionStrategy === RootKeyEncryptionStrategy.HSM && !licenseService.onPremFeatures.hsm) { + if ( + rootKmsConfigEncryptionStrategy === RootKeyEncryptionStrategy.HSM && + licenseService && + !licenseService.onPremFeatures.hsm + ) { throw new BadRequestError({ message: "Your license does not include HSM integration. Please upgrade to the Enterprise plan to use HSM." }); diff --git a/backend/src/ee/services/hsm/hsm-service.ts b/backend/src/ee/services/hsm/hsm-service.ts index 3b332e446a..1207b1cd3f 100644 --- a/backend/src/ee/services/hsm/hsm-service.ts +++ b/backend/src/ee/services/hsm/hsm-service.ts @@ -25,6 +25,8 @@ export const hsmServiceFactory = ({ hsmModule: { isInitialized, pkcs11 }, envCon const AES_KEY_SIZE = 256; const HMAC_KEY_SIZE = 256; + let pkcs11TestPassed = false; + const $withSession = async (callbackWithSession: SessionCallback): Promise => { const RETRY_INTERVAL = 200; // 200ms between attempts const MAX_TIMEOUT = 90_000; // 90 seconds maximum total time @@ -363,7 +365,9 @@ export const hsmServiceFactory = ({ hsmModule: { isInitialized, pkcs11 }, envCon return false; } - let pkcs11TestPassed = false; + if (pkcs11TestPassed) { + return true; + } try { pkcs11TestPassed = await $withSession($testPkcs11Module); @@ -371,7 +375,7 @@ export const hsmServiceFactory = ({ hsmModule: { isInitialized, pkcs11 }, envCon logger.error(err, "HSM: Error testing PKCS#11 module"); } - return envConfig.isHsmConfigured && isInitialized && pkcs11TestPassed; + return pkcs11TestPassed; }; const startService = async () => { diff --git a/backend/src/lib/config/env.ts b/backend/src/lib/config/env.ts index 31e0eaeb4f..2da7a245a3 100644 --- a/backend/src/lib/config/env.ts +++ b/backend/src/lib/config/env.ts @@ -1,5 +1,6 @@ import { z } from "zod"; +import { THsmServiceFactory } from "@app/ee/services/hsm/hsm-service"; import { crypto } from "@app/lib/crypto/cryptography"; import { QueueWorkerProfile } from "@app/lib/types"; import { TSuperAdminDALFactory } from "@app/services/super-admin/super-admin-dal"; @@ -8,6 +9,7 @@ import { BadRequestError } from "../errors"; import { removeTrailingSlash } from "../fn"; import { CustomLogger } from "../logger/logger"; import { zpStr } from "../zod"; +import { TKmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal"; export const GITLAB_URL = "https://gitlab.com"; @@ -448,7 +450,12 @@ export const getConfig = () => envCfg; export const getOriginalConfig = () => originalEnvConfig; // cannot import singleton logger directly as it needs config to load various transport -export const initEnvConfig = async (superAdminDAL?: TSuperAdminDALFactory, logger?: CustomLogger) => { +export const initEnvConfig = async ( + hsmService: THsmServiceFactory, + kmsRootConfigDAL: TKmsRootConfigDALFactory, + superAdminDAL?: TSuperAdminDALFactory, + logger?: CustomLogger +) => { const parsedEnv = envSchema.safeParse(process.env); if (!parsedEnv.success) { (logger ?? console).error("Invalid environment variables. Check the error below"); @@ -464,7 +471,7 @@ export const initEnvConfig = async (superAdminDAL?: TSuperAdminDALFactory, logge } if (superAdminDAL) { - const fipsEnabled = await crypto.initialize(superAdminDAL); + const fipsEnabled = await crypto.initialize(superAdminDAL, hsmService, kmsRootConfigDAL); if (fipsEnabled) { const newEnvCfg = { @@ -527,6 +534,22 @@ export const getDatabaseCredentials = (logger?: CustomLogger) => { }; }; +export const getHsmConfig = (logger?: CustomLogger) => { + const parsedEnv = envSchema.safeParse(process.env); + if (!parsedEnv.success) { + (logger ?? console).error("Invalid environment variables. Check the error below"); + (logger ?? console).error(parsedEnv.error.issues); + process.exit(-1); + } + return { + isHsmConfigured: parsedEnv.data.isHsmConfigured, + HSM_PIN: parsedEnv.data.HSM_PIN, + HSM_SLOT: parsedEnv.data.HSM_SLOT, + HSM_LIB_PATH: parsedEnv.data.HSM_LIB_PATH, + HSM_KEY_LABEL: parsedEnv.data.HSM_KEY_LABEL + }; +}; + // A list of environment variables that can be overwritten export const overwriteSchema: { [key: string]: { diff --git a/backend/src/lib/crypto/cryptography/crypto.ts b/backend/src/lib/crypto/cryptography/crypto.ts index af96deb7c8..49ee9c01fa 100644 --- a/backend/src/lib/crypto/cryptography/crypto.ts +++ b/backend/src/lib/crypto/cryptography/crypto.ts @@ -9,7 +9,11 @@ import nacl from "tweetnacl"; import naclUtils from "tweetnacl-util"; import { SecretEncryptionAlgo, SecretKeyEncoding } from "@app/db/schemas"; +import { isHsmActiveAndEnabled } from "@app/ee/services/hsm/hsm-fns"; +import { THsmServiceFactory } from "@app/ee/services/hsm/hsm-service"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; +import { TKmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal"; +import { RootKeyEncryptionStrategy } from "@app/services/kms/kms-types"; import { TSuperAdminDALFactory } from "@app/services/super-admin/super-admin-dal"; import { ADMIN_CONFIG_DB_UUID } from "@app/services/super-admin/super-admin-service"; @@ -106,7 +110,12 @@ const cryptographyFactory = () => { } }; - const $setFipsModeEnabled = (enabled: boolean, envCfg?: Pick) => { + const $setFipsModeEnabled = async ( + enabled: boolean, + hsmService: THsmServiceFactory, + kmsRootConfigDAL: TKmsRootConfigDALFactory, + envCfg?: Pick + ) => { // If FIPS is enabled, we need to validate that the ENCRYPTION_KEY is in a base64 format, and is a 256-bit key. if (enabled) { crypto.setFips(true); @@ -131,24 +140,42 @@ const cryptographyFactory = () => { }); } } else { - throw new CryptographyError({ - message: - "FIPS mode is enabled, but the ENCRYPTION_KEY environment variable is not set.\nYou can generate a 256-bit key using the following command: `openssl rand -base64 32`" + const hsmStatus = await isHsmActiveAndEnabled({ + hsmService, + kmsRootConfigDAL }); + + // if the encryption strategy is software - user needs to provide an encryption key + // if the encryption strategy is null AND the hsm is not configured - user needs to provide an encryption key + const needsEncryptionKey = + hsmStatus.rootKmsConfigEncryptionStrategy === RootKeyEncryptionStrategy.Software || + (hsmStatus.rootKmsConfigEncryptionStrategy === null && !hsmStatus.isHsmConfigured); + + if (needsEncryptionKey) { + throw new CryptographyError({ + message: + "FIPS mode is enabled, but the ENCRYPTION_KEY environment variable is not set.\nYou can generate a 256-bit key using the following command: `openssl rand -base64 32`" + }); + } } } $fipsEnabled = enabled; $isInitialized = true; }; - const initialize = async (superAdminDAL: TSuperAdminDALFactory, envCfg?: Pick) => { + const initialize = async ( + superAdminDAL: TSuperAdminDALFactory, + hsmService: THsmServiceFactory, + kmsRootConfigDAL: TKmsRootConfigDALFactory, + envCfg?: Pick + ) => { if ($isInitialized) { return isFipsModeEnabled(); } if (process.env.FIPS_ENABLED !== "true") { logger.info("Cryptography module initialized in normal operation mode."); - $setFipsModeEnabled(false, envCfg); + await $setFipsModeEnabled(false, hsmService, kmsRootConfigDAL, envCfg); return false; } @@ -158,11 +185,11 @@ const cryptographyFactory = () => { if (serverCfg) { if (serverCfg.fipsEnabled) { logger.info("[FIPS]: Instance is configured for FIPS mode of operation. Continuing startup with FIPS enabled."); - $setFipsModeEnabled(true, envCfg); + await $setFipsModeEnabled(true, hsmService, kmsRootConfigDAL, envCfg); return true; } logger.info("[FIPS]: Instance age predates FIPS mode inception date. Continuing without FIPS."); - $setFipsModeEnabled(false, envCfg); + await $setFipsModeEnabled(false, hsmService, kmsRootConfigDAL, envCfg); return false; } @@ -171,7 +198,7 @@ const cryptographyFactory = () => { // TODO(daniel): check if it's an enterprise deployment // if there is no server cfg, and FIPS_MODE is `true`, its a fresh FIPS deployment. We need to set the fipsEnabled to true. - $setFipsModeEnabled(true, envCfg); + await $setFipsModeEnabled(true, hsmService, kmsRootConfigDAL, envCfg); return true; }; diff --git a/backend/src/main.ts b/backend/src/main.ts index 7be9f43ec5..400804804a 100644 --- a/backend/src/main.ts +++ b/backend/src/main.ts @@ -9,14 +9,16 @@ import { keyValueStoreDALFactory } from "@app/keystore/key-value-store-dal"; import { runMigrations } from "./auto-start-migrations"; import { initAuditLogDbConnection, initDbConnection } from "./db"; +import { hsmServiceFactory } from "./ee/services/hsm/hsm-service"; import { keyStoreFactory } from "./keystore/keystore"; -import { formatSmtpConfig, getDatabaseCredentials, initEnvConfig } from "./lib/config/env"; +import { formatSmtpConfig, getDatabaseCredentials, getHsmConfig, initEnvConfig } from "./lib/config/env"; import { buildRedisFromConfig } from "./lib/config/redis"; import { removeTemporaryBaseDirectory } from "./lib/files"; import { initLogger } from "./lib/logger"; import { queueServiceFactory } from "./queue"; import { main } from "./server/app"; import { bootstrapCheck } from "./server/boot-strap-check"; +import { kmsRootConfigDALFactory } from "./services/kms/kms-root-config-dal"; import { smtpServiceFactory } from "./services/smtp/smtp-service"; import { superAdminDALFactory } from "./services/super-admin/super-admin-dal"; @@ -26,6 +28,18 @@ const run = async () => { const logger = initLogger(); await removeTemporaryBaseDirectory(); + const hsmConfig = getHsmConfig(logger); + + const hsmModule = initializeHsmModule(hsmConfig); + hsmModule.initialize(); + + const hsmService = hsmServiceFactory({ + hsmModule: hsmModule.getModule(), + envConfig: hsmConfig + }); + + await hsmService.startService(); + const databaseCredentials = getDatabaseCredentials(logger); const db = initDbConnection({ @@ -35,7 +49,8 @@ const run = async () => { }); const superAdminDAL = superAdminDALFactory(db); - const envConfig = await initEnvConfig(superAdminDAL, logger); + const kmsRootConfigDAL = kmsRootConfigDALFactory(db); + const envConfig = await initEnvConfig(hsmService, kmsRootConfigDAL, superAdminDAL, logger); const auditLogDb = envConfig.AUDIT_LOGS_DB_CONNECTION_URI ? initAuditLogDbConnection({ @@ -59,14 +74,12 @@ const run = async () => { const keyStore = keyStoreFactory(envConfig, keyValueStoreDAL); const redis = buildRedisFromConfig(envConfig); - const hsmModule = initializeHsmModule(envConfig); - hsmModule.initialize(); - const server = await main({ db, auditLogDb, superAdminDAL, - hsmModule: hsmModule.getModule(), + kmsRootConfigDAL, + hsmService, smtp, logger, queue, diff --git a/backend/src/server/app.ts b/backend/src/server/app.ts index 8cf23f7032..f1176b932b 100644 --- a/backend/src/server/app.ts +++ b/backend/src/server/app.ts @@ -15,12 +15,13 @@ import fastify from "fastify"; import { Cluster, Redis } from "ioredis"; import { Knex } from "knex"; -import { HsmModule } from "@app/ee/services/hsm/hsm-types"; +import { THsmServiceFactory } from "@app/ee/services/hsm/hsm-service"; import { TKeyStoreFactory } from "@app/keystore/keystore"; import { getConfig, IS_PACKAGED, TEnvConfig } from "@app/lib/config/env"; import { CustomLogger } from "@app/lib/logger/logger"; import { alphaNumericNanoId } from "@app/lib/nanoid"; import { TQueueServiceFactory } from "@app/queue"; +import { TKmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal"; import { TSmtpService } from "@app/services/smtp/smtp-service"; import { TSuperAdminDALFactory } from "@app/services/super-admin/super-admin-dal"; @@ -42,16 +43,16 @@ type TMain = { logger?: CustomLogger; queue: TQueueServiceFactory; keyStore: TKeyStoreFactory; - hsmModule: HsmModule; redis: Redis | Cluster; envConfig: TEnvConfig; superAdminDAL: TSuperAdminDALFactory; + hsmService: THsmServiceFactory; + kmsRootConfigDAL: TKmsRootConfigDALFactory; }; // Run the server! export const main = async ({ db, - hsmModule, auditLogDb, smtp, logger, @@ -59,7 +60,9 @@ export const main = async ({ keyStore, redis, envConfig, - superAdminDAL + superAdminDAL, + hsmService, + kmsRootConfigDAL }: TMain) => { const appCfg = getConfig(); @@ -148,9 +151,10 @@ export const main = async ({ db, auditLogDb, keyStore, - hsmModule, + hsmService, envConfig, - superAdminDAL + superAdminDAL, + kmsRootConfigDAL }); await server.register(registerServeUI, { diff --git a/backend/src/server/routes/index.ts b/backend/src/server/routes/index.ts index 646cf93e05..32deae7190 100644 --- a/backend/src/server/routes/index.ts +++ b/backend/src/server/routes/index.ts @@ -47,8 +47,7 @@ import { groupDALFactory } from "@app/ee/services/group/group-dal"; import { groupServiceFactory } from "@app/ee/services/group/group-service"; import { userGroupMembershipDALFactory } from "@app/ee/services/group/user-group-membership-dal"; import { isHsmActiveAndEnabled } from "@app/ee/services/hsm/hsm-fns"; -import { hsmServiceFactory } from "@app/ee/services/hsm/hsm-service"; -import { HsmModule } from "@app/ee/services/hsm/hsm-types"; +import { THsmServiceFactory } from "@app/ee/services/hsm/hsm-service"; import { identityAuthTemplateDALFactory } from "@app/ee/services/identity-auth-template/identity-auth-template-dal"; import { identityAuthTemplateServiceFactory } from "@app/ee/services/identity-auth-template/identity-auth-template-service"; import { kmipClientCertificateDALFactory } from "@app/ee/services/kmip/kmip-client-certificate-dal"; @@ -237,7 +236,7 @@ import { integrationAuthDALFactory } from "@app/services/integration-auth/integr import { integrationAuthServiceFactory } from "@app/services/integration-auth/integration-auth-service"; import { internalKmsDALFactory } from "@app/services/kms/internal-kms-dal"; import { kmskeyDALFactory } from "@app/services/kms/kms-key-dal"; -import { kmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal"; +import { TKmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal"; import { kmsServiceFactory } from "@app/services/kms/kms-service"; import { RootKeyEncryptionStrategy } from "@app/services/kms/kms-types"; import { membershipDALFactory } from "@app/services/membership/membership-dal"; @@ -365,20 +364,22 @@ export const registerRoutes = async ( auditLogDb, superAdminDAL, db, - hsmModule, smtp: smtpService, queue: queueService, keyStore, - envConfig + envConfig, + hsmService, + kmsRootConfigDAL }: { auditLogDb?: Knex; superAdminDAL: TSuperAdminDALFactory; db: Knex; - hsmModule: HsmModule; smtp: TSmtpService; queue: TQueueServiceFactory; keyStore: TKeyStoreFactory; envConfig: TEnvConfig; + hsmService: THsmServiceFactory; + kmsRootConfigDAL: TKmsRootConfigDALFactory; } ) => { const appCfg = getConfig(); @@ -509,7 +510,6 @@ export const registerRoutes = async ( const kmsDAL = kmskeyDALFactory(db); const internalKmsDAL = internalKmsDALFactory(db); const externalKmsDAL = externalKmsDALFactory(db); - const kmsRootConfigDAL = kmsRootConfigDALFactory(db); const slackIntegrationDAL = slackIntegrationDALFactory(db); const projectSlackConfigDAL = projectSlackConfigDALFactory(db); @@ -625,11 +625,6 @@ export const registerRoutes = async ( permissionService }); - const hsmService = hsmServiceFactory({ - hsmModule, - envConfig - }); - const kmsService = kmsServiceFactory({ kmsRootConfigDAL, keyStore,