From 3c5db1db9340f320cce72bf5f445c8add6016fad Mon Sep 17 00:00:00 2001 From: Taylor McLean Date: Sat, 17 Apr 2021 18:10:21 +0000 Subject: [PATCH] feat: add ssh tunnel functionality --- README.md | 22 +++++- lib/env/database.js | 26 +++++-- package-lock.json | 124 ++++++++++++++++++++++++++++++-- package.json | 3 +- samples/migrate-mongo-config.js | 14 ++++ 5 files changed, 174 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 4a697f1..de7c291 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,20 @@ Edit the migrate-mongo-config.js file. An object or promise can be returned. Mak // In this file you can configure migrate-mongo module.exports = { + // configure ssh tunnel, see https://github.com/agebrock/tunnel-ssh#readme + sshTunnel: { + // username:'root', + // password:'secret', + // host:sshServer, + // port:22, + // dstHost:destinationServer, + // dstPort:27017, + // privateKey:require(fs).readFileSync('/path/to/key'), + // passphrase:'secret', + // localHost:'127.0.0.1', + // localPort: 27000 + }, + mongodb: { // TODO Change (or review) the url to your MongoDB: url: "mongodb://localhost:27017", @@ -65,7 +79,8 @@ module.exports = { databaseName: "YOURDATABASENAME", options: { - useNewUrlParser: true // removes a deprecation warning when connecting + useNewUrlParser: true, // removes a deprecation warning when connecting + useUnifiedTopology: true, // removes a deprecating warning when connecting // connectTimeoutMS: 3600000, // increase connection timeout to 1 hour // socketTimeoutMS: 3600000, // increase socket timeout to 1 hour } @@ -78,7 +93,7 @@ module.exports = { changelogCollectionName: "changelog", // The file extension to create migrations and search for in migration dir - migrationFileExtension: ".js" + migrationFileExtension: ".js", // Enable the algorithm to create a checksum of the file contents and use that in the comparison to determin // if the file should be run. Requires that scripts are coded to be run multiple times. @@ -323,6 +338,9 @@ Now the status will also include the file hash in the output ``` +### Connecting to your database via SSH tunnel +If your database resides in a secured VPN or is privately accessible through an SSH tunnel, you can add configurations for your ssh tunnel to the `sshTunnel` option in the config file. If this option is defined, your migration scripts will automatically be run via the SSH tunnel. This feature uses the [tunnel-ssh](https://github.com/agebrock/tunnel-ssh#readme) package to wrap database connections, and borrows the same configuration options, therefore the docs for the repo are a good place to look for further clarifications for this functionality. + ### Version To know which version of migrate-mongo you're running, just pass the `version` option: diff --git a/lib/env/database.js b/lib/env/database.js index 70ce8fd..14b804e 100644 --- a/lib/env/database.js +++ b/lib/env/database.js @@ -1,5 +1,6 @@ const { MongoClient } = require("mongodb"); const _ = require("lodash"); +const tunnel = require("tunnel-ssh"); const config = require("./config"); module.exports = { @@ -8,15 +9,30 @@ module.exports = { const url = _.get(configContent, "mongodb.url"); const databaseName = _.get(configContent, "mongodb.databaseName"); const options = _.get(configContent, "mongodb.options"); + const sshOptions = _.get(configContent, "sshTunnel"); if (!url) { throw new Error("No `url` defined in config file!"); } - const client = await MongoClient.connect( - url, - options - ); + if (sshOptions) + return new Promise((resolve) => { + tunnel(sshOptions, async (err) => { + if (err) throw err; + console.log("tunnel connected..."); + + const client = await MongoClient.connect(url, options); + + const db = client.db(databaseName); + db.close = client.close; + resolve({ + client, + db, + }); + }); + }); + + const client = await MongoClient.connect(url, options); const db = client.db(databaseName); db.close = client.close; @@ -24,5 +40,5 @@ module.exports = { client, db, }; - } + }, }; diff --git a/package-lock.json b/package-lock.json index c70bd04..b0f23af 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,8 @@ "fs-extra": "^9.1.0", "lodash": "^4.17.21", "mongodb": "^3.6.4", - "p-each-series": "^2.2.0" + "p-each-series": "^2.2.0", + "tunnel-ssh": "^4.1.4" }, "bin": { "migrate-mongo": "bin/migrate-mongo.js" @@ -653,7 +654,6 @@ "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "dev": true, "dependencies": { "safer-buffer": "~2.1.0" } @@ -2718,6 +2718,11 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" + }, "node_modules/lodash.flattendeep": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", @@ -3941,8 +3946,7 @@ "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/saslprep": { "version": "1.0.3", @@ -4168,6 +4172,30 @@ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, + "node_modules/ssh2": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-0.5.4.tgz", + "integrity": "sha1-G/a2soyW6u8mf01sRqWiUXpZnic=", + "dependencies": { + "ssh2-streams": "~0.1.15" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ssh2-streams": { + "version": "0.1.20", + "resolved": "https://registry.npmjs.org/ssh2-streams/-/ssh2-streams-0.1.20.tgz", + "integrity": "sha1-URGNFUVV31Rp7h9n4M8efoosDjo=", + "dependencies": { + "asn1": "~0.2.0", + "semver": "^5.1.0", + "streamsearch": "~0.1.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/sshpk": { "version": "1.16.1", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", @@ -4188,6 +4216,14 @@ "node": ">=0.10.0" } }, + "node_modules/streamsearch": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", + "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=", + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -4434,6 +4470,29 @@ "node": "*" } }, + "node_modules/tunnel-ssh": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tunnel-ssh/-/tunnel-ssh-4.1.4.tgz", + "integrity": "sha512-CjBqboGvAbM7iXSX2F95kzoI+c2J81YkrHbyyo4SWNKCzU6w5LfEvXBCHu6PPriYaNvfhMKzD8bFf5Vl14YTtg==", + "dependencies": { + "debug": "2.6.9", + "lodash.defaults": "^4.1.0", + "ssh2": "0.5.4" + } + }, + "node_modules/tunnel-ssh/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/tunnel-ssh/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, "node_modules/tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", @@ -5312,7 +5371,6 @@ "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "dev": true, "requires": { "safer-buffer": "~2.1.0" } @@ -6939,6 +6997,11 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" + }, "lodash.flattendeep": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", @@ -7897,8 +7960,7 @@ "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "saslprep": { "version": "1.0.3", @@ -8085,6 +8147,24 @@ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, + "ssh2": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-0.5.4.tgz", + "integrity": "sha1-G/a2soyW6u8mf01sRqWiUXpZnic=", + "requires": { + "ssh2-streams": "~0.1.15" + } + }, + "ssh2-streams": { + "version": "0.1.20", + "resolved": "https://registry.npmjs.org/ssh2-streams/-/ssh2-streams-0.1.20.tgz", + "integrity": "sha1-URGNFUVV31Rp7h9n4M8efoosDjo=", + "requires": { + "asn1": "~0.2.0", + "semver": "^5.1.0", + "streamsearch": "~0.1.2" + } + }, "sshpk": { "version": "1.16.1", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", @@ -8102,6 +8182,11 @@ "tweetnacl": "~0.14.0" } }, + "streamsearch": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", + "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=" + }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -8313,6 +8398,31 @@ "safe-buffer": "^5.0.1" } }, + "tunnel-ssh": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tunnel-ssh/-/tunnel-ssh-4.1.4.tgz", + "integrity": "sha512-CjBqboGvAbM7iXSX2F95kzoI+c2J81YkrHbyyo4SWNKCzU6w5LfEvXBCHu6PPriYaNvfhMKzD8bFf5Vl14YTtg==", + "requires": { + "debug": "2.6.9", + "lodash.defaults": "^4.1.0", + "ssh2": "0.5.4" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, "tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", diff --git a/package.json b/package.json index 1a97ec2..beec16a 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,8 @@ "fs-extra": "^9.1.0", "lodash": "^4.17.21", "mongodb": "^3.6.4", - "p-each-series": "^2.2.0" + "p-each-series": "^2.2.0", + "tunnel-ssh": "^4.1.4" }, "devDependencies": { "chai": "^4.3.3", diff --git a/samples/migrate-mongo-config.js b/samples/migrate-mongo-config.js index 7993812..018850b 100644 --- a/samples/migrate-mongo-config.js +++ b/samples/migrate-mongo-config.js @@ -1,6 +1,20 @@ // In this file you can configure migrate-mongo const config = { + // configure ssh tunnel, see https://github.com/agebrock/tunnel-ssh#readme + sshTunnel: { + // username:'root', + // password:'secret', + // host:sshServer, + // port:22, + // dstHost:destinationServer, + // dstPort:27017, + // privateKey:require(fs).readFileSync('/path/to/key'), + // passphrase:'secret', + // localHost:'127.0.0.1', + // localPort: 27000 + }, + mongodb: { // TODO Change (or review) the url to your MongoDB: url: "mongodb://localhost:27017",