fix: removed recovery

This commit is contained in:
Daniel Hougaard
2024-11-03 22:13:45 +04:00
parent 472f02e8b1
commit a3ec1a27de
19 changed files with 34 additions and 591 deletions

View File

@@ -1,8 +1,6 @@
export {
useAdminDeleteUser,
useCreateAdminUser,
useExportServerDecryptionKey,
useImportServerDecryptionKey,
useUpdateAdminSlackConfig,
useUpdateServerConfig,
useUpdateServerEncryptionStrategy

View File

@@ -98,24 +98,3 @@ export const useUpdateServerEncryptionStrategy = () => {
}
});
};
export const useExportServerDecryptionKey = () => {
return useMutation({
mutationFn: async () => {
const { data } = await apiRequest.post<{ secretParts: string[] }>("/api/v1/admin/kms-export");
return data.secretParts;
}
});
};
export const useImportServerDecryptionKey = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (secretParts: string[]) => {
await apiRequest.post("/api/v1/admin/kms-import", { secretParts });
},
onSuccess: () => {
queryClient.invalidateQueries(adminQueryKeys.serverConfig());
}
});
};

View File

@@ -61,7 +61,6 @@ export type TGetServerRootKmsEncryptionDetails = {
enabled: boolean;
name: string;
}[];
keyExported: boolean;
};
export enum RootKeyEncryptionStrategy {

View File

@@ -23,6 +23,7 @@ export type SubscriptionPlan = {
workspacesUsed: number;
environmentLimit: number;
samlSSO: boolean;
hsm: boolean;
oidcSSO: boolean;
scim: boolean;
ldap: boolean;

View File

@@ -1,12 +1,11 @@
import { useCallback } from "react";
import { Controller, useForm } from "react-hook-form";
import { faExclamationCircle } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { createNotification } from "@app/components/notifications";
import { Button, FormControl, Modal, Select, SelectItem, Tooltip } from "@app/components/v2";
import { Button, FormControl, Select, SelectItem, UpgradePlanModal } from "@app/components/v2";
import { useSubscription } from "@app/context";
import { usePopUp } from "@app/hooks";
import { useUpdateServerEncryptionStrategy } from "@app/hooks/api";
import {
@@ -14,9 +13,6 @@ import {
TGetServerRootKmsEncryptionDetails
} from "@app/hooks/api/admin/types";
import { ExportRootKmsKeyModalContent } from "./components/ExportRootKmsKeyModalContent";
import { RestoreRootKmsKeyModalContent } from "./components/RestoreRootKmsKeyModalContent";
const formSchema = z.object({
encryptionStrategy: z.nativeEnum(RootKeyEncryptionStrategy)
});
@@ -29,10 +25,9 @@ type Props = {
export const EncryptionPanel = ({ rootKmsDetails }: Props) => {
const { mutateAsync: updateEncryptionStrategy } = useUpdateServerEncryptionStrategy();
const { handlePopUpToggle, handlePopUpOpen, popUp } = usePopUp([
"exportKey",
"restoreKey"
] as const);
const { subscription } = useSubscription();
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp(["upgradePlan"] as const);
const {
control,
@@ -48,16 +43,18 @@ export const EncryptionPanel = ({ rootKmsDetails }: Props) => {
});
const onSubmit = useCallback(async (formData: TForm) => {
if (!subscription) return;
if (!subscription.hsm) {
handlePopUpOpen("upgradePlan", {
description: "Hardware Security Module's (HSM's), are only available on Enterprise plans."
});
return;
}
try {
await updateEncryptionStrategy(formData.encryptionStrategy);
if (
!rootKmsDetails.keyExported &&
formData.encryptionStrategy !== RootKeyEncryptionStrategy.Basic
) {
handlePopUpOpen("exportKey");
}
createNotification({
type: "success",
text: "Encryption strategy updated successfully"
@@ -81,50 +78,6 @@ export const EncryptionPanel = ({ rootKmsDetails }: Props) => {
<div className="mb-2 text-xl font-semibold text-mineshaft-100">
KMS Encryption Strategy
</div>
<Tooltip
content={
<div>
{!rootKmsDetails.keyExported && (
<div className="mb-2 text-sm">
<FontAwesomeIcon icon={faExclamationCircle} className="mr-1 text-red-500" />
You have not exported the KMS root encryption key. Switch to HSM encryption or
run the{" "}
<code>
<span className="mt-2 rounded-md bg-mineshaft-600 p-1 text-xs text-primary-500">
infisical kms export
</span>
</code>{" "}
CLI command to export the key parts.
</div>
)}
<br />
If you experience issues with accessing projects while not using Regular
Encryption (default), you can restore the KMS root encryption key by using your
exported key parts.
<br /> <br />
If you do not have the exported key parts, you can export them by using the CLI
command
<br />
<code>
<span className="mt-2 rounded-md bg-mineshaft-600 p-1 text-xs text-primary-500">
infisical kms export
</span>
</code>
. <br />
<br />
<span className="font-bold">
Please keep in mind that you can only export the key parts once.
</span>
</div>
}
>
<Button
isDisabled={!rootKmsDetails.keyExported}
onClick={() => handlePopUpToggle("restoreKey", true)}
>
Restore Root KMS Encryption Key
</Button>
</Tooltip>
</div>
<div className="mb-4 max-w-sm text-sm text-mineshaft-400">
Select which type of encryption strategy you want to use for your KMS root key. HSM is
@@ -163,20 +116,11 @@ export const EncryptionPanel = ({ rootKmsDetails }: Props) => {
Save
</Button>
</form>
<Modal
isOpen={popUp.exportKey.isOpen}
onOpenChange={(state) => handlePopUpToggle("exportKey", state)}
>
<ExportRootKmsKeyModalContent handlePopUpToggle={handlePopUpToggle} />
</Modal>
<Modal
isOpen={popUp.restoreKey.isOpen}
onOpenChange={(state) => handlePopUpToggle("restoreKey", state)}
>
<RestoreRootKmsKeyModalContent handlePopUpToggle={handlePopUpToggle} />
</Modal>
<UpgradePlanModal
isOpen={popUp.upgradePlan.isOpen}
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
text={(popUp.upgradePlan?.data as { description: string })?.description}
/>
</>
);
};

View File

@@ -1,53 +0,0 @@
import { useCallback, useState } from "react";
import { Button, ModalContent } from "@app/components/v2";
import { useExportServerDecryptionKey } from "@app/hooks/api";
import { useFileDownload } from "@app/hooks/useFileDownload";
import { UsePopUpState } from "@app/hooks/usePopUp";
type Props = {
handlePopUpToggle: (popUpName: keyof UsePopUpState<["exportKey"]>, state?: boolean) => void;
};
export const ExportRootKmsKeyModalContent = ({ handlePopUpToggle }: Props) => {
const { mutateAsync: exportKey, isLoading } = useExportServerDecryptionKey();
const downloadFile = useFileDownload();
const [downloaded, setDownloaded] = useState(false);
const onExport = useCallback(async () => {
const keyParts = await exportKey();
downloadFile(keyParts.join("\n\n"), "infisical-encryption-key-parts.txt");
setDownloaded(true);
}, []);
return (
<ModalContent
title="Export Root KMS Encryption Key"
subTitle="We highly recommend exporting the KMS root encryption key and storing it in a secure location. Incase of a disaster, you can use our CLI to recover your projects with zero loss."
>
<div className="flex w-full justify-end">
{!downloaded ? (
<>
<Button
variant="plain"
colorSchema="secondary"
onClick={() => handlePopUpToggle("exportKey", false)}
>
Close
</Button>
<Button isLoading={isLoading} className="ml-2" onClick={onExport}>
Download Key
</Button>
</>
) : (
<div className="flex max-w-fit flex-col overflow-clip break-words px-2 text-sm font-normal text-gray-400">
The key parts have been downloaded. Please store them in a safe place. You will need
these keys incase you need to recovery the KMS root encryption key. Please consult our
documentation for further instructions.
</div>
)}
</div>
</ModalContent>
);
};

View File

@@ -1,98 +0,0 @@
import { useMemo } from "react";
import { Controller, useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { createNotification } from "@app/components/notifications";
import { Button, FormControl, Input, ModalContent } from "@app/components/v2";
import { useImportServerDecryptionKey } from "@app/hooks/api";
import { UsePopUpState } from "@app/hooks/usePopUp";
type Props = {
handlePopUpToggle: (popUpName: keyof UsePopUpState<["restoreKey"]>, state?: boolean) => void;
};
const formSchema = z.object({
keyParts: z
.array(z.string())
.refine((data) => data.length === 4 && data.every((part) => part.length > 0), {
message: "Enter at least 4 key parts in order to restore the KMS root decryption key."
})
});
type TForm = z.infer<typeof formSchema>;
export const RestoreRootKmsKeyModalContent = ({ handlePopUpToggle }: Props) => {
const { mutateAsync: importKmsRootKey } = useImportServerDecryptionKey();
const {
control,
handleSubmit,
watch,
formState: { isSubmitting, errors, isLoading, isValid }
} = useForm<TForm>({
resolver: zodResolver(formSchema),
values: {
keyParts: ["", "", "", ""]
}
});
const keyParts = useMemo(() => watch("keyParts"), []);
return (
<ModalContent
title="Export Root KMS Encryption Key"
subTitle="Recover the KMS root encryption key by entering the key parts. You can recover the key if you have 4 out of 8 key parts."
footerContent={
<div className="flex w-full justify-end">
<Button
variant="plain"
colorSchema="secondary"
onClick={() => handlePopUpToggle("restoreKey", false)}
>
Close
</Button>
<Button
isDisabled={!!errors.keyParts || !isValid}
isLoading={isSubmitting || isLoading}
className="ml-2"
onClick={handleSubmit(async (data) => {
await importKmsRootKey(data.keyParts);
createNotification({
type: "success",
title: "Successfully restored KMS root key",
text: "The KMS root key has been successfully restored."
});
handlePopUpToggle("restoreKey", false);
})}
>
Restore Key
</Button>
</div>
}
>
<form>
<div className="flex w-full flex-col justify-end">
{keyParts.map((_, index) => (
<Controller
key={`key-part-${index + 1}`}
name={`keyParts.${index}`}
control={control}
render={({ field }) => (
<div>
<FormControl label={`Key Part ${index + 1}`}>
<Input {...field} placeholder={`Enter key part ${index + 1}`} />
</FormControl>
</div>
)}
/>
))}
{errors.keyParts && (
<div className="mt-2 text-sm font-normal text-red-500">{errors.keyParts.message}</div>
)}
</div>
</form>
</ModalContent>
);
};