Troy Griffiths
Troy Griffiths

Reputation: 361

Node.js connecting through ssh

I have a node.js server that works but needs to be set up for ssh connections:

var mysql = require('mysql')
var io = require('socket.io').listen(3000)
var db = mysql.createConnection({

    host: 'hostname',
    user: 'username',
    password: '12345',
    database: '12345',
    port: 3306,
    socket: '/var/run/mysqld/mysqld.sock' 
})

db.connect(function(err){
    if (err) console.log(err)
})

I'm aware that there are ssh npm libraries for this purpose, however the options available (ssh2, node-sshclient, etc) appear to deal with pretty intricate features that may overcomplicate things. I'm looking for the simplest way to connect to my mysql db through ssh. What would be the best way to accomplish this?

Upvotes: 10

Views: 17931

Answers (2)

swimmer
swimmer

Reputation: 3313

Sometimes it's preferrable to instantiate the SSH tunnel connection dynamically (in code) rather than separately using OS libraries. For example, it makes it easier to automatically close the connection, share the environment with other developers, or conditionally use an SSH tunnel depending on the environment.

With packages such as tunnel-ssh, this is easy. Building on the example provided, the connection code would look like:

import { createSSHTunnel } from "./sshTunnel";

const { srcAddr, srcPort } = await createSSHTunnel();

var db = mysql.createConnection({
    host: srcAddr,
    port: srcPort,
    user: 'username',
    password: '12345',
    database: '12345'  
});

With all logic cleanly abstracted away in the sshTunnel module, that could look like:

// sshTunnel.js

import { createTunnel } from "tunnel-ssh";

export async function createSSHTunnel(srcAddr = "127.0.0.1", srcPort = 12345) {
  const tunnelOptions = {
    autoClose: true,
  };
  const serverOptions = {
    port: srcPort,
  };
  const sshOptions = {
    host: process.env.SSH_HOST,
    port: parseInt(process.env.SSH_PORT),
    username: process.env.SSH_TUNNEL_USER,
    password: process.env.SSH_TUNNEL_PASSWORD,
  };
  const forwardOptions = {
    srcAddr: srcAddr,
    srcPort: srcPort,
    dstAddr: process.env.DB_HOST,
    dstPort: parseInt(process.env.DB_PORT),
  };
  try {
    await createTunnel(
      tunnelOptions,
      serverOptions,
      sshOptions,
      forwardOptions
    );
  } catch (error) {
    if (error.code === "EADDRINUSE") {
      // Assume port is uniquely used by SSH tunnel, so existing connection can be reused
      console.log(`Returning existing SSH tunnel on ${srcAddr}:${srcPort}.`);
      return { srcAddr, srcPort };
    } else {
      throw error;
    }
  }
  console.log(`SSH tunnel successfully created on ${srcAddr}:${srcPort}.`);
  return { srcAddr, srcPort };
}

Remarks:

  • The SSH tunnel arbitrarily uses local port 12345
  • The environment variables involved are:
    • DB_HOST: the database hostname
    • DB_PORT: the database port, 3306 in the original MySQL example, 5432 for Postgres etc.
    • SSH_HOST: the hostname of the machine serving the SSH tunnel
    • SSH_PORT: the port of the machine serving the SSH tunnel
    • SSH_TUNNEL_USER: the username for the SSH tunnel
    • SSH_TUNNEL_PASSWORD: the password for the SSH tunnel

Upvotes: 4

Bernie
Bernie

Reputation: 5055

If you are running a linux/unix system do the following:

Connect to your mysql server via ssh and proxy the mysql port (default is 3306) via this ssh tunnel.

This works as follows:

1 Type in screen (to start a screen session which is permanent even if the shell gets closed).

2 Type into screen shell:

ssh -L 3306:127.0.0.1:3306 your_servers_domain_or_ip -lyour_login_name

3 Enter your ssh password / or use a PKI auth to avoid manual steps

4 Done... now it’s possible to connect MySQL like you would do when it’s installed on the same machine as your application.

Connect to MySQL from node.js like below:

var db = mysql.createConnection({
    host: '127.0.0.1', // Important to connect to localhost after connecting via ssh in screen
    user: 'username',
    password: '12345',
    database: '12345',
    port: 3306
});

Upvotes: 35

Related Questions