feat: optionally include encryption key in web server backups

This commit is contained in:
Mauricio Siu
2026-07-10 03:05:28 -06:00
parent e87a245cdc
commit 2e867c5be1
8 changed files with 8652 additions and 4 deletions

View File

@@ -1,6 +1,7 @@
import {
decryptValue,
encryptValue,
exportEncryptionKey,
isEncrypted,
} from "@dokploy/server/lib/encryption";
import { afterEach, describe, expect, it, vi } from "vitest";
@@ -42,6 +43,10 @@ describe("encryptValue / decryptValue", () => {
const tampered = `${encrypted.slice(0, -4)}AAAA`;
expect(() => decryptValue(tampered)).toThrow(/BETTER_AUTH_SECRET/);
});
it("exports the primary key as 32-byte hex for backups", () => {
expect(exportEncryptionKey()).toMatch(/^[0-9a-f]{64}$/);
});
});
describe("dedicated ENCRYPTION_KEY", () => {

View File

@@ -79,6 +79,7 @@ const Schema = z
schedule: z.string().min(1, "Schedule (Cron) required"),
prefix: z.string().min(1, "Prefix required"),
enabled: z.boolean(),
includeEncryptionKey: z.boolean(),
database: z.string().min(1, "Database required"),
keepLatestCount: z.coerce.number().optional(),
serviceName: z.string().nullable(),
@@ -223,6 +224,7 @@ export const HandleBackup = ({
: "",
destinationId: "",
enabled: true,
includeEncryptionKey: true,
prefix: "/",
schedule: "",
keepLatestCount: undefined,
@@ -262,6 +264,7 @@ export const HandleBackup = ({
: "",
destinationId: backup?.destinationId ?? "",
enabled: backup?.enabled ?? true,
includeEncryptionKey: backup?.includeEncryptionKey ?? true,
prefix: backup?.prefix ?? "/",
schedule: backup?.schedule ?? "",
keepLatestCount: backup?.keepLatestCount ?? undefined,
@@ -309,6 +312,7 @@ export const HandleBackup = ({
prefix: data.prefix,
schedule: data.schedule,
enabled: data.enabled,
includeEncryptionKey: data.includeEncryptionKey,
database: data.database,
keepLatestCount: data.keepLatestCount ?? null,
databaseType: data.databaseType || databaseType,
@@ -665,6 +669,31 @@ export const HandleBackup = ({
</FormItem>
)}
/>
{databaseType === "web-server" && (
<FormField
control={form.control}
name="includeEncryptionKey"
render={({ field }) => (
<FormItem className="flex flex-row items-center justify-between rounded-lg border p-3 ">
<div className="space-y-0.5">
<FormLabel>Include encryption key</FormLabel>
<FormDescription>
Stores the encryption key inside the backup so
environment variables can be restored on a new server.
Anyone with access to the backup file can decrypt
them.
</FormDescription>
</div>
<FormControl>
<Switch
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
</FormItem>
)}
/>
)}
{backupType === "compose" && (
<>
{form.watch("databaseType") === "postgres" && (

View File

@@ -0,0 +1 @@
ALTER TABLE "backup" ADD COLUMN "includeEncryptionKey" boolean DEFAULT true NOT NULL;

File diff suppressed because it is too large Load Diff

View File

@@ -1219,6 +1219,13 @@
"when": 1783494977500,
"tag": "0173_aspiring_annihilus",
"breakpoints": true
},
{
"idx": 174,
"version": "7",
"when": 1783674181297,
"tag": "0174_great_naoko",
"breakpoints": true
}
]
}