or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

http-tunneling.mdindex.mdkey-management.mdport-forwarding.mdsftp-operations.mdssh-agents.mdssh-client.mdssh-server.md

port-forwarding.mddocs/

0

# Port Forwarding

1

2

Comprehensive port forwarding capabilities for creating secure tunnels through SSH connections, including local forwarding, remote forwarding, and Unix domain socket support.

3

4

## Capabilities

5

6

### Local Port Forwarding

7

8

Forward connections from client through server to destination (client initiates connections).

9

10

```javascript { .api }

11

/**

12

* Create local port forwarding connection (client -> server -> destination)

13

* @param srcIP - Source IP address (usually from client perspective)

14

* @param srcPort - Source port number

15

* @param dstIP - Destination IP address (from server perspective)

16

* @param dstPort - Destination port number

17

* @param callback - Callback receiving connection stream

18

* @returns false if connection not ready, true otherwise

19

*/

20

forwardOut(srcIP: string, srcPort: number, dstIP: string, dstPort: number, callback: ChannelCallback): boolean;

21

22

type ChannelCallback = (err: Error | null, stream?: ClientChannel) => void;

23

```

24

25

**Local Forwarding Examples:**

26

27

```javascript

28

const { Client } = require('ssh2');

29

30

const conn = new Client();

31

conn.on('ready', () => {

32

console.log('Client :: ready');

33

34

// Forward connection to database through SSH server

35

conn.forwardOut('127.0.0.1', 0, 'database.internal', 5432, (err, stream) => {

36

if (err) throw err;

37

38

console.log('Connected to database through SSH tunnel');

39

40

// Use stream as regular TCP connection

41

stream.on('data', (data) => {

42

console.log('Received:', data.toString());

43

});

44

45

stream.write('SELECT version();\n');

46

});

47

48

}).connect({

49

host: 'jump-server.com',

50

username: 'user',

51

privateKey: privateKey

52

});

53

```

54

55

### Remote Port Forwarding

56

57

Set up server to listen on port and forward connections to client (server accepts connections).

58

59

```javascript { .api }

60

/**

61

* Request remote port forwarding (server binds port and forwards to client)

62

* Server will listen on bindAddr:bindPort and forward connections to client

63

* @param bindAddr - Address for server to bind ('' for all interfaces)

64

* @param bindPort - Port for server to bind (0 for dynamic allocation)

65

* @param callback - Callback with actual bound port

66

* @returns false if connection not ready, true otherwise

67

*/

68

forwardIn(bindAddr: string, bindPort: number, callback: ForwardCallback): boolean;

69

70

/**

71

* Cancel remote port forwarding

72

* @param bindAddr - Address that was bound

73

* @param bindPort - Port that was bound

74

* @param callback - Callback with cancellation result

75

* @returns false if connection not ready, true otherwise

76

*/

77

unforwardIn(bindAddr: string, bindPort: number, callback: UnforwardCallback): boolean;

78

79

type ForwardCallback = (err: Error | null, bindPort?: number) => void;

80

type UnforwardCallback = (err: Error | null) => void;

81

```

82

83

**Remote Forwarding Examples:**

84

85

```javascript

86

const { Client } = require('ssh2');

87

88

const conn = new Client();

89

conn.on('ready', () => {

90

console.log('Client :: ready');

91

92

// Request server to listen on port 8080 and forward to client

93

conn.forwardIn('', 8080, (err, bindPort) => {

94

if (err) throw err;

95

96

console.log(`Server listening on port ${bindPort}`);

97

console.log('Remote connections will be forwarded to client');

98

});

99

100

}).on('tcp connection', (details, accept, reject) => {

101

console.log('Incoming connection:', details);

102

103

// Accept the forwarded connection

104

const stream = accept();

105

106

// Handle the connection (e.g., proxy to local service)

107

const net = require('net');

108

const localConnection = net.createConnection(3000, 'localhost');

109

110

stream.pipe(localConnection);

111

localConnection.pipe(stream);

112

113

stream.on('close', () => {

114

console.log('Forwarded connection closed');

115

localConnection.end();

116

});

117

118

}).connect({

119

host: 'public-server.com',

120

username: 'user',

121

privateKey: privateKey

122

});

123

```

124

125

### Unix Domain Socket Forwarding (OpenSSH Extensions)

126

127

Forward Unix domain sockets for local IPC communication.

128

129

```javascript { .api }

130

/**

131

* Request Unix domain socket forwarding (OpenSSH extension)

132

* Server will listen on Unix socket and forward connections to client

133

* @param socketPath - Unix socket path on server

134

* @param callback - Callback with operation result

135

* @returns false if connection not ready, true otherwise

136

*/

137

openssh_forwardInStreamLocal(socketPath: string, callback: ForwardCallback): boolean;

138

139

/**

140

* Cancel Unix domain socket forwarding (OpenSSH extension)

141

* @param socketPath - Unix socket path to unbind

142

* @param callback - Callback with cancellation result

143

* @returns false if connection not ready, true otherwise

144

*/

145

openssh_unforwardInStreamLocal(socketPath: string, callback: UnforwardCallback): boolean;

146

147

/**

148

* Connect to Unix domain socket through server (OpenSSH extension)

149

* @param socketPath - Unix socket path on server to connect to

150

* @param callback - Callback receiving connection stream

151

* @returns false if connection not ready, true otherwise

152

*/

153

openssh_forwardOutStreamLocal(socketPath: string, callback: ChannelCallback): boolean;

154

```

155

156

**Unix Socket Forwarding Examples:**

157

158

```javascript

159

const { Client } = require('ssh2');

160

161

const conn = new Client();

162

conn.on('ready', () => {

163

console.log('Client :: ready');

164

165

// Forward Unix socket from server to client

166

conn.openssh_forwardInStreamLocal('/tmp/remote-socket', (err) => {

167

if (err) throw err;

168

console.log('Unix socket forwarding established');

169

});

170

171

// Connect to Unix socket on server

172

conn.openssh_forwardOutStreamLocal('/var/run/docker.sock', (err, stream) => {

173

if (err) throw err;

174

console.log('Connected to Docker socket through SSH');

175

176

// Use stream to communicate with Docker daemon

177

stream.write('GET /containers/json HTTP/1.1\r\nHost: localhost\r\n\r\n');

178

});

179

180

}).on('unix connection', (info, accept, reject) => {

181

console.log('Incoming Unix connection:', info.socketPath);

182

183

const stream = accept();

184

// Handle Unix socket connection...

185

186

}).connect({

187

host: 'docker-host.com',

188

username: 'user',

189

privateKey: privateKey

190

});

191

```

192

193

## Server-Side Port Forwarding

194

195

Handle port forwarding requests when operating as SSH server.

196

197

### Connection Request Events

198

199

```javascript { .api }

200

/**

201

* Handle direct TCP/IP connection requests (local forwarding from client)

202

*/

203

client.on('tcpip', (accept, reject, info: TcpipInfo) => {

204

console.log(`Client wants to connect to ${info.destIP}:${info.destPort}`);

205

206

if (allowConnection(info)) {

207

const stream = accept();

208

// Set up connection to destination

209

const net = require('net');

210

const connection = net.createConnection(info.destPort, info.destIP);

211

stream.pipe(connection);

212

connection.pipe(stream);

213

} else {

214

reject();

215

}

216

});

217

218

/**

219

* Handle OpenSSH streamlocal connection requests (Unix socket forwarding)

220

*/

221

client.on('openssh.streamlocal', (accept, reject, info: StreamLocalInfo) => {

222

console.log(`Client wants to connect to Unix socket: ${info.socketPath}`);

223

224

const stream = accept();

225

const net = require('net');

226

const connection = net.createConnection(info.socketPath);

227

stream.pipe(connection);

228

connection.pipe(stream);

229

});

230

231

interface TcpipInfo {

232

srcIP: string;

233

srcPort: number;

234

destIP: string;

235

destPort: number;

236

}

237

238

interface StreamLocalInfo {

239

socketPath: string;

240

}

241

```

242

243

### Global Request Handling

244

245

```javascript { .api }

246

/**

247

* Handle global requests for port forwarding setup

248

*/

249

client.on('request', (accept, reject, name: string, info: RequestInfo) => {

250

console.log(`Global request: ${name}`, info);

251

252

switch (name) {

253

case 'tcpip-forward':

254

// Set up remote port forwarding

255

console.log(`Binding ${info.bindAddr}:${info.bindPort}`);

256

const server = net.createServer((connection) => {

257

// Forward incoming connections to client

258

client.forwardOut(

259

info.bindAddr, info.bindPort,

260

connection.remoteAddress, connection.remotePort,

261

(err, stream) => {

262

if (!err) {

263

connection.pipe(stream);

264

stream.pipe(connection);

265

} else {

266

connection.end();

267

}

268

}

269

);

270

});

271

272

server.listen(info.bindPort, info.bindAddr, () => {

273

accept(); // Accept the forwarding request

274

});

275

break;

276

277

case 'cancel-tcpip-forward':

278

// Cancel remote port forwarding

279

console.log(`Unbinding ${info.bindAddr}:${info.bindPort}`);

280

// Clean up server listening on this port

281

accept();

282

break;

283

284

case 'streamlocal-forward@openssh.com':

285

// Set up Unix socket forwarding

286

console.log(`Binding Unix socket: ${info.socketPath}`);

287

accept();

288

break;

289

290

default:

291

reject();

292

}

293

});

294

295

interface RequestInfo {

296

bindAddr?: string;

297

bindPort?: number;

298

socketPath?: string;

299

[key: string]: any;

300

}

301

```

302

303

## Advanced Port Forwarding Patterns

304

305

### Dynamic Port Forwarding (SOCKS Proxy)

306

307

Create SOCKS proxy functionality using port forwarding.

308

309

```javascript

310

const { Client } = require('ssh2');

311

const net = require('net');

312

313

function createSOCKSProxy(sshConfig, localPort) {

314

const sshClient = new Client();

315

316

// Create local SOCKS server

317

const socksServer = net.createServer((clientSocket) => {

318

let stage = 0;

319

let targetHost, targetPort;

320

321

clientSocket.on('data', (data) => {

322

if (stage === 0) {

323

// SOCKS handshake

324

if (data[0] === 0x05) {

325

clientSocket.write(Buffer.from([0x05, 0x00])); // No auth required

326

stage = 1;

327

}

328

} else if (stage === 1) {

329

// SOCKS connect request

330

if (data[0] === 0x05 && data[1] === 0x01) {

331

const addrType = data[3];

332

let offset = 4;

333

334

if (addrType === 0x01) {

335

// IPv4

336

targetHost = `${data[4]}.${data[5]}.${data[6]}.${data[7]}`;

337

offset = 8;

338

} else if (addrType === 0x03) {

339

// Domain name

340

const domainLen = data[4];

341

targetHost = data.slice(5, 5 + domainLen).toString();

342

offset = 5 + domainLen;

343

}

344

345

targetPort = data.readUInt16BE(offset);

346

347

// Forward through SSH

348

sshClient.forwardOut('127.0.0.1', 0, targetHost, targetPort, (err, stream) => {

349

if (err) {

350

clientSocket.write(Buffer.from([0x05, 0x01])); // General failure

351

clientSocket.end();

352

} else {

353

clientSocket.write(Buffer.from([0x05, 0x00, 0x00, 0x01, 0, 0, 0, 0, 0, 0])); // Success

354

clientSocket.pipe(stream);

355

stream.pipe(clientSocket);

356

}

357

});

358

}

359

}

360

});

361

});

362

363

sshClient.on('ready', () => {

364

socksServer.listen(localPort, () => {

365

console.log(`SOCKS proxy listening on port ${localPort}`);

366

});

367

}).connect(sshConfig);

368

369

return { sshClient, socksServer };

370

}

371

372

// Usage

373

const proxy = createSOCKSProxy({

374

host: 'ssh-server.com',

375

username: 'user',

376

privateKey: privateKey

377

}, 1080);

378

```

379

380

### Multi-Hop Port Forwarding

381

382

Chain multiple SSH connections for complex routing.

383

384

```javascript

385

const { Client } = require('ssh2');

386

387

function createMultiHopTunnel() {

388

const jump1 = new Client();

389

const jump2 = new Client();

390

391

jump1.on('ready', () => {

392

console.log('Jump1 connected');

393

394

// Connect through first jump server to second

395

jump1.forwardOut('127.0.0.1', 0, 'internal-jump.corp', 22, (err, stream) => {

396

if (err) throw err;

397

398

// Use first tunnel as transport for second SSH connection

399

jump2.connect({

400

username: 'user2',

401

privateKey: privateKey2,

402

sock: stream

403

});

404

});

405

406

}).connect({

407

host: 'external-jump.com',

408

username: 'user1',

409

privateKey: privateKey1

410

});

411

412

jump2.on('ready', () => {

413

console.log('Jump2 connected through Jump1');

414

415

// Now forward to final destination through both jumps

416

jump2.forwardOut('127.0.0.1', 0, 'secure-server.corp', 3389, (err, stream) => {

417

if (err) throw err;

418

419

console.log('Connected to final destination through multi-hop tunnel');

420

// Use stream for RDP connection or other protocol

421

});

422

});

423

}

424

```

425

426

## Connection Event Details

427

428

### TCP Connection Events

429

430

```javascript { .api }

431

interface TcpConnectionDetails {

432

srcIP: string; // Source IP address

433

srcPort: number; // Source port number

434

destIP: string; // Destination IP address

435

destPort: number; // Destination port number

436

}

437

438

// Client event for incoming forwarded connections

439

client.on('tcp connection', (details: TcpConnectionDetails, accept: AcceptConnection, reject: RejectConnection) => {

440

console.log(`Incoming TCP connection from ${details.srcIP}:${details.srcPort} to ${details.destIP}:${details.destPort}`);

441

442

const stream = accept();

443

// Handle the forwarded connection...

444

});

445

```

446

447

### Unix Connection Events

448

449

```javascript { .api }

450

interface UnixConnectionInfo {

451

socketPath: string; // Unix socket path

452

}

453

454

// Client event for incoming Unix socket connections

455

client.on('unix connection', (info: UnixConnectionInfo, accept: AcceptConnection, reject: RejectConnection) => {

456

console.log(`Incoming Unix connection to ${info.socketPath}`);

457

458

const stream = accept();

459

// Handle the Unix socket connection...

460

});

461

```

462

463

## Type Definitions

464

465

### Connection Handling Types

466

467

```javascript { .api }

468

type AcceptConnection<T = ClientChannel> = () => T;

469

type RejectConnection = () => boolean;

470

471

interface ClientChannel extends Duplex {

472

stderr: Duplex;

473

setWindow(rows: number, cols: number, height?: number, width?: number): boolean;

474

signal(signalName: string): boolean;

475

exit(status: number): boolean;

476

}

477

```

478

479

## Best Practices

480

481

### Security Considerations

482

483

```javascript

484

// Restrict forwarding destinations

485

client.on('tcpip', (accept, reject, info) => {

486

const allowedHosts = ['127.0.0.1', 'localhost', '192.168.1.0/24'];

487

const allowedPorts = [80, 443, 3000, 8080];

488

489

if (isAllowedDestination(info.destIP, allowedHosts) &&

490

allowedPorts.includes(info.destPort)) {

491

accept();

492

} else {

493

console.log(`Rejected connection to ${info.destIP}:${info.destPort}`);

494

reject();

495

}

496

});

497

498

// Rate limiting

499

const connectionCounts = new Map();

500

501

client.on('tcpip', (accept, reject, info) => {

502

const key = `${info.srcIP}:${info.srcPort}`;

503

const count = connectionCounts.get(key) || 0;

504

505

if (count > 10) {

506

console.log(`Rate limit exceeded for ${key}`);

507

reject();

508

} else {

509

connectionCounts.set(key, count + 1);

510

const stream = accept();

511

512

stream.on('close', () => {

513

connectionCounts.set(key, Math.max(0, connectionCounts.get(key) - 1));

514

});

515

}

516

});

517

```

518

519

### Connection Management

520

521

```javascript

522

// Track active forwards

523

const activeForwards = new Map();

524

525

function createForward(remoteAddr, remotePort, localAddr, localPort) {

526

const key = `${remoteAddr}:${remotePort}`;

527

528

if (activeForwards.has(key)) {

529

console.log(`Forward already exists: ${key}`);

530

return;

531

}

532

533

conn.forwardIn(remoteAddr, remotePort, (err, bindPort) => {

534

if (err) {

535

console.error(`Failed to create forward: ${err.message}`);

536

} else {

537

activeForwards.set(key, { bindPort, localAddr, localPort });

538

console.log(`Forward created: ${remoteAddr}:${bindPort} -> ${localAddr}:${localPort}`);

539

}

540

});

541

}

542

543

function removeForward(remoteAddr, remotePort) {

544

const key = `${remoteAddr}:${remotePort}`;

545

const forward = activeForwards.get(key);

546

547

if (forward) {

548

conn.unforwardIn(remoteAddr, forward.bindPort, (err) => {

549

if (!err) {

550

activeForwards.delete(key);

551

console.log(`Forward removed: ${key}`);

552

}

553

});

554

}

555

}

556

```