Coder Social home page Coder Social logo

Tunnel with forwardOut question. about ssh2 HOT 14 CLOSED

mscdex avatar mscdex commented on May 29, 2024
Tunnel with forwardOut question.

from ssh2.

Comments (14)

mscdex avatar mscdex commented on May 29, 2024 1

Ok, check out the previous example again, I had also forgotten about the need to manually emit 'connect' on the stream.

from ssh2.

mscdex avatar mscdex commented on May 29, 2024 1

@jpillora and anyone else who may be interested:

Here's an example HTTP/HTTPS Agent implementation that tunnels requests over SSH. It could be improved a bit (e.g. actual/proper ref()/unref() handling) and it might be possible to simplify it a bit because for example, I think node may retry requests that never received a socket, so we may not need to manually handle the 'ready' event handlers.

var HttpAgent = require('http').Agent;
var HttpsAgent = require('https').Agent;
var inherits = require('util').inherits;

var Client = require('ssh2').Client;

[HttpAgent, HttpsAgent].forEach(function(ctor) {
  function SSHAgent(connectCfg, agentOptions) {
    if (!(this instanceof SSHAgent))
      return new SSHAgent(connectCfg, agentOptions);

    ctor.call(this, agentOptions);

    createSSH(this, connectCfg);
    this._defaultSrcIP = (agentOptions && agentOptions.srcIP) || 'localhost';
  }
  inherits(SSHAgent, ctor);

  SSHAgent.prototype.createConnection = createConnection;

  exports[ctor === HttpAgent ? 'SSHTTPAgent' : 'SSHTTPSAgent'] = SSHAgent;
});

function createConnection(options, cb) {
  if (!this._ready) {
    const onReady = () => { this.createConnection(options, cb); };
    onReady.httpCb = cb;
    this._client.once('ready', onReady);
    return;
  }

  var srcIP = (options && options.localAddress) || this._defaultSrcIP;
  var srcPort = (options && options.localPort) || 0;
  var dstIP = options.host;
  var dstPort = options.port;

  this._client.forwardOut(srcIP, srcPort, dstIP, dstPort, (err, stream) => {
    if (err)
      return cb(err);
    cb(null, decorateStream(stream));
  });
}

function createSSH(agent, config) {
  agent._ready = false;
  agent._client = new Client();
  agent._client.on('ready', function() {
    agent._ready = true;
  }).on('error', function(err) {
    // Treat all errors has fatal, canceling pending http
    // requests
    const readies = this.listeners('ready');
    for (var i = 0; i < readies.length; ++i) {
      const fn = readies[i];
      if (typeof fn.httpCb === 'function')
        fn.httpCb(err);
    }
    this.removeAllListeners('ready');
  }).on('close', function() {
    createSSH(agent, config);

    // Re-add callbacks for http requests that were waiting
    // for an SSH connection
    const readies = this.listeners('ready');
    for (var i = 0; i < readies.length; ++i) {
      const fn = readies[i];
      if (typeof fn.httpCb === 'function')
        agent._client.once('ready', fn);
    }
  }).connect(config);
}

function decorateStream(stream) {
  stream.setKeepAlive = noop;
  stream.setNoDelay = noop;
  stream.setTimeout = noop;
  stream.ref = noop;
  stream.unref = noop;
  stream.destroySoon = stream.destroy;
  return stream;
}

function noop() {}

Then use like:

// Or use `SSHTTPSAgent` for HTTPS
const httpAgent = new SSHTTPAgent({
  host: '192.168.100.1',
  username: 'foo',
  agent: process.env.SSH_AUTH_SOCK
});

require('http').get({
  host: '192.168.200.1',
  agent: httpAgent
}, (res) => {
  console.log(res.statusCode);
  console.dir(res.headers);
  res.resume();
});

from ssh2.

jpillora avatar jpillora commented on May 29, 2024 1

Published ssh-http-agent:

const sshAgent = require("ssh-http-agent");
const http = require("http");

https.get(
  {
    host: "echo.jpillora.com",
    path: "/foo/bar",
    agent: sshAgent.http({
      host: "1.2.3.4",
      port: 22,
      username: "root",
      password: "supersecret"
    })
  },
  res => {
    console.log(res.headers);
  }
);

I thought Node's http would call ref and unref though it doesn't seem to...

from ssh2.

mscdex avatar mscdex commented on May 29, 2024

forwardOut() doesn't actually listen on the local port (2221). If you want to do that and pipe to stream and back on incoming connections, that's up to you.

However for connection hopping like this, you can specify a sock connection setting that can be used instead of a hostname and port. Example:

var  fs = require('fs'),
     Connection = require('ssh2');

var c1 = new Connection();

c1.connect({
  host: '[F/WALL IP]', 
  port: 222, 
  username: '[F/WALL UNAME]', 
  privateKey: require('fs').readFileSync('./keys/sample.key') 
});

c1.on('ready', function(){
  c1.exec('nc [SERVER IP] 22', function(err, stream){

    if(err) throw err;
    console.log('ForwardOut Ready!');

    var c2 = new Connection();

    c2.connect({
      sock: stream,
      username: '[SERVER UNAME]', 
      password: '[SERVER PASSWORD]' 
    });

    c2.on('ready', function(){
      console.log('Connected!');
    });

    c2.on('error', function(err) {
      console.log('Connection2 :: error :: ' + err);
    });
    stream.emit('connect'); // needed to simulate a TCP socket
  })
});

c1.on('error', function(err) {
  console.log('Connection1 :: error :: ' + err);
});

from ssh2.

matouka avatar matouka commented on May 29, 2024

That looks perfect.. However, when I run that code and expect to see:

ForwardOut Ready!
Connected!

But I just get:

ForwardOut Ready!

and it just sits there, so I'm assuming something still isn't right if "ready" isn't being triggered?

from ssh2.

mscdex avatar mscdex commented on May 29, 2024

@matouka I've updated the previous example. I know using netcat works for tunneling.

from ssh2.

matouka avatar matouka commented on May 29, 2024

Thanks for such quick help! It's very much appreciated.

I tried that and it seems to just be stopping at the same spot.

To see if I could see what was being sent I opened up a listening port on the server nc -l 2222 and then tried to see if anything came through, and surprisingly it didn't.

The interesting thing though, was that when I manually did it from the firewall nc [serverIP] 2222 `it worked fine, everything I typed was transferred.

NodeJS seems to open a connection to the server though, because when the command is run in Node I then can't connect to that port on the server from another computer. It seems that it the c2.connect function though is not sending the data though.

#Edit
Actually, I just stuck the server in verbose mode and got a little more.

Listening on [0.0.0.0] (family 0, port 2222)
Connection from [192.168.0.1] port 2222 [tcp/*] accepted (family 2, sport 46775)

Then is just stops.

from ssh2.

matouka avatar matouka commented on May 29, 2024

Mate, you are a legend!!!

Thanks so much for all your help!

from ssh2.

jpillora avatar jpillora commented on May 29, 2024

@mscdex Hey Brian, is this possible without nc installed on the ssh host?

from ssh2.

mscdex avatar mscdex commented on May 29, 2024

@jpillora Yes, you can use forwardOut() to get a similarly suitable stream for setting sock.

from ssh2.

jpillora avatar jpillora commented on May 29, 2024

Thanks @mscdex ! Got it working:

const http = require("http");
const tls = require("tls");
const Client = require("ssh2").Client;
//borrowed from
//https://github.com/mafintosh/http-ssh-agent/blob/master/tcpish.js
// (note: i need https, which http-ssh-agent does not support)
const tcpish = require("./tcpish");

const config = {
  //tunnel via this host
  ssh: {
    host: "1.1.1.1",
    port: 22,
    username: "root",
    password: "supersecret"
  },
  //and then make an http request against this host
  http: {
    host: "2.2.2.2",
    port: 443
  }
};

function httpReady(httpSock) {
  const req = http.request(
    {
      method: "GET",
      host: config.http.host,
      path: "/",
      createConnection(opts) {
        var t = tcpish();
        t.connect(httpSock);
        return t;
      }
    },
    res => {
      console.log(`response ${res.statusCode}`);
      res.on("data", d => console.log(d.toString()));
    }
  );
  req.on("error", e => {
    console.error("request error", e);
  });
  req.end();
}

function handleForward(err, tcpSock) {
  if (err) {
    console.log("forward err", err);
    return;
  }
  console.log("forward open");
  tcpSock.on("close", function() {
    console.log("forward closed");
    conn.end();
  });
  tcpSock.on("data", function(data) {
    console.log("tcp data:", data.length);
  });
  //in order to speak https, we
  //manually upgrade tcp to tls
  let tlsSock = tls.connect({
    socket: tcpSock,
    rejectUnauthorized: false, //localhost testing
    servername: config.http.host //support SNI
  });
  tlsSock.on("secureConnect", function() {
    console.log("tls connect");
    tlsSock.setEncoding("utf8");
    httpReady(tlsSock);
  });
  tlsSock.on("data", function(data) {
    console.log("tls data:", data.length);
  });
  tlsSock.on("tlsClientError", function(err) {
    console.log("tls client error", err);
  });
  tlsSock.on("error", function(err) {
    console.log("tls error", err);
  });
  tlsSock.on("end", function() {
    console.log("tls end");
  });
}

var conn = new Client();

conn.on("ready", function() {
  console.log("ssh ready");
  conn.forwardOut(
    "127.0.0.1",
    0,
    config.http.host,
    config.http.port,
    handleForward
  );
});

console.log("ssh connect");
conn.connect(config.ssh);

Warning for anyone using this: This has many debug console logs and does not do proper error checking.

Suggestion: Maybe forwardOut could optionally omit local interface/port (default to 127.0.0.1/0)?

from ssh2.

jpillora avatar jpillora commented on May 29, 2024

Ah cool, thanks

from ssh2.

fmaldonados avatar fmaldonados commented on May 29, 2024

server.start(options, async () => {
conn.on('ready', async function (err) {
if (err) throw err;
console.log('Client :: ready');
conn.forwardOut('localhost', 22, 'remote address', 8003, async function (err, stream) {
if (err) throw err;
//ORACLE CONNECTION
console.log(>> HERMES API is up and running in port ${options.port});
});
}).connect({
host: 'host-name',
port: 22,
username: 'user',
privateKey: require('fs').readFileSync('file.pem'),
readyTimeout: 40000
});
});

Hello I'm trying to use tunneling ssh using the code above with nodejs for connecting to an Oracle db but it's not working, any idea of what is going on?

from ssh2.

aeastern avatar aeastern commented on May 29, 2024

Does forwardOut method can only local forward 127.0.0.1:0?

from ssh2.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.