mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
misc: developed create kms backup feature
This commit is contained in:
@@ -254,4 +254,36 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/:workspaceId/kms/backup",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
workspaceId: z.string().trim()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
secretManager: z.string()
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const backup = await server.services.project.getProjectKmsBackup({
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
projectId: req.params.workspaceId
|
||||
});
|
||||
|
||||
// TODO: add audit log
|
||||
|
||||
return backup;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -585,6 +585,29 @@ export const kmsServiceFactory = ({
|
||||
});
|
||||
};
|
||||
|
||||
const getProjectKeyBackup = async (projectId: string) => {
|
||||
const project = await projectDAL.findById(projectId);
|
||||
if (!project) {
|
||||
throw new NotFoundError({
|
||||
message: "Project not found"
|
||||
});
|
||||
}
|
||||
|
||||
const secretManagerDataKey = await getProjectSecretManagerKmsDataKey(projectId);
|
||||
const kmsKeyIdForEncrypt = await getOrgKmsKeyId(project.orgId);
|
||||
const kmsEncryptor = await encryptWithKmsKey({ kmsId: kmsKeyIdForEncrypt });
|
||||
const { cipherTextBlob: encryptedSecretManagerDataKey } = await kmsEncryptor({ plainText: secretManagerDataKey });
|
||||
|
||||
// format: projectId.kmsFunction.kmsId.Base64(encryptedDataKey)
|
||||
const secretManagerBackup = `${projectId}.secretManager.${kmsKeyIdForEncrypt}.${encryptedSecretManagerDataKey.toString(
|
||||
"base64"
|
||||
)}`;
|
||||
|
||||
return {
|
||||
secretManager: secretManagerBackup
|
||||
};
|
||||
};
|
||||
|
||||
const startService = async () => {
|
||||
const appCfg = getConfig();
|
||||
// This will switch to a seal process and HMS flow in future
|
||||
@@ -642,6 +665,7 @@ export const kmsServiceFactory = ({
|
||||
getOrgKmsDataKey,
|
||||
getProjectSecretManagerKmsDataKey,
|
||||
getProjectSecretManagerKmsKey,
|
||||
updateProjectSecretManagerKmsKey
|
||||
updateProjectSecretManagerKmsKey,
|
||||
getProjectKeyBackup
|
||||
};
|
||||
};
|
||||
|
||||
@@ -78,7 +78,7 @@ type TProjectServiceFactoryDep = {
|
||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||
orgDAL: Pick<TOrgDALFactory, "findOne">;
|
||||
keyStore: Pick<TKeyStoreFactory, "deleteItem">;
|
||||
kmsService: Pick<TKmsServiceFactory, "updateProjectSecretManagerKmsKey">;
|
||||
kmsService: Pick<TKmsServiceFactory, "updateProjectSecretManagerKmsKey" | "getProjectKeyBackup">;
|
||||
};
|
||||
|
||||
export type TProjectServiceFactory = ReturnType<typeof projectServiceFactory>;
|
||||
@@ -693,6 +693,34 @@ export const projectServiceFactory = ({
|
||||
};
|
||||
};
|
||||
|
||||
const getProjectKmsBackup = async ({
|
||||
projectId,
|
||||
actor,
|
||||
actorId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
}: TProjectPermission) => {
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Kms);
|
||||
|
||||
const plan = await licenseService.getPlan(actorOrgId);
|
||||
if (!plan.externalKms) {
|
||||
throw new BadRequestError({
|
||||
message: "Failed to create KMS backup due to plan restriction. Upgrade to the enterprise plan."
|
||||
});
|
||||
}
|
||||
|
||||
const kmsBackup = await kmsService.getProjectKeyBackup(projectId);
|
||||
return kmsBackup;
|
||||
};
|
||||
|
||||
return {
|
||||
createProject,
|
||||
deleteProject,
|
||||
@@ -707,6 +735,7 @@ export const projectServiceFactory = ({
|
||||
listProjectCertificates,
|
||||
updateVersionLimit,
|
||||
updateAuditLogsRetention,
|
||||
updateProjectKmsKey
|
||||
updateProjectKmsKey,
|
||||
getProjectKmsBackup
|
||||
};
|
||||
};
|
||||
|
||||
@@ -53,3 +53,11 @@ export const useGetActiveProjectKms = (projectId: string) => {
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const fetchProjectKmsBackup = async (projectId: string) => {
|
||||
const { data } = await apiRequest.get<{
|
||||
secretManager: string;
|
||||
}>(`/api/v1/workspace/${projectId}/kms/backup`);
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import FileSaver from "file-saver";
|
||||
import { z } from "zod";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
@@ -13,6 +14,7 @@ import {
|
||||
useWorkspace
|
||||
} from "@app/context";
|
||||
import { useGetActiveProjectKms, useGetExternalKmsList, useUpdateProjectKms } from "@app/hooks/api";
|
||||
import { fetchProjectKmsBackup } from "@app/hooks/api/kms/queries";
|
||||
|
||||
const formSchema = z.object({
|
||||
kmsKeyId: z.string()
|
||||
@@ -55,7 +57,7 @@ export const EncryptionTab = () => {
|
||||
}
|
||||
}, [kmsKeyId]);
|
||||
|
||||
const onFormSubmit = async (data: TForm) => {
|
||||
const onUpdateProjectKms = async (data: TForm) => {
|
||||
try {
|
||||
await updateProjectKms({
|
||||
secretManagerKmsKeyId: data.kmsKeyId
|
||||
@@ -70,12 +72,38 @@ export const EncryptionTab = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const downloadKmsBackup = async () => {
|
||||
if (!currentWorkspace || !currentOrg) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { secretManager } = await fetchProjectKmsBackup(currentWorkspace.id);
|
||||
|
||||
const [, kmsFunction] = secretManager.split(".");
|
||||
const file = secretManager;
|
||||
|
||||
const blob = new Blob([file], { type: "text/plain;charset=utf-8" });
|
||||
FileSaver.saveAs(
|
||||
blob,
|
||||
`kms-backup-${currentOrg.slug}-${currentWorkspace.slug}-${kmsFunction}.infisical.txt`
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<form
|
||||
onSubmit={handleSubmit(onFormSubmit)}
|
||||
onSubmit={handleSubmit(onUpdateProjectKms)}
|
||||
className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4"
|
||||
>
|
||||
<h2 className="mb-2 flex-1 text-xl font-semibold text-mineshaft-100">Key Management</h2>
|
||||
<div className="flex justify-between">
|
||||
<h2 className="mb-2 flex-1 text-xl font-semibold text-mineshaft-100">Key Management</h2>
|
||||
<div className="space-x-2">
|
||||
<Button colorSchema="secondary">Load Backup</Button>
|
||||
<Button colorSchema="secondary" onClick={downloadKmsBackup}>
|
||||
Create Backup
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="mb-4 text-gray-400">
|
||||
Select which Key Management System to use for encrypting your project data
|
||||
</p>
|
||||
|
||||
Reference in New Issue
Block a user