SSH2 client and server modules written in pure JavaScript for node.js
—
Comprehensive SSH agent integration supporting OpenSSH, Windows Pageant, and Cygwin agents for secure key management and authentication.
Abstract base class defining the SSH agent interface.
/**
* Abstract base class for SSH agents
* Provides standard interface for key management and signing operations
*/
class BaseAgent {
/**
* Get list of identities (keys) available in the agent
* @param callback - Callback receiving array of available keys
*/
getIdentities(callback: IdentitiesCallback): void;
/**
* Sign data using a key from the agent
* @param pubKey - Public key to use for signing
* @param data - Data to sign
* @param options - Optional signing options
* @param callback - Callback receiving signature
*/
sign(pubKey: ParsedKey, data: Buffer, options?: SignOptions, callback?: SignCallback): void;
sign(pubKey: ParsedKey, data: Buffer, callback: SignCallback): void;
}
interface SignOptions {
hash?: string;
}
type IdentitiesCallback = (err: Error | null, keys?: ParsedKey[]) => void;
type SignCallback = (err: Error | null, signature?: Buffer) => void;OpenSSH SSH agent implementation for Unix-like systems.
/**
* OpenSSH SSH agent implementation
* Connects to SSH agent via Unix domain socket
*/
class OpenSSHAgent extends BaseAgent {
/**
* Create OpenSSH agent instance
* @param socketPath - Path to SSH agent socket (default: SSH_AUTH_SOCK env var)
*/
constructor(socketPath?: string);
/**
* Get connection stream to SSH agent
* @param callback - Callback receiving agent connection stream
*/
getStream(callback: StreamCallback): void;
}
type StreamCallback = (err: Error | null, stream?: Duplex) => void;Usage Examples:
const { OpenSSHAgent } = require('ssh2');
// Connect to default SSH agent
const agent = new OpenSSHAgent();
// List available keys
agent.getIdentities((err, keys) => {
if (err) throw err;
console.log('Available keys:');
keys.forEach((key, index) => {
console.log(`${index}: ${key.type} ${key.comment || '(no comment)'}`);
});
// Sign data with first key
if (keys.length > 0) {
const data = Buffer.from('Hello, SSH agent!');
agent.sign(keys[0], data, (err, signature) => {
if (err) throw err;
console.log('Signature:', signature.toString('base64'));
});
}
});
// Connect to specific agent socket
const customAgent = new OpenSSHAgent('/custom/path/to/agent.sock');Windows Pageant SSH agent implementation.
/**
* Windows Pageant SSH agent implementation
* Communicates with Pageant via Windows messaging
*/
class PageantAgent extends OpenSSHAgent {
constructor();
}Usage Example:
// Windows Pageant agent (Windows only)
const { PageantAgent } = require('ssh2');
const agent = new PageantAgent();
agent.getIdentities((err, keys) => {
if (err) {
console.error('Pageant not available or no keys loaded');
return;
}
console.log(`Found ${keys.length} keys in Pageant`);
});Cygwin SSH agent implementation for Cygwin environments.
/**
* Cygwin SSH agent implementation
* Handles Cygwin-specific agent communication
*/
class CygwinAgent extends OpenSSHAgent {
constructor();
}Low-level SSH agent protocol implementation for creating custom agents.
/**
* SSH agent protocol implementation
* Handles SSH agent wire protocol for client and server modes
*/
class AgentProtocol extends EventEmitter {
/**
* Create agent protocol instance
* @param isClient - true for client mode, false for server mode
*/
constructor(isClient: boolean);
}/**
* Request identities from agent (client mode)
* @param callback - Callback receiving available keys
*/
getIdentities(callback: IdentitiesCallback): void;
/**
* Request signature from agent (client mode)
* @param pubKey - Public key to use for signing
* @param data - Data to sign
* @param options - Optional signing options
* @param callback - Callback receiving signature
*/
sign(pubKey: ParsedKey, data: Buffer, options?: SignOptions, callback?: SignCallback): void;
sign(pubKey: ParsedKey, data: Buffer, callback: SignCallback): void;/**
* Send failure response (server mode)
* @param reqId - Request ID to respond to
*/
failureReply(reqId: number): void;
/**
* Send identities response (server mode)
* @param reqId - Request ID to respond to
* @param keys - Array of available keys
*/
getIdentitiesReply(reqId: number, keys: ParsedKey[]): void;
/**
* Send signature response (server mode)
* @param reqId - Request ID to respond to
* @param signature - Signature data
*/
signReply(reqId: number, signature: Buffer): void;Helper class for iterating through agent keys during authentication.
/**
* Helper for iterating through SSH agent keys
* Simplifies multi-key authentication attempts
*/
class AgentContext {
/**
* Create agent context wrapper
* @param agent - SSH agent instance to wrap
*/
constructor(agent: BaseAgent);
/**
* Initialize key list from agent
* @param callback - Callback with initialization result
*/
init(callback: InitCallback): void;
/**
* Move to next available key
* @returns true if next key available, false if at end
*/
nextKey(): boolean;
/**
* Get current key
* @returns Current key or null if none available
*/
currentKey(): ParsedKey | null;
/**
* Get current position in key list
* @returns Current key index
*/
pos(): number;
/**
* Reset to first key
*/
reset(): void;
/**
* Sign data with current key
* @param data - Data to sign
* @param options - Optional signing options
* @param callback - Callback receiving signature
*/
sign(data: Buffer, options?: SignOptions, callback?: SignCallback): void;
sign(data: Buffer, callback: SignCallback): void;
}
type InitCallback = (err: Error | null) => void;Usage Example:
const { OpenSSHAgent, AgentContext } = require('ssh2');
const agent = new OpenSSHAgent();
const agentCtx = new AgentContext(agent);
agentCtx.init((err) => {
if (err) throw err;
console.log('Trying agent keys...');
function tryNextKey() {
const key = agentCtx.currentKey();
if (!key) {
console.log('No more keys to try');
return;
}
console.log(`Trying key: ${key.type} ${key.comment || '(no comment)'}`);
// Simulate authentication attempt
const authData = Buffer.from('authentication challenge');
agentCtx.sign(authData, (err, signature) => {
if (err) {
console.log('Key failed, trying next...');
if (agentCtx.nextKey()) {
tryNextKey();
} else {
console.log('All keys exhausted');
}
} else {
console.log('Authentication successful!');
}
});
}
if (agentCtx.currentKey()) {
tryNextKey();
}
});Utility function to create appropriate agent instance based on environment.
/**
* Create appropriate SSH agent instance
* Auto-detects agent type based on environment and input
* @param agent - Agent socket path, existing agent instance, or 'pageant'
* @returns Appropriate agent instance
*/
function createAgent(agent: string | BaseAgent): BaseAgent;Usage Examples:
const { createAgent } = require('ssh2');
// Auto-detect from environment
const agent1 = createAgent(process.env.SSH_AUTH_SOCK);
// Use specific socket path
const agent2 = createAgent('/tmp/ssh-agent.sock');
// Use Pageant on Windows
const agent3 = createAgent('pageant');
// Use existing agent instance
const existingAgent = new OpenSSHAgent();
const agent4 = createAgent(existingAgent);
// Use in SSH client
const conn = new Client();
conn.connect({
host: 'example.com',
username: 'user',
agent: createAgent(process.env.SSH_AUTH_SOCK),
agentForward: true
});Using SSH agents with the SSH client for automatic key-based authentication.
// Client configuration with agent
interface ClientConfigWithAgent {
agent?: string | BaseAgent;
agentForward?: boolean;
allowAgentFwd?: boolean;
}Client Integration Examples:
const { Client, OpenSSHAgent } = require('ssh2');
// Method 1: Use environment variable
const conn1 = new Client();
conn1.connect({
host: 'server.com',
username: 'user',
agent: process.env.SSH_AUTH_SOCK,
agentForward: true // Forward agent to remote server
});
// Method 2: Create agent explicitly
const agent = new OpenSSHAgent();
const conn2 = new Client();
conn2.connect({
host: 'server.com',
username: 'user',
agent: agent,
agentForward: true
});
// Method 3: Use specific agent socket
const conn3 = new Client();
conn3.connect({
host: 'server.com',
username: 'user',
agent: '/custom/agent/socket'
});
// Method 4: Pageant on Windows
const conn4 = new Client();
conn4.connect({
host: 'server.com',
username: 'user',
agent: 'pageant'
});SSH agents emit events for connection and operation status.
interface AgentEvents {
/**
* Emitted when agent connection is established
*/
'connect': () => void;
/**
* Emitted when agent connection ends
*/
'end': () => void;
/**
* Emitted when agent connection is closed
*/
'close': () => void;
/**
* Emitted when agent error occurs
*/
'error': (err: Error) => void;
}interface ParsedKey {
type: string;
comment?: string;
public: Buffer;
private?: Buffer;
// Key methods
getPrivatePEM(): string;
getPublicPEM(): string;
getPublicSSH(): Buffer;
sign(data: Buffer, hashAlgo?: string): Buffer | Error;
verify(data: Buffer, signature: Buffer, hashAlgo?: string): boolean | Error;
}
// Agent detection helper
function isAgent(obj: any): obj is BaseAgent;SSH agents typically use environment variables for configuration:
// Check if agent is available
if (process.env.SSH_AUTH_SOCK) {
console.log('SSH agent available at:', process.env.SSH_AUTH_SOCK);
const agent = new OpenSSHAgent(process.env.SSH_AUTH_SOCK);
} else {
console.log('No SSH agent configured');
}// Platform-specific agent selection
const { platform } = require('os');
let agent;
switch (platform()) {
case 'win32':
agent = new PageantAgent();
break;
case 'linux':
case 'darwin':
case 'freebsd':
agent = new OpenSSHAgent();
break;
default:
agent = createAgent(process.env.SSH_AUTH_SOCK);
}Install with Tessl CLI
npx tessl i tessl/npm-ssh2