refactor(sftp-backend): use node spawn to properly send password stdin instead of echo
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
0e5438897c
commit
ffc43e0c25
2 changed files with 44 additions and 11 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
import * as fs from "node:fs/promises";
|
import * as fs from "node:fs/promises";
|
||||||
import * as os from "node:os";
|
import * as os from "node:os";
|
||||||
import * as path from "node:path";
|
import * as path from "node:path";
|
||||||
import { $ } from "bun";
|
import { spawn } from "node:child_process";
|
||||||
import { OPERATION_TIMEOUT } from "../../../core/constants";
|
import { OPERATION_TIMEOUT } from "../../../core/constants";
|
||||||
import { cryptoUtils } from "../../../utils/crypto";
|
import { cryptoUtils } from "../../../utils/crypto";
|
||||||
import { toMessage } from "../../../utils/errors";
|
import { toMessage } from "../../../utils/errors";
|
||||||
|
|
@ -93,20 +93,52 @@ const mount = async (config: BackendConfig, mountPath: string) => {
|
||||||
|
|
||||||
logger.debug(`Mounting SFTP volume ${mountPath}...`);
|
logger.debug(`Mounting SFTP volume ${mountPath}...`);
|
||||||
|
|
||||||
let result: $.ShellOutput;
|
const runSshfs = async (mountArgs: string[], password?: string) => {
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
const child = spawn("sshfs", mountArgs, { stdio: ["pipe", "pipe", "pipe"] });
|
||||||
|
let stdout = "";
|
||||||
|
let stderr = "";
|
||||||
|
|
||||||
|
child.stdout.setEncoding("utf8");
|
||||||
|
child.stderr.setEncoding("utf8");
|
||||||
|
|
||||||
|
child.stdout.on("data", (data) => {
|
||||||
|
stdout += data;
|
||||||
|
});
|
||||||
|
|
||||||
|
child.stderr.on("data", (data) => {
|
||||||
|
stderr += data;
|
||||||
|
});
|
||||||
|
|
||||||
|
child.on("error", (error) => {
|
||||||
|
reject(new Error(`Failed to start sshfs: ${error.message}`));
|
||||||
|
});
|
||||||
|
|
||||||
|
child.on("close", (code) => {
|
||||||
|
if (code === 0) {
|
||||||
|
resolve();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const errorMsg = stderr.trim() || stdout.trim() || "Unknown error";
|
||||||
|
reject(new Error(`Failed to mount SFTP volume: ${errorMsg}`));
|
||||||
|
});
|
||||||
|
|
||||||
|
if (password) {
|
||||||
|
child.stdin.write(password);
|
||||||
|
}
|
||||||
|
child.stdin.end();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
if (config.password) {
|
if (config.password) {
|
||||||
const password = await cryptoUtils.resolveSecret(config.password);
|
const password = await cryptoUtils.resolveSecret(config.password);
|
||||||
args.push("-o", "password_stdin");
|
args.push("-o", "password_stdin");
|
||||||
logger.info(`Executing sshfs: echo "******" | sshfs ${args.join(" ")}`);
|
logger.info(`Executing sshfs: sshfs ${args.join(" ")}`);
|
||||||
result = await $`echo ${password} | sshfs ${args}`.nothrow();
|
await runSshfs(args, password);
|
||||||
} else {
|
} else {
|
||||||
logger.info(`Executing sshfs: sshfs ${args.join(" ")}`);
|
logger.info(`Executing sshfs: sshfs ${args.join(" ")}`);
|
||||||
result = await $`sshfs ${args}`.nothrow();
|
await runSshfs(args);
|
||||||
}
|
|
||||||
|
|
||||||
if (result.exitCode !== 0) {
|
|
||||||
const errorMsg = result.stderr.toString() || result.stdout.toString() || "Unknown error";
|
|
||||||
throw new Error(`Failed to mount SFTP volume: ${errorMsg}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info(`SFTP volume at ${mountPath} mounted successfully.`);
|
logger.info(`SFTP volume at ${mountPath} mounted successfully.`);
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,8 @@ const mount = async (config: BackendConfig, path: string) => {
|
||||||
if (config.username && config.password) {
|
if (config.username && config.password) {
|
||||||
const password = await cryptoUtils.resolveSecret(config.password);
|
const password = await cryptoUtils.resolveSecret(config.password);
|
||||||
const secretsFile = "/etc/davfs2/secrets";
|
const secretsFile = "/etc/davfs2/secrets";
|
||||||
const secretsContent = `${source} ${config.username} ${password}\n`;
|
const entry = [source, config.username, password].map((value) => value.replace(/[\r\n\t\s]+/g, " ")).join(" ");
|
||||||
|
const secretsContent = `${entry}\n`;
|
||||||
await fs.appendFile(secretsFile, secretsContent, { mode: 0o600 });
|
await fs.appendFile(secretsFile, secretsContent, { mode: 0o600 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue