Connecting to remote SSH server (via Node.js/html5 console) Connecting to remote SSH server (via Node.js/html5 console) windows windows

Connecting to remote SSH server (via Node.js/html5 console)


This is easily doable with modules like ssh2, xterm, and socket.io.

Here's an example:

  1. npm install ssh2 xterm socket.io
  2. Create index.html:
<html>  <head>    <title>SSH Terminal</title>    <link rel="stylesheet" href="/src/xterm.css" />    <script src="/src/xterm.js"></script>    <script src="/addons/fit/fit.js"></script>    <script src="/socket.io/socket.io.js"></script>    <script>      window.addEventListener('load', function() {        var terminalContainer = document.getElementById('terminal-container');        var term = new Terminal({ cursorBlink: true });        term.open(terminalContainer);        term.fit();        var socket = io.connect();        socket.on('connect', function() {          term.write('\r\n*** Connected to backend***\r\n');          // Browser -> Backend          term.on('data', function(data) {            socket.emit('data', data);          });          // Backend -> Browser          socket.on('data', function(data) {            term.write(data);          });          socket.on('disconnect', function() {            term.write('\r\n*** Disconnected from backend***\r\n');          });        });      }, false);    </script>    <style>      body {        font-family: helvetica, sans-serif, arial;        font-size: 1em;        color: #111;      }      h1 {        text-align: center;      }      #terminal-container {        width: 960px;        height: 600px;        margin: 0 auto;        padding: 2px;      }      #terminal-container .terminal {        background-color: #111;        color: #fafafa;        padding: 2px;      }      #terminal-container .terminal:focus .terminal-cursor {        background-color: #fafafa;      }    </style>  </head>  <body>    <div id="terminal-container"></div>  </body></html>
  1. Create server.js:
var fs = require('fs');var path = require('path');var server = require('http').createServer(onRequest);var io = require('socket.io')(server);var SSHClient = require('ssh2').Client;// Load static files into memoryvar staticFiles = {};var basePath = path.join(require.resolve('xterm'), '..');[ 'addons/fit/fit.js',  'src/xterm.css',  'src/xterm.js'].forEach(function(f) {  staticFiles['/' + f] = fs.readFileSync(path.join(basePath, f));});staticFiles['/'] = fs.readFileSync('index.html');// Handle static file servingfunction onRequest(req, res) {  var file;  if (req.method === 'GET' && (file = staticFiles[req.url])) {    res.writeHead(200, {      'Content-Type': 'text/'                      + (/css$/.test(req.url)                         ? 'css'                         : (/js$/.test(req.url) ? 'javascript' : 'html'))    });    return res.end(file);  }  res.writeHead(404);  res.end();}io.on('connection', function(socket) {  var conn = new SSHClient();  conn.on('ready', function() {    socket.emit('data', '\r\n*** SSH CONNECTION ESTABLISHED ***\r\n');    conn.shell(function(err, stream) {      if (err)        return socket.emit('data', '\r\n*** SSH SHELL ERROR: ' + err.message + ' ***\r\n');      socket.on('data', function(data) {        stream.write(data);      });      stream.on('data', function(d) {        socket.emit('data', d.toString('binary'));      }).on('close', function() {        conn.end();      });    });  }).on('close', function() {    socket.emit('data', '\r\n*** SSH CONNECTION CLOSED ***\r\n');  }).on('error', function(err) {    socket.emit('data', '\r\n*** SSH CONNECTION ERROR: ' + err.message + ' ***\r\n');  }).connect({    host: '192.168.100.105',    username: 'foo',    password: 'barbaz'  });});server.listen(8000);
  1. Edit the SSH server configuration passed to .connect() in server.js
  2. node server.js
  3. Visit http://localhost:8000 in your browser


Just adding updated code to @mscdex great answer because the libraries have changed over the years.

Libraries:

npm install express socket.io ssh2 xterm xterm-addon-fit

index.html:

<html>  <head>    <title>SSH Terminal</title>    <link rel="stylesheet" href="/xterm.css" />    <script src="/xterm.js"></script>    <script src="/xterm-addon-fit.js"></script>    <script src="/socket.io/socket.io.js"></script>    <script>      window.addEventListener('load', function() {        var terminalContainer = document.getElementById('terminal-container');        const term = new Terminal({ cursorBlink: true });                const fitAddon = new FitAddon.FitAddon();        term.loadAddon(fitAddon);        term.open(terminalContainer);        fitAddon.fit();        var socket = io() //.connect();        socket.on('connect', function() {          term.write('\r\n*** Connected to backend ***\r\n');        });        // Browser -> Backend        term.onKey(function (ev) {          socket.emit('data', ev.key);        });        // Backend -> Browser        socket.on('data', function(data) {          term.write(data);        });        socket.on('disconnect', function() {          term.write('\r\n*** Disconnected from backend ***\r\n');        });      }, false);    </script>    <style>      body {        font-family: helvetica, sans-serif, arial;        font-size: 1em;        color: #111;      }      h1 {        text-align: center;      }      #terminal-container {        width: 960px;        height: 600px;        margin: 0 auto;        padding: 2px;      }      #terminal-container .terminal {        background-color: #111;        color: #fafafa;        padding: 2px;      }      #terminal-container .terminal:focus .terminal-cursor {        background-color: #fafafa;      }    </style>  </head>  <body>    <h3>WebSSH</h3>    <div id="terminal-container"></div>  </body></html>

server.js:

var fs = require('fs');var path = require('path');var server = require('http').createServer(onRequest);var io = require('socket.io')(server);var SSHClient = require('ssh2').Client;// Load static files into memoryvar staticFiles = {};var basePath = path.join(require.resolve('xterm'), '..');staticFiles['/xterm.css'] = fs.readFileSync(path.join(basePath, '../css/xterm.css'));staticFiles['/xterm.js'] = fs.readFileSync(path.join(basePath, 'xterm.js'));basePath = path.join(require.resolve('xterm-addon-fit'), '..');staticFiles['/xterm-addon-fit.js'] = fs.readFileSync(path.join(basePath, 'xterm-addon-fit.js'));staticFiles['/'] = fs.readFileSync('index.html');// Handle static file servingfunction onRequest(req, res) {  var file;  if (req.method === 'GET' && (file = staticFiles[req.url])) {    res.writeHead(200, {      'Content-Type': 'text/'        + (/css$/.test(req.url)        ? 'css'        : (/js$/.test(req.url) ? 'javascript' : 'html'))    });    return res.end(file);  }  res.writeHead(404);  res.end();}io.on('connection', function(socket) {  var conn = new SSHClient();  conn.on('ready', function() {    socket.emit('data', '\r\n*** SSH CONNECTION ESTABLISHED ***\r\n');    conn.shell(function(err, stream) {      if (err)        return socket.emit('data', '\r\n*** SSH SHELL ERROR: ' + err.message + ' ***\r\n');      socket.on('data', function(data) {        stream.write(data);      });      stream.on('data', function(d) {        socket.emit('data', d.toString('binary'));      }).on('close', function() {        conn.end();      });    });  }).on('close', function() {    socket.emit('data', '\r\n*** SSH CONNECTION CLOSED ***\r\n');  }).on('error', function(err) {    socket.emit('data', '\r\n*** SSH CONNECTION ERROR: ' + err.message + ' ***\r\n');  }).connect({    host: 'domain.tld',    port: 22,    username: 'root',    privateKey: require('fs').readFileSync('path/to/keyfile')  });});let port = 8000;console.log('Listening on port', port)server.listen(port);


Same as the above answer but actually using express and modern syntax and libraries

const express = require('express');const app = express();const http = require('http').Server(app);const io = require('socket.io')(http, {  cors: {    origin: "*"  }});app.set('view engine', 'ejs');app.use(express.urlencoded({  extended: false,  limit: '150mb'}));app.use(express.static(__dirname + '/public'));app.use('/xterm.css', express.static(require.resolve('xterm/css/xterm.css')));app.use('/xterm.js', express.static(require.resolve('xterm')));app.use('/xterm-addon-fit.js', express.static(require.resolve('xterm-addon-fit')));const SSHClient = require('ssh2').Client;app.get('/', (req, res) => {  // res.sendFile(__dirname + '/index.html');  res.render('index');  // I am using ejs as my templating engine but HTML file work just fine.});io.on('connection', function(socket) {  var conn = new SSHClient();  conn.on('ready', function() {    socket.emit('data', '\r\n*** SSH CONNECTION ESTABLISHED ***\r\n');    conn.shell(function(err, stream) {      if (err)        return socket.emit('data', '\r\n*** SSH SHELL ERROR: ' + err.message + ' ***\r\n');      socket.on('data', function(data) {        stream.write(data);      });      stream.on('data', function(d) {        socket.emit('data', d.toString('binary'));      }).on('close', function() {        conn.end();      });    });  }).on('close', function() {    socket.emit('data', '\r\n*** SSH CONNECTION CLOSED ***\r\n');  }).on('error', function(err) {    socket.emit('data', '\r\n*** SSH CONNECTION ERROR: ' + err.message + ' ***\r\n');  }).connect({    host: '192.168.0.103',    port: 22,    username: 'kali',    password: 'kali'  });});http.listen(3000, () => {  console.log('Listening on http://localhost:3000');});
* {  padding: 0%;  margin: 0%;  box-sizing: border-box;}body {  font-family: Helvetica, sans-serif, arial;  font-size: 1em;  color: #111;}h1 {  text-align: center;}#terminal-container {  width: 960px;  height: 600px;  margin: 0 auto;  padding: 2px;}#terminal-container .terminal {  background-color: #111;  color: #fafafa;  padding: 2px;}#terminal-container .terminal:focus .terminal-cursor {  background-color: #fafafa;}
<!DOCTYPE html><html lang="en"><head>  <meta charset="UTF-8">  <meta http-equiv="X-UA-Compatible" content="IE=edge">  <meta name="viewport" content="width=device-width, initial-scale=1.0">  <title>SSH SERVER</title>  <link rel="stylesheet" href="/xterm.css" />  <script defer src="/xterm.js"></script>  <script defer src="/xterm-addon-fit.js"></script>  <script defer src="/socket.io/socket.io.js"></script>  <script defer src='/js/app.js'></script>  <link rel='stylesheet' href='/css/main.css'></head><body>  <h3>WebSSH</h3>  <div id="terminal-container"></div>  <script>  // PLEASE USE A SEPERATE FILE FOR THE JS and defer it  // like the above app.js file     window.addEventListener('load', function() {      const terminalContainer = document.getElementById('terminal-container');      const term = new Terminal({        cursorBlink: true      });      const fitAddon = new FitAddon.FitAddon();      term.loadAddon(fitAddon);      term.open(terminalContainer);      fitAddon.fit();      const socket = io() //.connect();      socket.on('connect', function() {        term.write('\r\n*** Connected to backend ***\r\n');      });      // Browser -> Backend      term.onKey(function(ev) {        socket.emit('data', ev.key);      });      // Backend -> Browser      socket.on('data', function(data) {        term.write(data);      });      socket.on('disconnect', function() {        term.write('\r\n*** Disconnected from backend ***\r\n');      });    }, false);  </script></body></html>