javascriptnode.jslibssh2ssh2-sftp

ssh2: can't run the server on node js


I'm sorry if this question is duplicate, but I can't find the solution.

Seems my problem is very simple but I can't understand what I'm doing wrong. I want to set up ssh client to connect to it and make some commands in shell. So the first thing - I chose ssh2 library and tried to use the following code.

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;

var pubKey = utils.genPublicKey(
  utils.parseKey(fs.readFileSync("user.pub")),
);

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

    client
      .on("authentication", function(ctx) {
        if (
          ctx.method === "password" &&
          // Note: Don't do this in production code, see
          // https://www.brendanlong.com/timing-attacks-and-usernames.html
          // In node v6.0.0+, you can use `crypto.timingSafeEqual()` to safely
          // compare two values.
          ctx.username === "foo" &&
          ctx.password === "bar"
        )
          ctx.accept();
        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("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();
          });
        });
      })
      .on("end", function() {
        console.log("Client disconnected");
      });
  },
).listen(0, "127.0.0.1", function() {
  console.log("Listening on port " + this.address().port);
});

I put it into index.js file. Before using this code:

  1. I set up key pair via command ssh-keygen -t rsa -b 4096 , got 2 keys ssh.pub and ssh;
  2. Then renamed to user.pub and host.key;
  3. After that I made ssh-add host.key. Response was Identity added: host.key (host.key). Seems everything is fine.

I ran node index.js and had following error:

throw new Error('Missing passphrase for encrypted private key')

What I did wrong? Maybe wrong command to create a key pair? I'll be very appreciated if you'll help me.


Solution

  • So the problem stems from the fact that you're (as you admitted in the comments) are using a passphrase-protected host key. ssh2 naturally needs the passphrase too.

    The documentation says

    hostKeys - array - An array of either Buffers/strings that contain host private keys or objects in the format of { key: <Buffer/string>, passphrase: <string> } for encrypted private keys. (Required)

    so try

    {
      hostKeys: [
        {
          key: fs.readFileSync('...'),
          passphrase: 'the-passphrase-you-used',
        },
      ],
    }
    

    Without passphrases

    I'm unable to reproduce this problem (with simpler code):

    $ yarn add ssh2
    $ cat > index.js
    
    var fs = require("fs");
    var crypto = require("crypto");
    var inspect = require("util").inspect;
    
    var ssh2 = require("ssh2");
    var utils = ssh2.utils;
    
    new ssh2.Server(
      {
        hostKeys: [fs.readFileSync("host.key")],
      },
      function(client) {
        console.log("Client connected!");
    
        client
          .on("authentication", function(ctx) {
            ctx.accept();
          })
          .on("ready", function() {
            console.log("Client authenticated!");
            client.on("session", function(accept, reject) {
              var session = reject();
              console.log("Client session rejected.");
            });
          })
          .on("end", function() {
            console.log("Client disconnected");
          });
      },
    ).listen(0, "127.0.0.1", function() {
      console.log("Listening on port " + this.address().port);
    });
    
    $ ssh-keygen -t rsa -b 1024
    Generating public/private rsa key pair.
    Enter file in which to save the key: ./host
    Enter passphrase (empty for no passphrase):
    Enter same passphrase again:
    Your identification has been saved in ./host.
    Your public key has been saved in ./host.pub.
    $ mv host host.key
    $ node index.js
    Listening on port 52182
    Client connected!
    Client authenticated!
    Client session rejected.
    Client disconnected
    

    (where my ssh connection attempt was:)

    $ ssh foo@127.0.0.1 -p 52182
    channel 0: open failed: administratively prohibited:
    Connection to 127.0.0.1 closed.
    ~ $