Code Monkey home page Code Monkey logo

ssh2-promise's Introduction

SSH2-Promise

ssh2-promise is a powerful promise wrapper around ssh2 client. It supports all the ssh2 client operation such as connection hopping, exec, spawn, shell, sftp, open tunnel, open socks connection etc... in promisify and async-await way. It helps in caching the sshconnection, to reduce time, during connection hopping. It have reconnect logic, so that, once disconnected, it can retry the sshconnection, automatically.
It has promise wrapper around sftp operations too. This module is written in Typescript. It can be used in Javascript or in Typescript with full type support.

We have upgraded to ssh2 v1.1.0. It means minimum Node requirement v10.16.0, if you need support for older version please use ssh2-promise v0.2.0

Change in sftp api, now ssh.sftp() provide wrapped SFTP session instead of raw sftp session.

Installation

//(Require Node v10.16.0 or newer)
npm install ssh2-promise;

//for older version (Supports for any version of Node)
npm install ssh2-promise@0.2.0

Local Testing

Prerequisite to be installed

  • docker
  • docker-compose
cd pretest
docker-compose up -d
cd ..
yarn test

Usage

All examples are shown in promisify and async-await manner.

Require

//in javascript manner
var SSH2Promise = require('ssh2-promise');

//or in typescript manner
import SSH2Promise = require('ssh2-promise');

//or in typescript manner (with esModuleInterop enabled)
import SSH2Promise from 'ssh2-promise';


//To import SFTP, SSHConfig, TunnelConfig Type definition, SSHConstants in typescript

//without esModuleInterop
import SFTP = require('ssh2-promise/lib/sftp')
import SSHConfig = require('ssh2-promise/lib/sshConfig');
import TunnelConfig = require('ssh2-promise/lib/tunnelConfig');
import SSHConstants = require('ssh2-promise/lib/sshConstants');

//with esModuleInterop
import SFTP from 'ssh2-promise/lib/sftp'
import SSHConfig from 'ssh2-promise/lib/sshConfig';
import TunnelConfig from 'ssh2-promise/lib/tunnelConfig';
import SSHConstants from 'ssh2-promise/lib/sshConstants';

Connect to SSH Server

Configuration passed to SSH2Promise is aligned to ssh2 library. For debugging, pass a debug function in configuration, similary how we do for SSH2

// The config passed to the Client constructor should match the config required by ssh2.
// Extra identity option is provided to directly pass the path of private key file
var sshconfig = {
  host: '192.168.1.2',
  username: 'ubuntu',
  identity: '/here/is/my/key'
}

var ssh = new SSH2Promise(sshconfig);

//Promise
ssh.connect().then(() => {
  console.log("Connection established") 
});


//Async Await
(async function(){
    await ssh.connect();
    console.log("Connection established");
})();

//Close the ssh connection 
//very important otherwise event leaks can happen
ssh.close(); 

Connect to SSH Server via hopping

//SSH server detail used for hopping
var sshconfig1 = {
  host: '192.168.1.2',
  username: 'ubuntu',
  identity: '/here/is/my/key1'
}

//SSH server detail to be connected
var sshconfig2 = {
  host: '192.168.1.3',
  username: 'ubuntu',
  password: 'mysecret2'
}

//It will establish connection to sshconfig2 via sshconfig1
//by default it will cache connection, 
//to disable caching, pass second parameter as true
//new SSH2Promise([sshconfig1, sshconfig2], true)
var ssh = new SSH2Promise([sshconfig1, sshconfig2]);

//Promise
ssh.connect().then(() => {
  console.log("Connection established") 
});


//Async Await
(async function(){
    await ssh.connect();
    console.log("Connection established");
})();

exec, spawn cmd

var ssh = new SSH2Promise(sshconfig);

//Promise
//use exec, if output of command is limited
ssh.exec("whoami").then((data) => {
  console.log(data); //ubuntu
});
//use spawn, if you want to stream on output of command
ssh.spawn("tail -f /var/log.txt").then((socket) => {
  socket.on('data', () => {
    //file content will be available here
  })
});


//Async Await
//use exec, if output of command is limited
(async function(){
    var data = await ssh.exec("whoami");
    console.log(data); //ubuntu
})();

//use spawn, if you want to stream on output of command
(async function(){
    var socket = await ssh.spawn("tail -f /var/log.txt");
    socket.on('data', () => {
      //file content will be available here
    })
})();

sftp, shell cmd

var ssh = new SSH2Promise(sshconfig);

//Promise
//Get a sftp session
//see: https://github.com/mscdex/ssh2-streams/blob/master/SFTPStream.md

//in typescript import sftp type definition
//import SFTP = require('ssh2-promise/lib/sftp')

var sftp/*:SFTP*/ = ssh.sftp()
sftp.readdir("/").then((data) => {
  console.log(data); //file listing
}).catch((err) => {
  console.log(err);
})


//Get a shell session
ssh.shell().then((socket) => {
  socket.on('data', () => {
    //shell content will be available here
  })
  //Can write to socket 
  socket.write("")
});


//Async Await
//Get a sftp session
//see: https://github.com/mscdex/ssh2-streams/blob/master/SFTPStream.md
(async function(){
    var sftp = ssh.sftp();
    var data = await sftp.readdir("/")
    console.log(data); //file listing
})();

//Get a shell session
(async function(){
    var socket = await ssh.shell();
    socket.on('data', () => {
      //shell content will be available here
    })
    //Can write to socket 
    socket.write("")
})();

sftp operation in promise & async await manner

//in typescript import sftp type definition
//import SFTP = require('ssh2-promise/lib/sftp')
var ssh = new SSH2Promise(sshconfig);
var sftp/*:SFTP*/ = ssh.sftp(); //or new SSH2Promise.SFTP(ssh);

//We can do all sftp client operation listed in "https://github.com/mscdex/ssh2-streams/blob/master/SFTPStream.md" in promisify or async await manner.

//Promise
//Read dir
sftp.readdir("/").then((list) => {
  console.log(list); //list of files in directory '/'
});
//Create ReadStream
sftp.createReadStream("/test.sh").then((stream) => {
  console.log(stream); //Readable stream, which support data, close events etc.. 
});
//Get stat
sftp.getStat("/test.sh").then((stat) => {
  console.log(stat); //Stat object 
});


//Async Await
//Read dir
(async function(){
    var list = await sftp.readdir("/");
    console.log(list); //list of files in directory '/'
})();
//Create ReadStream
(async function(){
    var stream = await sftp.createReadStream("/test.sh");
    console.log(stream); //Readable stream, which support data, close events etc.. 
})();
//Get stat
(async function(){
    var st = await sftp.getStat("/test.sh");
    console.log(stat); //Stat object 
})();

tunnel and socks server

var ssh = new SSH2Promise(sshconfig);

//Promise
//It will establish the socks connection, one per ssh connection, and return the port
//It is mainly used for reverse tunneling
ssh.getSocksPort().then((port) => {
  console.log(port); //Socks port
});

//Establish a forward tunneling to any resource over above server
ssh.addTunnel({remoteAddr: "www.google.com", remotePort: "80"}).then((tunnel) => {
  console.log(tunnel.localPort); //Local port 
});


//Async Await
//It will establish the socks connection, one per ssh connection, and return the port
//It is mainly used for reverse tunneling
(async function(){
    var port = await ssh.getSocksPort();
    console.log(port); //Socks port
})();

//Establish a forward tunneling to any resource over above server
(async function(){
    var tunnel = await ssh.addTunnel({remoteAddr: "www.google.com", remotePort: "80"});
    console.log(tunnel.localPort); //Local port
})();

x11

sshconfig.x11 = {srcIP: 'localhost', srcPort: 6000}
//sshconfig.x11 = '/tmp/.X11-unix/X0' //connect to unix socket
var ssh = new SSH2Promise(sshconfig);

//It will establish the x11 forwarding, if
//x server running locally, 
//x forwarding enabled on remote server 
//xeyes command is installed on remote server

//Promise
ssh.x11('xeyes').then(() => {
  console.log("success"); //if x server running locally, (x forwarding enabled & xeyes command is installed) on remote server
}, () => {
  console.log("error"); //if any success condition is not met
});

//Async Await
(async function(){
  try{
    await ssh.x11('xeyes');
    console.log("success"); //if x server running locally, (x forwarding enabled & xeyes command is installed) on remote server
  }catch(err){
    console.log("error"); //if any success condition is not met
  }
})();

subsys

var ssh = new SSH2Promise(sshconfig);

//It will start subsystem

//Promise
ssh.subsys('sftp').then((stream) => {
  console.log("success"); //sftp system started successfully
});

//Async Await
(async function(){
  var stream = await ssh.subsys('sftp');
  console.log("success"); //sftp system started successfully
})();

API

require('ssh2-promise') require('ssh2-promise\lib\sftp')

SSH2

Events

  • ssh(< string >status, < object >sshconnection, < object >payload) - Event get generated, when sshconnection status get changed. Various status can be "beforeconnect", "connect", "beforedisconnect", "disconnect"

  • ssh:< status >(< object >sshconnection, < object >payload) - Event get generated, when sshconnection status is at particular status. Various status can be "beforeconnect", "connect", "beforedisconnect", "disconnect"

  • tunnel(< string >status, < object >sshconnection, < object >payload) - Event get generated, when tunnel status get changed. Various status can be "beforeconnect", "connect", "beforedisconnect", "disconnect"

  • tunnel:< status >(< object >sshconnection, < object >payload) - Event get generated, when tunnel status is at particular status. Various status can be "beforeconnect", "connect", "beforedisconnect", "disconnect"

Methods

  • (constructor)(< array >|< object >sshConfig, < (Promise) >disableCache) - Creates and returns a new SSH2Promise instance. Single or multiple sshconfigs can be passed. sshConfig passed to SSH2Promise is aligned to ssh2 library. It has few extra options other than ssh2 configuration.

    • host - string - Hostname or IP address of the server. Default: 'localhost'

    • port - integer - Port number of the server. Default: 22

    • forceIPv4 - (Promise) - Only connect via resolved IPv4 address for host. Default: false

    • forceIPv6 - (Promise) - Only connect via resolved IPv6 address for host. Default: false

    • hostHash - string - 'md5' or 'sha1'. The host's key is hashed using this method and passed to the hostVerifier function. Default: (none)

    • hostVerifier - function - Function with parameters (hashedKey[, callback]) where hashedKey is a string hex hash of the host's key for verification purposes. Return true to continue with the handshake or false to reject and disconnect, or call callback() with true or false if you need to perform asynchronous verification. Default: (auto-accept if hostVerifier is not set)

    • username - string - Username for authentication. Default: (none)

    • password - string - Password for password-based user authentication. Default: (none)

    • agent - string - Path to ssh-agent's UNIX socket for ssh-agent-based user authentication. Windows users: set to 'pageant' for authenticating with Pageant or (actual) path to a cygwin "UNIX socket." Default: (none)

    • agentForward - (Promise) - Set to true to use OpenSSH agent forwarding ([email protected]) for the life of the connection. agent must also be set to use this feature. Default: false

    • privateKey - mixed - Buffer or string that contains a private key for either key-based or hostbased user authentication (OpenSSH format). Default: (none)

    • passphrase - string - For an encrypted private key, this is the passphrase used to decrypt it. Default: (none)

    • localHostname - string - Along with localUsername and privateKey, set this to a non-empty string for hostbased user authentication. Default: (none)

    • localUsername - string - Along with localHostname and privateKey, set this to a non-empty string for hostbased user authentication. Default: (none)

    • tryKeyboard - (Promise) - Try keyboard-interactive user authentication if primary user authentication method fails. If you set this to true, you need to handle the keyboard-interactive event. Default: false

    • keepaliveInterval - integer - How often (in milliseconds) to send SSH-level keepalive packets to the server (in a similar way as OpenSSH's ServerAliveInterval config option). Set to 0 to disable. Default: 0

    • keepaliveCountMax - integer - How many consecutive, unanswered SSH-level keepalive packets that can be sent to the server before disconnection (similar to OpenSSH's ServerAliveCountMax config option). Default: 3

    • readyTimeout - integer - How long (in milliseconds) to wait for the SSH handshake to complete. Default: 20000

    • sock - ReadableStream - A ReadableStream to use for communicating with the server instead of creating and using a new TCP connection (useful for connection hopping).

    • strictVendor - (Promise) - Performs a strict server vendor check before sending vendor-specific requests, etc. (e.g. check for OpenSSH server when using openssh_noMoreSessions()) Default: true

    • algorithms - object - This option allows you to explicitly override the default transport layer algorithms used for the connection. The value for each category must either be an array of valid algorithm names to set an exact list (with the most preferable first) or an object containing append, prepend, and/or remove properties that each contain an array of algorithm names or RegExps to match to adjust default lists for each category. Valid keys:

      • cipher - mixed - Ciphers.

        • Default list (in order from most to least preferable):
        • Other supported names:
          • 3des-cbc
          • aes256-cbc
          • aes192-cbc
          • aes128-cbc
          • arcfour256
          • arcfour128
          • arcfour
          • blowfish-cbc
          • cast128-cbc
      • compress - mixed - Compression algorithms.

        • Default list (in order from most to least preferable):
        • Other supported names:
      • hmac - mixed - (H)MAC algorithms.

      • kex - mixed - Key exchange algorithms.

        • Default list (in order from most to least preferable):
          • curve25519-sha256 (node v14.0.0+)
          • [email protected] (node v14.0.0+)
          • ecdh-sha2-nistp256
          • ecdh-sha2-nistp384
          • ecdh-sha2-nistp521
          • diffie-hellman-group-exchange-sha256
          • diffie-hellman-group14-sha256
          • diffie-hellman-group15-sha512
          • diffie-hellman-group16-sha512
          • diffie-hellman-group17-sha512
          • diffie-hellman-group18-sha512
        • Other supported names:
          • diffie-hellman-group-exchange-sha1
          • diffie-hellman-group14-sha1
          • diffie-hellman-group1-sha1
      • serverHostKey - mixed - Server host key formats.

        • Default list (in order from most to least preferable):
          • ssh-ed25519 (node v12.0.0+)
          • ecdsa-sha2-nistp256
          • ecdsa-sha2-nistp384
          • ecdsa-sha2-nistp521
          • rsa-sha2-512
          • rsa-sha2-256
          • ssh-rsa
        • Other supported names:
          • ssh-dss
    • compress - mixed - Set to true to enable compression if server supports it, 'force' to force compression (disconnecting if server does not support it), or false to explicitly opt out of compression all of the time. Note: this setting is overridden when explicitly setting a compression algorithm in the algorithms configuration option. Default: (only use compression if that is only what the server supports)

    • debug - function - Set this to a function that receives a single string argument to get detailed (local) debug information.

    • identity - to directly pass the path of private key file.

    • reconnect - to reconnect automatically, once disconnected. Default: 'true'.

    • reconnectTries - Number of reconnect tries. Default: '10'.

    • reconnectDelay - Delay after which reconnect should be done. Default: '5000'.

    • hoppingTool - To hop connection using this tool. Default: 'nc'. Supported Tools are 'nc', 'socat', 'native'

    • x11 - Connect to x11 server in different manner. Default: 'localhost:6000'. Supported Options are {srcIP: 'localhost', srcPort: 6005} or custom unix socket for eg: '/tmp/.X11-unix/X0'

  • connect() - (Promise) - Try to establish a connection. No need to explicitly call connect method. It get called automatically, while doing operation.

  • exec(< string >cmd, < array >params, < objects >options) - (Promise) - Execute a cmd, return a result. Options are passed directly to ssh2 exec command.

  • spawn(< string >cmd, < array >params, < objects >options) - (Promise) - Spawn a cmd, return a stream. Options are passed directly to ssh2 exec command.

  • sftp() - (SFTP) - Get a new sftp session.

  • subsys(< string >subsystem) - (Promise) - Invoke a subsystem.

  • x11(< string >cmd) - (Promise) - Start a x11 forwarding, by invoking 'cmd' on remote server. It handles error scenario, such as if x11 command not installed on remote server, x11 forwarding not enabled on remote server, & x11 server not running locally, by rejecting it gracefully.

  • shell() - (Promise) - Get a shell session.

  • close() - (Promise) - Close the sshconnection and associated tunnels.

  • addTunnel(< object >tunnelConfig) - (Promise) - Establish a forward tunnel over ssh machine. TunnelConfig has following properties.

    • name - Unique name. Default: '${remoteAddr}@${remotePort}'

    • remoteAddr - Remote Address to connect.

    • remotePort - Remote Port to connect.

    • localPort - Local port to bind to. By default, it will bind to a random port, if not passed.

  • getTunnel(< string >name) - Get a tunnel by name.

  • closeTunnel([< string >name]) - (Promise) - Close a tunnel, if name is passed. Otherwise, will close all the tunnels.

  • getSocksPort([< number >localPort]) - (Promise) - Start a socks server. And, return a socks port, for reverse tunneling purpose. localPort is optional. By default, it will bind to a random port, if not passed.

SFTP

It supports all the sftp client operations, in promisify way. For detailed explanation of all the operation, please visit sftp. It has few extra methods getStat, setStat, changeTimestamp, readFileData, writeFileData, changeMode, changeOwner.

Methods

  • (constructor)(< object > ssh2) - Creates and returns a new SFTP instance, which can perform all sftp client operation such readdir, mkdir etc... in promisify way.

  • fastGet(< string >remotePath, < string >localPath[, < object >options]) - (Promise) - Downloads a file at remotePath to localPath using parallel reads for faster throughput. options can have the following properties:

    • concurrency - integer - Number of concurrent reads Default: 64

    • chunkSize - integer - Size of each read in bytes Default: 32768

    • step - function(< integer >total_transferred, < integer >chunk, < integer >total) - Called every time a part of a file was transferred

  • fastPut(< string >localPath, < string >remotePath[, < object >options]) - (Promise) - Uploads a file from localPath to remotePath using parallel reads for faster throughput. options can have the following properties:

    • concurrency - integer - Number of concurrent reads Default: 64

    • chunkSize - integer - Size of each read in bytes Default: 32768

    • step - function(< integer >total_transferred, < integer >chunk, < integer >total) - Called every time a part of a file was transferred

    • mode - mixed - Integer or string representing the file mode to set for the uploaded file.

  • createReadStream(< string >path[, < object >options]) - (Promise) - if resolved successfully, returns a new readable stream for path. options has the following defaults:

    { flags: 'r',
      encoding: null,
      handle: null,
      mode: 0o666,
      autoClose: true
    }

    options can include start and end values to read a range of bytes from the file instead of the entire file. Both start and end are inclusive and start at 0. The encoding can be 'utf8', 'ascii', or 'base64'.

    If autoClose is false, then the file handle won't be closed, even if there's an error. It is your responsiblity to close it and make sure there's no file handle leak. If autoClose is set to true (default behavior), on error or end the file handle will be closed automatically.

    An example to read the last 10 bytes of a file which is 100 bytes long:

    sftp.createReadStream('sample.txt', {start: 90, end: 99});
  • createWriteStream(< string >path[, < object >options]) - (Promise) - if resolved successfully, returns a new writable stream for path. options has the following defaults:

    {
      flags: 'w',
      encoding: null,
      mode: 0o666,
      autoClose: true
    }

    options may also include a start option to allow writing data at some position past the beginning of the file. Modifying a file rather than replacing it may require a flags mode of 'r+' rather than the default mode 'w'.

    If 'autoClose' is set to false and you pipe to this stream, this stream will not automatically close after there is no more data upstream -- allowing future pipes and/or manual writes.

  • open(< string >filename, < string >flags, [< mixed >attrs_mode]) - (Promise) - Opens a file filename with flags with optional ATTRS object or file mode attrs_mode. flags is any of the flags supported by fs.open (except sync flag). If promise resolved successfully, then return < Buffer >handle, otherwise < Error >err.

  • close(< Buffer >handle) - (Promise) - Closes the resource associated with handle given by open() or opendir(). If promise is rejected, then return < Error >err.

  • readFile(< string >path, [< string|object >encoding]) - (Promise) - Reads file content at given path. Default encoding is null. If promise resolved successfully, then (if encoding is defined, then return < string >content otherwise return < buffer >content), otherwise < Error >err.

  • writeFile(< string >path, < string >data, [< object >options]) - (Promise) - Writes data at given path. options can have two properties encoding and flag, Default encoding is utf8, and flag is w. If promise is rejected, then return < Error >err.

  • readFileData(< string >filename, < Buffer >buffer, < integer >offset, < integer >length, < integer >position) - (Promise) - Reads length bytes from the resource associated with file starting at position and stores the bytes in buffer starting at offset. If promise resolved successfully, then return Array [< integer >bytesRead, < Buffer >buffer (offset adjusted), < integer >position], otherwise < Error >err.

  • writeFileData(< string >filename, < integer >offset, < integer >length, < integer >position) - (Promise) - Writes length bytes from buffer starting at offset to the resource associated with file starting at position. If promise is rejected, then return < Error >err.

  • getStat(< string >filename) - (Promise) - Retrieves attributes for the resource associated with file. If promise resolved successfully, then return < Stats >stats, otherwise < Error >err.

  • setStat(< string >filename, < ATTRS >attributes) - (Promise) - Sets the attributes defined in attributes for the resource associated with file. If promise is rejected, then return < Error >err.

  • changeTimestamp(< string >filename, < mixed >atime, < mixed >mtime) - (Promise) - Sets the access time and modified time for the resource associated with file. atime and mtime can be Date instances or UNIX timestamps. If promise is rejected, then return < Error >err.

  • changeOwner(< string >filename, < integer >uid, < integer >gid) - (Promise) - Sets the owner for the resource associated with file. If promise is rejected, then return < Error >err.

  • changeMode(< string >filename, < mixed >mode) - (Promise) - Sets the mode for the resource associated with file. mode can be an integer or a string containing an octal number. If promise is rejected, then return < Error >err.

  • opendir(< string >path) - (Promise) - Opens a directory path. If promise resolved successfully, then return < Buffer >handle, otherwise < Error >err.

  • readdir(< mixed >location) - (Promise) - Retrieves a directory listing. location can either be a Buffer containing a valid directory handle from opendir() or a string containing the path to a directory. If promise resolved successfully, then return < mixed >list, otherwise < Error >err. list is an Array of { filename: 'foo', longname: '....', attrs: {...} } style objects (attrs is of type ATTR). If location is a directory handle, this function may need to be called multiple times until list is boolean false, which indicates that no more directory entries are available for that directory handle.

  • unlink(< string >path) - (Promise) - Removes the file/symlink at path. If promise is rejected, then return < Error >err.

  • rename(< string >srcPath, < string >destPath) - (Promise) - Renames/moves srcPath to destPath. If promise is rejected, then return < Error >err.

  • mkdir(< string >path, [< ATTRS >attributes, ]) - (Promise) - Creates a new directory path. If promise is rejected, then return < Error >err.

  • rmdir(< string >path) - (Promise) - Removes the directory at path. If promise is rejected, then return < Error >err.

  • stat(< string >path) - (Promise) - Retrieves attributes for path. If promise resolved successfully, then return < Stats >stats, otherwise < Error >err.

  • lstat(< string >path) - (Promise) - Retrieves attributes for path. If path is a symlink, the link itself is stat'ed instead of the resource it refers to. If promise resolved successfully, then return < Stats >stats, otherwise < Error >err.

  • setstat(< string >path, < ATTRS >attributes) - (Promise) - Sets the attributes defined in attributes for path. If promise is rejected, then return < Error >err.

  • utimes(< string >path, < mixed >atime, < mixed >mtime) - (Promise) - Sets the access time and modified time for path. atime and mtime can be Date instances or UNIX timestamps. If promise is rejected, then return < Error >err.

  • chown(< string >path, < integer >uid, < integer >gid) - (Promise) - Sets the owner for path. If promise is rejected, then return < Error >err.

  • chmod(< string >path, < mixed >mode) - (Promise) - Sets the mode for path. mode can be an integer or a string containing an octal number. If promise is rejected, then return < Error >err.

  • readlink(< string >path) - (Promise) - Retrieves the target for a symlink at path. If promise resolved successfully, then return < string >target, otherwise < Error >err.

  • symlink(< string >targetPath, < string >linkPath) - (Promise) - Creates a symlink at linkPath to targetPath. If promise is rejected, then return < Error >err.

  • realpath(< string >path) - (Promise) - Resolves path to an absolute path. If promise resolved successfully, then return < string >absPath, otherwise < Error >err.

  • ext_openssh_rename(< string >srcPath, < string >destPath) - (Promise) - OpenSSH extension Performs POSIX rename(3) from srcPath to destPath. If promise is rejected, then return < Error >err.

  • ext_openssh_statvfs(< string >path) - (Promise) - OpenSSH extension Performs POSIX statvfs(2) on path. If promise resolved successfully, then return < object >fsInfo, otherwise < Error >err. fsInfo contains the information as found in the statvfs struct.

  • ext_openssh_fstatvfs(< Buffer >handle) - (Promise) - OpenSSH extension Performs POSIX fstatvfs(2) on open handle handle. If promise resolved successfully, then return < object >fsInfo, otherwise < Error >err. fsInfo contains the information as found in the statvfs struct.

  • ext_openssh_hardlink(< string >targetPath, < string >linkPath) - (Promise) - OpenSSH extension Performs POSIX link(2) to create a hard link to targetPath at linkPath. If promise is rejected, then return < Error >err.

  • ext_openssh_fsync(< Buffer >handle) - (Promise) - OpenSSH extension Performs POSIX fsync(3) on the open handle handle. If promise is rejected, then return < Error >err.

ATTRS

An object with the following valid properties:

  • mode - integer - Mode/permissions for the resource.

  • uid - integer - User ID of the resource.

  • gid - integer - Group ID of the resource.

  • size - integer - Resource size in bytes.

  • atime - integer - UNIX timestamp of the access time of the resource.

  • mtime - integer - UNIX timestamp of the modified time of the resource.

When supplying an ATTRS object to one of the SFTP methods:

  • atime and mtime can be either a Date instance or a UNIX timestamp.

  • mode can either be an integer or a string containing an octal number.

Stats

An object with the same attributes as an ATTRS object with the addition of the following methods:

  • stats.isDirectory()

  • stats.isFile()

  • stats.isBlockDevice()

  • stats.isCharacterDevice()

  • stats.isSymbolicLink()

  • stats.isFIFO()

  • stats.isSocket()

LICENSE

MIT

ssh2-promise's People

Contributors

bgeschka avatar jeffrson avatar sanketbajoria avatar yantrio avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

ssh2-promise's Issues

ssh.exe does not resolve on Ubuntu 18.04 nodejs v8.10.0

Below code works. It returns the username. But it does not close the SSH session. I have to manually stop it. I tried other Linux command. None of them resolve...

  `var SSH = require('ssh2-promise');

    var sshconfig = {
    host: 'x.x.x.x'
    username: 'yyyyy',
    privateKey: '-----BEGIN RSA PRIVATE KEY-----'};``
    var ssh= new SSH(sshconfig);
    (async function(){
        var data = await ssh.exec('whoami');
        console.log(data); //ubuntu
    })();`

linux command execution on remote server

I am trying to execute command yum install <package_name> on a remote linux server using ssh2-promise package, But I could not get the command response back for further processing and validation.

I have tried the following,

// Node:33528) UnhandledPromiseRejectionWarning: Unhandled promise rejection.
(async function(){
try {
const data = await this.ssh.exec("yum repolist all");
console.log("resp: ", data);
} catch(e) {
console.log(e)
}
})(); // This fails

    const socket = await this.ssh.spawn("yum repolist all");
    socket.on('data', function(data) {
          console.log("resp: " , data); // I get binary data not the human readable output
    });


    // Node:33528) UnhandledPromiseRejectionWarning: Unhandled promise rejection.
    this.ssh.exec("yum install <name>").then((data) => {
        console.log("resp: yum repolist all output: ", data); // This also fails and throws exception

    });

    // Throws again (Node:33528) UnhandledPromiseRejectionWarning: Unhandled promise rejection.
    const output = await this.ssh.exec("yum upgrade <name>");
    console.log("resp: ", output) 

I tried with proper try catch block as well, still throws unhandledPromise Exception. Can someone help me figure this out?

Event leak when cache is disabled

I started receiving Node warnings that multiple listeners were being added to events and growing, specifically for [tunnel, ssh:continue, ssh:disconnect, and ssh].

After debugging, it appears that when caching is enabled, the listeners are repeatedly added because this isRegistered check always returns false since "this.deregister" is always an empty array since I'm calling "new SSH2Promise(config);" on each new configuration.

This line:

if (!isRegistered(ret, this)) {

Quickly updating to this seems to resolve the issue...
if (!isRegistered(ret, ret && ret.__sshTunnels && ret.__sshTunnels.length > 0 ? ret.__sshTunnels[0] : this )) {...

There seems to be a state issue where the cache is static and overlaps objects.

Note: if I disable caching, it all seems to work fine for my use case.

Get the return code of an execution of a script

Hi,

I'm trying to get the return code of a exec from a bash script on my server.
It would be nice to have in output, both stderr and stdout to detect technical issues and the return code (eg. 0 or 1).

It's possible to do that ?

Timed out while waiting for handshake

Hi, Thank for great library

I used this as SFTP Client to ReadStreamfile to S3 for year but now I got an error

"Timed out while waiting for handshake"

If I use SSH2 package to connect same server it works but with this SSH2-promise it stall and timeout

Could you please suggest :D

Silent failure if netcat is not installed

If nc is not installed on the target, this part of connect fails, but does not appear to set any errors:

ssh2-promise/src/index.ts

Lines 176 to 178 in 930f5f2

lastSSH = lastSSH.then((ssh: SSHConnection) => {
return ssh.spawn(`nc ${sshConfig.host} ${sshConfig.port}`);
}).then((stream: any) => {

It also appears that in multihop usage, if the next hop is not available, it also silently fails without setting an error.

Are either of these solvable?

Timeout when connecting to Salesforce Marketing SFTP host

I piggybacked onto #51 , but the solution to that problem didn't work for me. I'll reproduce the issue here.

I have a Lambda function that uses this module that has been timing out. Looking at the debug output, I see that it is connecting to the remote host but after DEBUG: Parser: IN_PACKETDATAAFTER, packet: KEXDH_GEX_GROUP it seems to wait 30 seconds, disconnect, and then 5 seconds later try to connect again. Complete log is attached.

log-events-viewer-result-2.zip

find Repeat call readFile error

when i Repeat call readFile i find process be hanging [not continue ] only finish first readFile
then i read src find err and fix but i think my fix is out of place..
fix picture
image
$read has been change
so
image
3Q,

Unknown message digest on node v10.12.0

The following works fine on node v8.12.0 but fails with an Unknown message digest on v10.12.0

{
  host: 'sftp.my-site.com'
  , username: 'username'
  , password: 'password'
  , compress: true
  , algorithms: {
    kex: ['diffie-hellman-group1-sha1']
    , cipher: ['aes128-cbc']
    , serverHostKey: ['ssh-dss']
}

sftp.fastGet(file, `${targetPath}${file}`)

DEBUG

DEBUG: Parser: pktLen:1020,padLen:4,remainLen:1016
DEBUG: Parser: IN_PACKETDATA
DEBUG: Parser: IN_PACKETDATAAFTER, packet: KEXDH_REPLY
DEBUG: Checking host key format
DEBUG: Checking signature format
DEBUG: Verifying host fingerprint
DEBUG: Host accepted by default (no verification)
internal/crypto/sig.js:98
  this._handle.init(algorithm);
               ^

Error: Unknown message digest
    at new Verify (internal/crypto/sig.js:98:16)
    at Object.createVerify (crypto.js:141:10)
    at onKEXDH_REPLY (/home/rich/Projects/omn/backend/node_modules/ssh2-streams/lib/ssh.js:2876:25)
    at SSH2Stream.<anonymous> (/home/rich/Projects/omn/backend/node_modules/ssh2-streams/lib/ssh.js:215:45)
    at SSH2Stream.emit (events.js:182:13)
    at parse_KEXDH_REPLY (/home/rich/Projects/omn/backend/node_modules/ssh2-streams/lib/ssh.js:4220:8)
    at parse_KEX (/home/rich/Projects/omn/backend/node_modules/ssh2-streams/lib/ssh.js:4182:14)
    at parsePacket (/home/rich/Projects/omn/backend/node_modules/ssh2-streams/lib/ssh.js:4017:12)
    at SSH2Stream._transform (/home/rich/Projects/omn/backend/node_modules/ssh2-streams/lib/ssh.js:669:13)
    at SSH2Stream.Transform._read (_stream_transform.js:190:10)
    at SSH2Stream._read (/home/rich/Projects/omn/backend/node_modules/ssh2-streams/lib/ssh.js:251:15)
    at SSH2Stream.Transform._write (_stream_transform.js:178:12)
    at doWrite (_stream_writable.js:410:12)
    at writeOrBuffer (_stream_writable.js:394:5)
    at SSH2Stream.Writable.write (_stream_writable.js:294:11)
    at Socket.ondata (_stream_readable.js:666:20)
    at Socket.emit (events.js:182:13)
    at addChunk (_stream_readable.js:283:12)
    at readableAddChunk (_stream_readable.js:264:11)
    at Socket.Readable.push (_stream_readable.js:219:10)
    at TCP.onStreamRead [as onread] (internal/stream_base_commons.js:94:17)

Remove console.log statements from SSHConnection.spawn()

It would be really helpful if the console.log() statements in SSHConnection.spawn() could be removed. I'm trying to use this library in a CLI application, and every time a command finishes executing I get two "Closed stream" messages whether I like it or not.

Best way to upload a folder?

What's the best way to upload a folder to a server? I tried using fastPut but that only works with single files. I need to upload a folder including its sub-directories and files.

How to stop a command?

How do i stop a command, i.e.: tail:
//use spawn, if you want to stream on output of command
(async function(){
var socket = await ssh.spawn("tail -f /var/log.txt");
socket.on('data', () => {
//file content will be available here
})
})();

Basically, I would like to kill the tail command when terminating a connection.

Thank you

Typescript error with createReadStream

This typescript source

import SSH2Promise = require('ssh2-promise')

let sshconfig = {}

let ssh = new SSH2Promise(sshconfig)
let sftp = ssh.sftp()

sftp.createReadStream('/file')

gives the following error (TS 3.3):

error TS2554: Expected 0 arguments, but got 1.

for sftp.createReadStream.

fastGet TypeError: cb is not a function

Node V 12 & 14
Mac OSX
ssh2-promise": "^0.1.7"

const SSH2Promise = require('ssh2-promise');
const main = async () => { 
    try { 
var sshconfig = {
    host: '***',
    username: '***',
    port: '***',
    password: '***',
    debug: (msg) => {
            console.log(msg);
    }}
   
  const ssh = new SSH2Promise(sshconfig);
  const ssh2 = await ssh.connect();
  const sftp = await ssh2.sftp();
  await sftp.fastGet('remotedir/test.zip', 'test.zip', {});
    } catch (e) {
        console.log(e);
    }
}

main();

Error:
TypeError: cb is not a function
at cbfinal (/Users/jnandal/z/jessica/sftp/node_modules/ssh2-streams/lib/sftp.js:1050:11)
at SFTPStream._transform (/Users/jnandal/z/jessica/sftp/node_modules/ssh2-streams/lib/sftp.js:395:17)
at SFTPStream.Transform._read (_stream_transform.js:191:10)
at SFTPStream._read (/Users/jnandal/z/jessica/sftp/node_modules/ssh2-streams/lib/sftp.js:183:15)
at SFTPStream.Transform._write (_stream_transform.js:179:12)
at doWrite (_stream_writable.js:403:12)
at writeOrBuffer (_stream_writable.js:387:5)
at SFTPStream.Writable.write (_stream_writable.js:318:11)
at Channel.ondata (_stream_readable.js:716:22)
at Channel.emit (events.js:315:20)

Log:

[Arguments] {
'0': 'ssh',
'1': 'beforeconnect',
'2': SSHConnection {
_events: [Object: null prototype] {
ssh: [Function: bound ],
tunnel: [Function: bound ],
'ssh:disconnect': [Function: disconnectCb],
'ssh:continue': [Function: continueCb]
},
_eventsCount: 4,
_maxListeners: undefined,
activeTunnels: {},
'__$connectPromise': null,
__retries: 1,
__err: null,
__sftp: null,
__x11: null,
sshConnection: null,
config: {
reconnect: true,
port: '',
reconnectTries: 10,
reconnectDelay: 5000,
host: '
',
username: '',
password: '
',
debug: [Function: debug],
uniqueId: '**'
},
sshTunnels: [ [SSH2Promise] ],
[Symbol(kCapture)]: false
},
'3': undefined
}
DEBUG: Local ident: 'SSH-2.0-ssh2js0.4.10'
DEBUG: Client: Trying **** ...
DEBUG: Client: Connected
DEBUG: Parser: IN_INIT
DEBUG: Parser: IN_GREETING
DEBUG: Parser: IN_HEADER
DEBUG: Remote ident: 'SSH-2.0-XFB.Gateway Unix'
DEBUG: Outgoing: Writing KEXINIT
DEBUG: Parser: IN_PACKETBEFORE (expecting 8)
DEBUG: Parser: IN_PACKET
DEBUG: Parser: pktLen:388,padLen:9,remainLen:384
DEBUG: Parser: IN_PACKETDATA
DEBUG: Parser: IN_PACKETDATAAFTER, packet: KEXINIT
DEBUG: Comparing KEXINITs ...
DEBUG: (local) KEX algorithms: [email protected],curve25519-sha256,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group14-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group14-sha1
DEBUG: (remote) KEX algorithms: diffie-hellman-group1-sha1,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521
DEBUG: KEX algorithm: ecdh-sha2-nistp256
DEBUG: (local) Host key formats: ssh-ed25519,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-rsa
DEBUG: (remote) Host key formats: ssh-rsa
DEBUG: Host key format: ssh-rsa
DEBUG: (local) Client->Server ciphers: aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm,[email protected],aes256-gcm,[email protected]
DEBUG: (remote) Client->Server ciphers: aes256-ctr,aes128-ctr,aes128-cbc,3des-cbc,aes256-cbc
DEBUG: Client->Server Cipher: aes128-ctr
DEBUG: (local) Server->Client ciphers: aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm,[email protected],aes256-gcm,[email protected]
DEBUG: (remote) Server->Client ciphers: aes256-ctr,aes128-ctr,aes128-cbc,3des-cbc,aes256-cbc
DEBUG: Server->Client Cipher: aes128-ctr
DEBUG: (local) Client->Server HMAC algorithms: hmac-sha2-256,hmac-sha2-512,hmac-sha1
DEBUG: (remote) Client->Server HMAC algorithms: hmac-sha2-256,hmac-sha1,hmac-md5,hmac-sha1-96,hmac-md5-96
DEBUG: Client->Server HMAC algorithm: hmac-sha2-256
DEBUG: (local) Server->Client HMAC algorithms: hmac-sha2-256,hmac-sha2-512,hmac-sha1
DEBUG: (remote) Server->Client HMAC algorithms: hmac-sha2-256,hmac-sha1,hmac-md5,hmac-sha1-96,hmac-md5-96
DEBUG: Server->Client HMAC algorithm: hmac-sha2-256
DEBUG: (local) Client->Server compression algorithms: none,[email protected],zlib
DEBUG: (remote) Client->Server compression algorithms: none
DEBUG: Client->Server compression algorithm: none
DEBUG: (local) Server->Client compression algorithms: none,[email protected],zlib
DEBUG: (remote) Server->Client compression algorithms: none
DEBUG: Server->Client compression algorithm: none
DEBUG: Outgoing: Writing KEXECDH_INIT
DEBUG: Parser: IN_PACKETBEFORE (expecting 8)
DEBUG: Parser: IN_PACKET
DEBUG: Parser: pktLen:380,padLen:7,remainLen:376
DEBUG: Parser: IN_PACKETDATA
DEBUG: Parser: IN_PACKETDATAAFTER, packet: KEXECDH_REPLY
DEBUG: Checking host key format
DEBUG: Checking signature format
DEBUG: Verifying host fingerprint
DEBUG: Host accepted by default (no verification)
DEBUG: Verifying signature
DEBUG: Outgoing: Writing NEWKEYS
DEBUG: Parser: IN_PACKETBEFORE (expecting 8)
DEBUG: Parser: IN_PACKET
DEBUG: Parser: pktLen:12,padLen:10,remainLen:8
DEBUG: Parser: IN_PACKETDATA
DEBUG: Parser: IN_PACKETDATAAFTER, packet: NEWKEYS
DEBUG: Outgoing: Writing SERVICE_REQUEST (ssh-userauth)
DEBUG: Parser: IN_PACKETBEFORE (expecting 16)
DEBUG: Parser: IN_PACKET
DEBUG: Parser: Decrypting
DEBUG: Parser: pktLen:28,padLen:10,remainLen:16
DEBUG: Parser: IN_PACKETDATA
DEBUG: Parser: Decrypting
DEBUG: Parser: HMAC size:32
DEBUG: Parser: IN_PACKETDATAVERIFY
DEBUG: Parser: Verifying MAC
DEBUG: Parser: IN_PACKETDATAVERIFY (Valid HMAC)
DEBUG: Parser: IN_PACKETDATAAFTER, packet: SERVICE_ACCEPT
DEBUG: Outgoing: Writing USERAUTH_REQUEST (none)
DEBUG: Parser: IN_PACKETBEFORE (expecting 16)
DEBUG: Parser: IN_PACKET
DEBUG: Parser: Decrypting
DEBUG: Parser: pktLen:28,padLen:13,remainLen:16
DEBUG: Parser: IN_PACKETDATA
DEBUG: Parser: Decrypting
DEBUG: Parser: HMAC size:32
DEBUG: Parser: IN_PACKETDATAVERIFY
DEBUG: Parser: Verifying MAC
DEBUG: Parser: IN_PACKETDATAVERIFY (Valid HMAC)
DEBUG: Parser: IN_PACKETDATAAFTER, packet: USERAUTH_FAILURE
DEBUG: Client: none auth failed
DEBUG: Outgoing: Writing USERAUTH_REQUEST (password)
DEBUG: Parser: IN_PACKETBEFORE (expecting 16)
DEBUG: Parser: IN_PACKET
DEBUG: Parser: Decrypting
DEBUG: Parser: pktLen:12,padLen:10,remainLen:0
DEBUG: Parser: IN_PACKETDATA
DEBUG: Parser: HMAC size:32
DEBUG: Parser: IN_PACKETDATAVERIFY
DEBUG: Parser: Verifying MAC
DEBUG: Parser: IN_PACKETDATAVERIFY (Valid HMAC)
DEBUG: Parser: IN_PACKETDATAAFTER, packet: USERAUTH_SUCCESS
[Arguments] {
'0': 'ssh',
'1': 'connect',
'2': SSHConnection {
_events: [Object: null prototype] {
ssh: [Function: bound ],
tunnel: [Function: bound ],
'ssh:disconnect': [Function: disconnectCb],
'ssh:continue': [Function: continueCb]
},
_eventsCount: 4,
_maxListeners: undefined,
activeTunnels: {},
'
$connectPromise': Promise { },
__retries: 1,
__err: null,
__sftp: null,
__x11: null,
sshConnection: Client {
_events: [Object: null prototype],
_eventsCount: 6,
_maxListeners: undefined,
config: [Object],
_readyTimeout: Timeout {
_idleTimeout: -1,
_idlePrev: null,
_idleNext: null,
_idleStart: 137,
_onTimeout: null,
_timerArgs: undefined,
_repeat: null,
_destroyed: true,
[Symbol(refed)]: true,
[Symbol(asyncId)]: 5,
[Symbol(triggerId)]: 1
},
_channels: {},
_callbacks: [],
_forwarding: {},
_forwardingUnix: {},
_acceptX11: 0,
_agentFwdEnabled: false,
_curChan: -1,
_remoteVer: 'XFB.Gateway',
_sshstream: [SSH2Stream],
_sock: [Socket],
_resetKA: [Function: resetKA],
[Symbol(kCapture)]: false
},
config: {
reconnect: true,

  reconnectTries: 10,
  reconnectDelay: 5000,
  host: '',
  username: '',
  password: '',
  debug: [Function: debug],
  uniqueId: ''
},
__sshTunnels: [ [SSH2Promise] ],
[Symbol(kCapture)]: false

},
'3': undefined
}
DEBUG: Parser: IN_PACKETBEFORE (expecting 16)
[Arguments] {
'0': 'ssh',
'1': 'connect',
'2': SSHConnection {
_events: [Object: null prototype] {
ssh: [Function: bound ],
tunnel: [Function: bound ],
'ssh:disconnect': [Function: disconnectCb],
'ssh:continue': [Function: continueCb]
},
_eventsCount: 4,
_maxListeners: undefined,
activeTunnels: {},
'__$connectPromise': Promise { [Circular] },
__retries: 0,
__err: null,
__sftp: null,
__x11: null,
sshConnection: Client {
_events: [Object: null prototype],
_eventsCount: 6,
_maxListeners: undefined,
config: [Object],
_readyTimeout: Timeout {
_idleTimeout: -1,
_idlePrev: null,
_idleNext: null,
_idleStart: 137,
_onTimeout: null,
_timerArgs: undefined,
_repeat: null,
_destroyed: true,
[Symbol(refed)]: true,
[Symbol(asyncId)]: 5,
[Symbol(triggerId)]: 1
},
_channels: {},
_callbacks: [],
_forwarding: {},
_forwardingUnix: {},
_acceptX11: 0,
_agentFwdEnabled: false,
_curChan: -1,
_remoteVer: 'XFB.Gateway',
_sshstream: [SSH2Stream],
_sock: [Socket],
_resetKA: [Function: resetKA],
[Symbol(kCapture)]: false
},
config: {
reconnect: true,
port: '',
reconnectTries: 10,
reconnectDelay: 5000,

},
__sshTunnels: [ [SSH2Promise] ],
[Symbol(kCapture)]: false

},
'3': undefined
}
DEBUG: Outgoing: Writing CHANNEL_OPEN (0, session)
DEBUG: Parser: IN_PACKET
DEBUG: Parser: Decrypting
DEBUG: Parser: pktLen:28,padLen:10,remainLen:16
DEBUG: Parser: IN_PACKETDATA
DEBUG: Parser: Decrypting
DEBUG: Parser: HMAC size:32
DEBUG: Parser: IN_PACKETDATAVERIFY
DEBUG: Parser: Verifying MAC
DEBUG: Parser: IN_PACKETDATAVERIFY (Valid HMAC)
DEBUG: Parser: IN_PACKETDATAAFTER, packet: CHANNEL_OPEN_CONFIRMATION
DEBUG: Outgoing: Writing CHANNEL_REQUEST (929789, subsystem: sftp)
DEBUG: Parser: IN_PACKETBEFORE (expecting 16)
DEBUG: Parser: IN_PACKET
DEBUG: Parser: Decrypting
DEBUG: Parser: pktLen:12,padLen:6,remainLen:0
DEBUG: Parser: IN_PACKETDATA
DEBUG: Parser: HMAC size:32
DEBUG: Parser: IN_PACKETDATAVERIFY
DEBUG: Parser: Verifying MAC
DEBUG: Parser: IN_PACKETDATAVERIFY (Valid HMAC)
DEBUG: Parser: IN_PACKETDATAAFTER, packet: CHANNEL_SUCCESS (0)
DEBUG: Parser: IN_PACKETBEFORE (expecting 16)
DEBUG: Outgoing: Writing CHANNEL_DATA (929789)
DEBUG: Parser: IN_PACKET
DEBUG: Parser: Decrypting
DEBUG: Parser: pktLen:28,padLen:9,remainLen:16
DEBUG: Parser: IN_PACKETDATA
DEBUG: Parser: Decrypting
DEBUG: Parser: HMAC size:32
DEBUG: Parser: IN_PACKETDATAVERIFY
DEBUG: Parser: Verifying MAC
DEBUG: Parser: IN_PACKETDATAVERIFY (Valid HMAC)
DEBUG: Parser: IN_PACKETDATAAFTER, packet: CHANNEL_DATA (0)
DEBUG: Parser: IN_PACKETBEFORE (expecting 16)
DEBUG[SFTP]: Outgoing: Writing OPEN
DEBUG: Outgoing: Writing CHANNEL_DATA (929789)
DEBUG: Parser: IN_PACKET
DEBUG: Parser: Decrypting
DEBUG: Parser: pktLen:28,padLen:4,remainLen:16
DEBUG: Parser: IN_PACKETDATA
DEBUG: Parser: Decrypting
DEBUG: Parser: HMAC size:32
DEBUG: Parser: IN_PACKETDATAVERIFY
DEBUG: Parser: Verifying MAC
DEBUG: Parser: IN_PACKETDATAVERIFY (Valid HMAC)
DEBUG: Parser: IN_PACKETDATAAFTER, packet: CHANNEL_DATA (0)
DEBUG[SFTP]: Parser: Response: HANDLE
DEBUG[SFTP]: Outgoing: Writing FSTAT
DEBUG: Outgoing: Writing CHANNEL_DATA (929789)
DEBUG: Parser: IN_PACKETBEFORE (expecting 16)
DEBUG: Parser: IN_PACKET
DEBUG: Parser: Decrypting
DEBUG: Parser: pktLen:60,padLen:9,remainLen:48
DEBUG: Parser: IN_PACKETDATA
DEBUG: Parser: Decrypting
DEBUG: Parser: HMAC size:32
DEBUG: Parser: IN_PACKETDATAVERIFY
DEBUG: Parser: Verifying MAC
DEBUG: Parser: IN_PACKETDATAVERIFY (Valid HMAC)
DEBUG: Parser: IN_PACKETDATAAFTER, packet: CHANNEL_DATA (0)
DEBUG[SFTP]: Parser: Response: ATTRS
DEBUG: Parser: IN_PACKETBEFORE (expecting 16)
DEBUG[SFTP]: Outgoing: Writing CLOSE
DEBUG: Outgoing: Writing CHANNEL_DATA (929789)
DEBUG: Parser: IN_PACKET
DEBUG: Parser: Decrypting
DEBUG: Parser: pktLen:44,padLen:4,remainLen:32
DEBUG: Parser: IN_PACKETDATA
DEBUG: Parser: Decrypting
DEBUG: Parser: HMAC size:32
DEBUG: Parser: IN_PACKETDATAVERIFY
DEBUG: Parser: Verifying MAC
DEBUG: Parser: IN_PACKETDATAVERIFY (Valid HMAC)
DEBUG: Parser: IN_PACKETDATAAFTER, packet: CHANNEL_DATA (0)
DEBUG[SFTP]: Parser: Response: STATUS

/node_modules/ssh2-streams/lib/sftp.js:1044
cb(err);
^

TypeError: cb is not a function
at cbfinal (sftp/node_modules/ssh2-streams/lib/sftp.js:1044:11)
at SFTPStream._transform (/sftp/node_modules/ssh2-streams/lib/sftp.js:389:17)
at SFTPStream.Transform._read (_stream_transform.js:191:10)
at SFTPStream._read (/sftp/node_modules/ssh2-streams/lib/sftp.js:183:15)
at SFTPStream.Transform._write (_stream_transform.js:179:12)
at doWrite (_stream_writable.js:403:12)
at writeOrBuffer (_stream_writable.js:387:5)
at SFTPStream.Writable.write (_stream_writable.js:318:11)
at Channel.ondata (_stream_readable.js:716:22)
at Channel.emit (events.js:315:20)

If I define a call back, I creates a file with no data.

Issue downloading large files

Hello,

I am loading some US States data, and it's working fine with all 50 states except the state of California. Size of the compressed file is ~3GB. It keeps downloading, but suddenly stops after around 1GB. Any clue? I tried different chunk sizes and different concurrency counts but in vain.

Below is a snippet of my code.

var Client = require("ssh2-promise");

const downloadOptions = { // options to define how the download should be processed
concurrency: 640,
chunkSize: 32768,
step: async () => { // this callback runs unpon downloading each chunck
totChunck = totChunck + 32;
if (totChunck % 1024 === 0)
console.log("read = " + Math.floor(totChunck / 1024).toString() + "MB");
}
};

ssh = new Client(L2Connection);
sftp = await ssh.sftp();

await sftp
.fastGet("/Folder/" + file.filename, file.filename, downloadOptions)
.catch(err => reject(err)); // Extract State File
await ssh.close();

btw l2connection is
{
"host": "sftp.xxxxx.com",
"port": "22",
"username": "my-user",
"password": "my-password",
"identity": "identityFilePath",
"keepaliveInterval" :2000,
"keepaliveCountMax" :1000,
"reconnect": "true",
"reconnectTries": "50",
"reconnectDelay": "5000"

Thanks,
Sherif

readData/writeData invalid

Apparently there should be methods readData and writeData.

I don't know, whether has SSH2 renamed these or why these methods don't exist. In any case it seems they should be called read and write, respectively.

This bug is critical, because readData/writeData are unusable, as are read/write.

BTW, what are readFileData and writeFileData?

Connect: Uncaught Exception on Connection Error

Issue: Failed connections persist after error, causing the connection to stay open for a long period of time.

Error

(node:13401) UnhandledPromiseRejectionWarning: Error: connect ECONNREFUSED 192.168.1.101:22
    at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1162:14)
(node:13401) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block,or by rejecting a promise which was not handled with .catch(). (rejection id: 16)
(node:13401) UnhandledPromiseRejectionWarning: Error: connect ECONNREFUSED 192.168.1.101:22

Adding a catch to the wrapper promise causes this error to duplicate (just catching the same error). It seems to stop after 12 occurrences in VSCode, and 30 times in a spec test.

Following the process to see where it's hitting, it looks like this is happening directly on the emit method after the connect in the SSHConnection. After that it throws the unhandled promise, but it doesn't seem to close the connection after that, or even hit the error event. Throwing a catch on the connect does wrap the error, but that's about it.

sshconnection 182

sshconnection 36

Anyways, I hope that helps, and at least it stops eventually. ๐Ÿ˜„

closeTunnel has high latency

After I run closeTunnel, the tunnel website can still be accessed. Then it took 2 minutes for the tunnel to be completely closed

const SSH2Promise = require('ssh2-promise')

const sshconfig = {
host: 'xxxxx',
username: 'xxxx',
password: 'xxxx',
port: 'xxxx'
}

const tunnelConfig = {
name: 'hahaha',
remoteAddr: 'localhost',
remotePort: '80'
}

var ssh = new SSH2Promise(sshconfig);

ssh.addTunnel(tunnelConfig).then((tunnel) => {
console.log(tunnel.localPort);
});

ssh.closeTunnel('hahaha')
.then(res => {
console.log('24')
console.log(res)
})
.catch(err => {
console.log('28')
console.log(err)
})

Error on `npm install` after upgrading to v1.0.0

Error thrown on npm i:

$ npm i

> [email protected] install C:\<omitted>\node_modules\cpu-features
> node-gyp rebuild


C:\<omitted>\node_modules\cpu-features>if not defined npm_config_node_gyp (node "C:\<omitted>\AppData\Roaming\nvm\v14.17.3\node_modules\npm\node_modules\npm-lifecycle\node-gyp-bin\\..\..\node_modules\node-gyp\bin\node-gyp.js" rebuild )  else (node "C:\<omitted>\AppData\Roaming\nvm\v14.17.3\node_modules\npm\node_modules\node-gyp\bin\node-gyp.js" rebuild )
Building the projects in this solution one at a time. To enable parallel build, please add the "-m" switch.
  Configuring dependencies
  'cmake' is not recognized as an internal or external command,
  operable program or batch file.
C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Microsoft\VC\v160\Microsoft.CppCommon.targets(231,5): error MSB6006: "cmd.exe" exited with code 9009. [C:\<omitted>\node_modules\cpu-features\build\config_deps.vcxproj]
  • Windows 10
  • Node v14.17.3 (also tried on a bunch of other Node versions)

The error disappears if I rollback to v0.2.0

createReadStream does not finish when connection is killed

Hi,
let's say I have this program to read an sftp stream via pure ssh2-streams:

const Client = require('ssh2').Client;

let srcSshConfig = {
    host: 'server',
    port: 22,
    username: 'user',
    password: 'pass'
}

let srcConn = new Client();
srcConn.connect(srcSshConfig)
srcConn.on('ready', () => {
    srcConn.sftp((err, sftpr) => {
        let count = 0
        let streamInput = sftpr.createReadStream('srcFile')
        streamInput.on('data', (chunk) => console.log(count++))
    })
})
srcConn.on('error', (err) => console.log('error!'))

When the server kills the connection (or the server is killed), I get "error!". This is expected.

OTOH, a similar program with SSH2Promise hangs forever when the connection will be killed:

const SSH2Promise = require('ssh2-promise')

let srcSshConfig = {
    host: 'server',
    port: 22,
    username: 'user',
    password: 'pass'
}

let srcSftp = new SSH2Promise(srcSshConfig).sftp()
srcSftp.on('error', (err) => console.log('error1'))
srcSftp.ssh.on('error', (err) => console.log('error2'))
srcSftp.createReadStream(srcFile).then(streamInput =>
            new Promise(async (resolve, reject) => {
                try {
                    let count=0
                    streamInput.on('error', (err) => { console.log('error3'); reject(err) })
                    streamInput.on('data', (chunk) => console.log(count++))
                } catch (err) {
                    console.log('error4')
                    reject(err)
                }
            }),
    )
    .catch(err => console.log('error5'))

I tried different on('error') handlers but it doesn't help. Well, btw, when I add "reconnect: false" option, the program simply exits - apparently because the event loop finishs. But without any message?!

I first thought this is a ssh2-streams bug, because ssh2-promise on the first sight only wraps createReadStream - but with pure ssh2-streams the program does not hang.
Reference: mscdex/ssh2-streams#127

Would be VERY grateful to get this solved.

Typescript integration

Hi,
would love to see typescript support like
import { SFTP } from 'ssh2-promise'

Currently there seems to be no way to access the type of SFTP (in class or interface) at all.

Convert exports to es6 format

When I try to import modules as es6 modules like this: import * as SSH2Promise from 'SSH2Promise'; I got error 'This module can only be referenced with ECMAScript imports/exports by turning on the esModuleInterop' flag and referencing its default export'. Although the described in README method import SSH2Promise = require('ssh2-promise'); works well it would be nice to make exports in es6 style:
export {SSH2Promise} unfortunately it probably will break require import in using code.

ssh "connect" event is not fired when connected after close()

Here is a short test:

let sshconfig = {
  host: '192.168.1.1',
  port: 22,
  username: 'test',
  password: 'test',
  reconnect: false
}

let ssh = new SSH2Promise(sshconfig)
ssh.on('ssh', (...args) => console.log(args[0]))

let sftp = ssh.sftp()

sftp.readdir('/home/test')
  .then((data) => console.dir('data'))
  .then(() => ssh.close())
  .then(() => ssh.connect())
  .then(() => sftp.readdir('/home/test'))
  .then((data) => console.dir('next try'))

it results in

beforeconnect
connect
connect
'data'
beforedisconnect
disconnect
beforedisconnect
'next try'

As you can see, there's no 'connect' after close. Furthermore, two 'connect' on the first try seem strange as well as the second 'beforedisconnect' after 'disconnect'.

SCP support?

Are there any plans or is it somehow already possible to use SCP with ssh2-promise?

Shell/Spawn: Stream Encapsulation?

Version: 0.1.1

Issue: It seems shell and spawn still use non-wrapped event handlers, so using promises, async/await isn't feasible for waiting for the shell output result.

Details: It seems no matter what I've tried, generators, promises, or async/await, every time the surrounding function is considered resolved after the listener being initiated, not after the stream data being passed like the exec and sftp methods.

Is there a specific use-case that is expected to be used with these methods to avoid having to use the abstraction pattern required by the base ssh2 library? So far the only methods I've found to be feasible is polling for the result, or making the listener the messenger, both which remove the ability to return to the instantiating scope.

Exec doesn't suffice, since I need access to the user environment via the tty. I had to do this with ssh2 as well. Suggestions, or possible feature addition in the future?

Question: How to add "keyboard-interactive" event

I wish to set an ssh connection and setting tryKeyboard: true. The problem is I am adding the listener correctly because it does not seem to work (this works correctly in the ssh2 library). Can you assist please? This is my setup:

 var SSH2Promise = require("ssh2-promise");

const connectionInfo = {
          host: newValue.server_hostname,
          port: 5022,
          username: newValue.server_username,
          password: newValue.server_password,
          tryKeyboard: true
};

var ssh = new SSH2Promise(connectionInfo);
        ssh = ssh.addListener(
          "keyboard-interactive",
          (name, instructions, instructionsLang, prompts, finish) => {
            console.log("Connection :: keyboard-interactive");
            finish([connectionInfo.password]);
          }
        );

        ssh
          .connect()
          .then(x => {
            console.log("Connection established");
          })
          .catch(error => {
            console.error(error);
          });

Promises are not resolved/rejected when connection is lost during operation

This may sound similar to #27, but it's not.

While the former hands off control to streams, there are more direct issues with commands like "rmdir" (just an example which happened to me...).

I have written an SFTP server with NodeJs/SSH2. I use SSH2Promises to, say, remove a directory. Yes, it was a bug that the server crashed when I was trying to do that. But there are other circumstances which could lead to disconnection during operations. The problem here is, that the promise that's returned from sftp.rmdir(directory) is never resolved or rejected when this happens.

Now I could try to listen for 'ssh' events again and continue somehow (have another promise and use Promise.race) - but this would still keep the original promise pending for ever.

Any idea how to "cancel" the promise?

readyTimeout not honored on connection attempt

Hi all,

I was attempting to shorten the time to failure for unresponsive machines but found the readyTImeout doesn't behave as expected. With SSH2 if I set readyTimeout to 2000 and attempt to connect to a non-existent machine it fails in 2 seconds as expected. With ssh2-promise the failure takes over a minute.

Here is sample code to demonstrate what I'm seeing. There are code blocks for ssh2-promise and ssh2. I would expect the behavior to be identical.

ssh2-promise: long time for connection failure

var SSH2Promise = require("ssh2-promise");

var theIp = '192.168.1.242';
var theUser = 'root'
var readyTimeout = 2000;

var sshconfig = {
  host: theIp,
  username: theUser,
  readyTimeout: readyTimeout,
};

var ssh = new SSH2Promise(sshconfig);

//Promise
ssh.connect().then(() => {
  console.log("Success");
  ssh.end();
}, () => {
  console.log("Failure"); 
  ssh.end();
});

ssh2: fails in 2sec as expected

var Client = require('ssh2').Client;

var theIp = '192.168.1.242';
var theUser = 'root'
var readyTimeout = 2000;

var conn = new Client();

conn.on('ready', function() {
  console.log('Success');
  conn.end();
})
.on('error', function(err) {
  console.log('Failure');
  conn.end();
})
.connect({
  host: theIp,
  username: theUser,
  readyTimeout: readyTimeout,
});

fastPut error

I get this when usign fastPut:

node_modules\ssh2-streams\lib\sftp.js:1137
                    cb();
                    ^
TypeError: cb is not a function
    at \node_modules\ssh2-streams\lib\sftp.js:1137:21
    at \node_modules\graceful-fs\graceful-fs.js:43:10
    at FSReqWrap.oncomplete (fs.js:139:20)

Connection Limit ??

I'm running ssh.exec in a loop firing 35 commands at the same server, i'm assuming ssh2-promise either reuses or disconnects before each ssh.exec call.

Is there some limit in ssh2-promise or the dependencies that limits to either 5 or 10 connections?

Hang after sftp.fastPut

~80% of the time I call stfp.fastPut(...) since upgrading to Node 12, the promise never resolves. I've tried adding debugging/step function (which does not fire when it hangs) but haven not figured out why it's hanging. I can leave it "hung" for hours to no avail. Yet 20% of the time it "just works". It seems like it might hang more on a very fast/low latency connection but I haven't been able to really confirm that difference. Has anyone else seen problems like this?

PS, this is on Windows 10.

Spawn rejects on stderr

I wonder why change in commit 2fc2ce2 was made to src/sshConnection.ts file.
My command writes to stderr within the first 500ms, so the whole spawned process fails, which should not happen.

I think that lines:

.stderr.on('data', function (data) {
    reject(data.toString());
});
setTimeout(() => {
    resolve(stream);
}, 500);

should be reversed and eventual hopping error (as the commit name suggests) should be handled differently. What do you think?

Handling of 'continue' event fails critically

The handling of 'continue' in https://github.com/jeffrson/ssh2-promise/blob/master/src/sftp.ts#L21 doesn't do what it is supposed to.

"continue" is not emitted by ssh (this.ssh), but by sftp() subsystem (which is an SFTPWrapper here). Actually it is the sftp from https://github.com/sanketbajoria/ssh2-promise/blob/master/src/sftp.ts#L39

Also, the event is called indeed just 'continue', not 'ssh:continue'.

This is critically: Once stopped because somehow continue is needed, the promise stays pending forever, because the event is missed that should resolve it.

`specs/fixture.json` file is missing

It is impossible to run the tests because there is no fixture.json file. It seems that it should contain a bunch of types of configuration. Presumably it was left out because there was passwords in there. If you could commit an example it would make it easier to reproduce. Plus then folks could actually test before submitting PR's!

Switch socksv5 dependency

socksv5 has a minor vulnerability via the ipv6 module that has since been renamed to ip-address. Heroku has published a fork of socksv5 that replaces the outdated ipv6 with ip-address.

You can find their fork here.

using sudo

There is not much guidance on using sudo with the ssh.exec() command.

I have tried

await ssh.exec('sudo mkdir /data/jestdir', { pty: true });

await ssh.exec('sudo mkdir /data/jestdir', null, { pty: true });

documentation isn't too clear about how to use, I either get an error or it hangs.

Please could you advise? Thanks

TypeScript: SSHConnections Missing Properties

Version: 0.1.1

I was checking this library out for a TS project, and noticed the ssh.shell method prop could not be found:

screen shot 2018-10-21 at 20 41 25

By the way, this happens with both ssh.exec, and ssh.shell (naturally).

While I'm very use to JS, I'm not too use to TS so this could be user error, in which I apologize.

I did manually reference the sshConnections.d file, to which nothing changed leading me to believe the above was correct. While it may not be needed, I do have the @types/ssh2 installed.

I'm curious, is there a reason the type defs aren't being run through DefinitelyTyped? It seems everything else works regardless, but I guess to me it's nice to know where they are and if there is anything I need to add to the compiler types. Is there a possibility of adding a note about this in the README?

Anyways, thanks for this library! It looks great so far. ๐Ÿ‘

Typing of sftp methods missing

Your sftp.d.ts file specifies the SFTP class type as:

export default class SFTP extends EventEmitter {
    ssh: SSH2Promise;
    constructor(ssh: SSH2Promise);
    createReadStream(): Promise<any>;
    createWriteStream(): Promise<any>;
}

However, this only tells the typescript compiler about EventEmitter methods, it does not indicate all of the SSH2 methods like 'readdir()', 'mkdir()', etc

I am wondering if you can simply import the SFTPWrapper interface from the SSH2 library type definition, here:
https://github.com/DefinitelyTyped/DefinitelyTyped/blob/11b48a05a8c7f2fb931ae89886006b7e1ab420e7/types/ssh2/index.d.ts#L1247

Then simply have your SFTP class extend that interface, which should give you all the methods...although those are not promisified

X11 forwarding may fail if X server is listening on a Unix Socket

Hello,

for x11, it might be that the X server is listening on a Unix socket and not a TCP port and host (see the ssh2/issue#1053 ).

For that case to work, we need to connect to the X server with the socket, for example :

xserversock.connect('/tmp/.X11-unix/X0');

rather than

xserversock.connect(6000, 'localhost');

which is actually the code used in src/sshConnection.ts#L265 which means the x11 call will fail if the X server is using a Unix socket.

I do not know what is the best way to parametrize this, maybe by providing an option to the SSH2Promise.x11 or an option of SSHConfig given to the constructor ? Actually , I do not know if it is possible to programatically determine if the X server is using a socket or a port and on which ones it is listening.

Best.
Jeremy.

error when a tunnel connection fails

When a tunnel errors, an error is thrown that the name cant be found.

line 352 of sshConnections looks like its the culprit.

name should be changed to tunnelConfig.name

Nested tunnel connection will need "sock" instead of "host" configuration.

Will need to use {sock: stream} instead of {host: host} in ssh configuration in order to build a nested tunnel.

[email protected]
sshConnections.js

connect(c) {^M
this.config = Object.assign(this.config, c);^M
++this.retries;^M
if (this.
$connectPromise != null)^M
return this.$connectPromise;^M
this.
$connectPromise = new Promise((resolve, reject) => {^M
this.emit(sshConstants_1.default.CHANNEL.SSH, sshConstants_1.default.STATUS.BEFORECONNECT);^M

  •        if (!this.config || typeof this.config === 'function' || !this.config.host || !this.config.username) 
    

{^M

  •       if (!this.config || typeof this.config === 'function' || !(this.config.host || this.config.sock) || !this.config.username) 
    

{^M
reject("Invalid SSH connection configuration host/username can't be empty");^M
this.__$connectPromise = null;^M
return;^M
}^M

String identity

Is there any way to use a string as identity instead of using path of the private key.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.