Install ssh2 via npm
masterInstall the ssh2 package using npm to use the SSH2 client and server modules in your Node.js project.
npm install ssh2repository·master·Indexed 26 days ago
https://github.com/mscdex/ssh2Pure JavaScript SSH2 client and server modules for Node.js (version 16.0.0 or newer). It supports implementing SSH protocols, including client connections, server hosting, SFTP, port forwarding, X11 forwarding, and connection hopping. The library provides methods for executing remote commands via exec(), starting interactive shell sessions, and interacting with arbitrary subsystems via subsys().
Install the ssh2 package using npm to use the SSH2 client and server modules in your Node.js project.
npm install ssh2To use ssh2, ensure your environment meets the following requirements:
ssh2 module provides a complete SSH1/SSH2 implementation for Node.js, including both Client and Server capabilities, SSH Agent support, and SFTP functionality. You can access the main components via the default export.When setting up an SSH2 server, you can customize the server identifier and inject custom socket streams.
ident: A string representing the custom server software name/version identifier. Default: 'ssh2js' + moduleVersion + 'srv'.injectSocket(socket): Injects a bidirectional DuplexStream as if it were a TCP socket. The provided socket should include net.Socket-like properties such as socket.remoteAddress, socket.remotePort, and socket.remoteFamily for maximum compatibility.If tryKeyboard is enabled, you must implement a prompt handler within the keyboard-interactive authentication configuration to respond to server challenges:
{
type: 'keyboard-interactive',
username: 'foo',
prompt: (name, instructions, instructionsLang, prompts, finish) => {
// ... handle prompts and call finish(true/false) to proceed
},
}{
type: 'keyboard-interactive',
username: 'foo',
// This works exactly the same way as a 'keyboard-interactive'
// Client event handler
prompt: (name, instructions, instructionsLang, prompts, finish) => {
// ...
},
}To build a server dedicated to SFTP, handle the session event and then listen for the sftp event on the session object.
Inside the sftp event handler, you can respond to file operations by listening to events like OPEN, WRITE, and CLOSE. Use sftp.status(reqid, STATUS_CODE.OK) or sftp.status(reqid, STATUS_CODE.FAILURE) to communicate the result of an operation to the client. For OPEN requests, you can return a handle using sftp.handle(reqid, handle).
const { timingSafeEqual } = require('crypto');
const { readFileSync } = require('fs');
const { inspect } = require('util');
const {
Server,
sftp: {
OPEN_MODE,
STATUS_CODE,
},
} = require('ssh2');
const allowedUser = Buffer.from('foo');
const allowedPassword = Buffer.from('bar');
function checkValue(input, allowed) {
const autoReject = (input.length !== allowed.length);
if (autoReject) {
allowed = input;
}
const isMatch = timingSafeEqual(input, allowed);
return (!autoReject && isMatch);
}
new ssh2.Server({
hostKeys: [readFileSync('host.key')]
}, (client) => {
console.log('Client connected!');
client.on('authentication', (ctx) => {
let allowed = true;
if (!checkValue(Buffer.from(ctx.username), allowedUser))
allowed = false;
switch (ctx.method) {
case 'password':
if (!checkValue(Buffer.from(ctx.password), allowedPassword))
return ctx.reject();
break;
default:
return ctx.reject();
}
if (allowed)
ctx.accept();
else
ctx.reject();
}).on('ready', () => {
console.log('Client authenticated!');
client.on('session', (accept, reject) => {
const session = accept();
session.on('sftp', (accept, reject) => {
console.log('Client SFTP session');
const openFiles = new Map();
let handleCount = 0;
const sftp = accept();
sftp.on('OPEN', (reqid, filename, flags, attrs) => {
if (filename !== '/tmp/foo.txt' || !(flags & OPEN_MODE.WRITE))
return sftp.status(reqid, STATUS_CODE.FAILURE);
const handle = Buffer.alloc(4);
openFiles.set(handleCount, true);
handle.writeUInt32BE(handleCount++, 0);
console.log('Opening file for write')
sftp.handle(reqid, handle);
}).on('WRITE', (reqid, handle, offset, data) => {
if (handle.length !== 4
|| !openFiles.has(handle.readUInt32BE(0))) {
return sftp.status(reqid, STATUS_CODE.FAILURE);
}
sftp.status(reqid, STATUS_CODE.OK);
console.log('Write to file at offset ${offset}: ${inspect(data)}');
}).on('CLOSE', (reqid, handle) => {
let fnum;
if (handle.length !== 4
|| !openFiles.has(fnum = handle.readUInt32BE(0))) {
return sftp.status(reqid, STATUS_CODE.FAILURE);
}
console.log('Closing file');
openFiles.delete(fnum);
sftp.status(reqid, STATUS_CODE.OK);
});
});
});
}).on('close', () => {
console.log('Client disconnected');
});
}).listen(0, '127.0.0.1', function() {
console.log('Listening on port ' + this.address().port);
});Use the Client.exec() method to run a single command on a remote server. The method accepts a command string and a callback that provides an error object and a readable/writable stream. You can listen to the data event for stdout and the stderr property of the stream for error output.
const { readFileSync } = require('fs');
const { Client } = require('ssh2');
const conn = new Client();
conn.on('ready', () => {
console.log('Client :: ready');
conn.exec('uptime', (err, stream) => {
if (err) throw err;
stream.on('close', (code, signal) => {
console.log('Stream :: close :: code: ' + code + ', signal: ' + signal);
conn.end();
}).on('data', (data) => {
console.log('STDOUT: ' + data);
}).stderr.on('data', (data) => {
console.log('STDERR: ' + data);
});
});
}).connect({
host: '192.168.100.100',
port: 22,
username: 'frylock',
privateKey: readFileSync('/path/to/my/key')
});Use Client.sftp(callback) to initiate an SFTP session. Once the SFTP client is available, use sftp.readdir(path, callback) to get a list of files and directories in the specified path. The list contains objects with filename, longname, and attrs (size, uid, gid, mode, etc.).
const { Client } = require('ssh2');
const conn = new Client();
conn.on('ready', () => {
console.log('Client :: ready');
conn.sftp((err, sftp) => {
if (err) throw err;
sftp.readdir('foo', (err, list) => {
if (err) throw err;
console.dir(list);
conn.end();
});
});
}).connect({
host: '192.168.100.100',
port: 22,
username: 'frylock',
password: 'nodejsrules'
});To support X11 forwarding, listen for the x11 event on the Client instance. The event provides info, accept, and reject arguments. You can then pipe the accepted stream to a local X server socket.
const { Socket } = require('net');
const { Client } = require('ssh2');
const conn = new Client();
conn.on('x11', (info, accept, reject) => {
const xserversock = new net.Socket();
xserversock.on('connect', () => {
const xclientsock = accept();
xclientsock.pipe(xserversock).pipe(xclientsock);
});
xserversock.connect(6000, 'localhost');
});
conn.on('ready', () => {
conn.exec('xeyes', { x11: true }, (err, stream) => {
if (err) throw err;
let code = 0;
stream.on('close', () => {
if (code !== 0)
console.log('Do you have X11 forwarding enabled on your SSH server?');
conn.end();
}).on('exit', (exitcode) => {
code = exitcode;
});
});
}).connect({
host: '192.168.1.1',
username: 'foo',
password: 'bar'
});Use Client.shell() to request an interactive shell session. This returns a stream that allows you to send commands (e.g., via stream.end('command\n')) and receive interactive output. This is useful for simulating a terminal session.
const { readFileSync } = require('fs');
const { Client } = require('ssh2');
const conn = new Client();
conn.on('ready', () => {
console.log('Client :: ready');
conn.shell((err, stream) => {
if (err) throw err;
stream.on('close', () => {
console.log('Stream :: close');
conn.end();
}).on('data', (data) => {
console.log('OUTPUT: ' + data);
});
stream.end('ls -l\nexit\n');
});
}).connect({
host: '192.168.100.100',
port: 22,
username: 'frylock',
privateKey: readFileSync('/path/to/my/key')
});You can chain SSH connections to hop through a jump server. Use forwardOut() on the first connection to create a stream, then pass that stream as the sock option in the .connect() method of a second Client instance.
const { Client } = require('ssh2');
const conn1 = new Client();
const conn2 = new Client();
// Checks uptime on 10.1.1.40 via 192.168.1.1
conn1.on('ready', () => {
console.log('FIRST :: connection ready');
conn1.forwardOut('127.0.0.1', 12345, '10.1.1.40', 22, (err, stream) => {
if (err) {
console.log('FIRST :: forwardOut error: ' + err);
return conn1.end();
}
conn2.connect({
sock: stream,
username: 'user2',
password: 'password2',
});
});
}).connect({
host: '192.168.1.1',
username: 'user1',
password: 'password1',
});
conn2.on('ready', () => {
console.log('SECOND :: connection ready');
conn2.exec('uptime', (err, stream) => {
if (err) {
console.log('SECOND :: exec error: ' + err);
return conn1.end();
}
stream.on('close', () => {
conn1.end();
}).on('data', (data) => {
console.log(data.toString());
});
});
});Use Client.forwardOut(host, port, remoteHost, remotePort, callback) to create a remote port forward. This allows you to tunnel a connection from the remote server to a specific destination (e.g., sending an HTTP request from the server to a local service).
const { Client } = require('ssh2');
const conn = new Client();
conn.on('ready', () => {
console.log('Client :: ready');
conn.forwardOut('192.168.100.102', 8000, '127.0.0.1', 80, (err, stream) => {
if (err) throw err;
stream.on('close', () => {
console.log('TCP :: CLOSED');
conn.end();
}).on('data', (data) => {
console.log('TCP :: DATA: ' + data);
}).end([
'HEAD / HTTP/1.1',
'User-Agent: curl/7.27.0',
'Host: 127.0.0.1',
'Accept: */*',
'Connection: close',
'',
''
].join('\r\n'));
});
}).connect({
host: '192.168.100.100',
port: 22,
username: 'frylock',
password: 'nodejsrules'
});