diff --git a/src/backupsites.js b/src/backupsites.js index 3deb09291..9ee101659 100644 --- a/src/backupsites.js +++ b/src/backupsites.js @@ -51,7 +51,6 @@ const assert = require('node:assert'), eventlog = require('./eventlog.js'), hush = require('./hush.js'), locks = require('./locks.js'), - mounts = require('./mounts.js'), path = require('node:path'), paths = require('./paths.js'), safe = require('safetydance'), @@ -109,12 +108,6 @@ function postProcess(result) { result.config = result.configJson ? safe.JSON.parse(result.configJson) : {}; delete result.configJson; - // the optimized code paths for rsync remote copy and delete require the keyfile path see filesystem.js:copyInternal() - if (result.config._provider === mounts.MOUNT_TYPE_SSHFS) { - const privateKeyFilePath = path.join(paths.SSHFS_KEYS_DIR, `identity_file_${result.id}`); - result.config.mountOptions = { ...result.config.mountOptions, _privateKeyFile: privateKeyFilePath }; - } - result.limits = safe.JSON.parse(result.limitsJson) || {}; delete result.limitsJson; diff --git a/src/storage/filesystem.js b/src/storage/filesystem.js index 9e7d41a79..2f8d213a5 100644 --- a/src/storage/filesystem.js +++ b/src/storage/filesystem.js @@ -209,9 +209,16 @@ async function copyInternal(config, fromPath, toPath, options, progressCallback) cpOptions += config.noHardlinks ? '' : 'l'; // this will hardlink backups saving space if (config._provider === mounts.MOUNT_TYPE_SSHFS) { - const sshOptions = [ '-o', '"StrictHostKeyChecking no"', '-i', config.mountOptions._privateKeyFile, '-p', config.mountOptions.port, `${config.mountOptions.user}@${config.mountOptions.host}` ]; + // we use a temporary key file instead of passing it as stdin + const identityFilePath = `/tmp/identity_file${config._managedMountPath.replaceAll('/', '-')}`; + // have to unlink first, in case a previous run crash before cleanup. With mode 0c600 we cannot overwrite it + safe.fs.unlinkSync(identityFilePath); + if (!safe.fs.writeFileSync(identityFilePath, `${config.mountOptions.privateKey}\n`, { mode: 0o600 })) throw new BoxError(BoxError.FS_ERROR, `Could not write temporary private key: ${safe.error.message}`); + + const sshOptions = [ '-o', '"StrictHostKeyChecking no"', '-i', identityFilePath, '-p', config.mountOptions.port, `${config.mountOptions.user}@${config.mountOptions.host}` ]; const sshArgs = sshOptions.concat([ 'cp', cpOptions, path.join(config.prefix ?? '', fromPath), path.join(config.prefix ?? '', toPath) ]); const [remoteCopyError] = await safe(shell.spawn('ssh', sshArgs, { shell: true })); + safe.fs.unlinkSync(identityFilePath); if (!remoteCopyError) return; if (remoteCopyError.code === 255) throw new BoxError(BoxError.EXTERNAL_ERROR, `SSH connection error: ${remoteCopyError.message}`); // do not attempt fallback copy for ssh errors debug('SSH remote copy failed, trying sshfs copy'); // this can happen for sshfs mounted windows server @@ -265,9 +272,16 @@ async function removeDir(config, limits, remotePathPrefix, progressCallback) { progressCallback({ message: `Removing directory ${fullPathPrefix}` }); if (config._provider === mounts.MOUNT_TYPE_SSHFS) { - const sshOptions = [ '-o', '"StrictHostKeyChecking no"', '-i', config.mountOptions._privateKeyFile, '-p', config.mountOptions.port, `${config.mountOptions.user}@${config.mountOptions.host}` ]; + // we use a temporary key file instead of passing it as stdin + const identityFilePath = `/tmp/identity_file${config._managedMountPath.replaceAll('/', '-')}`; + // have to unlink first, in case a previous run crash before cleanup. With mode 0c600 we cannot overwrite it + safe.fs.unlinkSync(identityFilePath); + if (!safe.fs.writeFileSync(identityFilePath, `${config.mountOptions.privateKey}\n`, { mode: 0o600 })) throw new BoxError(BoxError.FS_ERROR, `Could not write temporary private key: ${safe.error.message}`); + + const sshOptions = [ '-o', '"StrictHostKeyChecking no"', '-i', identityFilePath, '-p', config.mountOptions.port, `${config.mountOptions.user}@${config.mountOptions.host}` ]; const sshArgs = sshOptions.concat([ 'rm', '-rf', path.join(config.prefix ?? '', remotePathPrefix) ]); const [remoteRmError] = await safe(shell.spawn('ssh', sshArgs, { shell: true })); + safe.fs.unlinkSync(identityFilePath); if (!remoteRmError) return; if (remoteRmError.code === 255) throw new BoxError(BoxError.EXTERNAL_ERROR, `SSH connection error: ${remoteRmError.message}`); // do not attempt fallback copy for ssh errors debug('SSH remote rm failed, trying sshfs rm'); // this can happen for sshfs mounted windows server