Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

I have been scouring the face of the web looking to answer a question which I had thought would be simple. My goal is straight forward. I want to build out a simple web-based SSH client using Node.js module(s). I have found several options if I want to connect to the node server itself, but can't seem to find any examples of connecting to a REMOTE server.

Essentially the outcome I am looking for is a workflow like this : Connect to webserver -> Click on a server name in a list of servers -> Enter SSH session to the server I clicked on

The only thing I have found that's even remotely close to what I am looking for is guacamole. I do not want to use guacamole, however, as I want this application to be OS independent. Currently I am building it on a windows 10 platform, and will port it over to fedora when I am done.

I found this tutorial for creating an SSH terminal. However, all this does is creates (or attempts to create) an SSH connection to the local system.

Another options that looked absolutely fantastic was tty.js. Alas, the bottom-line is the same as the above tutorial. The module only allows you to connect to the node.js server, NOT to remote servers.

Anyone have information on a possible path to this goal?

like image 560
Ethan Avatar asked Aug 01 '16 00:08

Ethan


People also ask

How do I link node JS and HTML?

For html page we have to use URL so for that in Node JS “url” module has been used so we have to add this module in our program file. And then we can get the path of request URL as shown below. var url=require("url"); var path=url.


2 Answers

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 memory var 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 serving function 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
like image 82
mscdex Avatar answered Oct 30 '22 04:10

mscdex


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 memory var 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 serving function 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); 
like image 43
bluepuma77 Avatar answered Oct 30 '22 04:10

bluepuma77