how do I use npm module ssh2 to tunnel a port

857 views Asked by At

I have a working node.js Server that uses a socket.io websocket connection and ssh2 connection to open a shell and return it to a react client in the browser that uses xterm-for-react. I'm now trying to also tunnel a port over the same ssh connection but am having some difficulty. The code is below and the commented out part is where I've been trying to do the port forwarding - equivalent to ssh -L 2233:192.168.2.1:80 [email protected]

    const socketIO = require("socket.io");
    const sshClient = require("ssh2").Client;
    const utf8 = require("utf8");
    const fs = require("fs");
    
    class SocketServiceFwd {
      constructor() {
        this.socket = null;
        this.pty = null;
        this.devicesList = [
          {
            name: "Device 1",
            host: "192.168.0.65",
            port: "22",
            username: "test",
            password: "test12345",
          },
        ];

      attachServer(server) {
        if (!server) {
          throw new Error("Server not found...");
        }
    
        const io = socketIO(server, {cors: {origin: "*",},});
        console.log("Created socket server. Waiting for client connection.");
        // "connection" event happens when any client connects to this io instance.
        io.on("connection", (socket) => {
          console.log("Client connect to socket.", socket.id);
    
          this.socket = socket;
          const ssh = new sshClient();
    
          this.socket.on("devices", (cb) => {
            console.log("Get devices list from " + socket.id);
            return cb(null,this.devicesList.map((d) => ({ name: d.name }))
            );
          });
    
          this.socket.on("connectToDevice", (deviceName) => {
            console.log(socket.id + " connecting to device " + deviceName);
            const selectedDevice = this.devicesList.find(
              (d) => d.name === deviceName
            );
    
            if (selectedDevice) {
              ssh.on("ready", function () {
                console.log(socket.id + " successfully connected to " + deviceName);
                socket.emit("output", "\r\n*** SSH CONNECTION ESTABLISHED ***\r\n");
                // ssh.forwardOut("127.0.0.1", 2233, "192.168.2.1", 80, 
                //   (err, stream) => {
                //     console.log(
                //       "ssh forwardOut '127.0.0.1:2233' '192.168.2.1:80'"
                //     );
                //     if (err)
                //       return socket.emit(
                //         "output",
                //         "\r\n*** SSH FORWARDOUT ERROR: " + err.message + " ***\r\n"
                //       );
                //   }
                // );
                ssh.shell(function (err, stream) {
                  if (err)
                    return socket.emit(
                      "output",
                      "\r\n*** SSH SHELL ERROR: " + err.message + " ***\r\n"
                    );
    
                  socket.on("input", function (data) {
                    stream.write(data);
                  });
    
                  stream.on("data", function (d) {
                    socket.emit("output", utf8.decode(d.toString("binary")));
                  });
                  stream.on("close", function () {
                    console.log(socket.id + " terminal stream close");
                    ssh.end();
                  });
                });
              });
              ssh.on("close", function () {
                console.log(socket.id + " ssh connection closed to " + deviceName);
                socket.emit("output", "\r\n*** SSH CONNECTION CLOSED ***\r\n");
                socket.removeListener("input", () => {});
              });
              ssh.on("error", function (err) {
                console.log(err);
                socket.emit("output","\r\n*** SSH CONNECTION ERROR: " + err.message + " ***\r\n");
              });
              ssh.connect(selectedDevice);
            }
          });
    
          this.socket.on("disconnectFromDevice", () => {
            console.log(socket.id + " disconnecting from device");
            ssh.destroy();
          });
        });
      }
    }
    
    module.exports = SocketServiceFwd;

***** EDITED ***** It would be great if someone could please provide some advice :-) I have been pushing ahead but still need some assistance... I have had a little more success using the npm "tunnel-ssh" module. The tunnel does open long enough for me to view the website but shortly after that, the tunnel stops working. Is there something else I'm supposed to be doing after opening the tunnel?

The code below goes where the commented out section above is. I have tried to follow the examples I've seen online - can anyone please advise me what I'm doing wrong?

I'm running DEBUG=* in my npm start and see the following :

  tunnel-ssh sshConnection:ready +3s
  tunnel-ssh sshConnection:ready +1ms
  tunnel-ssh sshStream:create +19ms
  tunnel-ssh sshStream:create +0ms
TCP :: ERROR: read ECONNRESET
TCP :: ERROR: read ECONNRESET

So It's clear that the tunnel starts up but then an error occurs. Often the connection recovers and I can continue browsing the website through the tunnel, but eventually an unrecoverable error occurs. So the tunnel is unstable. If i run the tunnel using openssh it is stable. Therefore my question really now boils down to a request for assistance in determining reasons for the instability. Am I not handling something in my code or my configuration correctly? Please Help! Note also, that despite this my shell session continues to work without any problem.

const tunnel = require("tunnel-ssh");
const tnl = tunnel({host: '192.168.0.65', username: 'test', password: 'test12345', 
        dstHost: '192.168.2.1', dstPort: 80, localHost: '127.0.0.1',
        localPort: 2233}, (er, stream) => {
    if(er) {
        console.log("something went wrong " + er.message);
        stream.close();
    }
    stream.on("data", (data) => {
        console.log("TCP :: DATA: " + data);
    });
    stream.on("error", (error) => {
        console.log("TCP :: ERROR: " + error.message);
    }); 
});
0

There are 0 answers