Files
DLE/webssh-agent/utils/sshUtils.js

164 lines
6.2 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const { exec } = require('child_process');
const fs = require('fs-extra');
const os = require('os');
const path = require('path');
const log = require('./logger');
const sshDir = path.join(os.homedir(), '.ssh');
const privateKeyPath = path.join(sshDir, 'id_rsa');
const publicKeyPath = `${privateKeyPath}.pub`;
const sshConfigPath = path.join(sshDir, 'config');
const ensureSshPermissions = async () => {
try {
await fs.ensureDir(sshDir);
await fs.chmod(sshDir, 0o700).catch(() => {});
await fs.chmod(privateKeyPath, 0o600).catch(() => {});
await fs.chmod(publicKeyPath, 0o644).catch(() => {});
await fs.chmod(sshConfigPath, 0o600).catch(() => {});
} catch (error) {
log.warn('Не удалось скорректировать права доступа к SSH директории: ' + error.message);
}
};
const execSshCommand = async (command, options = {}) => {
const {
sshHost,
sshPort = 22,
sshConnectUser,
sshConnectPassword,
vdsIp
} = options;
await ensureSshPermissions();
const privateKeyExists = await fs.pathExists(privateKeyPath);
const escapedCommand = command.replace(/"/g, '\\"');
// Удаляем пробелы и проверяем, что значения не пустые
const user = String(sshConnectUser || 'root').trim();
const host = String((sshHost || vdsIp || '')).trim();
if (!host) {
throw new Error('Не указан хост для SSH подключения (sshHost или vdsIp)');
}
if (!user) {
throw new Error('Не указан пользователь для SSH подключения (sshConnectUser)');
}
let sshCommand = `ssh -p ${sshPort} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR ${user}@${host} "${escapedCommand}"`;
if (privateKeyExists) {
sshCommand = `ssh -i "${privateKeyPath}" -p ${sshPort} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR ${user}@${host} "${escapedCommand}"`;
}
log.info(`🔍 Выполняем SSH команду: ${sshCommand}`);
return new Promise((resolve) => {
exec(sshCommand, (error, stdout, stderr) => {
log.info(`📤 SSH результат - код: ${error ? error.code : 0}, stdout: "${stdout}", stderr: "${stderr}"`);
if (error && error.code === 255 && sshConnectPassword) {
log.info('SSH ключи не сработали, пробуем с паролем...');
const passwordCommand = `sshpass -p "${String(sshConnectPassword || '').trim()}" ssh -p ${sshPort} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR ${user}@${host} "${escapedCommand}"`;
exec(passwordCommand, (passwordError, passwordStdout, passwordStderr) => {
log.info(`📤 SSH с паролем результат - код: ${passwordError ? passwordError.code : 0}, stdout: "${passwordStdout}", stderr: "${passwordStderr}"`);
resolve({
code: passwordError ? passwordError.code : 0,
stdout: passwordStdout || '',
stderr: passwordStderr || ''
});
});
} else {
resolve({
code: error ? error.code : 0,
stdout: stdout || '',
stderr: stderr || ''
});
}
});
});
};
const execScpCommand = async (sourcePath, targetPath, options = {}) => {
const {
sshHost,
sshPort = 22,
sshConnectUser,
sshConnectPassword,
vdsIp
} = options;
await ensureSshPermissions();
const privateKeyExists = await fs.pathExists(privateKeyPath);
// Удаляем пробелы и проверяем, что значения не пустые
const user = String(sshConnectUser || 'root').trim();
const host = String((sshHost || vdsIp || '')).trim();
if (!host) {
throw new Error('Не указан хост для SCP подключения (sshHost или vdsIp)');
}
if (!user) {
throw new Error('Не указан пользователь для SCP подключения (sshConnectUser)');
}
let scpCommand = `scp -P ${sshPort} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR ${sourcePath} ${user}@${host}:${targetPath}`;
if (privateKeyExists) {
scpCommand = `scp -i "${privateKeyPath}" -P ${sshPort} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR ${sourcePath} ${user}@${host}:${targetPath}`;
}
log.info(`🔍 Выполняем SCP команду: scp ${sourcePath} -> ${user}@${host}:${targetPath}`);
return new Promise((resolve) => {
exec(scpCommand, (error, stdout, stderr) => {
if (error && error.code === 255 && sshConnectPassword) {
log.info('SCP с ключами не сработал, пробуем с паролем...');
const passwordScpCommand = `sshpass -p "${String(sshConnectPassword || '').trim()}" scp -P ${sshPort} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR ${sourcePath} ${user}@${host}:${targetPath}`;
exec(passwordScpCommand, (passwordError, passwordStdout, passwordStderr) => {
if (passwordError) {
log.error(`❌ Ошибка SCP с паролем (код: ${passwordError.code}): ${passwordError.message}`);
log.error(`📋 stderr: ${passwordStderr}`);
log.error(`📋 stdout: ${passwordStdout}`);
} else {
log.success('✅ SCP успешно выполнен с паролем');
}
resolve({
code: passwordError ? passwordError.code : 0,
stdout: passwordStdout || '',
stderr: passwordStderr || ''
});
});
} else {
if (error) {
log.error(`❌ Ошибка SCP (код: ${error.code}): ${error.message}`);
log.error(`📋 stderr: ${stderr}`);
log.error(`📋 stdout: ${stdout}`);
} else {
log.success('✅ SCP успешно выполнен');
if (stdout) {
log.info(`📋 SCP stdout: ${stdout}`);
}
}
resolve({
code: error ? error.code : 0,
stdout: stdout || '',
stderr: stderr || ''
});
}
});
});
};
module.exports = {
execSshCommand,
execScpCommand,
fixSshPermissions: ensureSshPermissions
};