Node.js SSH Server Example

  • Author:Robert Whitney
  • Date:2018/01/14

This server accepts predefined commands and offers no real shell. This is an example to show you how to create your own custom commands on an ssh server using the SSH2 library in Node.js.

Before you can run this server you will need to run the following command in the same directory as server.js

$ npm install --save crypto util uptimer ssh2 buffer-equal-constant-time

Then you will need to generate an ssh host keypair for your server, and save the private key file as ssh.key.
Place your public key into the directory as ssh.pub.

Once the server is running you can connect to it via any ssh client using any username provided that your ssh key matches. ($ ssh [email protected] -p 2222).

Commands

  • uptime - Displays uptime information from the OS and application.
  • whoami - Displays the username that you signed in with.
  • echo - Echos back the text following the command.
  • exit - Exits the session and closes the connection.

server.js:

var fs = require('fs');
var crypto = require('crypto');
var inspect = require('util').inspect;
var uptimer = require('uptimer')
var username = null;
var buffersEqual = require('buffer-equal-constant-time');
var ssh2 = require('ssh2');
var utils = ssh2.utils;

var pubKey = utils.genPublicKey(utils.parseKey(fs.readFileSync('ssh.pub')));

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

  client.on('authentication', function(ctx) {
      username = ctx.username;
    if (ctx.method === 'password')
    {
      ctx.reject();
    }
    else if (ctx.method === 'publickey'
             && ctx.key.algo === pubKey.fulltype
             && buffersEqual(ctx.key.data, pubKey.public)) {
      if (ctx.signature) {
        var verifier = crypto.createVerify(ctx.sigAlgo);
        verifier.update(ctx.blob);
        if (verifier.verify(pubKey.publicOrig, ctx.signature))
          ctx.accept();
        else
          ctx.reject();
      } else {
        // if no signature present, that means the client is just checking
        // the validity of the given public key
        ctx.accept();
      }
    } else
      ctx.reject();
  }).on('ready', function() {
    console.log('Client authenticated!');

    client.on('session', function(accept, reject) {
      var session = accept();
      session.once('shell', function(accept, reject, info) {
        var stream = accept();
        stream.write("$ ");
        stream.on('data', function(data) {
            console.log(data.toString().replace("\n\n", "\n"));
            var args = data.toString().replace("\n","").split(" ");
            switch(args[0])
            {
                case "uptime":
                    stream.write("System Uptime: " + uptimer.getSystemUptime() + "\n");
                    stream.write("Server Uptime: " + uptimer.getAppUptime() + "\n");
                    break;
                case "echo":
                    args.shift();
                    stream.write(args.join(" ") + "\n");
                    break;
                case "whoami":
                    stream.write(username + "\n");
                    break;
                case "exit":
                    stream.exit(0);
                    stream.end();
                    stream = undefined;
                    break;
                default:
                    stream.stderr.write(args[0] + ": No such command!\n");
                    break;
            }
            if(typeof stream != 'undefined')
            {
                stream.write("$ ");
            }
        });
      });
    });
  }).on('end', function() {
    console.log('Client disconnected');
  });
}).listen(2222, '127.0.0.1', function() {
  console.log('Listening on port ' + this.address().port);
});
Robert Whitney
I'm a geek, gamer and breaker of things. I'm a programmer by day and an apache attack helicopter by night. Some would call me their spirit animal.
Opinions expressed here, even 💩 ones, are my own and do not represent those of my employer or associates.
Referral Links

Using my referral links is the best way to help me pay for my projects and development servers and get something out of it for yourself.

Copyright©2011 - 2018, Robert Whitney; All rights reserved.