Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for encrypted SSH keys + MySQL socket #36

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 26 additions & 26 deletions dist/Swiff.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ var _username = _interopRequireDefault(require("username"));

var _path = _interopRequireDefault(require("path"));

var _ssh = _interopRequireDefault(require("ssh2"));

var _chalk = _interopRequireDefault(require("chalk"));

var _utils = require("./utils");
Expand All @@ -31,7 +29,7 @@ var _database = require("./database");

var _templates = require("./templates");

var _ssh2 = require("./ssh");
var _ssh = require("./ssh");

var _config = require("./config");

Expand Down Expand Up @@ -332,10 +330,10 @@ class Swiff extends _react.Component {

if (!doesSshKeyExist) return _this.setMessage(`Your${!(0, _utils.isEmpty)(localEnv.SWIFF_CUSTOM_KEY) ? ' custom' : ''} SSH key file wasn’t found at:\n ${(0, _palette.colourNotice)(sshKey)}\n\nYou can either:\n\na) Create a SSH key with this command (leave passphrase empty):\n ${(0, _palette.colourNotice)(`ssh-keygen -m PEM -b 4096 -f ${sshKey}`)}\n\nb) Or add an existing key path in your .env with:\n ${(0, _palette.colourNotice)(`SWIFF_CUSTOM_KEY="/Users/${user}/.ssh/[your-key-name]"`)}${isInteractive ? `\n\nThen hit [ enter ↵ ] to rerun this task` : ''}`); // Check the users SSH key has been added to the server

const checkSshSetup = yield (0, _utils.executeCommands)((0, _ssh2.getSshTestCommand)(config.server.user, config.server.host, config.server.port, !(0, _utils.isEmpty)(localEnv.SWIFF_CUSTOM_KEY) ? localEnv.SWIFF_CUSTOM_KEY : null)); // If there's an issue with the connection then give some assistance
const checkSshSetup = yield (0, _utils.executeCommands)((0, _ssh.getSshTestCommand)(config.server.user, config.server.host, config.server.port, !(0, _utils.isEmpty)(localEnv.SWIFF_CUSTOM_KEY) ? localEnv.SWIFF_CUSTOM_KEY : null)); // If there's an issue with the connection then give some assistance

if (checkSshSetup instanceof Error) {
return _this.setMessage(`A SSH connection couldn’t be made with these details:\n\nServer host: ${config.server.host}\nServer user: ${config.server.user}\nPort: ${config.server.port}\nSSH key: ${sshKey}\n\n${(0, _ssh2.getSshCopyInstructions)(config, sshKey)}\n\n${(0, _utils.isEmpty)(localEnv.SWIFF_CUSTOM_KEY) ? `${_chalk.default.bold(`Is the 'SSH key' path above wrong?`)}\nAdd the correct path to your project .env like this:\nSWIFF_CUSTOM_KEY="/Users/${user}/.ssh/id_rsa"` : ''}`);
return _this.setMessage(`A SSH connection couldn’t be made with these details:\n\nServer host: ${config.server.host}\nServer user: ${config.server.user}\nPort: ${config.server.port}\nSSH key: ${sshKey}\n\n${(0, _ssh.getSshCopyInstructions)(config, sshKey)}\n\n${(0, _utils.isEmpty)(localEnv.SWIFF_CUSTOM_KEY) ? `${_chalk.default.bold(`Is the 'SSH key' path above wrong?`)}\nAdd the correct path to your project .env like this:\nSWIFF_CUSTOM_KEY="/Users/${user}/.ssh/id_rsa"` : ''}`);
}

return true;
Expand Down Expand Up @@ -366,7 +364,7 @@ class Swiff extends _react.Component {
_this.setWorking(`Pulling files from ${(0, _utils.commaAmpersander)(filteredPullFolders)}`); // Create the rsync commands required to pull the files


const pullCommands = (0, _ssh2.getSshPullCommands)({
const pullCommands = (0, _ssh.getSshPullCommands)({
pullFolders: filteredPullFolders,
user: user,
host: host,
Expand Down Expand Up @@ -445,7 +443,7 @@ class Swiff extends _react.Component {
_this.setWorking(`Pushing files in ${(0, _utils.commaAmpersander)(filteredPushFolders)}`); // Get the rsync push commands


const pushCommands = (0, _ssh2.getSshPushCommands)({
const pushCommands = (0, _ssh.getSshPushCommands)({
pushFolders: filteredPushFolders,
user: user,
host: host,
Expand Down Expand Up @@ -476,7 +474,8 @@ class Swiff extends _react.Component {
DB_PORT,
DB_DATABASE,
DB_USER,
DB_PASSWORD
DB_PASSWORD,
DB_SOCKET
} = localEnv; // Get the remote env file via SSH

const remoteEnv = yield (0, _env.getRemoteEnv)({
Expand All @@ -494,7 +493,7 @@ class Swiff extends _react.Component {
const remoteDbNameZipped = `${remoteDbName}.gz`;
const importFile = `${_paths.pathBackups}/${remoteDbName}`; // Download and store the remote DB via SSH

const dbSsh = yield (0, _ssh2.getSshDatabase)({
const dbSsh = yield (0, _ssh.getSshDatabase)({
remoteEnv: remoteEnv,
host: serverConfig.host,
user: serverConfig.user,
Expand Down Expand Up @@ -527,7 +526,8 @@ class Swiff extends _react.Component {
port: DB_PORT,
user: DB_USER,
password: DB_PASSWORD,
database: DB_DATABASE
database: DB_DATABASE,
socketPath: DB_SOCKET
}); // If there's any dropping issues then return the messages

if (dropTables instanceof Error) return String(dropTables).includes('ER_BAD_DB_ERROR: Unknown database ') ? _this.setMessage(`First create a database named ${(0, _palette.colourNotice)(DB_DATABASE)} on ${(0, _palette.colourNotice)(DB_SERVER)} with these login details:\n\nUsername: ${DB_USER}\nPassword: ${DB_PASSWORD}`) : _this.setError(`There were issues connecting to your local ${(0, _palette.colourAttention)(DB_DATABASE)} database\n\nCheck these settings are correct in your local .env file:\n\n${(0, _palette.colourAttention)(`DB_SERVER="${DB_SERVER}"\nDB_PORT="${DB_PORT}"\nDB_USER="${DB_USER}"\nDB_PASSWORD="${DB_PASSWORD}"\nDB_DATABASE="${DB_DATABASE}"`)}\n\n${(0, _palette.colourMuted)(String(dropTables).replace('Error: ', ''))}`); // Import the remote .sql into the local database
Expand Down Expand Up @@ -579,7 +579,7 @@ class Swiff extends _react.Component {
const remoteDbName = `${remoteEnv.DB_DATABASE}-remote.sql`;
const remoteDbNameZipped = `${remoteDbName}.gz`; // Download and store the remote DB via SSH

const dbSsh = yield (0, _ssh2.getSshDatabase)({
const dbSsh = yield (0, _ssh.getSshDatabase)({
remoteEnv: remoteEnv,
host: serverConfig.host,
user: serverConfig.user,
Expand Down Expand Up @@ -609,7 +609,7 @@ class Swiff extends _react.Component {
if (localDbDump instanceof Error) return _this.setError(localDbDump);
const remoteDbDumpPath = serverConfig.appPath; // Upload local db to remote

const pushDatabase = yield (0, _ssh2.pushSshDatabase)({
const pushDatabase = yield (0, _ssh.pushSshDatabase)({
host: serverConfig.host,
user: serverConfig.user,
port: serverConfig.port,
Expand All @@ -622,7 +622,7 @@ class Swiff extends _react.Component {
if (pushDatabase instanceof Error) return _this.setError(pushDatabase); // Create a SSH connection
// TODO: Test swiff custom key

const ssh = yield (0, _ssh2.sshConnect)({
const ssh = yield (0, _ssh.sshConnect)({
host: serverConfig.host,
username: serverConfig.user,
port: serverConfig.port,
Expand Down Expand Up @@ -688,7 +688,7 @@ class Swiff extends _react.Component {

yield (0, _utils.executeCommands)(`cp composer.json ${_paths.pathBackups}/${DB_DATABASE}-local-composer.json && cp composer.lock ${_paths.pathBackups}/${DB_DATABASE}-local-composer.lock`); // Connect to the remote server

const ssh = yield (0, _ssh2.getSshInit)({
const ssh = yield (0, _ssh.getSshInit)({
host: serverConfig.host,
user: serverConfig.user,
sshKeyPath: SWIFF_CUSTOM_KEY
Expand All @@ -699,7 +699,7 @@ class Swiff extends _react.Component {
_this.setWorking(`Fetching the composer files from the remote server at ${(0, _palette.colourHighlight)(serverConfig.host)}`); // Download composer.json from the remote server


const sshDownload1 = yield (0, _ssh2.getSshFile)({
const sshDownload1 = yield (0, _ssh.getSshFile)({
connection: ssh,
from: _path.default.join(serverConfig.appPath, 'composer.json'),
to: _path.default.join(_paths.pathApp, 'composer.json')
Expand All @@ -711,7 +711,7 @@ class Swiff extends _react.Component {
} // Download composer.lock from the remote server


const sshDownload2 = yield (0, _ssh2.getSshFile)({
const sshDownload2 = yield (0, _ssh.getSshFile)({
connection: ssh,
from: _path.default.join(serverConfig.appPath, 'composer.lock'),
to: _path.default.join(_paths.pathApp, 'composer.lock')
Expand Down Expand Up @@ -744,19 +744,19 @@ class Swiff extends _react.Component {
_this.setWorking(`Backing up the remote composer files on ${(0, _palette.colourHighlight)(serverConfig.host)}`); // Connect to the remote server


const ssh = yield (0, _ssh2.getSshInit)({
const ssh = yield (0, _ssh.getSshInit)({
host: serverConfig.host,
user: serverConfig.user,
sshKeyPath: SWIFF_CUSTOM_KEY
}); // Download composer.json from the remote server

const sshDownload1 = yield (0, _ssh2.getSshFile)({
const sshDownload1 = yield (0, _ssh.getSshFile)({
connection: ssh,
from: _path.default.join(serverConfig.appPath, 'composer.json'),
to: _path.default.join(_paths.pathBackups, `${DB_DATABASE}-remote-composer.json`)
}); // Download composer.lock from the remote server

const sshDownload2 = yield (0, _ssh2.getSshFile)({
const sshDownload2 = yield (0, _ssh.getSshFile)({
connection: ssh,
from: _path.default.join(serverConfig.appPath, 'composer.lock'),
to: _path.default.join(_paths.pathBackups, `${DB_DATABASE}-remote-composer.lock`)
Expand Down Expand Up @@ -816,8 +816,13 @@ class Swiff extends _react.Component {
// https://github.com/mscdex/ssh2#start-an-interactive-shell-session

let gs = null;
const conn = new _ssh.default();
conn.on('ready', () => {
const ssh = yield (0, _ssh.sshConnect)({
host: serverConfig.host,
username: serverConfig.user,
port: serverConfig.port,
sshKeyPath: privateKey
}).then(ssh => {
const conn = ssh.connection;
conn.shell((err, stream) => {
if (err) throw err; // Build the commands to run once we're logged in

Expand All @@ -837,11 +842,6 @@ class Swiff extends _react.Component {
process.exit(1);
});
});
}).connect({
host: serverConfig.host,
privateKey: require('fs').readFileSync(privateKey),
username: serverConfig.user,
port: serverConfig.port
}); // Push our input to the server input
// http://stackoverflow.com/questions/5006821/nodejs-how-to-read-keystrokes-from-stdin

Expand Down
3 changes: 2 additions & 1 deletion dist/database.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ function () {
host: null,
user: null,
password: null,
database: null // Create the connection to the local database
database: null,
socketPath: null // Create the connection to the local database

};
const conn = yield _promiseMysql.default.createConnection(_objectSpread({}, defaultConfig, {}, config)).catch(error => errorMessage = error);
Expand Down
50 changes: 38 additions & 12 deletions dist/ssh.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ var _palette = require("./palette");

var _chalk = _interopRequireDefault(require("chalk"));

var _readlineSync = _interopRequireDefault(require("readline-sync"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { keys.push.apply(keys, Object.getOwnPropertySymbols(object)); } if (enumerableOnly) keys = keys.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); return keys; }
Expand All @@ -35,6 +37,8 @@ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try

function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }

let passphrase;

const getSshInit =
/*#__PURE__*/
function () {
Expand Down Expand Up @@ -107,12 +111,34 @@ function () {
const sshKeyResolvedPath = !(0, _utils.isEmpty)(sshKeyPath) ? sshKeyPath : `/Users/${user}/.ssh/id_rsa`; // Create a SSH connection

const ssh = new _nodeSsh.default();
yield ssh.connect({
host: host,
username: username,
port: port,
privateKey: sshKeyResolvedPath
}).catch(error => errorMessage = error);

const tryToConnect =
/*#__PURE__*/
function () {
var _ref4 = _asyncToGenerator(function* () {
errorMessage = null;
yield ssh.connect({
host: host,
username: username,
port: port,
privateKey: sshKeyResolvedPath,
passphrase: passphrase
}).catch(error => errorMessage = error);

if (String(errorMessage).includes('Encrypted OpenSSH private key detected, but no passphrase given') || String(errorMessage).includes('Malformed OpenSSH private key. Bad passphrase?')) {
passphrase = _readlineSync.default.question((String(errorMessage).includes('Malformed') ? 'Incorrect passphrase! ' : '') + 'Please enter the private key’s passphrase: ', {
hideEchoBack: true
});
yield tryToConnect();
}
});

return function tryToConnect() {
return _ref4.apply(this, arguments);
};
}();

yield tryToConnect();
if (errorMessage) return new Error(String(errorMessage).includes('Error: Cannot parse privateKey: Unsupported key format') ? `Your SSH key isn't in a format Swiff can work with\n (${sshKeyResolvedPath})\n\n1. Generate a new one with:\n ${(0, _palette.colourNotice)(`ssh-keygen -m PEM -b 4096 -f /Users/${user}/.ssh/swiff`)}\n\n2. Then add the key to the server:\n ${(0, _palette.colourNotice)(`ssh-copy-id -i /Users/${user}/.ssh/swiff ${port !== 22 ? `-p ${port} ` : ''}${username}@${host}`)}` : String(errorMessage).includes('config.privateKey does not exist at') ? `Your SSH key isn’t found at ${(0, _palette.colourAttention)(sshKeyResolvedPath)}\n\nCheck the ${(0, _palette.colourAttention)(`SWIFF_CUSTOM_KEY`)} value is correct in your local .env\n\nmacOS path example:\n${(0, _palette.colourAttention)(`SWIFF_CUSTOM_KEY="/Users/${user}/.ssh/[key-filename]"`)}` : errorMessage);
return ssh;
});
Expand All @@ -127,7 +153,7 @@ exports.sshConnect = sshConnect;
const getSshEnv =
/*#__PURE__*/
function () {
var _ref4 = _asyncToGenerator(function* ({
var _ref5 = _asyncToGenerator(function* ({
host,
username,
port,
Expand Down Expand Up @@ -178,7 +204,7 @@ function () {
});

return function getSshEnv(_x4) {
return _ref4.apply(this, arguments);
return _ref5.apply(this, arguments);
};
}();

Expand Down Expand Up @@ -288,14 +314,14 @@ exports.getSshTestCommand = getSshTestCommand;
const pushSshDatabase =
/*#__PURE__*/
function () {
var _ref5 = _asyncToGenerator(function* (config) {
var _ref6 = _asyncToGenerator(function* (config) {
const pushDatabaseStatus = yield (0, _utils.executeCommands)(getPushDatabaseCommands(config));
if (pushDatabaseStatus instanceof Error) return new Error(`There was an issue uploading your local ${(0, _palette.colourAttention)(config.dbName)} database\n\n${pushDatabaseStatus}`);
return;
});

return function pushSshDatabase(_x5) {
return _ref5.apply(this, arguments);
return _ref6.apply(this, arguments);
};
}(); // Download a database over SSH to a local folder

Expand All @@ -305,7 +331,7 @@ exports.pushSshDatabase = pushSshDatabase;
const getSshDatabase =
/*#__PURE__*/
function () {
var _ref6 = _asyncToGenerator(function* ({
var _ref7 = _asyncToGenerator(function* ({
remoteEnv,
host,
user,
Expand Down Expand Up @@ -372,7 +398,7 @@ function () {
});

return function getSshDatabase(_x6) {
return _ref6.apply(this, arguments);
return _ref7.apply(this, arguments);
};
}();

Expand Down
Loading