mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-14 13:22:08 +00:00
feat: changed webhook and dynamic secret change to migration mode, resolved snapshot deletion issue in update
This commit is contained in:
@@ -575,6 +575,7 @@ export const kmsServiceFactory = ({
|
||||
|
||||
// by keeping the decrypted data key in inner scope
|
||||
// none of the entities outside can interact directly or expose the data key
|
||||
// NOTICE: If changing here update migrations/utils/kms
|
||||
const createCipherPairWithDataKey = async (encryptionContext: TEncryptWithKmsDataKeyDTO) => {
|
||||
const dataKey = await $getDataKey(encryptionContext);
|
||||
const cipher = symmetricCipherService(SymmetricEncryption.AES_GCM_256);
|
||||
@@ -753,6 +754,7 @@ export const kmsServiceFactory = ({
|
||||
return { id, slug, orgId, isExternal };
|
||||
};
|
||||
|
||||
// akhilmhdh: a copy of this is made in migrations/utils/kms
|
||||
const startService = async () => {
|
||||
const appCfg = getConfig();
|
||||
// This will switch to a seal process and HMS flow in future
|
||||
|
||||
@@ -36,8 +36,7 @@ export const getBotKeyFnFactory = (
|
||||
if (!bot || !bot.isActive || !bot.encryptedProjectKey || !bot.encryptedProjectKeyNonce) {
|
||||
// trying to set bot automatically
|
||||
const projectV1Keys = await projectBotDAL.findProjectUserWorkspaceKey(projectId);
|
||||
if (!projectV1Keys)
|
||||
throw new BadRequestError({ message: "Bot not found. [no-private-key]. Please ask admin user to login" });
|
||||
if (!projectV1Keys) throw new BadRequestError({ message: "Bot not found. Please ask admin user to login" });
|
||||
|
||||
let userPrivateKey = "";
|
||||
if (
|
||||
|
||||
@@ -1,21 +1,13 @@
|
||||
/* eslint-disable no-await-in-loop */
|
||||
import { AxiosError } from "axios";
|
||||
|
||||
import {
|
||||
ProjectUpgradeStatus,
|
||||
ProjectVersion,
|
||||
SecretKeyEncoding,
|
||||
TSecretSnapshotSecretsV2,
|
||||
TSecretVersionsV2
|
||||
} from "@app/db/schemas";
|
||||
import { TDynamicSecretDALFactory } from "@app/ee/services/dynamic-secret/dynamic-secret-dal";
|
||||
import { ProjectUpgradeStatus, ProjectVersion, TSecretSnapshotSecretsV2, TSecretVersionsV2 } from "@app/db/schemas";
|
||||
import { TSecretApprovalRequestDALFactory } from "@app/ee/services/secret-approval-request/secret-approval-request-dal";
|
||||
import { TSecretRotationDALFactory } from "@app/ee/services/secret-rotation/secret-rotation-dal";
|
||||
import { TSnapshotDALFactory } from "@app/ee/services/secret-snapshot/snapshot-dal";
|
||||
import { TSnapshotSecretV2DALFactory } from "@app/ee/services/secret-snapshot/snapshot-secret-v2-dal";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { decryptSymmetric128BitHexKeyUTF8 } from "@app/lib/crypto";
|
||||
import { infisicalSymmetricDecrypt } from "@app/lib/crypto/encryption";
|
||||
import { daysToMillisecond, secondsToMillis } from "@app/lib/dates";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { groupBy, isSamePath, unique } from "@app/lib/fn";
|
||||
@@ -68,7 +60,7 @@ type TSecretQueueFactoryDep = {
|
||||
folderDAL: TSecretFolderDALFactory;
|
||||
secretDAL: TSecretDALFactory;
|
||||
secretImportDAL: Pick<TSecretImportDALFactory, "find" | "findByFolderIds">;
|
||||
webhookDAL: Pick<TWebhookDALFactory, "findAllWebhooks" | "find" | "transaction" | "update" | "bulkUpdate" | "upsert">;
|
||||
webhookDAL: Pick<TWebhookDALFactory, "findAllWebhooks" | "transaction" | "update" | "bulkUpdate">;
|
||||
projectEnvDAL: Pick<TProjectEnvDALFactory, "findOne" | "find">;
|
||||
projectDAL: TProjectDALFactory;
|
||||
projectBotDAL: TProjectBotDALFactory;
|
||||
@@ -87,7 +79,6 @@ type TSecretQueueFactoryDep = {
|
||||
secretApprovalRequestDAL: Pick<TSecretApprovalRequestDALFactory, "deleteByProjectId">;
|
||||
snapshotDAL: Pick<TSnapshotDALFactory, "findNSecretV1SnapshotByFolderId" | "deleteSnapshotsAboveLimit">;
|
||||
snapshotSecretV2BridgeDAL: Pick<TSnapshotSecretV2DALFactory, "insertMany">;
|
||||
dynamicSecretDAL: Pick<TDynamicSecretDALFactory, "find" | "upsert">;
|
||||
};
|
||||
|
||||
export type TGetSecrets = {
|
||||
@@ -131,8 +122,7 @@ export const secretQueueFactory = ({
|
||||
secretRotationDAL,
|
||||
snapshotDAL,
|
||||
snapshotSecretV2BridgeDAL,
|
||||
secretApprovalRequestDAL,
|
||||
dynamicSecretDAL
|
||||
secretApprovalRequestDAL
|
||||
}: TSecretQueueFactoryDep) => {
|
||||
const removeSecretReminder = async (dto: TRemoveSecretReminderDTO) => {
|
||||
const appCfg = getConfig();
|
||||
@@ -891,38 +881,7 @@ export const secretQueueFactory = ({
|
||||
await secretV2BridgeDAL.upsertSecretReferences(secretReferences, tx);
|
||||
}
|
||||
|
||||
const dynamicSecrets = await dynamicSecretDAL.find({ folderId }, { tx });
|
||||
if (dynamicSecrets.length) {
|
||||
await dynamicSecretDAL.upsert(
|
||||
dynamicSecrets.map((el) => {
|
||||
let { encryptedConfig } = el;
|
||||
if (!encryptedConfig) {
|
||||
if (el.keyEncoding && el.inputCiphertext && el.inputTag && el.inputIV) {
|
||||
const decryptedConfig = infisicalSymmetricDecrypt({
|
||||
keyEncoding: el.keyEncoding as SecretKeyEncoding,
|
||||
ciphertext: el.inputCiphertext,
|
||||
tag: el.inputTag,
|
||||
iv: el.inputIV
|
||||
});
|
||||
encryptedConfig = secretManagerEncryptor({ plainText: Buffer.from(decryptedConfig) }).cipherTextBlob;
|
||||
}
|
||||
}
|
||||
return {
|
||||
...el,
|
||||
encryptedConfig,
|
||||
keyEncoding: null,
|
||||
inputCiphertext: null,
|
||||
inputTag: null,
|
||||
inputIV: null,
|
||||
algorithm: null
|
||||
};
|
||||
}),
|
||||
"id",
|
||||
tx
|
||||
);
|
||||
}
|
||||
|
||||
const SNAPSHOT_BATCH_SIZE = 15;
|
||||
const SNAPSHOT_BATCH_SIZE = 10;
|
||||
const snapshots = await snapshotDAL.findNSecretV1SnapshotByFolderId(folderId, SNAPSHOT_BATCH_SIZE, tx);
|
||||
const projectV3SecretVersionsGroupById: Record<string, TSecretVersionsV2> = {};
|
||||
const projectV3SecretVersionTags: { secret_versions_v2Id: string; secret_tagsId: string }[] = [];
|
||||
@@ -1151,71 +1110,6 @@ export const secretQueueFactory = ({
|
||||
tx
|
||||
);
|
||||
|
||||
/*
|
||||
* webhooks
|
||||
* */
|
||||
const projectV1Webhooks = await webhookDAL.find({ projectId }, tx);
|
||||
if (projectV1Webhooks.length) {
|
||||
await webhookDAL.upsert(
|
||||
projectV1Webhooks.map((el) => {
|
||||
let { encryptedSecretKeyWithKms, encryptedUrl } = el;
|
||||
if (!encryptedSecretKeyWithKms) {
|
||||
if (el.encryptedSecretKey && el.iv && el.tag) {
|
||||
const webhookSecretKey = infisicalSymmetricDecrypt({
|
||||
keyEncoding: el.keyEncoding as SecretKeyEncoding,
|
||||
ciphertext: el.encryptedSecretKey,
|
||||
iv: el.iv,
|
||||
tag: el.tag
|
||||
});
|
||||
encryptedSecretKeyWithKms = secretManagerEncryptor({
|
||||
plainText: Buffer.from(webhookSecretKey)
|
||||
}).cipherTextBlob;
|
||||
}
|
||||
}
|
||||
if (!encryptedUrl) {
|
||||
if (el.urlTag && el.urlCipherText && el.urlIV) {
|
||||
const webhookUrl = infisicalSymmetricDecrypt({
|
||||
keyEncoding: el.keyEncoding as SecretKeyEncoding,
|
||||
ciphertext: el.urlCipherText,
|
||||
iv: el.urlIV,
|
||||
tag: el.urlTag
|
||||
});
|
||||
encryptedUrl = secretManagerEncryptor({
|
||||
plainText: Buffer.from(webhookUrl)
|
||||
}).cipherTextBlob;
|
||||
} else {
|
||||
encryptedUrl = secretManagerEncryptor({
|
||||
plainText: Buffer.from(el.url)
|
||||
}).cipherTextBlob;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
id: el.id,
|
||||
url: el.url,
|
||||
envId: el.envId,
|
||||
type: el.type,
|
||||
isDisabled: el.isDisabled,
|
||||
lastStatus: el.lastStatus,
|
||||
secretPath: el.secretPath,
|
||||
lastRunErrorMessage: el.lastRunErrorMessage,
|
||||
encryptedSecretKeyWithKms,
|
||||
encryptedUrl,
|
||||
urlCipherText: null,
|
||||
urlIV: null,
|
||||
urlTag: null,
|
||||
encryptedSecretKey: null,
|
||||
iv: null,
|
||||
tag: null,
|
||||
keyEncoding: null,
|
||||
algorithm: null
|
||||
};
|
||||
}),
|
||||
"id",
|
||||
tx
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* approvals: we will delete all approvals this is because some secret versions may not be added yet
|
||||
* Thus doesn't make sense for rest to be there
|
||||
|
||||
@@ -3,9 +3,7 @@ import crypto from "node:crypto";
|
||||
import { AxiosError } from "axios";
|
||||
import picomatch from "picomatch";
|
||||
|
||||
import { SecretKeyEncoding } from "@app/db/schemas";
|
||||
import { request } from "@app/lib/config/request";
|
||||
import { infisicalSymmetricDecrypt } from "@app/lib/crypto/encryption";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { logger } from "@app/lib/logger";
|
||||
|
||||
@@ -127,28 +125,10 @@ export const fnTriggerWebhook = async ({
|
||||
|
||||
const webhooksTriggered = await Promise.allSettled(
|
||||
toBeTriggeredHooks.map((hook) => {
|
||||
let webhookUrl = hook.url;
|
||||
let webhookSecretKey;
|
||||
if (hook.urlTag && hook.urlCipherText && hook.urlIV) {
|
||||
webhookUrl = infisicalSymmetricDecrypt({
|
||||
keyEncoding: hook.keyEncoding as SecretKeyEncoding,
|
||||
ciphertext: hook.urlCipherText,
|
||||
iv: hook.urlIV,
|
||||
tag: hook.urlTag
|
||||
});
|
||||
} else if (hook.encryptedUrl) {
|
||||
webhookUrl = kmsDataKeyDecryptor({ cipherTextBlob: hook.encryptedUrl }).toString();
|
||||
}
|
||||
if (hook.encryptedSecretKey && hook.iv && hook.tag) {
|
||||
webhookSecretKey = infisicalSymmetricDecrypt({
|
||||
keyEncoding: hook.keyEncoding as SecretKeyEncoding,
|
||||
ciphertext: hook.encryptedSecretKey,
|
||||
iv: hook.iv,
|
||||
tag: hook.tag
|
||||
});
|
||||
} else if (hook.encryptedSecretKeyWithKms) {
|
||||
webhookSecretKey = kmsDataKeyDecryptor({ cipherTextBlob: hook.encryptedSecretKeyWithKms }).toString();
|
||||
}
|
||||
const webhookUrl = kmsDataKeyDecryptor({ cipherTextBlob: hook.encryptedUrl }).toString();
|
||||
const webhookSecretKey = hook.encryptedSecretKeyWithKms
|
||||
? kmsDataKeyDecryptor({ cipherTextBlob: hook.encryptedSecretKeyWithKms }).toString()
|
||||
: undefined;
|
||||
|
||||
return triggerWebhookRequest(
|
||||
{ webhookUrl, webhookSecretKey },
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
|
||||
import { SecretKeyEncoding, TWebhooksInsert } from "@app/db/schemas";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||
import { infisicalSymmetricDecrypt } from "@app/lib/crypto/encryption";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
|
||||
import { TKmsServiceFactory } from "../kms/kms-service";
|
||||
@@ -60,34 +58,28 @@ export const webhookServiceFactory = ({
|
||||
const env = await projectEnvDAL.findOne({ projectId, slug: environment });
|
||||
if (!env) throw new BadRequestError({ message: "Env not found" });
|
||||
|
||||
const insertDoc: TWebhooksInsert = {
|
||||
url: "", // deprecated - we are moving away from plaintext URLs
|
||||
envId: env.id,
|
||||
isDisabled: false,
|
||||
secretPath: secretPath || "/",
|
||||
type
|
||||
};
|
||||
|
||||
const { encryptor: secretManagerEncryptor } = await kmsService.createCipherPairWithDataKey({
|
||||
projectId,
|
||||
type: KmsDataKey.SecretManager
|
||||
});
|
||||
if (webhookSecretKey) {
|
||||
const encryptedSecretKeyWithKms = secretManagerEncryptor({
|
||||
plainText: Buffer.from(webhookSecretKey)
|
||||
}).cipherTextBlob;
|
||||
insertDoc.encryptedSecretKeyWithKms = encryptedSecretKeyWithKms;
|
||||
}
|
||||
|
||||
if (webhookUrl) {
|
||||
const encryptedUrl = secretManagerEncryptor({
|
||||
plainText: Buffer.from(webhookUrl)
|
||||
}).cipherTextBlob;
|
||||
const encryptedSecretKeyWithKms = webhookSecretKey
|
||||
? secretManagerEncryptor({
|
||||
plainText: Buffer.from(webhookSecretKey)
|
||||
}).cipherTextBlob
|
||||
: null;
|
||||
const encryptedUrl = secretManagerEncryptor({
|
||||
plainText: Buffer.from(webhookUrl)
|
||||
}).cipherTextBlob;
|
||||
|
||||
insertDoc.encryptedUrl = encryptedUrl;
|
||||
}
|
||||
|
||||
const webhook = await webhookDAL.create(insertDoc);
|
||||
const webhook = await webhookDAL.create({
|
||||
encryptedUrl,
|
||||
encryptedSecretKeyWithKms,
|
||||
envId: env.id,
|
||||
isDisabled: false,
|
||||
secretPath: secretPath || "/",
|
||||
type
|
||||
});
|
||||
return { ...webhook, projectId, environment: env };
|
||||
};
|
||||
|
||||
@@ -145,28 +137,11 @@ export const webhookServiceFactory = ({
|
||||
projectId: project.id,
|
||||
type: KmsDataKey.SecretManager
|
||||
});
|
||||
let webhookUrl = webhook.url;
|
||||
let webhookSecretKey;
|
||||
if (webhook.urlTag && webhook.urlCipherText && webhook.urlIV) {
|
||||
webhookUrl = infisicalSymmetricDecrypt({
|
||||
keyEncoding: webhook.keyEncoding as SecretKeyEncoding,
|
||||
ciphertext: webhook.urlCipherText,
|
||||
iv: webhook.urlIV,
|
||||
tag: webhook.urlTag
|
||||
});
|
||||
} else if (webhook.encryptedUrl) {
|
||||
webhookUrl = kmsDataKeyDecryptor({ cipherTextBlob: webhook.encryptedUrl }).toString();
|
||||
}
|
||||
if (webhook.encryptedSecretKey && webhook.iv && webhook.tag) {
|
||||
webhookSecretKey = infisicalSymmetricDecrypt({
|
||||
keyEncoding: webhook.keyEncoding as SecretKeyEncoding,
|
||||
ciphertext: webhook.encryptedSecretKey,
|
||||
iv: webhook.iv,
|
||||
tag: webhook.tag
|
||||
});
|
||||
} else if (webhook.encryptedSecretKeyWithKms) {
|
||||
webhookSecretKey = kmsDataKeyDecryptor({ cipherTextBlob: webhook.encryptedSecretKeyWithKms }).toString();
|
||||
}
|
||||
const webhookUrl = kmsDataKeyDecryptor({ cipherTextBlob: webhook.encryptedUrl }).toString();
|
||||
const webhookSecretKey = webhook.encryptedSecretKeyWithKms
|
||||
? kmsDataKeyDecryptor({ cipherTextBlob: webhook.encryptedSecretKeyWithKms }).toString()
|
||||
: undefined;
|
||||
|
||||
try {
|
||||
await triggerWebhookRequest(
|
||||
{ webhookUrl, webhookSecretKey },
|
||||
@@ -213,17 +188,7 @@ export const webhookServiceFactory = ({
|
||||
projectId
|
||||
});
|
||||
return webhooks.map((w) => {
|
||||
let decryptedUrl = w.url;
|
||||
if (w.urlTag && w.urlCipherText && w.urlIV) {
|
||||
decryptedUrl = infisicalSymmetricDecrypt({
|
||||
keyEncoding: w.keyEncoding as SecretKeyEncoding,
|
||||
ciphertext: w.urlCipherText,
|
||||
iv: w.urlIV,
|
||||
tag: w.urlTag
|
||||
});
|
||||
} else if (w.encryptedUrl) {
|
||||
decryptedUrl = kmsDataKeyDecryptor({ cipherTextBlob: w.encryptedUrl }).toString();
|
||||
}
|
||||
const decryptedUrl = kmsDataKeyDecryptor({ cipherTextBlob: w.encryptedUrl }).toString();
|
||||
return {
|
||||
...w,
|
||||
url: decryptedUrl
|
||||
|
||||
Reference in New Issue
Block a user