Search code examples
node.jssshserverforwarding

Create a Node SSH2 Server with ability to treat Remote Forwarding


After some research on staskoverflow, Google and official Node SSH2 repository, I still can not create a Node SSH2 server which work with remote port forwarding...

Actually I can do what I want with standard SSH daemon on distant server and this command on client side :

ssh -R 8100:localsite.tld:80 sub.distantserver.com

All traffic from sub.distantserver.com:8100 is redirect to localsite.tld:80. (port 22, traditional SSH daemon).

My only goal is to achieve this with a Node SSH2 Server on port 21 :

ssh -R 8100:localsite.tld:80 [email protected] -p 21
password: bar

When it'll work, I can do some check on the user and start some other process ;)

Basing on official Github issues example I try something like this, just like a POC to catch stream but it fail on forwardIn that does not exists.

var fs = require('fs');
var crypto = require('crypto');
var inspect = require('util').inspect;

var buffersEqual = require('buffer-equal-constant-time');
var ssh2 = require('ssh2');
var utils = ssh2.utils;

new ssh2.Server({
  hostKeys: [fs.readFileSync('/etc/ssh/ssh_host_rsa_key')]
}, function(client) {
  console.log('Client connected!');

  client.on('authentication', function(ctx) {
    if (ctx.method === 'password'
        && ctx.username === 'foo'
        && ctx.password === 'bar')
      ctx.accept();
    else
      ctx.reject();
  }).on('ready', function() {
    console.log('Client authenticated!');

    client.on('session', function(accept, reject) {
      var session = accept();
      session.once('exec', function(accept, reject, info) {
        console.log('Client wants to execute: ' + inspect(info.command));
        var stream = accept();
        stream.stderr.write('Oh no, the dreaded errors!\n');
        stream.write('Just kidding about the errors!\n');
        stream.exit(0);
        stream.end();
      });
    });
    client.on('request', function(accept, reject, name, info) {
        console.log(info);
        if (name === 'tcpip-forward') {
        accept();
        setTimeout(function() {
          console.log('Sending incoming tcpip forward');
          client.forwardIn(info.bindAddr,
                           info.bindPort,
                           function(err, stream) {
                                if (err)
                                  return;
                                stream.end('hello world\n');
                            });
        }, 1000);
      } else {
        reject();
      }
    });
  });
}).listen(21, '0.0.0.0', function() {
  console.log('Listening on port ' + this.address().port);
});

Does anybody know how to achieve a simple conventional SSH forward server side ?

Thanks !


Solution

  • Found a solution with author's help : Official Github solution on issue

    let fs = require('fs'),
      inspect = require('util').inspect,
      ssh2 = require('ssh2'),
      net = require('net');
    
    new ssh2.Server({
      hostKeys: [fs.readFileSync('/etc/ssh/ssh_host_rsa_key')]
    }, client => {
      console.log('Client connected!');
      client
        .on('authentication', ctx => {
          if (
            ctx.method === 'password'
            && ctx.username === 'foo'
            && ctx.password === 'bar'
          ) {
            ctx.accept();
          } else {
            ctx.reject();
          }
        })
        .on('ready', () => {
          console.log('Client authenticated!');
          client
            .on('session', (accept, reject) => {
              let session = accept();
              session.on('shell', function(accept, reject) {
                let stream = accept();
              });
            })
            .on('request', (accept, reject, name, info) => {
              if (name === 'tcpip-forward') {
                accept();
                net.createServer(function(socket) {
                  socket.setEncoding('utf8');
                  client.forwardOut(
                    info.bindAddr, info.bindPort,
                    socket.remoteAddress, socket.remotePort,
                    (err, upstream) => {
                      if (err) {
                        socket.end();
                        return console.error('not working: ' + err);
                      }
                      upstream.pipe(socket).pipe(upstream);
                    });
                }).listen(info.bindPort);
              } else {
                reject();
              }
            });
        });
    }).listen(21, '0.0.0.0', function() {
      console.log('Listening on port ' + server.address().port);
    });