fix: force user to provide known hosts values in sftp volume / repository creation
Some checks failed
Release Workflow / determine-release-type (push) Has been cancelled
Release Workflow / checks (push) Has been cancelled
Release Workflow / e2e-tests (push) Has been cancelled
Release Workflow / build-images (push) Has been cancelled
Release Workflow / publish-release (push) Has been cancelled
Some checks failed
Release Workflow / determine-release-type (push) Has been cancelled
Release Workflow / checks (push) Has been cancelled
Release Workflow / e2e-tests (push) Has been cancelled
Release Workflow / build-images (push) Has been cancelled
Release Workflow / publish-release (push) Has been cancelled
This commit is contained in:
parent
aa7eaa0929
commit
3acf565ccd
7 changed files with 49 additions and 25 deletions
|
|
@ -52,16 +52,26 @@ const formBaseFields = {
|
|||
compressionMode: z.enum(COMPRESSION_MODES).optional(),
|
||||
};
|
||||
|
||||
export const formSchema = z.discriminatedUnion("backend", [
|
||||
localRepositoryConfigSchema.extend(formBaseFields),
|
||||
s3RepositoryConfigSchema.extend(formBaseFields),
|
||||
r2RepositoryConfigSchema.extend(formBaseFields),
|
||||
gcsRepositoryConfigSchema.extend(formBaseFields),
|
||||
azureRepositoryConfigSchema.extend(formBaseFields),
|
||||
rcloneRepositoryConfigSchema.extend(formBaseFields),
|
||||
restRepositoryConfigSchema.extend(formBaseFields),
|
||||
sftpRepositoryConfigSchema.extend(formBaseFields),
|
||||
]);
|
||||
export const formSchema = z
|
||||
.discriminatedUnion("backend", [
|
||||
localRepositoryConfigSchema.extend(formBaseFields),
|
||||
s3RepositoryConfigSchema.extend(formBaseFields),
|
||||
r2RepositoryConfigSchema.extend(formBaseFields),
|
||||
gcsRepositoryConfigSchema.extend(formBaseFields),
|
||||
azureRepositoryConfigSchema.extend(formBaseFields),
|
||||
rcloneRepositoryConfigSchema.extend(formBaseFields),
|
||||
restRepositoryConfigSchema.extend(formBaseFields),
|
||||
sftpRepositoryConfigSchema.extend(formBaseFields),
|
||||
])
|
||||
.superRefine((value, ctx) => {
|
||||
if (value.backend === "sftp" && !value.skipHostKeyCheck && !value.knownHosts?.trim()) {
|
||||
ctx.addIssue({
|
||||
code: "custom",
|
||||
message: "Known hosts are required unless host key verification is skipped",
|
||||
path: ["knownHosts"],
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export type RepositoryFormValues = z.input<typeof formSchema>;
|
||||
|
||||
|
|
|
|||
|
|
@ -32,14 +32,24 @@ import { useSystemInfo } from "~/client/hooks/use-system-info";
|
|||
import { useScrollToFormError } from "~/client/hooks/use-scroll-to-form-error";
|
||||
import { DirectoryForm, NFSForm, SMBForm, WebDAVForm, RcloneForm, SFTPForm } from "./volume-forms";
|
||||
|
||||
export const formSchema = z.discriminatedUnion("backend", [
|
||||
directoryConfigSchema.extend({ name: z.string().min(2).max(32) }),
|
||||
nfsConfigSchema.extend({ name: z.string().min(2).max(32) }),
|
||||
smbConfigSchema.extend({ name: z.string().min(2).max(32) }),
|
||||
webdavConfigSchema.extend({ name: z.string().min(2).max(32) }),
|
||||
rcloneConfigSchema.extend({ name: z.string().min(2).max(32) }),
|
||||
sftpConfigSchema.extend({ name: z.string().min(2).max(32) }),
|
||||
]);
|
||||
export const formSchema = z
|
||||
.discriminatedUnion("backend", [
|
||||
directoryConfigSchema.extend({ name: z.string().min(2).max(32) }),
|
||||
nfsConfigSchema.extend({ name: z.string().min(2).max(32) }),
|
||||
smbConfigSchema.extend({ name: z.string().min(2).max(32) }),
|
||||
webdavConfigSchema.extend({ name: z.string().min(2).max(32) }),
|
||||
rcloneConfigSchema.extend({ name: z.string().min(2).max(32) }),
|
||||
sftpConfigSchema.extend({ name: z.string().min(2).max(32) }),
|
||||
])
|
||||
.superRefine((value, ctx) => {
|
||||
if (value.backend === "sftp" && !value.skipHostKeyCheck && !value.knownHosts?.trim()) {
|
||||
ctx.addIssue({
|
||||
code: "custom",
|
||||
message: "Known hosts are required unless host key verification is skipped",
|
||||
path: ["knownHosts"],
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export type FormValues = z.input<typeof formSchema>;
|
||||
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ export const sftpConfigSchema = z.object({
|
|||
privateKey: z.string().optional(),
|
||||
path: z.string().min(1),
|
||||
readOnly: z.boolean().optional(),
|
||||
skipHostKeyCheck: z.boolean().default(true),
|
||||
skipHostKeyCheck: z.boolean().default(false),
|
||||
knownHosts: z.string().optional(),
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -60,12 +60,14 @@ const mount = async (config: BackendConfig, mountPath: string) => {
|
|||
`gid=${gid}`,
|
||||
];
|
||||
|
||||
if (config.skipHostKeyCheck || !config.knownHosts) {
|
||||
if (config.skipHostKeyCheck) {
|
||||
options.push("StrictHostKeyChecking=no", "UserKnownHostsFile=/dev/null");
|
||||
} else if (config.knownHosts) {
|
||||
const knownHostsPath = getKnownHostsPath(mountPath);
|
||||
await writeFileWithMode(knownHostsPath, config.knownHosts, FILE_MODES.ownerReadWrite);
|
||||
options.push(`UserKnownHostsFile=${knownHostsPath}`, "StrictHostKeyChecking=yes");
|
||||
} else {
|
||||
options.push("StrictHostKeyChecking=yes");
|
||||
}
|
||||
|
||||
if (config.readOnly) {
|
||||
|
|
|
|||
|
|
@ -317,11 +317,11 @@ describe("buildEnv", () => {
|
|||
expect(env._SFTP_SSH_ARGS).toContain("UserKnownHostsFile=/dev/null");
|
||||
});
|
||||
|
||||
test("uses StrictHostKeyChecking=no when knownHosts is absent", async () => {
|
||||
test("uses StrictHostKeyChecking=yes when knownHosts is absent", async () => {
|
||||
const env = await buildEnvForTest({ ...baseSftpConfig, skipHostKeyCheck: false, knownHosts: undefined }, "org-1");
|
||||
|
||||
expect(env._SFTP_SSH_ARGS).toContain("StrictHostKeyChecking=no");
|
||||
expect(env._SFTP_SSH_ARGS).toContain("UserKnownHostsFile=/dev/null");
|
||||
expect(env._SFTP_SSH_ARGS).toContain("StrictHostKeyChecking=yes");
|
||||
expect(env._SFTP_SSH_ARGS).not.toContain("UserKnownHostsFile=/dev/null");
|
||||
});
|
||||
|
||||
test("uses StrictHostKeyChecking=yes and a known hosts file when knownHosts is provided", async () => {
|
||||
|
|
|
|||
|
|
@ -114,13 +114,15 @@ export const buildEnv = async (
|
|||
keyPath,
|
||||
];
|
||||
|
||||
if (config.skipHostKeyCheck || !config.knownHosts) {
|
||||
if (config.skipHostKeyCheck) {
|
||||
sshArgs.push("-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null");
|
||||
} else if (config.knownHosts) {
|
||||
const knownHostsPath = path.join("/tmp", `zerobyte-known-hosts-${crypto.randomBytes(8).toString("hex")}`);
|
||||
await writeFileWithMode(knownHostsPath, config.knownHosts, FILE_MODES.ownerReadWrite);
|
||||
env._SFTP_KNOWN_HOSTS_PATH = knownHostsPath;
|
||||
sshArgs.push("-o", "StrictHostKeyChecking=yes", "-o", `UserKnownHostsFile=${knownHostsPath}`);
|
||||
} else {
|
||||
sshArgs.push("-o", "StrictHostKeyChecking=yes");
|
||||
}
|
||||
|
||||
if (config.port && config.port !== 22) {
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ export const sftpRepositoryConfigSchema = z
|
|||
user: z.string().min(1),
|
||||
path: z.string().min(1),
|
||||
privateKey: z.string().min(1),
|
||||
skipHostKeyCheck: z.boolean().default(true),
|
||||
skipHostKeyCheck: z.boolean().default(false),
|
||||
knownHosts: z.string().optional(),
|
||||
})
|
||||
.extend(baseRepositoryConfigSchema.shape);
|
||||
|
|
|
|||
Loading…
Reference in a new issue