mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-10 15:28:42 +00:00
Merge pull request #4705 from Infisical/daniel/hsm-improvements-prep-work
feat: HSM improvements
This commit is contained in:
@@ -49,9 +49,6 @@ RUN rm -fr ${SOFTHSM2_SOURCES}
|
|||||||
# Install pkcs11-tool
|
# Install pkcs11-tool
|
||||||
RUN apt-get install -y opensc
|
RUN apt-get install -y opensc
|
||||||
|
|
||||||
RUN mkdir -p /etc/softhsm2/tokens && \
|
|
||||||
softhsm2-util --init-token --slot 0 --label "auth-app" --pin 1234 --so-pin 0000
|
|
||||||
|
|
||||||
# ? App setup
|
# ? App setup
|
||||||
|
|
||||||
# Install Infisical CLI
|
# Install Infisical CLI
|
||||||
@@ -64,10 +61,14 @@ WORKDIR /app
|
|||||||
COPY package.json package.json
|
COPY package.json package.json
|
||||||
COPY package-lock.json package-lock.json
|
COPY package-lock.json package-lock.json
|
||||||
|
|
||||||
|
COPY dev-entrypoint.sh dev-entrypoint.sh
|
||||||
|
RUN chmod +x dev-entrypoint.sh
|
||||||
|
|
||||||
RUN npm install
|
RUN npm install
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
ENV HOST=0.0.0.0
|
ENV HOST=0.0.0.0
|
||||||
|
|
||||||
|
ENTRYPOINT ["/app/dev-entrypoint.sh"]
|
||||||
CMD ["npm", "run", "dev:docker"]
|
CMD ["npm", "run", "dev:docker"]
|
||||||
|
|||||||
@@ -50,9 +50,6 @@ RUN rm -fr ${SOFTHSM2_SOURCES}
|
|||||||
# Install pkcs11-tool
|
# Install pkcs11-tool
|
||||||
RUN apt-get install -y opensc
|
RUN apt-get install -y opensc
|
||||||
|
|
||||||
RUN mkdir -p /etc/softhsm2/tokens && \
|
|
||||||
softhsm2-util --init-token --slot 0 --label "auth-app" --pin 1234 --so-pin 0000
|
|
||||||
|
|
||||||
WORKDIR /openssl-build
|
WORKDIR /openssl-build
|
||||||
RUN wget https://www.openssl.org/source/openssl-3.1.2.tar.gz \
|
RUN wget https://www.openssl.org/source/openssl-3.1.2.tar.gz \
|
||||||
&& tar -xf openssl-3.1.2.tar.gz \
|
&& tar -xf openssl-3.1.2.tar.gz \
|
||||||
@@ -77,6 +74,9 @@ WORKDIR /app
|
|||||||
COPY package.json package.json
|
COPY package.json package.json
|
||||||
COPY package-lock.json package-lock.json
|
COPY package-lock.json package-lock.json
|
||||||
|
|
||||||
|
COPY dev-entrypoint.sh dev-entrypoint.sh
|
||||||
|
RUN chmod +x dev-entrypoint.sh
|
||||||
|
|
||||||
RUN npm install
|
RUN npm install
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
@@ -87,4 +87,5 @@ ENV OPENSSL_MODULES=/usr/local/lib/ossl-modules
|
|||||||
# ENV NODE_OPTIONS=--force-fips # Note(Daniel): We can't set this on the node options because it may break for existing folks using the infisical/infisical-fips image. Instead we call crypto.setFips(true) at runtime.
|
# ENV NODE_OPTIONS=--force-fips # Note(Daniel): We can't set this on the node options because it may break for existing folks using the infisical/infisical-fips image. Instead we call crypto.setFips(true) at runtime.
|
||||||
ENV FIPS_ENABLED=true
|
ENV FIPS_ENABLED=true
|
||||||
|
|
||||||
|
ENTRYPOINT ["/app/dev-entrypoint.sh"]
|
||||||
CMD ["npm", "run", "dev:docker"]
|
CMD ["npm", "run", "dev:docker"]
|
||||||
|
|||||||
Executable
+16
@@ -0,0 +1,16 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
update-ca-certificates
|
||||||
|
|
||||||
|
# Initialize SoftHSM token if it doesn't exist
|
||||||
|
if [ ! -f /etc/softhsm2/tokens/auth-app.db ]; then
|
||||||
|
echo "Initializing SoftHSM token..."
|
||||||
|
mkdir -p /etc/softhsm2/tokens
|
||||||
|
softhsm2-util --init-token --slot 0 --label "auth-app" --pin 1234 --so-pin 0000
|
||||||
|
echo "SoftHSM token initialized"
|
||||||
|
else
|
||||||
|
echo "SoftHSM token already exists, skipping initialization"
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
exec "$@"
|
||||||
@@ -146,7 +146,8 @@ describe("Service token secret ops", async () => {
|
|||||||
let folderId = "";
|
let folderId = "";
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
initLogger();
|
initLogger();
|
||||||
await initEnvConfig(testSuperAdminDAL, logger);
|
|
||||||
|
await initEnvConfig(testHsmService, testKmsRootConfigDAL, testSuperAdminDAL, logger);
|
||||||
|
|
||||||
serviceToken = await createServiceToken(
|
serviceToken = await createServiceToken(
|
||||||
[{ secretPath: "/**", environment: seedData1.environment.slug }],
|
[{ secretPath: "/**", environment: seedData1.environment.slug }],
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ describe("Secret V3 Router", async () => {
|
|||||||
let folderId = "";
|
let folderId = "";
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
initLogger();
|
initLogger();
|
||||||
await initEnvConfig(testSuperAdminDAL, logger);
|
await initEnvConfig(testHsmService, testKmsRootConfigDAL, testSuperAdminDAL, logger);
|
||||||
|
|
||||||
const projectKeyRes = await testServer.inject({
|
const projectKeyRes = await testServer.inject({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { crypto } from "@app/lib/crypto/cryptography";
|
|||||||
import path from "path";
|
import path from "path";
|
||||||
|
|
||||||
import { seedData1 } from "@app/db/seed-data";
|
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 { initLogger } from "@app/lib/logger";
|
||||||
import { main } from "@app/server/app";
|
import { main } from "@app/server/app";
|
||||||
import { AuthMethod, AuthTokenType } from "@app/services/auth/auth-type";
|
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 { buildRedisFromConfig } from "@app/lib/config/redis";
|
||||||
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
||||||
import { bootstrapCheck } from "@app/server/boot-strap-check";
|
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 });
|
dotenv.config({ path: path.join(__dirname, "../../.env.test"), debug: true });
|
||||||
export default {
|
export default {
|
||||||
@@ -28,6 +30,7 @@ export default {
|
|||||||
async setup() {
|
async setup() {
|
||||||
const logger = initLogger();
|
const logger = initLogger();
|
||||||
const databaseCredentials = getDatabaseCredentials(logger);
|
const databaseCredentials = getDatabaseCredentials(logger);
|
||||||
|
const hsmConfig = getHsmConfig(logger);
|
||||||
|
|
||||||
const db = initDbConnection({
|
const db = initDbConnection({
|
||||||
dbConnectionUri: databaseCredentials.dbConnectionUri,
|
dbConnectionUri: databaseCredentials.dbConnectionUri,
|
||||||
@@ -35,7 +38,19 @@ export default {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const superAdminDAL = superAdminDALFactory(db);
|
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);
|
const redis = buildRedisFromConfig(envCfg);
|
||||||
await redis.flushdb("SYNC");
|
await redis.flushdb("SYNC");
|
||||||
@@ -68,16 +83,14 @@ export default {
|
|||||||
|
|
||||||
await queue.initialize();
|
await queue.initialize();
|
||||||
|
|
||||||
const hsmModule = initializeHsmModule(envCfg);
|
|
||||||
hsmModule.initialize();
|
|
||||||
|
|
||||||
const server = await main({
|
const server = await main({
|
||||||
db,
|
db,
|
||||||
smtp,
|
smtp,
|
||||||
logger,
|
logger,
|
||||||
queue,
|
queue,
|
||||||
keyStore,
|
keyStore,
|
||||||
hsmModule: hsmModule.getModule(),
|
hsmService,
|
||||||
|
kmsRootConfigDAL,
|
||||||
superAdminDAL,
|
superAdminDAL,
|
||||||
redis,
|
redis,
|
||||||
envConfig: envCfg
|
envConfig: envCfg
|
||||||
@@ -92,6 +105,10 @@ export default {
|
|||||||
// @ts-expect-error type
|
// @ts-expect-error type
|
||||||
globalThis.testSuperAdminDAL = superAdminDAL;
|
globalThis.testSuperAdminDAL = superAdminDAL;
|
||||||
// @ts-expect-error type
|
// @ts-expect-error type
|
||||||
|
globalThis.testKmsRootConfigDAL = kmsRootConfigDAL;
|
||||||
|
// @ts-expect-error type
|
||||||
|
globalThis.testHsmService = hsmService;
|
||||||
|
// @ts-expect-error type
|
||||||
globalThis.jwtAuthToken = crypto.jwt().sign(
|
globalThis.jwtAuthToken = crypto.jwt().sign(
|
||||||
{
|
{
|
||||||
authTokenType: AuthTokenType.ACCESS_TOKEN,
|
authTokenType: AuthTokenType.ACCESS_TOKEN,
|
||||||
|
|||||||
Vendored
+4
@@ -1,7 +1,9 @@
|
|||||||
import { FastifyInstance, RawReplyDefaultExpression, RawRequestDefaultExpression, RawServerDefault } from "fastify";
|
import { FastifyInstance, RawReplyDefaultExpression, RawRequestDefaultExpression, RawServerDefault } from "fastify";
|
||||||
|
|
||||||
|
import { THsmServiceFactory } from "@app/ee/services/hsm/hsm-service";
|
||||||
import { CustomLogger } from "@app/lib/logger/logger";
|
import { CustomLogger } from "@app/lib/logger/logger";
|
||||||
import { ZodTypeProvider } from "@app/server/plugins/fastify-zod";
|
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";
|
import { TSuperAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
@@ -16,5 +18,7 @@ declare global {
|
|||||||
// used only for testing
|
// used only for testing
|
||||||
const testServer: FastifyZodProvider;
|
const testServer: FastifyZodProvider;
|
||||||
const testSuperAdminDAL: TSuperAdminDALFactory;
|
const testSuperAdminDAL: TSuperAdminDALFactory;
|
||||||
|
const testKmsRootConfigDAL: TKmsRootConfigDALFactory;
|
||||||
|
const testHsmService: THsmServiceFactory;
|
||||||
const jwtAuthToken: string;
|
const jwtAuthToken: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,13 +3,14 @@ import { Knex } from "knex";
|
|||||||
import { inMemoryKeyStore } from "@app/keystore/memory";
|
import { inMemoryKeyStore } from "@app/keystore/memory";
|
||||||
import { crypto } from "@app/lib/crypto/cryptography";
|
import { crypto } from "@app/lib/crypto/cryptography";
|
||||||
import { initLogger } from "@app/lib/logger";
|
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 { KmsDataKey } from "@app/services/kms/kms-types";
|
||||||
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
||||||
|
|
||||||
import { SecretKeyEncoding, TableName } from "../schemas";
|
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 { createCircularCache } from "./utils/ring-buffer";
|
||||||
import { getMigrationEncryptionServices } from "./utils/services";
|
import { getMigrationEncryptionServices, getMigrationHsmService } from "./utils/services";
|
||||||
|
|
||||||
const BATCH_SIZE = 500;
|
const BATCH_SIZE = 500;
|
||||||
export async function up(knex: Knex): Promise<void> {
|
export async function up(knex: Knex): Promise<void> {
|
||||||
@@ -25,10 +26,12 @@ export async function up(knex: Knex): Promise<void> {
|
|||||||
if (hasUrl) t.string("url").nullable().alter();
|
if (hasUrl) t.string("url").nullable().alter();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
initLogger();
|
initLogger();
|
||||||
|
|
||||||
|
const { hsmService } = await getMigrationHsmService({ envConfig: getMigrationHsmConfig() });
|
||||||
const superAdminDAL = superAdminDALFactory(knex);
|
const superAdminDAL = superAdminDALFactory(knex);
|
||||||
const envConfig = await getMigrationEnvConfig(superAdminDAL);
|
const kmsRootConfigDAL = kmsRootConfigDALFactory(knex);
|
||||||
|
const envConfig = await getMigrationEnvConfig(superAdminDAL, hsmService, kmsRootConfigDAL);
|
||||||
|
|
||||||
const keyStore = inMemoryKeyStore();
|
const keyStore = inMemoryKeyStore();
|
||||||
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
|
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
|
||||||
|
|||||||
@@ -4,13 +4,14 @@ import { inMemoryKeyStore } from "@app/keystore/memory";
|
|||||||
import { crypto } from "@app/lib/crypto/cryptography";
|
import { crypto } from "@app/lib/crypto/cryptography";
|
||||||
import { selectAllTableCols } from "@app/lib/knex";
|
import { selectAllTableCols } from "@app/lib/knex";
|
||||||
import { initLogger } from "@app/lib/logger";
|
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 { KmsDataKey } from "@app/services/kms/kms-types";
|
||||||
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
||||||
|
|
||||||
import { SecretKeyEncoding, TableName } from "../schemas";
|
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 { createCircularCache } from "./utils/ring-buffer";
|
||||||
import { getMigrationEncryptionServices } from "./utils/services";
|
import { getMigrationEncryptionServices, getMigrationHsmService } from "./utils/services";
|
||||||
|
|
||||||
const BATCH_SIZE = 500;
|
const BATCH_SIZE = 500;
|
||||||
export async function up(knex: Knex): Promise<void> {
|
export async function up(knex: Knex): Promise<void> {
|
||||||
@@ -30,8 +31,12 @@ export async function up(knex: Knex): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
initLogger();
|
initLogger();
|
||||||
|
|
||||||
|
const { hsmService } = await getMigrationHsmService({ envConfig: getMigrationHsmConfig() });
|
||||||
|
|
||||||
const superAdminDAL = superAdminDALFactory(knex);
|
const superAdminDAL = superAdminDALFactory(knex);
|
||||||
const envConfig = await getMigrationEnvConfig(superAdminDAL);
|
const kmsRootConfigDAL = kmsRootConfigDALFactory(knex);
|
||||||
|
const envConfig = await getMigrationEnvConfig(superAdminDAL, hsmService, kmsRootConfigDAL);
|
||||||
|
|
||||||
const keyStore = inMemoryKeyStore();
|
const keyStore = inMemoryKeyStore();
|
||||||
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
|
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
|
||||||
|
|||||||
@@ -4,13 +4,14 @@ import { inMemoryKeyStore } from "@app/keystore/memory";
|
|||||||
import { crypto } from "@app/lib/crypto/cryptography";
|
import { crypto } from "@app/lib/crypto/cryptography";
|
||||||
import { selectAllTableCols } from "@app/lib/knex";
|
import { selectAllTableCols } from "@app/lib/knex";
|
||||||
import { initLogger } from "@app/lib/logger";
|
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 { KmsDataKey } from "@app/services/kms/kms-types";
|
||||||
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
||||||
|
|
||||||
import { SecretKeyEncoding, TableName } from "../schemas";
|
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 { createCircularCache } from "./utils/ring-buffer";
|
||||||
import { getMigrationEncryptionServices } from "./utils/services";
|
import { getMigrationEncryptionServices, getMigrationHsmService } from "./utils/services";
|
||||||
|
|
||||||
const BATCH_SIZE = 500;
|
const BATCH_SIZE = 500;
|
||||||
export async function up(knex: Knex): Promise<void> {
|
export async function up(knex: Knex): Promise<void> {
|
||||||
@@ -24,8 +25,11 @@ export async function up(knex: Knex): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
initLogger();
|
initLogger();
|
||||||
|
const { hsmService } = await getMigrationHsmService({ envConfig: getMigrationHsmConfig() });
|
||||||
|
|
||||||
const superAdminDAL = superAdminDALFactory(knex);
|
const superAdminDAL = superAdminDALFactory(knex);
|
||||||
const envConfig = await getMigrationEnvConfig(superAdminDAL);
|
const kmsRootConfigDAL = kmsRootConfigDALFactory(knex);
|
||||||
|
const envConfig = await getMigrationEnvConfig(superAdminDAL, hsmService, kmsRootConfigDAL);
|
||||||
|
|
||||||
const keyStore = inMemoryKeyStore();
|
const keyStore = inMemoryKeyStore();
|
||||||
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
|
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
|
||||||
|
|||||||
@@ -4,13 +4,14 @@ import { inMemoryKeyStore } from "@app/keystore/memory";
|
|||||||
import { crypto, SymmetricKeySize } from "@app/lib/crypto/cryptography";
|
import { crypto, SymmetricKeySize } from "@app/lib/crypto/cryptography";
|
||||||
import { selectAllTableCols } from "@app/lib/knex";
|
import { selectAllTableCols } from "@app/lib/knex";
|
||||||
import { initLogger } from "@app/lib/logger";
|
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 { KmsDataKey } from "@app/services/kms/kms-types";
|
||||||
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
||||||
|
|
||||||
import { SecretKeyEncoding, TableName, TOrgBots } from "../schemas";
|
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 { createCircularCache } from "./utils/ring-buffer";
|
||||||
import { getMigrationEncryptionServices } from "./utils/services";
|
import { getMigrationEncryptionServices, getMigrationHsmService } from "./utils/services";
|
||||||
|
|
||||||
const BATCH_SIZE = 500;
|
const BATCH_SIZE = 500;
|
||||||
const reencryptIdentityK8sAuth = async (knex: Knex) => {
|
const reencryptIdentityK8sAuth = async (knex: Knex) => {
|
||||||
@@ -55,9 +56,11 @@ const reencryptIdentityK8sAuth = async (knex: Knex) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
initLogger();
|
initLogger();
|
||||||
const superAdminDAL = superAdminDALFactory(knex);
|
const { hsmService } = await getMigrationHsmService({ envConfig: getMigrationHsmConfig() });
|
||||||
const envConfig = await getMigrationEnvConfig(superAdminDAL);
|
|
||||||
|
|
||||||
|
const superAdminDAL = superAdminDALFactory(knex);
|
||||||
|
const kmsRootConfigDAL = kmsRootConfigDALFactory(knex);
|
||||||
|
const envConfig = await getMigrationEnvConfig(superAdminDAL, hsmService, kmsRootConfigDAL);
|
||||||
const keyStore = inMemoryKeyStore();
|
const keyStore = inMemoryKeyStore();
|
||||||
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
|
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
|
||||||
const orgEncryptionRingBuffer =
|
const orgEncryptionRingBuffer =
|
||||||
|
|||||||
@@ -4,13 +4,14 @@ import { inMemoryKeyStore } from "@app/keystore/memory";
|
|||||||
import { crypto, SymmetricKeySize } from "@app/lib/crypto/cryptography";
|
import { crypto, SymmetricKeySize } from "@app/lib/crypto/cryptography";
|
||||||
import { selectAllTableCols } from "@app/lib/knex";
|
import { selectAllTableCols } from "@app/lib/knex";
|
||||||
import { initLogger } from "@app/lib/logger";
|
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 { KmsDataKey } from "@app/services/kms/kms-types";
|
||||||
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
||||||
|
|
||||||
import { SecretKeyEncoding, TableName, TOrgBots } from "../schemas";
|
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 { createCircularCache } from "./utils/ring-buffer";
|
||||||
import { getMigrationEncryptionServices } from "./utils/services";
|
import { getMigrationEncryptionServices, getMigrationHsmService } from "./utils/services";
|
||||||
|
|
||||||
const BATCH_SIZE = 500;
|
const BATCH_SIZE = 500;
|
||||||
const reencryptIdentityOidcAuth = async (knex: Knex) => {
|
const reencryptIdentityOidcAuth = async (knex: Knex) => {
|
||||||
@@ -35,8 +36,11 @@ const reencryptIdentityOidcAuth = async (knex: Knex) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
initLogger();
|
initLogger();
|
||||||
|
const { hsmService } = await getMigrationHsmService({ envConfig: getMigrationHsmConfig() });
|
||||||
|
|
||||||
const superAdminDAL = superAdminDALFactory(knex);
|
const superAdminDAL = superAdminDALFactory(knex);
|
||||||
const envConfig = await getMigrationEnvConfig(superAdminDAL);
|
const kmsRootConfigDAL = kmsRootConfigDALFactory(knex);
|
||||||
|
const envConfig = await getMigrationEnvConfig(superAdminDAL, hsmService, kmsRootConfigDAL);
|
||||||
|
|
||||||
const keyStore = inMemoryKeyStore();
|
const keyStore = inMemoryKeyStore();
|
||||||
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
|
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
|
||||||
|
|||||||
@@ -4,16 +4,18 @@ import { inMemoryKeyStore } from "@app/keystore/memory";
|
|||||||
import { crypto, SymmetricKeySize } from "@app/lib/crypto/cryptography";
|
import { crypto, SymmetricKeySize } from "@app/lib/crypto/cryptography";
|
||||||
import { selectAllTableCols } from "@app/lib/knex";
|
import { selectAllTableCols } from "@app/lib/knex";
|
||||||
import { initLogger } from "@app/lib/logger";
|
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 { KmsDataKey } from "@app/services/kms/kms-types";
|
||||||
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
||||||
|
|
||||||
import { SecretKeyEncoding, TableName } from "../schemas";
|
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 { createCircularCache } from "./utils/ring-buffer";
|
||||||
import { getMigrationEncryptionServices } from "./utils/services";
|
import { getMigrationEncryptionServices, getMigrationHsmService } from "./utils/services";
|
||||||
|
|
||||||
const BATCH_SIZE = 500;
|
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 hasEncryptedEntrypointColumn = await knex.schema.hasColumn(TableName.SamlConfig, "encryptedSamlEntryPoint");
|
||||||
const hasEncryptedIssuerColumn = await knex.schema.hasColumn(TableName.SamlConfig, "encryptedSamlIssuer");
|
const hasEncryptedIssuerColumn = await knex.schema.hasColumn(TableName.SamlConfig, "encryptedSamlIssuer");
|
||||||
const hasEncryptedCertificateColumn = await knex.schema.hasColumn(TableName.SamlConfig, "encryptedSamlCertificate");
|
const hasEncryptedCertificateColumn = await knex.schema.hasColumn(TableName.SamlConfig, "encryptedSamlCertificate");
|
||||||
@@ -28,10 +30,6 @@ const reencryptSamlConfig = async (knex: Knex) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
initLogger();
|
initLogger();
|
||||||
const superAdminDAL = superAdminDALFactory(knex);
|
|
||||||
const envConfig = await getMigrationEnvConfig(superAdminDAL);
|
|
||||||
const keyStore = inMemoryKeyStore();
|
|
||||||
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
|
|
||||||
const orgEncryptionRingBuffer =
|
const orgEncryptionRingBuffer =
|
||||||
createCircularCache<Awaited<ReturnType<(typeof kmsService)["createCipherPairWithDataKey"]>>>(25);
|
createCircularCache<Awaited<ReturnType<(typeof kmsService)["createCipherPairWithDataKey"]>>>(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 hasEncryptedLdapBindDNColum = await knex.schema.hasColumn(TableName.LdapConfig, "encryptedLdapBindDN");
|
||||||
const hasEncryptedLdapBindPassColumn = await knex.schema.hasColumn(TableName.LdapConfig, "encryptedLdapBindPass");
|
const hasEncryptedLdapBindPassColumn = await knex.schema.hasColumn(TableName.LdapConfig, "encryptedLdapBindPass");
|
||||||
const hasEncryptedCertificateColumn = await knex.schema.hasColumn(TableName.LdapConfig, "encryptedLdapCaCertificate");
|
const hasEncryptedCertificateColumn = await knex.schema.hasColumn(TableName.LdapConfig, "encryptedLdapCaCertificate");
|
||||||
@@ -194,10 +192,6 @@ const reencryptLdapConfig = async (knex: Knex) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
initLogger();
|
initLogger();
|
||||||
const superAdminDAL = superAdminDALFactory(knex);
|
|
||||||
const envConfig = await getMigrationEnvConfig(superAdminDAL);
|
|
||||||
const keyStore = inMemoryKeyStore();
|
|
||||||
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
|
|
||||||
const orgEncryptionRingBuffer =
|
const orgEncryptionRingBuffer =
|
||||||
createCircularCache<Awaited<ReturnType<(typeof kmsService)["createCipherPairWithDataKey"]>>>(25);
|
createCircularCache<Awaited<ReturnType<(typeof kmsService)["createCipherPairWithDataKey"]>>>(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 hasEncryptedOidcClientIdColumn = await knex.schema.hasColumn(TableName.OidcConfig, "encryptedOidcClientId");
|
||||||
const hasEncryptedOidcClientSecretColumn = await knex.schema.hasColumn(
|
const hasEncryptedOidcClientSecretColumn = await knex.schema.hasColumn(
|
||||||
TableName.OidcConfig,
|
TableName.OidcConfig,
|
||||||
@@ -354,10 +348,6 @@ const reencryptOidcConfig = async (knex: Knex) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
initLogger();
|
initLogger();
|
||||||
const superAdminDAL = superAdminDALFactory(knex);
|
|
||||||
const envConfig = await getMigrationEnvConfig(superAdminDAL);
|
|
||||||
const keyStore = inMemoryKeyStore();
|
|
||||||
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
|
|
||||||
const orgEncryptionRingBuffer =
|
const orgEncryptionRingBuffer =
|
||||||
createCircularCache<Awaited<ReturnType<(typeof kmsService)["createCipherPairWithDataKey"]>>>(25);
|
createCircularCache<Awaited<ReturnType<(typeof kmsService)["createCipherPairWithDataKey"]>>>(25);
|
||||||
|
|
||||||
@@ -462,9 +452,18 @@ const reencryptOidcConfig = async (knex: Knex) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export async function up(knex: Knex): Promise<void> {
|
export async function up(knex: Knex): Promise<void> {
|
||||||
await reencryptSamlConfig(knex);
|
initLogger();
|
||||||
await reencryptLdapConfig(knex);
|
|
||||||
await reencryptOidcConfig(knex);
|
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) => {
|
const dropSamlConfigColumns = async (knex: Knex) => {
|
||||||
|
|||||||
@@ -3,12 +3,13 @@ import { Knex } from "knex";
|
|||||||
import { inMemoryKeyStore } from "@app/keystore/memory";
|
import { inMemoryKeyStore } from "@app/keystore/memory";
|
||||||
import { selectAllTableCols } from "@app/lib/knex";
|
import { selectAllTableCols } from "@app/lib/knex";
|
||||||
import { initLogger } from "@app/lib/logger";
|
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 { KmsDataKey } from "@app/services/kms/kms-types";
|
||||||
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
||||||
|
|
||||||
import { TableName } from "../schemas";
|
import { TableName } from "../schemas";
|
||||||
import { getMigrationEnvConfig } from "./utils/env-config";
|
import { getMigrationEnvConfig, getMigrationHsmConfig } from "./utils/env-config";
|
||||||
import { getMigrationEncryptionServices } from "./utils/services";
|
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.
|
// 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.
|
// 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<void> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
initLogger();
|
initLogger();
|
||||||
|
const { hsmService } = await getMigrationHsmService({ envConfig: getMigrationHsmConfig() });
|
||||||
const superAdminDAL = superAdminDALFactory(knex);
|
const superAdminDAL = superAdminDALFactory(knex);
|
||||||
const envConfig = await getMigrationEnvConfig(superAdminDAL);
|
const kmsRootConfigDAL = kmsRootConfigDALFactory(knex);
|
||||||
|
const envConfig = await getMigrationEnvConfig(superAdminDAL, hsmService, kmsRootConfigDAL);
|
||||||
const keyStore = inMemoryKeyStore();
|
const keyStore = inMemoryKeyStore();
|
||||||
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
|
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
|
||||||
|
|
||||||
|
|||||||
@@ -2,19 +2,23 @@ import { Knex } from "knex";
|
|||||||
|
|
||||||
import { inMemoryKeyStore } from "@app/keystore/memory";
|
import { inMemoryKeyStore } from "@app/keystore/memory";
|
||||||
import { selectAllTableCols } from "@app/lib/knex";
|
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 { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
||||||
|
|
||||||
import { TableName } from "../schemas";
|
import { TableName } from "../schemas";
|
||||||
import { getMigrationEnvConfig } from "./utils/env-config";
|
import { getMigrationEnvConfig, getMigrationHsmConfig } from "./utils/env-config";
|
||||||
import { getMigrationEncryptionServices } from "./utils/services";
|
import { getMigrationEncryptionServices, getMigrationHsmService } from "./utils/services";
|
||||||
|
|
||||||
export async function up(knex: Knex) {
|
export async function up(knex: Knex) {
|
||||||
const existingSuperAdminsWithGithubConnection = await knex(TableName.SuperAdmin)
|
const existingSuperAdminsWithGithubConnection = await knex(TableName.SuperAdmin)
|
||||||
.select(selectAllTableCols(TableName.SuperAdmin))
|
.select(selectAllTableCols(TableName.SuperAdmin))
|
||||||
.whereNotNull(`${TableName.SuperAdmin}.encryptedGitHubAppConnectionClientId`);
|
.whereNotNull(`${TableName.SuperAdmin}.encryptedGitHubAppConnectionClientId`);
|
||||||
|
|
||||||
|
const { hsmService } = await getMigrationHsmService({ envConfig: getMigrationHsmConfig() });
|
||||||
|
|
||||||
const superAdminDAL = superAdminDALFactory(knex);
|
const superAdminDAL = superAdminDALFactory(knex);
|
||||||
const envConfig = await getMigrationEnvConfig(superAdminDAL);
|
const kmsRootConfigDAL = kmsRootConfigDALFactory(knex);
|
||||||
|
const envConfig = await getMigrationEnvConfig(superAdminDAL, hsmService, kmsRootConfigDAL);
|
||||||
const keyStore = inMemoryKeyStore();
|
const keyStore = inMemoryKeyStore();
|
||||||
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
|
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
|
||||||
|
|
||||||
|
|||||||
@@ -2,13 +2,14 @@ import { Knex } from "knex";
|
|||||||
|
|
||||||
import { inMemoryKeyStore } from "@app/keystore/memory";
|
import { inMemoryKeyStore } from "@app/keystore/memory";
|
||||||
import { crypto } from "@app/lib/crypto/cryptography";
|
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 { KmsDataKey } from "@app/services/kms/kms-types";
|
||||||
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
||||||
|
|
||||||
import { SecretKeyEncoding, TableName } from "../schemas";
|
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 { createCircularCache } from "./utils/ring-buffer";
|
||||||
import { getMigrationEncryptionServices } from "./utils/services";
|
import { getMigrationEncryptionServices, getMigrationHsmService } from "./utils/services";
|
||||||
|
|
||||||
const BATCH_SIZE = 500;
|
const BATCH_SIZE = 500;
|
||||||
export async function up(knex: Knex): Promise<void> {
|
export async function up(knex: Knex): Promise<void> {
|
||||||
@@ -25,8 +26,10 @@ export async function up(knex: Knex): Promise<void> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!hasEncryptedCredentials) {
|
if (!hasEncryptedCredentials) {
|
||||||
|
const { hsmService } = await getMigrationHsmService({ envConfig: getMigrationHsmConfig() });
|
||||||
const superAdminDAL = superAdminDALFactory(knex);
|
const superAdminDAL = superAdminDALFactory(knex);
|
||||||
const envConfig = await getMigrationEnvConfig(superAdminDAL);
|
const kmsRootConfigDAL = kmsRootConfigDALFactory(knex);
|
||||||
|
const envConfig = await getMigrationEnvConfig(superAdminDAL, hsmService, kmsRootConfigDAL);
|
||||||
const keyStore = inMemoryKeyStore();
|
const keyStore = inMemoryKeyStore();
|
||||||
|
|
||||||
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
|
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
|
||||||
@@ -131,8 +134,11 @@ export async function down(knex: Knex): Promise<void> {
|
|||||||
const hasEncryptedCredentials = await knex.schema.hasColumn(TableName.AuditLogStream, "encryptedCredentials");
|
const hasEncryptedCredentials = await knex.schema.hasColumn(TableName.AuditLogStream, "encryptedCredentials");
|
||||||
|
|
||||||
if (hasEncryptedCredentials) {
|
if (hasEncryptedCredentials) {
|
||||||
|
const { hsmService } = await getMigrationHsmService({ envConfig: getMigrationHsmConfig() });
|
||||||
|
|
||||||
const superAdminDAL = superAdminDALFactory(knex);
|
const superAdminDAL = superAdminDALFactory(knex);
|
||||||
const envConfig = await getMigrationEnvConfig(superAdminDAL);
|
const kmsRootConfigDAL = kmsRootConfigDALFactory(knex);
|
||||||
|
const envConfig = await getMigrationEnvConfig(superAdminDAL, hsmService, kmsRootConfigDAL);
|
||||||
const keyStore = inMemoryKeyStore();
|
const keyStore = inMemoryKeyStore();
|
||||||
|
|
||||||
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
|
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { THsmServiceFactory } from "@app/ee/services/hsm/hsm-service";
|
||||||
import { crypto } from "@app/lib/crypto/cryptography";
|
import { crypto } from "@app/lib/crypto/cryptography";
|
||||||
|
import { removeTrailingSlash } from "@app/lib/fn";
|
||||||
import { zpStr } from "@app/lib/zod";
|
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";
|
import { TSuperAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
||||||
|
|
||||||
const envSchema = z
|
const envSchema = z
|
||||||
@@ -22,13 +25,17 @@ const envSchema = z
|
|||||||
HSM_LIB_PATH: zpStr(z.string().optional()),
|
HSM_LIB_PATH: zpStr(z.string().optional()),
|
||||||
HSM_PIN: zpStr(z.string().optional()),
|
HSM_PIN: zpStr(z.string().optional()),
|
||||||
HSM_KEY_LABEL: zpStr(z.string().optional()),
|
HSM_KEY_LABEL: zpStr(z.string().optional()),
|
||||||
HSM_SLOT: z.coerce.number().optional().default(0)
|
HSM_SLOT: z.coerce.number().optional().default(0),
|
||||||
|
|
||||||
|
LICENSE_SERVER_URL: zpStr(z.string().optional().default("https://portal.infisical.com")),
|
||||||
|
LICENSE_SERVER_KEY: zpStr(z.string().optional()),
|
||||||
|
LICENSE_KEY: zpStr(z.string().optional()),
|
||||||
|
LICENSE_KEY_OFFLINE: zpStr(z.string().optional()),
|
||||||
|
INTERNAL_REGION: zpStr(z.enum(["us", "eu"]).optional()),
|
||||||
|
|
||||||
|
SITE_URL: zpStr(z.string().transform((val) => (val ? removeTrailingSlash(val) : val))).optional()
|
||||||
})
|
})
|
||||||
// To ensure that basic encryption is always possible.
|
// To ensure that basic encryption is always possible.
|
||||||
.refine(
|
|
||||||
(data) => Boolean(data.ENCRYPTION_KEY) || Boolean(data.ROOT_ENCRYPTION_KEY),
|
|
||||||
"Either ENCRYPTION_KEY or ROOT_ENCRYPTION_KEY must be defined."
|
|
||||||
)
|
|
||||||
.transform((data) => ({
|
.transform((data) => ({
|
||||||
...data,
|
...data,
|
||||||
isHsmConfigured:
|
isHsmConfigured:
|
||||||
@@ -37,7 +44,27 @@ const envSchema = z
|
|||||||
|
|
||||||
export type TMigrationEnvConfig = z.infer<typeof envSchema>;
|
export type TMigrationEnvConfig = z.infer<typeof envSchema>;
|
||||||
|
|
||||||
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);
|
const parsedEnv = envSchema.safeParse(process.env);
|
||||||
if (!parsedEnv.success) {
|
if (!parsedEnv.success) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
@@ -53,7 +80,7 @@ export const getMigrationEnvConfig = async (superAdminDAL: TSuperAdminDALFactory
|
|||||||
|
|
||||||
let envCfg = Object.freeze(parsedEnv.data);
|
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:
|
// 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.
|
// 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.
|
||||||
|
|||||||
@@ -1,28 +1,23 @@
|
|||||||
import { Knex } from "knex";
|
import { Knex } from "knex";
|
||||||
|
|
||||||
import { initializeHsmModule } from "@app/ee/services/hsm/hsm-fns";
|
import { initializeHsmModule, isHsmActiveAndEnabled } from "@app/ee/services/hsm/hsm-fns";
|
||||||
import { hsmServiceFactory } from "@app/ee/services/hsm/hsm-service";
|
import { hsmServiceFactory } from "@app/ee/services/hsm/hsm-service";
|
||||||
|
import { licenseDALFactory } from "@app/ee/services/license/license-dal";
|
||||||
|
import { licenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||||
|
import { permissionDALFactory } from "@app/ee/services/permission/permission-dal";
|
||||||
|
import { permissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||||
import { TKeyStoreFactory } from "@app/keystore/keystore";
|
import { TKeyStoreFactory } from "@app/keystore/keystore";
|
||||||
import { folderCheckpointDALFactory } from "@app/services/folder-checkpoint/folder-checkpoint-dal";
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
import { folderCheckpointResourcesDALFactory } from "@app/services/folder-checkpoint-resources/folder-checkpoint-resources-dal";
|
|
||||||
import { folderCommitDALFactory } from "@app/services/folder-commit/folder-commit-dal";
|
|
||||||
import { folderCommitServiceFactory } from "@app/services/folder-commit/folder-commit-service";
|
|
||||||
import { folderCommitChangesDALFactory } from "@app/services/folder-commit-changes/folder-commit-changes-dal";
|
|
||||||
import { folderTreeCheckpointDALFactory } from "@app/services/folder-tree-checkpoint/folder-tree-checkpoint-dal";
|
|
||||||
import { folderTreeCheckpointResourcesDALFactory } from "@app/services/folder-tree-checkpoint-resources/folder-tree-checkpoint-resources-dal";
|
|
||||||
import { identityDALFactory } from "@app/services/identity/identity-dal";
|
import { identityDALFactory } from "@app/services/identity/identity-dal";
|
||||||
import { internalKmsDALFactory } from "@app/services/kms/internal-kms-dal";
|
import { internalKmsDALFactory } from "@app/services/kms/internal-kms-dal";
|
||||||
import { kmskeyDALFactory } from "@app/services/kms/kms-key-dal";
|
import { kmskeyDALFactory } from "@app/services/kms/kms-key-dal";
|
||||||
import { kmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal";
|
import { kmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal";
|
||||||
import { kmsServiceFactory } from "@app/services/kms/kms-service";
|
import { kmsServiceFactory } from "@app/services/kms/kms-service";
|
||||||
|
import { RootKeyEncryptionStrategy } from "@app/services/kms/kms-types";
|
||||||
import { orgDALFactory } from "@app/services/org/org-dal";
|
import { orgDALFactory } from "@app/services/org/org-dal";
|
||||||
import { projectDALFactory } from "@app/services/project/project-dal";
|
import { projectDALFactory } from "@app/services/project/project-dal";
|
||||||
import { resourceMetadataDALFactory } from "@app/services/resource-metadata/resource-metadata-dal";
|
import { roleDALFactory } from "@app/services/role/role-dal";
|
||||||
import { secretFolderDALFactory } from "@app/services/secret-folder/secret-folder-dal";
|
import { serviceTokenDALFactory } from "@app/services/service-token/service-token-dal";
|
||||||
import { secretFolderVersionDALFactory } from "@app/services/secret-folder/secret-folder-version-dal";
|
|
||||||
import { secretTagDALFactory } from "@app/services/secret-tag/secret-tag-dal";
|
|
||||||
import { secretV2BridgeDALFactory } from "@app/services/secret-v2-bridge/secret-v2-bridge-dal";
|
|
||||||
import { secretVersionV2BridgeDALFactory } from "@app/services/secret-v2-bridge/secret-version-dal";
|
|
||||||
import { userDALFactory } from "@app/services/user/user-dal";
|
import { userDALFactory } from "@app/services/user/user-dal";
|
||||||
|
|
||||||
import { TMigrationEnvConfig } from "./env-config";
|
import { TMigrationEnvConfig } from "./env-config";
|
||||||
@@ -33,8 +28,11 @@ type TDependencies = {
|
|||||||
keyStore: TKeyStoreFactory;
|
keyStore: TKeyStoreFactory;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getMigrationEncryptionServices = async ({ envConfig, db, keyStore }: TDependencies) => {
|
type THsmServiceDependencies = {
|
||||||
// eslint-disable-next-line no-param-reassign
|
envConfig: Pick<TMigrationEnvConfig, "HSM_PIN" | "HSM_SLOT" | "HSM_LIB_PATH" | "HSM_KEY_LABEL" | "isHsmConfigured">;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getMigrationHsmService = async ({ envConfig }: THsmServiceDependencies) => {
|
||||||
const hsmModule = initializeHsmModule(envConfig);
|
const hsmModule = initializeHsmModule(envConfig);
|
||||||
hsmModule.initialize();
|
hsmModule.initialize();
|
||||||
|
|
||||||
@@ -43,67 +41,72 @@ export const getMigrationEncryptionServices = async ({ envConfig, db, keyStore }
|
|||||||
envConfig
|
envConfig
|
||||||
});
|
});
|
||||||
|
|
||||||
const orgDAL = orgDALFactory(db);
|
|
||||||
const kmsRootConfigDAL = kmsRootConfigDALFactory(db);
|
|
||||||
const kmsDAL = kmskeyDALFactory(db);
|
|
||||||
const internalKmsDAL = internalKmsDALFactory(db);
|
|
||||||
const projectDAL = projectDALFactory(db);
|
|
||||||
|
|
||||||
const kmsService = kmsServiceFactory({
|
|
||||||
kmsRootConfigDAL,
|
|
||||||
keyStore,
|
|
||||||
kmsDAL,
|
|
||||||
internalKmsDAL,
|
|
||||||
orgDAL,
|
|
||||||
projectDAL,
|
|
||||||
hsmService,
|
|
||||||
envConfig
|
|
||||||
});
|
|
||||||
|
|
||||||
await hsmService.startService();
|
await hsmService.startService();
|
||||||
await kmsService.startService();
|
|
||||||
|
|
||||||
return { kmsService };
|
return { hsmService };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getMigrationPITServices = async ({
|
export const getMigrationEncryptionServices = async ({ envConfig, db, keyStore }: TDependencies) => {
|
||||||
db,
|
// ----- DAL dependencies -----
|
||||||
keyStore,
|
const orgDAL = orgDALFactory(db);
|
||||||
envConfig
|
const licenseDAL = licenseDALFactory(db);
|
||||||
}: {
|
const permissionDAL = permissionDALFactory(db);
|
||||||
db: Knex;
|
|
||||||
keyStore: TKeyStoreFactory;
|
|
||||||
envConfig: TMigrationEnvConfig;
|
|
||||||
}) => {
|
|
||||||
const projectDAL = projectDALFactory(db);
|
const projectDAL = projectDALFactory(db);
|
||||||
const folderCommitDAL = folderCommitDALFactory(db);
|
const roleDAL = roleDALFactory(db);
|
||||||
const folderCommitChangesDAL = folderCommitChangesDALFactory(db);
|
|
||||||
const folderCheckpointDAL = folderCheckpointDALFactory(db);
|
|
||||||
const folderTreeCheckpointDAL = folderTreeCheckpointDALFactory(db);
|
|
||||||
const userDAL = userDALFactory(db);
|
const userDAL = userDALFactory(db);
|
||||||
const identityDAL = identityDALFactory(db);
|
const identityDAL = identityDALFactory(db);
|
||||||
const folderDAL = secretFolderDALFactory(db);
|
const serviceTokenDAL = serviceTokenDALFactory(db);
|
||||||
const folderVersionDAL = secretFolderVersionDALFactory(db);
|
|
||||||
const secretVersionV2BridgeDAL = secretVersionV2BridgeDALFactory(db);
|
|
||||||
const folderCheckpointResourcesDAL = folderCheckpointResourcesDALFactory(db);
|
|
||||||
const secretV2BridgeDAL = secretV2BridgeDALFactory({ db, keyStore });
|
|
||||||
const folderTreeCheckpointResourcesDAL = folderTreeCheckpointResourcesDALFactory(db);
|
|
||||||
const secretTagDAL = secretTagDALFactory(db);
|
|
||||||
|
|
||||||
const orgDAL = orgDALFactory(db);
|
|
||||||
const kmsRootConfigDAL = kmsRootConfigDALFactory(db);
|
const kmsRootConfigDAL = kmsRootConfigDALFactory(db);
|
||||||
const kmsDAL = kmskeyDALFactory(db);
|
const kmsDAL = kmskeyDALFactory(db);
|
||||||
const internalKmsDAL = internalKmsDALFactory(db);
|
const internalKmsDAL = internalKmsDALFactory(db);
|
||||||
const resourceMetadataDAL = resourceMetadataDALFactory(db);
|
|
||||||
|
|
||||||
const hsmModule = initializeHsmModule(envConfig);
|
// ----- Service dependencies -----
|
||||||
hsmModule.initialize();
|
const permissionService = permissionServiceFactory({
|
||||||
|
permissionDAL,
|
||||||
|
serviceTokenDAL,
|
||||||
|
projectDAL,
|
||||||
|
keyStore,
|
||||||
|
roleDAL,
|
||||||
|
userDAL,
|
||||||
|
identityDAL
|
||||||
|
});
|
||||||
|
|
||||||
const hsmService = hsmServiceFactory({
|
const licenseService = licenseServiceFactory({
|
||||||
hsmModule: hsmModule.getModule(),
|
permissionService,
|
||||||
|
orgDAL,
|
||||||
|
licenseDAL,
|
||||||
|
keyStore,
|
||||||
|
projectDAL,
|
||||||
envConfig
|
envConfig
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ----- HSM startup -----
|
||||||
|
|
||||||
|
const { hsmService } = await getMigrationHsmService({ envConfig });
|
||||||
|
|
||||||
|
const hsmStatus = await isHsmActiveAndEnabled({
|
||||||
|
hsmService,
|
||||||
|
kmsRootConfigDAL,
|
||||||
|
licenseService
|
||||||
|
});
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
if (!envConfig.ROOT_ENCRYPTION_KEY && !envConfig.ENCRYPTION_KEY) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message:
|
||||||
|
"Root KMS encryption strategy is set to software. Please set the ENCRYPTION_KEY environment variable and restart your deployment.\nYou can enable HSM encryption in the Server Console."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----- KMS startup -----
|
||||||
|
|
||||||
const kmsService = kmsServiceFactory({
|
const kmsService = kmsServiceFactory({
|
||||||
kmsRootConfigDAL,
|
kmsRootConfigDAL,
|
||||||
keyStore,
|
keyStore,
|
||||||
@@ -115,27 +118,7 @@ export const getMigrationPITServices = async ({
|
|||||||
envConfig
|
envConfig
|
||||||
});
|
});
|
||||||
|
|
||||||
await hsmService.startService();
|
await kmsService.startService(hsmStatus);
|
||||||
await kmsService.startService();
|
|
||||||
|
|
||||||
const folderCommitService = folderCommitServiceFactory({
|
return { kmsService, hsmService };
|
||||||
folderCommitDAL,
|
|
||||||
folderCommitChangesDAL,
|
|
||||||
folderCheckpointDAL,
|
|
||||||
folderTreeCheckpointDAL,
|
|
||||||
userDAL,
|
|
||||||
identityDAL,
|
|
||||||
folderDAL,
|
|
||||||
folderVersionDAL,
|
|
||||||
secretVersionV2BridgeDAL,
|
|
||||||
projectDAL,
|
|
||||||
folderCheckpointResourcesDAL,
|
|
||||||
secretV2BridgeDAL,
|
|
||||||
folderTreeCheckpointResourcesDAL,
|
|
||||||
kmsService,
|
|
||||||
secretTagDAL,
|
|
||||||
resourceMetadataDAL
|
|
||||||
});
|
|
||||||
|
|
||||||
return { folderCommitService };
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
import { Knex } from "knex";
|
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 { 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 { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
||||||
|
|
||||||
import { AuthMethod } from "../../services/auth/auth-type";
|
import { AuthMethod } from "../../services/auth/auth-type";
|
||||||
@@ -17,7 +20,21 @@ export async function seed(knex: Knex): Promise<void> {
|
|||||||
initLogger();
|
initLogger();
|
||||||
|
|
||||||
const superAdminDAL = superAdminDALFactory(knex);
|
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([
|
await knex(TableName.SuperAdmin).insert([
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
import { Knex } from "knex";
|
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 { crypto, SymmetricKeySize } from "@app/lib/crypto/cryptography";
|
||||||
import { generateUserSrpKeys } from "@app/lib/crypto/srp";
|
import { generateUserSrpKeys } from "@app/lib/crypto/srp";
|
||||||
import { initLogger, logger } from "@app/lib/logger";
|
import { initLogger, logger } from "@app/lib/logger";
|
||||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
import { AuthMethod } from "@app/services/auth/auth-type";
|
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 { membershipRoleDALFactory } from "@app/services/membership/membership-role-dal";
|
||||||
import { membershipUserDALFactory } from "@app/services/membership-user/membership-user-dal";
|
import { membershipUserDALFactory } from "@app/services/membership-user/membership-user-dal";
|
||||||
import { assignWorkspaceKeysToMembers, createProjectKey } from "@app/services/project/project-fns";
|
import { assignWorkspaceKeysToMembers, createProjectKey } from "@app/services/project/project-fns";
|
||||||
@@ -192,7 +195,21 @@ export async function seed(knex: Knex): Promise<void> {
|
|||||||
initLogger();
|
initLogger();
|
||||||
|
|
||||||
const superAdminDAL = superAdminDALFactory(knex);
|
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)
|
const [project] = await knex(TableName.Project)
|
||||||
.insert({
|
.insert({
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
import { Knex } from "knex";
|
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 { crypto } from "@app/lib/crypto/cryptography";
|
||||||
import { initLogger, logger } from "@app/lib/logger";
|
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 { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
||||||
|
|
||||||
import { AccessScope, IdentityAuthMethod, OrgMembershipRole, ProjectMembershipRole, TableName } from "../schemas";
|
import { AccessScope, IdentityAuthMethod, OrgMembershipRole, ProjectMembershipRole, TableName } from "../schemas";
|
||||||
@@ -15,7 +18,20 @@ export async function seed(knex: Knex): Promise<void> {
|
|||||||
initLogger();
|
initLogger();
|
||||||
|
|
||||||
const superAdminDAL = superAdminDALFactory(knex);
|
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
|
// Inserts seed entries
|
||||||
await knex(TableName.Identity).insert([
|
await knex(TableName.Identity).insert([
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
import * as pkcs11js from "pkcs11js";
|
import * as pkcs11js from "pkcs11js";
|
||||||
|
|
||||||
import { TEnvConfig } from "@app/lib/config/env";
|
import { TEnvConfig } from "@app/lib/config/env";
|
||||||
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
import { logger } from "@app/lib/logger";
|
import { logger } from "@app/lib/logger";
|
||||||
|
import { KMS_ROOT_CONFIG_UUID } from "@app/services/kms/kms-fns";
|
||||||
|
import { TKmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal";
|
||||||
|
import { RootKeyEncryptionStrategy } from "@app/services/kms/kms-types";
|
||||||
|
|
||||||
|
import { TLicenseServiceFactory } from "../license/license-service";
|
||||||
|
import { THsmServiceFactory } from "./hsm-service";
|
||||||
import { HsmModule } from "./hsm-types";
|
import { HsmModule } from "./hsm-types";
|
||||||
|
|
||||||
export const initializeHsmModule = (envConfig: Pick<TEnvConfig, "isHsmConfigured" | "HSM_LIB_PATH">) => {
|
export const initializeHsmModule = (envConfig: Pick<TEnvConfig, "isHsmConfigured" | "HSM_LIB_PATH">) => {
|
||||||
@@ -25,10 +31,9 @@ export const initializeHsmModule = (envConfig: Pick<TEnvConfig, "isHsmConfigured
|
|||||||
|
|
||||||
logger.info("PKCS#11 module initialized");
|
logger.info("PKCS#11 module initialized");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(error, "Failed to initialize PKCS#11 module");
|
|
||||||
|
|
||||||
if ((error as { message?: string })?.message === "CKR_CRYPTOKI_ALREADY_INITIALIZED") {
|
if ((error as { message?: string })?.message === "CKR_CRYPTOKI_ALREADY_INITIALIZED") {
|
||||||
logger.info("Skipping HSM initialization because it's already initialized.");
|
logger.info("Skipping HSM initialization because it's already initialized.");
|
||||||
|
isInitialized = true;
|
||||||
} else {
|
} else {
|
||||||
logger.error(error, "Failed to initialize PKCS#11 module");
|
logger.error(error, "Failed to initialize PKCS#11 module");
|
||||||
throw error;
|
throw error;
|
||||||
@@ -60,3 +65,36 @@ export const initializeHsmModule = (envConfig: Pick<TEnvConfig, "isHsmConfigured
|
|||||||
getModule
|
getModule
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const isHsmActiveAndEnabled = async ({
|
||||||
|
hsmService,
|
||||||
|
kmsRootConfigDAL,
|
||||||
|
licenseService
|
||||||
|
}: {
|
||||||
|
hsmService: Pick<THsmServiceFactory, "isActive">;
|
||||||
|
kmsRootConfigDAL: Pick<TKmsRootConfigDALFactory, "findById">;
|
||||||
|
licenseService?: Pick<TLicenseServiceFactory, "onPremFeatures">;
|
||||||
|
}) => {
|
||||||
|
const isHsmConfigured = await hsmService.isActive();
|
||||||
|
|
||||||
|
// null if the root kms config does not exist
|
||||||
|
let rootKmsConfigEncryptionStrategy: RootKeyEncryptionStrategy | null = null;
|
||||||
|
|
||||||
|
const rootKmsConfig = await kmsRootConfigDAL.findById(KMS_ROOT_CONFIG_UUID).catch(() => null);
|
||||||
|
|
||||||
|
rootKmsConfigEncryptionStrategy = (rootKmsConfig?.encryptionStrategy || null) as RootKeyEncryptionStrategy | null;
|
||||||
|
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."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
rootKmsConfigEncryptionStrategy,
|
||||||
|
isHsmConfigured
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ export const hsmServiceFactory = ({ hsmModule: { isInitialized, pkcs11 }, envCon
|
|||||||
const AES_KEY_SIZE = 256;
|
const AES_KEY_SIZE = 256;
|
||||||
const HMAC_KEY_SIZE = 256;
|
const HMAC_KEY_SIZE = 256;
|
||||||
|
|
||||||
|
let pkcs11TestPassed = false;
|
||||||
|
|
||||||
const $withSession = async <T>(callbackWithSession: SessionCallback<T>): Promise<T> => {
|
const $withSession = async <T>(callbackWithSession: SessionCallback<T>): Promise<T> => {
|
||||||
const RETRY_INTERVAL = 200; // 200ms between attempts
|
const RETRY_INTERVAL = 200; // 200ms between attempts
|
||||||
const MAX_TIMEOUT = 90_000; // 90 seconds maximum total time
|
const MAX_TIMEOUT = 90_000; // 90 seconds maximum total time
|
||||||
@@ -363,7 +365,9 @@ export const hsmServiceFactory = ({ hsmModule: { isInitialized, pkcs11 }, envCon
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let pkcs11TestPassed = false;
|
if (pkcs11TestPassed) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
pkcs11TestPassed = await $withSession($testPkcs11Module);
|
pkcs11TestPassed = await $withSession($testPkcs11Module);
|
||||||
@@ -371,7 +375,7 @@ export const hsmServiceFactory = ({ hsmModule: { isInitialized, pkcs11 }, envCon
|
|||||||
logger.error(err, "HSM: Error testing PKCS#11 module");
|
logger.error(err, "HSM: Error testing PKCS#11 module");
|
||||||
}
|
}
|
||||||
|
|
||||||
return envConfig.isHsmConfigured && isInitialized && pkcs11TestPassed;
|
return pkcs11TestPassed;
|
||||||
};
|
};
|
||||||
|
|
||||||
const startService = async () => {
|
const startService = async () => {
|
||||||
@@ -460,10 +464,23 @@ export const hsmServiceFactory = ({ hsmModule: { isInitialized, pkcs11 }, envCon
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const randomBytes = async (length: number) => {
|
||||||
|
if (!pkcs11 || !isInitialized) {
|
||||||
|
throw new Error("PKCS#11 module is not initialized");
|
||||||
|
}
|
||||||
|
|
||||||
|
const randomData = await $withSession((sessionHandle) =>
|
||||||
|
pkcs11.C_GenerateRandom(sessionHandle, Buffer.alloc(length))
|
||||||
|
);
|
||||||
|
|
||||||
|
return randomData;
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
encrypt,
|
encrypt,
|
||||||
startService,
|
startService,
|
||||||
isActive,
|
isActive,
|
||||||
decrypt
|
decrypt,
|
||||||
|
randomBytes
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import pkcs11js from "pkcs11js";
|
import pkcs11js from "pkcs11js";
|
||||||
|
|
||||||
|
import { RootKeyEncryptionStrategy } from "@app/services/kms/kms-types";
|
||||||
|
|
||||||
export type HsmModule = {
|
export type HsmModule = {
|
||||||
pkcs11: pkcs11js.PKCS11;
|
pkcs11: pkcs11js.PKCS11;
|
||||||
isInitialized: boolean;
|
isInitialized: boolean;
|
||||||
@@ -9,3 +11,8 @@ export enum HsmKeyType {
|
|||||||
AES = "AES",
|
AES = "AES",
|
||||||
HMAC = "hmac"
|
HMAC = "hmac"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type THsmStatus = {
|
||||||
|
rootKmsConfigEncryptionStrategy: RootKeyEncryptionStrategy | null;
|
||||||
|
isHsmConfigured: boolean;
|
||||||
|
};
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { Knex } from "knex";
|
|||||||
|
|
||||||
import { OrganizationActionScope } from "@app/db/schemas";
|
import { OrganizationActionScope } from "@app/db/schemas";
|
||||||
import { TKeyStoreFactory } from "@app/keystore/keystore";
|
import { TKeyStoreFactory } from "@app/keystore/keystore";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { TEnvConfig } from "@app/lib/config/env";
|
||||||
import { verifyOfflineLicense } from "@app/lib/crypto";
|
import { verifyOfflineLicense } from "@app/lib/crypto";
|
||||||
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
import { logger } from "@app/lib/logger";
|
import { logger } from "@app/lib/logger";
|
||||||
@@ -45,6 +45,10 @@ import {
|
|||||||
} from "./license-types";
|
} from "./license-types";
|
||||||
|
|
||||||
type TLicenseServiceFactoryDep = {
|
type TLicenseServiceFactoryDep = {
|
||||||
|
envConfig: Pick<
|
||||||
|
TEnvConfig,
|
||||||
|
"LICENSE_SERVER_URL" | "LICENSE_SERVER_KEY" | "LICENSE_KEY" | "LICENSE_KEY_OFFLINE" | "INTERNAL_REGION" | "SITE_URL"
|
||||||
|
>;
|
||||||
orgDAL: Pick<TOrgDALFactory, "findRootOrgDetails" | "countAllOrgMembers" | "findById">;
|
orgDAL: Pick<TOrgDALFactory, "findRootOrgDetails" | "countAllOrgMembers" | "findById">;
|
||||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||||
licenseDAL: TLicenseDALFactory;
|
licenseDAL: TLicenseDALFactory;
|
||||||
@@ -65,26 +69,26 @@ export const licenseServiceFactory = ({
|
|||||||
permissionService,
|
permissionService,
|
||||||
licenseDAL,
|
licenseDAL,
|
||||||
keyStore,
|
keyStore,
|
||||||
projectDAL
|
projectDAL,
|
||||||
|
envConfig
|
||||||
}: TLicenseServiceFactoryDep) => {
|
}: TLicenseServiceFactoryDep) => {
|
||||||
let isValidLicense = false;
|
let isValidLicense = false;
|
||||||
let instanceType = InstanceType.OnPrem;
|
let instanceType = InstanceType.OnPrem;
|
||||||
let onPremFeatures: TFeatureSet = getDefaultOnPremFeatures();
|
let onPremFeatures: TFeatureSet = getDefaultOnPremFeatures();
|
||||||
let selfHostedLicense: TOfflineLicense | null = null;
|
let selfHostedLicense: TOfflineLicense | null = null;
|
||||||
|
|
||||||
const appCfg = getConfig();
|
|
||||||
const licenseServerCloudApi = setupLicenseRequestWithStore(
|
const licenseServerCloudApi = setupLicenseRequestWithStore(
|
||||||
appCfg.LICENSE_SERVER_URL || "",
|
envConfig.LICENSE_SERVER_URL || "",
|
||||||
LICENSE_SERVER_CLOUD_LOGIN,
|
LICENSE_SERVER_CLOUD_LOGIN,
|
||||||
appCfg.LICENSE_SERVER_KEY || "",
|
envConfig.LICENSE_SERVER_KEY || "",
|
||||||
appCfg.INTERNAL_REGION
|
envConfig.INTERNAL_REGION
|
||||||
);
|
);
|
||||||
|
|
||||||
const licenseServerOnPremApi = setupLicenseRequestWithStore(
|
const licenseServerOnPremApi = setupLicenseRequestWithStore(
|
||||||
appCfg.LICENSE_SERVER_URL || "",
|
envConfig.LICENSE_SERVER_URL || "",
|
||||||
LICENSE_SERVER_ON_PREM_LOGIN,
|
LICENSE_SERVER_ON_PREM_LOGIN,
|
||||||
appCfg.LICENSE_KEY || "",
|
envConfig.LICENSE_KEY || "",
|
||||||
appCfg.INTERNAL_REGION
|
envConfig.INTERNAL_REGION
|
||||||
);
|
);
|
||||||
|
|
||||||
const syncLicenseKeyOnPremFeatures = async (shouldThrow: boolean = false) => {
|
const syncLicenseKeyOnPremFeatures = async (shouldThrow: boolean = false) => {
|
||||||
@@ -118,7 +122,7 @@ export const licenseServiceFactory = ({
|
|||||||
|
|
||||||
const init = async () => {
|
const init = async () => {
|
||||||
try {
|
try {
|
||||||
if (appCfg.LICENSE_SERVER_KEY) {
|
if (envConfig.LICENSE_SERVER_KEY) {
|
||||||
const token = await licenseServerCloudApi.refreshLicense();
|
const token = await licenseServerCloudApi.refreshLicense();
|
||||||
if (token) instanceType = InstanceType.Cloud;
|
if (token) instanceType = InstanceType.Cloud;
|
||||||
logger.info(`Instance type: ${InstanceType.Cloud}`);
|
logger.info(`Instance type: ${InstanceType.Cloud}`);
|
||||||
@@ -126,7 +130,7 @@ export const licenseServiceFactory = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (appCfg.LICENSE_KEY) {
|
if (envConfig.LICENSE_KEY) {
|
||||||
const token = await licenseServerOnPremApi.refreshLicense();
|
const token = await licenseServerOnPremApi.refreshLicense();
|
||||||
if (token) {
|
if (token) {
|
||||||
await syncLicenseKeyOnPremFeatures(true);
|
await syncLicenseKeyOnPremFeatures(true);
|
||||||
@@ -137,10 +141,10 @@ export const licenseServiceFactory = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (appCfg.LICENSE_KEY_OFFLINE) {
|
if (envConfig.LICENSE_KEY_OFFLINE) {
|
||||||
let isValidOfflineLicense = true;
|
let isValidOfflineLicense = true;
|
||||||
const contents: TOfflineLicenseContents = JSON.parse(
|
const contents: TOfflineLicenseContents = JSON.parse(
|
||||||
Buffer.from(appCfg.LICENSE_KEY_OFFLINE, "base64").toString("utf8")
|
Buffer.from(envConfig.LICENSE_KEY_OFFLINE, "base64").toString("utf8")
|
||||||
);
|
);
|
||||||
const isVerified = await verifyOfflineLicense(JSON.stringify(contents.license), contents.signature);
|
const isVerified = await verifyOfflineLicense(JSON.stringify(contents.license), contents.signature);
|
||||||
|
|
||||||
@@ -179,7 +183,7 @@ export const licenseServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const initializeBackgroundSync = async () => {
|
const initializeBackgroundSync = async () => {
|
||||||
if (appCfg.LICENSE_KEY) {
|
if (envConfig.LICENSE_KEY) {
|
||||||
logger.info("Setting up background sync process for refresh onPremFeatures");
|
logger.info("Setting up background sync process for refresh onPremFeatures");
|
||||||
const job = new CronJob("*/10 * * * *", syncLicenseKeyOnPremFeatures);
|
const job = new CronJob("*/10 * * * *", syncLicenseKeyOnPremFeatures);
|
||||||
job.start();
|
job.start();
|
||||||
@@ -440,8 +444,8 @@ export const licenseServiceFactory = ({
|
|||||||
} = await licenseServerCloudApi.request.post(
|
} = await licenseServerCloudApi.request.post(
|
||||||
`/api/license-server/v1/customers/${organization.customerId}/billing-details/payment-methods`,
|
`/api/license-server/v1/customers/${organization.customerId}/billing-details/payment-methods`,
|
||||||
{
|
{
|
||||||
success_url: `${appCfg.SITE_URL}/organization/billing`,
|
success_url: `${envConfig.SITE_URL}/organization/billing`,
|
||||||
cancel_url: `${appCfg.SITE_URL}/organization/billing`
|
cancel_url: `${envConfig.SITE_URL}/organization/billing`
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -454,7 +458,7 @@ export const licenseServiceFactory = ({
|
|||||||
} = await licenseServerCloudApi.request.post(
|
} = await licenseServerCloudApi.request.post(
|
||||||
`/api/license-server/v1/customers/${organization.customerId}/billing-details/billing-portal`,
|
`/api/license-server/v1/customers/${organization.customerId}/billing-details/billing-portal`,
|
||||||
{
|
{
|
||||||
return_url: `${appCfg.SITE_URL}/organization/billing`
|
return_url: `${envConfig.SITE_URL}/organization/billing`
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { THsmServiceFactory } from "@app/ee/services/hsm/hsm-service";
|
||||||
import { crypto } from "@app/lib/crypto/cryptography";
|
import { crypto } from "@app/lib/crypto/cryptography";
|
||||||
import { QueueWorkerProfile } from "@app/lib/types";
|
import { QueueWorkerProfile } from "@app/lib/types";
|
||||||
import { TSuperAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
import { TSuperAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
||||||
@@ -8,6 +9,7 @@ import { BadRequestError } from "../errors";
|
|||||||
import { removeTrailingSlash } from "../fn";
|
import { removeTrailingSlash } from "../fn";
|
||||||
import { CustomLogger } from "../logger/logger";
|
import { CustomLogger } from "../logger/logger";
|
||||||
import { zpStr } from "../zod";
|
import { zpStr } from "../zod";
|
||||||
|
import { TKmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal";
|
||||||
|
|
||||||
export const GITLAB_URL = "https://gitlab.com";
|
export const GITLAB_URL = "https://gitlab.com";
|
||||||
|
|
||||||
@@ -363,11 +365,6 @@ const envSchema = z
|
|||||||
/* INTERNAL ----------------------------------------------------------------------------- */
|
/* INTERNAL ----------------------------------------------------------------------------- */
|
||||||
INTERNAL_REGION: zpStr(z.enum(["us", "eu"]).optional())
|
INTERNAL_REGION: zpStr(z.enum(["us", "eu"]).optional())
|
||||||
})
|
})
|
||||||
// To ensure that basic encryption is always possible.
|
|
||||||
.refine(
|
|
||||||
(data) => Boolean(data.ENCRYPTION_KEY) || Boolean(data.ROOT_ENCRYPTION_KEY),
|
|
||||||
"Either ENCRYPTION_KEY or ROOT_ENCRYPTION_KEY must be defined."
|
|
||||||
)
|
|
||||||
.refine(
|
.refine(
|
||||||
(data) => Boolean(data.REDIS_URL) || Boolean(data.REDIS_SENTINEL_HOSTS) || Boolean(data.REDIS_CLUSTER_HOSTS),
|
(data) => Boolean(data.REDIS_URL) || Boolean(data.REDIS_SENTINEL_HOSTS) || Boolean(data.REDIS_CLUSTER_HOSTS),
|
||||||
"Either REDIS_URL, REDIS_SENTINEL_HOSTS or REDIS_CLUSTER_HOSTS must be defined."
|
"Either REDIS_URL, REDIS_SENTINEL_HOSTS or REDIS_CLUSTER_HOSTS must be defined."
|
||||||
@@ -453,7 +450,12 @@ export const getConfig = () => envCfg;
|
|||||||
export const getOriginalConfig = () => originalEnvConfig;
|
export const getOriginalConfig = () => originalEnvConfig;
|
||||||
|
|
||||||
// cannot import singleton logger directly as it needs config to load various transport
|
// 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);
|
const parsedEnv = envSchema.safeParse(process.env);
|
||||||
if (!parsedEnv.success) {
|
if (!parsedEnv.success) {
|
||||||
(logger ?? console).error("Invalid environment variables. Check the error below");
|
(logger ?? console).error("Invalid environment variables. Check the error below");
|
||||||
@@ -469,7 +471,7 @@ export const initEnvConfig = async (superAdminDAL?: TSuperAdminDALFactory, logge
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (superAdminDAL) {
|
if (superAdminDAL) {
|
||||||
const fipsEnabled = await crypto.initialize(superAdminDAL);
|
const fipsEnabled = await crypto.initialize(superAdminDAL, hsmService, kmsRootConfigDAL);
|
||||||
|
|
||||||
if (fipsEnabled) {
|
if (fipsEnabled) {
|
||||||
const newEnvCfg = {
|
const newEnvCfg = {
|
||||||
@@ -532,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
|
// A list of environment variables that can be overwritten
|
||||||
export const overwriteSchema: {
|
export const overwriteSchema: {
|
||||||
[key: string]: {
|
[key: string]: {
|
||||||
|
|||||||
@@ -9,7 +9,11 @@ import nacl from "tweetnacl";
|
|||||||
import naclUtils from "tweetnacl-util";
|
import naclUtils from "tweetnacl-util";
|
||||||
|
|
||||||
import { SecretEncryptionAlgo, SecretKeyEncoding } from "@app/db/schemas";
|
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 { 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 { TSuperAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
||||||
import { ADMIN_CONFIG_DB_UUID } from "@app/services/super-admin/super-admin-service";
|
import { ADMIN_CONFIG_DB_UUID } from "@app/services/super-admin/super-admin-service";
|
||||||
|
|
||||||
@@ -106,49 +110,73 @@ const cryptographyFactory = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const $setFipsModeEnabled = (enabled: boolean, envCfg?: Pick<TEnvConfig, "ENCRYPTION_KEY">) => {
|
const $setFipsModeEnabled = async (
|
||||||
|
enabled: boolean,
|
||||||
|
hsmService: THsmServiceFactory,
|
||||||
|
kmsRootConfigDAL: TKmsRootConfigDALFactory,
|
||||||
|
envCfg?: Pick<TEnvConfig, "ENCRYPTION_KEY">
|
||||||
|
) => {
|
||||||
// If FIPS is enabled, we need to validate that the ENCRYPTION_KEY is in a base64 format, and is a 256-bit key.
|
// 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) {
|
if (enabled) {
|
||||||
crypto.setFips(true);
|
crypto.setFips(true);
|
||||||
|
|
||||||
const appCfg = envCfg || getConfig();
|
const appCfg = envCfg || getConfig();
|
||||||
|
|
||||||
if (appCfg.ENCRYPTION_KEY) {
|
const hsmStatus = await isHsmActiveAndEnabled({
|
||||||
// we need to validate that the ENCRYPTION_KEY is a base64 encoded 256-bit key
|
hsmService,
|
||||||
|
kmsRootConfigDAL
|
||||||
|
});
|
||||||
|
|
||||||
// note(daniel): for some reason this resolves as true for some hex-encoded strings.
|
// if the encryption strategy is software - user needs to provide an encryption key
|
||||||
if (!isBase64(appCfg.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);
|
||||||
|
|
||||||
|
// only perform encryption key validation if it's actually required.
|
||||||
|
if (needsEncryptionKey) {
|
||||||
|
if (appCfg.ENCRYPTION_KEY) {
|
||||||
|
// we need to validate that the ENCRYPTION_KEY is a base64 encoded 256-bit key
|
||||||
|
|
||||||
|
// note(daniel): for some reason this resolves as true for some hex-encoded strings.
|
||||||
|
if (!isBase64(appCfg.ENCRYPTION_KEY)) {
|
||||||
|
throw new CryptographyError({
|
||||||
|
message:
|
||||||
|
"FIPS mode is enabled, but the ENCRYPTION_KEY environment variable is not a base64 encoded 256-bit key.\nYou can generate a 256-bit key using the following command: `openssl rand -base64 32`"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bytesToBits(Buffer.from(appCfg.ENCRYPTION_KEY, "base64").length) !== 256) {
|
||||||
|
throw new CryptographyError({
|
||||||
|
message:
|
||||||
|
"FIPS mode is enabled, but the ENCRYPTION_KEY environment variable is not a 256-bit key.\nYou can generate a 256-bit key using the following command: `openssl rand -base64 32`"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
throw new CryptographyError({
|
throw new CryptographyError({
|
||||||
message:
|
message:
|
||||||
"FIPS mode is enabled, but the ENCRYPTION_KEY environment variable is not a base64 encoded 256-bit key.\nYou can generate a 256-bit key using the following command: `openssl rand -base64 32`"
|
"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`"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bytesToBits(Buffer.from(appCfg.ENCRYPTION_KEY, "base64").length) !== 256) {
|
|
||||||
throw new CryptographyError({
|
|
||||||
message:
|
|
||||||
"FIPS mode is enabled, but the ENCRYPTION_KEY environment variable is not a 256-bit key.\nYou can generate a 256-bit key using the following command: `openssl rand -base64 32`"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} 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`"
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$fipsEnabled = enabled;
|
$fipsEnabled = enabled;
|
||||||
$isInitialized = true;
|
$isInitialized = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const initialize = async (superAdminDAL: TSuperAdminDALFactory, envCfg?: Pick<TEnvConfig, "ENCRYPTION_KEY">) => {
|
const initialize = async (
|
||||||
|
superAdminDAL: TSuperAdminDALFactory,
|
||||||
|
hsmService: THsmServiceFactory,
|
||||||
|
kmsRootConfigDAL: TKmsRootConfigDALFactory,
|
||||||
|
envCfg?: Pick<TEnvConfig, "ENCRYPTION_KEY">
|
||||||
|
) => {
|
||||||
if ($isInitialized) {
|
if ($isInitialized) {
|
||||||
return isFipsModeEnabled();
|
return isFipsModeEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process.env.FIPS_ENABLED !== "true") {
|
if (process.env.FIPS_ENABLED !== "true") {
|
||||||
logger.info("Cryptography module initialized in normal operation mode.");
|
logger.info("Cryptography module initialized in normal operation mode.");
|
||||||
$setFipsModeEnabled(false, envCfg);
|
await $setFipsModeEnabled(false, hsmService, kmsRootConfigDAL, envCfg);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,11 +186,11 @@ const cryptographyFactory = () => {
|
|||||||
if (serverCfg) {
|
if (serverCfg) {
|
||||||
if (serverCfg.fipsEnabled) {
|
if (serverCfg.fipsEnabled) {
|
||||||
logger.info("[FIPS]: Instance is configured for FIPS mode of operation. Continuing startup with FIPS enabled.");
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
logger.info("[FIPS]: Instance age predates FIPS mode inception date. Continuing without FIPS.");
|
logger.info("[FIPS]: Instance age predates FIPS mode inception date. Continuing without FIPS.");
|
||||||
$setFipsModeEnabled(false, envCfg);
|
await $setFipsModeEnabled(false, hsmService, kmsRootConfigDAL, envCfg);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,7 +199,7 @@ const cryptographyFactory = () => {
|
|||||||
// TODO(daniel): check if it's an enterprise deployment
|
// 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.
|
// 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;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -258,6 +286,13 @@ const cryptographyFactory = () => {
|
|||||||
const rootEncryptionKey = appCfg.ROOT_ENCRYPTION_KEY;
|
const rootEncryptionKey = appCfg.ROOT_ENCRYPTION_KEY;
|
||||||
const encryptionKey = appCfg.ENCRYPTION_KEY;
|
const encryptionKey = appCfg.ENCRYPTION_KEY;
|
||||||
|
|
||||||
|
// Sanity check
|
||||||
|
if (!rootEncryptionKey && !encryptionKey) {
|
||||||
|
throw new CryptographyError({
|
||||||
|
message: "Tried to encrypt with instance root encryption key, but no root encryption key is set."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (rootEncryptionKey) {
|
if (rootEncryptionKey) {
|
||||||
const { iv, tag, ciphertext } = encrypt({
|
const { iv, tag, ciphertext } = encrypt({
|
||||||
plaintext: data,
|
plaintext: data,
|
||||||
@@ -303,6 +338,14 @@ const cryptographyFactory = () => {
|
|||||||
// the or gate is used used in migration
|
// the or gate is used used in migration
|
||||||
const rootEncryptionKey = appCfg?.ROOT_ENCRYPTION_KEY || process.env.ROOT_ENCRYPTION_KEY;
|
const rootEncryptionKey = appCfg?.ROOT_ENCRYPTION_KEY || process.env.ROOT_ENCRYPTION_KEY;
|
||||||
const encryptionKey = appCfg?.ENCRYPTION_KEY || process.env.ENCRYPTION_KEY;
|
const encryptionKey = appCfg?.ENCRYPTION_KEY || process.env.ENCRYPTION_KEY;
|
||||||
|
|
||||||
|
// Sanity check
|
||||||
|
if (!rootEncryptionKey && !encryptionKey) {
|
||||||
|
throw new CryptographyError({
|
||||||
|
message: "Tried to decrypt with instance root encryption key, but no root encryption key is set."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (rootEncryptionKey && keyEncoding === SecretKeyEncoding.BASE64) {
|
if (rootEncryptionKey && keyEncoding === SecretKeyEncoding.BASE64) {
|
||||||
const data = symmetric().decrypt({
|
const data = symmetric().decrypt({
|
||||||
key: rootEncryptionKey,
|
key: rootEncryptionKey,
|
||||||
|
|||||||
+19
-6
@@ -9,14 +9,16 @@ import { keyValueStoreDALFactory } from "@app/keystore/key-value-store-dal";
|
|||||||
|
|
||||||
import { runMigrations } from "./auto-start-migrations";
|
import { runMigrations } from "./auto-start-migrations";
|
||||||
import { initAuditLogDbConnection, initDbConnection } from "./db";
|
import { initAuditLogDbConnection, initDbConnection } from "./db";
|
||||||
|
import { hsmServiceFactory } from "./ee/services/hsm/hsm-service";
|
||||||
import { keyStoreFactory } from "./keystore/keystore";
|
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 { buildRedisFromConfig } from "./lib/config/redis";
|
||||||
import { removeTemporaryBaseDirectory } from "./lib/files";
|
import { removeTemporaryBaseDirectory } from "./lib/files";
|
||||||
import { initLogger } from "./lib/logger";
|
import { initLogger } from "./lib/logger";
|
||||||
import { queueServiceFactory } from "./queue";
|
import { queueServiceFactory } from "./queue";
|
||||||
import { main } from "./server/app";
|
import { main } from "./server/app";
|
||||||
import { bootstrapCheck } from "./server/boot-strap-check";
|
import { bootstrapCheck } from "./server/boot-strap-check";
|
||||||
|
import { kmsRootConfigDALFactory } from "./services/kms/kms-root-config-dal";
|
||||||
import { smtpServiceFactory } from "./services/smtp/smtp-service";
|
import { smtpServiceFactory } from "./services/smtp/smtp-service";
|
||||||
import { superAdminDALFactory } from "./services/super-admin/super-admin-dal";
|
import { superAdminDALFactory } from "./services/super-admin/super-admin-dal";
|
||||||
|
|
||||||
@@ -26,6 +28,18 @@ const run = async () => {
|
|||||||
const logger = initLogger();
|
const logger = initLogger();
|
||||||
await removeTemporaryBaseDirectory();
|
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 databaseCredentials = getDatabaseCredentials(logger);
|
||||||
|
|
||||||
const db = initDbConnection({
|
const db = initDbConnection({
|
||||||
@@ -35,7 +49,8 @@ const run = async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const superAdminDAL = superAdminDALFactory(db);
|
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
|
const auditLogDb = envConfig.AUDIT_LOGS_DB_CONNECTION_URI
|
||||||
? initAuditLogDbConnection({
|
? initAuditLogDbConnection({
|
||||||
@@ -59,14 +74,12 @@ const run = async () => {
|
|||||||
const keyStore = keyStoreFactory(envConfig, keyValueStoreDAL);
|
const keyStore = keyStoreFactory(envConfig, keyValueStoreDAL);
|
||||||
const redis = buildRedisFromConfig(envConfig);
|
const redis = buildRedisFromConfig(envConfig);
|
||||||
|
|
||||||
const hsmModule = initializeHsmModule(envConfig);
|
|
||||||
hsmModule.initialize();
|
|
||||||
|
|
||||||
const server = await main({
|
const server = await main({
|
||||||
db,
|
db,
|
||||||
auditLogDb,
|
auditLogDb,
|
||||||
superAdminDAL,
|
superAdminDAL,
|
||||||
hsmModule: hsmModule.getModule(),
|
kmsRootConfigDAL,
|
||||||
|
hsmService,
|
||||||
smtp,
|
smtp,
|
||||||
logger,
|
logger,
|
||||||
queue,
|
queue,
|
||||||
|
|||||||
@@ -15,12 +15,13 @@ import fastify from "fastify";
|
|||||||
import { Cluster, Redis } from "ioredis";
|
import { Cluster, Redis } from "ioredis";
|
||||||
import { Knex } from "knex";
|
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 { TKeyStoreFactory } from "@app/keystore/keystore";
|
||||||
import { getConfig, IS_PACKAGED, TEnvConfig } from "@app/lib/config/env";
|
import { getConfig, IS_PACKAGED, TEnvConfig } from "@app/lib/config/env";
|
||||||
import { CustomLogger } from "@app/lib/logger/logger";
|
import { CustomLogger } from "@app/lib/logger/logger";
|
||||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
import { TQueueServiceFactory } from "@app/queue";
|
import { TQueueServiceFactory } from "@app/queue";
|
||||||
|
import { TKmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal";
|
||||||
import { TSmtpService } from "@app/services/smtp/smtp-service";
|
import { TSmtpService } from "@app/services/smtp/smtp-service";
|
||||||
import { TSuperAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
import { TSuperAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
||||||
|
|
||||||
@@ -42,16 +43,16 @@ type TMain = {
|
|||||||
logger?: CustomLogger;
|
logger?: CustomLogger;
|
||||||
queue: TQueueServiceFactory;
|
queue: TQueueServiceFactory;
|
||||||
keyStore: TKeyStoreFactory;
|
keyStore: TKeyStoreFactory;
|
||||||
hsmModule: HsmModule;
|
|
||||||
redis: Redis | Cluster;
|
redis: Redis | Cluster;
|
||||||
envConfig: TEnvConfig;
|
envConfig: TEnvConfig;
|
||||||
superAdminDAL: TSuperAdminDALFactory;
|
superAdminDAL: TSuperAdminDALFactory;
|
||||||
|
hsmService: THsmServiceFactory;
|
||||||
|
kmsRootConfigDAL: TKmsRootConfigDALFactory;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Run the server!
|
// Run the server!
|
||||||
export const main = async ({
|
export const main = async ({
|
||||||
db,
|
db,
|
||||||
hsmModule,
|
|
||||||
auditLogDb,
|
auditLogDb,
|
||||||
smtp,
|
smtp,
|
||||||
logger,
|
logger,
|
||||||
@@ -59,7 +60,9 @@ export const main = async ({
|
|||||||
keyStore,
|
keyStore,
|
||||||
redis,
|
redis,
|
||||||
envConfig,
|
envConfig,
|
||||||
superAdminDAL
|
superAdminDAL,
|
||||||
|
hsmService,
|
||||||
|
kmsRootConfigDAL
|
||||||
}: TMain) => {
|
}: TMain) => {
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
|
|
||||||
@@ -148,9 +151,10 @@ export const main = async ({
|
|||||||
db,
|
db,
|
||||||
auditLogDb,
|
auditLogDb,
|
||||||
keyStore,
|
keyStore,
|
||||||
hsmModule,
|
hsmService,
|
||||||
envConfig,
|
envConfig,
|
||||||
superAdminDAL
|
superAdminDAL,
|
||||||
|
kmsRootConfigDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
await server.register(registerServeUI, {
|
await server.register(registerServeUI, {
|
||||||
|
|||||||
@@ -46,8 +46,8 @@ import { githubOrgSyncServiceFactory } from "@app/ee/services/github-org-sync/gi
|
|||||||
import { groupDALFactory } from "@app/ee/services/group/group-dal";
|
import { groupDALFactory } from "@app/ee/services/group/group-dal";
|
||||||
import { groupServiceFactory } from "@app/ee/services/group/group-service";
|
import { groupServiceFactory } from "@app/ee/services/group/group-service";
|
||||||
import { userGroupMembershipDALFactory } from "@app/ee/services/group/user-group-membership-dal";
|
import { userGroupMembershipDALFactory } from "@app/ee/services/group/user-group-membership-dal";
|
||||||
import { hsmServiceFactory } from "@app/ee/services/hsm/hsm-service";
|
import { isHsmActiveAndEnabled } from "@app/ee/services/hsm/hsm-fns";
|
||||||
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 { 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 { identityAuthTemplateServiceFactory } from "@app/ee/services/identity-auth-template/identity-auth-template-service";
|
||||||
import { kmipClientCertificateDALFactory } from "@app/ee/services/kmip/kmip-client-certificate-dal";
|
import { kmipClientCertificateDALFactory } from "@app/ee/services/kmip/kmip-client-certificate-dal";
|
||||||
@@ -138,6 +138,7 @@ import { keyValueStoreDALFactory } from "@app/keystore/key-value-store-dal";
|
|||||||
import { TKeyStoreFactory } from "@app/keystore/keystore";
|
import { TKeyStoreFactory } from "@app/keystore/keystore";
|
||||||
import { getConfig, TEnvConfig } from "@app/lib/config/env";
|
import { getConfig, TEnvConfig } from "@app/lib/config/env";
|
||||||
import { crypto } from "@app/lib/crypto/cryptography";
|
import { crypto } from "@app/lib/crypto/cryptography";
|
||||||
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
import { logger } from "@app/lib/logger";
|
import { logger } from "@app/lib/logger";
|
||||||
import { TQueueServiceFactory } from "@app/queue";
|
import { TQueueServiceFactory } from "@app/queue";
|
||||||
import { readLimit } from "@app/server/config/rateLimiter";
|
import { readLimit } from "@app/server/config/rateLimiter";
|
||||||
@@ -236,8 +237,9 @@ import { integrationAuthDALFactory } from "@app/services/integration-auth/integr
|
|||||||
import { integrationAuthServiceFactory } from "@app/services/integration-auth/integration-auth-service";
|
import { integrationAuthServiceFactory } from "@app/services/integration-auth/integration-auth-service";
|
||||||
import { internalKmsDALFactory } from "@app/services/kms/internal-kms-dal";
|
import { internalKmsDALFactory } from "@app/services/kms/internal-kms-dal";
|
||||||
import { kmskeyDALFactory } from "@app/services/kms/kms-key-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 { kmsServiceFactory } from "@app/services/kms/kms-service";
|
||||||
|
import { RootKeyEncryptionStrategy } from "@app/services/kms/kms-types";
|
||||||
import { membershipDALFactory } from "@app/services/membership/membership-dal";
|
import { membershipDALFactory } from "@app/services/membership/membership-dal";
|
||||||
import { membershipRoleDALFactory } from "@app/services/membership/membership-role-dal";
|
import { membershipRoleDALFactory } from "@app/services/membership/membership-role-dal";
|
||||||
import { membershipGroupDALFactory } from "@app/services/membership-group/membership-group-dal";
|
import { membershipGroupDALFactory } from "@app/services/membership-group/membership-group-dal";
|
||||||
@@ -255,7 +257,6 @@ import { userNotificationDALFactory } from "@app/services/notification/user-noti
|
|||||||
import { offlineUsageReportDALFactory } from "@app/services/offline-usage-report/offline-usage-report-dal";
|
import { offlineUsageReportDALFactory } from "@app/services/offline-usage-report/offline-usage-report-dal";
|
||||||
import { offlineUsageReportServiceFactory } from "@app/services/offline-usage-report/offline-usage-report-service";
|
import { offlineUsageReportServiceFactory } from "@app/services/offline-usage-report/offline-usage-report-service";
|
||||||
import { incidentContactDALFactory } from "@app/services/org/incident-contacts-dal";
|
import { incidentContactDALFactory } from "@app/services/org/incident-contacts-dal";
|
||||||
import { orgBotDALFactory } from "@app/services/org/org-bot-dal";
|
|
||||||
import { orgDALFactory } from "@app/services/org/org-dal";
|
import { orgDALFactory } from "@app/services/org/org-dal";
|
||||||
import { orgServiceFactory } from "@app/services/org/org-service";
|
import { orgServiceFactory } from "@app/services/org/org-service";
|
||||||
import { orgAdminServiceFactory } from "@app/services/org-admin/org-admin-service";
|
import { orgAdminServiceFactory } from "@app/services/org-admin/org-admin-service";
|
||||||
@@ -364,20 +365,22 @@ export const registerRoutes = async (
|
|||||||
auditLogDb,
|
auditLogDb,
|
||||||
superAdminDAL,
|
superAdminDAL,
|
||||||
db,
|
db,
|
||||||
hsmModule,
|
|
||||||
smtp: smtpService,
|
smtp: smtpService,
|
||||||
queue: queueService,
|
queue: queueService,
|
||||||
keyStore,
|
keyStore,
|
||||||
envConfig
|
envConfig,
|
||||||
|
hsmService,
|
||||||
|
kmsRootConfigDAL
|
||||||
}: {
|
}: {
|
||||||
auditLogDb?: Knex;
|
auditLogDb?: Knex;
|
||||||
superAdminDAL: TSuperAdminDALFactory;
|
superAdminDAL: TSuperAdminDALFactory;
|
||||||
db: Knex;
|
db: Knex;
|
||||||
hsmModule: HsmModule;
|
|
||||||
smtp: TSmtpService;
|
smtp: TSmtpService;
|
||||||
queue: TQueueServiceFactory;
|
queue: TQueueServiceFactory;
|
||||||
keyStore: TKeyStoreFactory;
|
keyStore: TKeyStoreFactory;
|
||||||
envConfig: TEnvConfig;
|
envConfig: TEnvConfig;
|
||||||
|
hsmService: THsmServiceFactory;
|
||||||
|
kmsRootConfigDAL: TKmsRootConfigDALFactory;
|
||||||
}
|
}
|
||||||
) => {
|
) => {
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
@@ -392,7 +395,6 @@ export const registerRoutes = async (
|
|||||||
const authTokenDAL = tokenDALFactory(db);
|
const authTokenDAL = tokenDALFactory(db);
|
||||||
const orgDAL = orgDALFactory(db);
|
const orgDAL = orgDALFactory(db);
|
||||||
const orgMembershipDAL = orgMembershipDALFactory(db);
|
const orgMembershipDAL = orgMembershipDALFactory(db);
|
||||||
const orgBotDAL = orgBotDALFactory(db);
|
|
||||||
const incidentContactDAL = incidentContactDALFactory(db);
|
const incidentContactDAL = incidentContactDALFactory(db);
|
||||||
const rateLimitDAL = rateLimitDALFactory(db);
|
const rateLimitDAL = rateLimitDALFactory(db);
|
||||||
const apiKeyDAL = apiKeyDALFactory(db);
|
const apiKeyDAL = apiKeyDALFactory(db);
|
||||||
@@ -509,7 +511,6 @@ export const registerRoutes = async (
|
|||||||
const kmsDAL = kmskeyDALFactory(db);
|
const kmsDAL = kmskeyDALFactory(db);
|
||||||
const internalKmsDAL = internalKmsDALFactory(db);
|
const internalKmsDAL = internalKmsDALFactory(db);
|
||||||
const externalKmsDAL = externalKmsDALFactory(db);
|
const externalKmsDAL = externalKmsDALFactory(db);
|
||||||
const kmsRootConfigDAL = kmsRootConfigDALFactory(db);
|
|
||||||
|
|
||||||
const slackIntegrationDAL = slackIntegrationDALFactory(db);
|
const slackIntegrationDAL = slackIntegrationDALFactory(db);
|
||||||
const projectSlackConfigDAL = projectSlackConfigDALFactory(db);
|
const projectSlackConfigDAL = projectSlackConfigDALFactory(db);
|
||||||
@@ -569,7 +570,8 @@ export const registerRoutes = async (
|
|||||||
orgDAL,
|
orgDAL,
|
||||||
licenseDAL,
|
licenseDAL,
|
||||||
keyStore,
|
keyStore,
|
||||||
projectDAL
|
projectDAL,
|
||||||
|
envConfig
|
||||||
});
|
});
|
||||||
|
|
||||||
const tokenService = tokenServiceFactory({ tokenDAL: authTokenDAL, userDAL, membershipUserDAL, orgDAL });
|
const tokenService = tokenServiceFactory({ tokenDAL: authTokenDAL, userDAL, membershipUserDAL, orgDAL });
|
||||||
@@ -624,11 +626,6 @@ export const registerRoutes = async (
|
|||||||
permissionService
|
permissionService
|
||||||
});
|
});
|
||||||
|
|
||||||
const hsmService = hsmServiceFactory({
|
|
||||||
hsmModule,
|
|
||||||
envConfig
|
|
||||||
});
|
|
||||||
|
|
||||||
const kmsService = kmsServiceFactory({
|
const kmsService = kmsServiceFactory({
|
||||||
kmsRootConfigDAL,
|
kmsRootConfigDAL,
|
||||||
keyStore,
|
keyStore,
|
||||||
@@ -901,7 +898,6 @@ export const registerRoutes = async (
|
|||||||
smtpService,
|
smtpService,
|
||||||
userDAL,
|
userDAL,
|
||||||
groupDAL,
|
groupDAL,
|
||||||
orgBotDAL,
|
|
||||||
oidcConfigDAL,
|
oidcConfigDAL,
|
||||||
ldapConfigDAL,
|
ldapConfigDAL,
|
||||||
loginService,
|
loginService,
|
||||||
@@ -2296,6 +2292,27 @@ export const registerRoutes = async (
|
|||||||
// Start HSM service if it's configured/enabled.
|
// Start HSM service if it's configured/enabled.
|
||||||
await hsmService.startService();
|
await hsmService.startService();
|
||||||
|
|
||||||
|
const hsmStatus = await isHsmActiveAndEnabled({
|
||||||
|
hsmService,
|
||||||
|
kmsRootConfigDAL,
|
||||||
|
licenseService
|
||||||
|
});
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
if (!envConfig.ROOT_ENCRYPTION_KEY && !envConfig.ENCRYPTION_KEY) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message:
|
||||||
|
"Root KMS encryption strategy is set to software. Please set the ENCRYPTION_KEY environment variable and restart your deployment.\nYou can enable HSM encryption in the Server Console."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await telemetryQueue.startTelemetryCheck();
|
await telemetryQueue.startTelemetryCheck();
|
||||||
await telemetryQueue.startAggregatedEventsJob();
|
await telemetryQueue.startAggregatedEventsJob();
|
||||||
await dailyResourceCleanUp.init();
|
await dailyResourceCleanUp.init();
|
||||||
@@ -2305,7 +2322,7 @@ export const registerRoutes = async (
|
|||||||
await dailyReminderQueueService.startSecretReminderMigrationJob();
|
await dailyReminderQueueService.startSecretReminderMigrationJob();
|
||||||
await dailyExpiringPkiItemAlert.startSendingAlerts();
|
await dailyExpiringPkiItemAlert.startSendingAlerts();
|
||||||
await pkiSubscriberQueue.startDailyAutoRenewalJob();
|
await pkiSubscriberQueue.startDailyAutoRenewalJob();
|
||||||
await kmsService.startService();
|
await kmsService.startService(hsmStatus);
|
||||||
await microsoftTeamsService.start();
|
await microsoftTeamsService.start();
|
||||||
await dynamicSecretQueueService.init();
|
await dynamicSecretQueueService.init();
|
||||||
await eventBusService.init();
|
await eventBusService.init();
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
TExternalKmsProviderFns
|
TExternalKmsProviderFns
|
||||||
} from "@app/ee/services/external-kms/providers/model";
|
} from "@app/ee/services/external-kms/providers/model";
|
||||||
import { THsmServiceFactory } from "@app/ee/services/hsm/hsm-service";
|
import { THsmServiceFactory } from "@app/ee/services/hsm/hsm-service";
|
||||||
|
import { THsmStatus } from "@app/ee/services/hsm/hsm-types";
|
||||||
import { KeyStorePrefixes, PgSqlLock, TKeyStoreFactory } from "@app/keystore/keystore";
|
import { KeyStorePrefixes, PgSqlLock, TKeyStoreFactory } from "@app/keystore/keystore";
|
||||||
import { TEnvConfig } from "@app/lib/config/env";
|
import { TEnvConfig } from "@app/lib/config/env";
|
||||||
import { symmetricCipherService, SymmetricKeyAlgorithm } from "@app/lib/crypto/cipher";
|
import { symmetricCipherService, SymmetricKeyAlgorithm } from "@app/lib/crypto/cipher";
|
||||||
@@ -1077,17 +1078,22 @@ export const kmsServiceFactory = ({
|
|||||||
return { id, name, orgId, isExternal };
|
return { id, name, orgId, isExternal };
|
||||||
};
|
};
|
||||||
|
|
||||||
const startService = async () => {
|
const startService = async (hsmStatus: THsmStatus) => {
|
||||||
const kmsRootConfig = await kmsRootConfigDAL.transaction(async (tx) => {
|
const kmsRootConfig = await kmsRootConfigDAL.transaction(async (tx) => {
|
||||||
await tx.raw("SELECT pg_advisory_xact_lock(?)", [PgSqlLock.KmsRootKeyInit]);
|
await tx.raw("SELECT pg_advisory_xact_lock(?)", [PgSqlLock.KmsRootKeyInit]);
|
||||||
// check if KMS root key was already generated and saved in DB
|
// check if KMS root key was already generated and saved in DB
|
||||||
const existingRootConfig = await kmsRootConfigDAL.findById(KMS_ROOT_CONFIG_UUID);
|
const existingRootConfig = await kmsRootConfigDAL.findById(KMS_ROOT_CONFIG_UUID);
|
||||||
if (existingRootConfig) return existingRootConfig;
|
if (existingRootConfig) return existingRootConfig;
|
||||||
|
|
||||||
logger.info("KMS: Generating new ROOT Key");
|
const isHsmActive = hsmStatus.isHsmConfigured;
|
||||||
const newRootKey = crypto.randomBytes(32);
|
|
||||||
const encryptedRootKey = await $encryptRootKey(newRootKey, RootKeyEncryptionStrategy.Software).catch((err) => {
|
logger.info(`KMS: Generating new ROOT Key with ${isHsmActive ? "HSM" : "software"} encryption`);
|
||||||
logger.error({ hsmEnabled: hsmService.isActive() }, "KMS: Failed to encrypt ROOT Key");
|
const newRootKey = isHsmActive ? await hsmService.randomBytes(32) : crypto.randomBytes(32);
|
||||||
|
|
||||||
|
const encryptionStrategy = isHsmActive ? RootKeyEncryptionStrategy.HSM : RootKeyEncryptionStrategy.Software;
|
||||||
|
|
||||||
|
const encryptedRootKey = await $encryptRootKey(newRootKey, encryptionStrategy).catch((err) => {
|
||||||
|
logger.error({ hsmEnabled: isHsmActive, encryptionStrategy }, "KMS: Failed to encrypt ROOT Key");
|
||||||
throw err;
|
throw err;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1095,7 +1101,7 @@ export const kmsServiceFactory = ({
|
|||||||
// @ts-expect-error id is kept as fixed for idempotence and to avoid race condition
|
// @ts-expect-error id is kept as fixed for idempotence and to avoid race condition
|
||||||
id: KMS_ROOT_CONFIG_UUID,
|
id: KMS_ROOT_CONFIG_UUID,
|
||||||
encryptedRootKey,
|
encryptedRootKey,
|
||||||
encryptionStrategy: RootKeyEncryptionStrategy.Software
|
encryptionStrategy
|
||||||
});
|
});
|
||||||
return newRootConfig;
|
return newRootConfig;
|
||||||
});
|
});
|
||||||
@@ -1117,6 +1123,15 @@ export const kmsServiceFactory = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (strategy === RootKeyEncryptionStrategy.Software) {
|
||||||
|
if (!envConfig.ROOT_ENCRYPTION_KEY && !envConfig.ENCRYPTION_KEY) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message:
|
||||||
|
"Root KMS encryption strategy is set to software. Please set the ENCRYPTION_KEY environment variable and restart your deployment before trying to update the encryption strategy to software mode."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const decryptedRootKey = await $decryptRootKey(kmsRootConfig);
|
const decryptedRootKey = await $decryptRootKey(kmsRootConfig);
|
||||||
const encryptedRootKey = await $encryptRootKey(decryptedRootKey, strategy);
|
const encryptedRootKey = await $encryptRootKey(decryptedRootKey, strategy);
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
import { TDbClient } from "@app/db";
|
|
||||||
import { TableName } from "@app/db/schemas";
|
|
||||||
import { ormify } from "@app/lib/knex";
|
|
||||||
|
|
||||||
export type TOrgBotDALFactory = ReturnType<typeof orgBotDALFactory>;
|
|
||||||
|
|
||||||
export const orgBotDALFactory = (db: TDbClient) => {
|
|
||||||
const orgBotOrm = ormify(db, TableName.OrgBot);
|
|
||||||
return orgBotOrm;
|
|
||||||
};
|
|
||||||
@@ -58,7 +58,6 @@ import { TSecretV2BridgeDALFactory } from "../secret-v2-bridge/secret-v2-bridge-
|
|||||||
import { SmtpTemplates, TSmtpService } from "../smtp/smtp-service";
|
import { SmtpTemplates, TSmtpService } from "../smtp/smtp-service";
|
||||||
import { TUserDALFactory } from "../user/user-dal";
|
import { TUserDALFactory } from "../user/user-dal";
|
||||||
import { TIncidentContactsDALFactory } from "./incident-contacts-dal";
|
import { TIncidentContactsDALFactory } from "./incident-contacts-dal";
|
||||||
import { TOrgBotDALFactory } from "./org-bot-dal";
|
|
||||||
import { TOrgDALFactory } from "./org-dal";
|
import { TOrgDALFactory } from "./org-dal";
|
||||||
import { deleteOrgMembershipsFn } from "./org-fns";
|
import { deleteOrgMembershipsFn } from "./org-fns";
|
||||||
import {
|
import {
|
||||||
@@ -82,7 +81,6 @@ type TOrgServiceFactoryDep = {
|
|||||||
secretV2BridgeDAL: Pick<TSecretV2BridgeDALFactory, "find">;
|
secretV2BridgeDAL: Pick<TSecretV2BridgeDALFactory, "find">;
|
||||||
folderDAL: Pick<TSecretFolderDALFactory, "findByProjectId">;
|
folderDAL: Pick<TSecretFolderDALFactory, "findByProjectId">;
|
||||||
orgDAL: TOrgDALFactory;
|
orgDAL: TOrgDALFactory;
|
||||||
orgBotDAL: TOrgBotDALFactory;
|
|
||||||
roleDAL: TRoleDALFactory;
|
roleDAL: TRoleDALFactory;
|
||||||
userDAL: TUserDALFactory;
|
userDAL: TUserDALFactory;
|
||||||
groupDAL: TGroupDALFactory;
|
groupDAL: TGroupDALFactory;
|
||||||
@@ -136,7 +134,6 @@ export const orgServiceFactory = ({
|
|||||||
projectKeyDAL,
|
projectKeyDAL,
|
||||||
orgMembershipDAL,
|
orgMembershipDAL,
|
||||||
tokenService,
|
tokenService,
|
||||||
orgBotDAL,
|
|
||||||
licenseService,
|
licenseService,
|
||||||
samlConfigDAL,
|
samlConfigDAL,
|
||||||
oidcConfigDAL,
|
oidcConfigDAL,
|
||||||
@@ -612,23 +609,6 @@ export const orgServiceFactory = ({
|
|||||||
},
|
},
|
||||||
trx?: Knex
|
trx?: Knex
|
||||||
) => {
|
) => {
|
||||||
const { privateKey, publicKey } = await crypto.encryption().asymmetric().generateKeyPair();
|
|
||||||
const key = crypto.randomBytes(32).toString("base64");
|
|
||||||
const {
|
|
||||||
ciphertext: encryptedPrivateKey,
|
|
||||||
iv: privateKeyIV,
|
|
||||||
tag: privateKeyTag,
|
|
||||||
encoding: privateKeyKeyEncoding,
|
|
||||||
algorithm: privateKeyAlgorithm
|
|
||||||
} = crypto.encryption().symmetric().encryptWithRootEncryptionKey(privateKey);
|
|
||||||
const {
|
|
||||||
ciphertext: encryptedSymmetricKey,
|
|
||||||
iv: symmetricKeyIV,
|
|
||||||
tag: symmetricKeyTag,
|
|
||||||
encoding: symmetricKeyKeyEncoding,
|
|
||||||
algorithm: symmetricKeyAlgorithm
|
|
||||||
} = crypto.encryption().symmetric().encryptWithRootEncryptionKey(key);
|
|
||||||
|
|
||||||
const customerId = await licenseService.generateOrgCustomerId(orgName, userEmail);
|
const customerId = await licenseService.generateOrgCustomerId(orgName, userEmail);
|
||||||
|
|
||||||
const createOrg = async (tx: Knex) => {
|
const createOrg = async (tx: Knex) => {
|
||||||
@@ -656,24 +636,7 @@ export const orgServiceFactory = ({
|
|||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
await orgBotDAL.create(
|
|
||||||
{
|
|
||||||
name: org.name,
|
|
||||||
publicKey,
|
|
||||||
privateKeyIV,
|
|
||||||
encryptedPrivateKey,
|
|
||||||
symmetricKeyIV,
|
|
||||||
symmetricKeyTag,
|
|
||||||
encryptedSymmetricKey,
|
|
||||||
symmetricKeyAlgorithm,
|
|
||||||
orgId: org.id,
|
|
||||||
privateKeyTag,
|
|
||||||
privateKeyAlgorithm,
|
|
||||||
privateKeyKeyEncoding,
|
|
||||||
symmetricKeyKeyEncoding
|
|
||||||
},
|
|
||||||
tx
|
|
||||||
);
|
|
||||||
return org;
|
return org;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ services:
|
|||||||
- TELEMETRY_ENABLED=false
|
- TELEMETRY_ENABLED=false
|
||||||
volumes:
|
volumes:
|
||||||
- ./backend/src:/app/src
|
- ./backend/src:/app/src
|
||||||
|
- softhsm_tokens:/etc/softhsm2/tokens # SoftHSM tokens are stored in a volume to persist across container restarts
|
||||||
extra_hosts:
|
extra_hosts:
|
||||||
- "host.docker.internal:host-gateway"
|
- "host.docker.internal:host-gateway"
|
||||||
|
|
||||||
@@ -198,3 +199,5 @@ volumes:
|
|||||||
ldap_data:
|
ldap_data:
|
||||||
ldap_config:
|
ldap_config:
|
||||||
grafana_storage:
|
grafana_storage:
|
||||||
|
softhsm_tokens:
|
||||||
|
driver: local
|
||||||
Reference in New Issue
Block a user