This script demonstrates how to create an SSH tunnel to a remote resource by first connecting through a jump host (also known as a bastion host). Optionally, you can route the connection through a SOCKS proxy first.
Local Machine → Jump Host → Remote Resource
↓ ↓ ↓
localhost: jump-host: remote-host:
3306 22 3306
Local Machine → SOCKS Proxy → Jump Host → Remote Resource
↓ ↓ ↓ ↓
localhost: socks-proxy: jump-host: remote-host:
3306 1080 22 3306
The script creates a local port that forwards all traffic through the chain (SOCKS proxy → jump host → remote resource).
pnpm install- Edit
index.jsand update the configuration object with your connection details:
const config = {
// Optional: SOCKS proxy configuration (uncomment to use)
// socksProxy: {
// host: 'socks-proxy.example.com',
// port: 1080, // SOCKS proxy port (default: 1080)
// type: 'socks5', // 'socks4', 'socks4a', or 'socks5' (default: 'socks5')
// // For SOCKS5 authentication (optional):
// username: 'proxy-username',
// password: 'proxy-password',
// // For SOCKS4 authentication (optional):
// // userId: 'user-id',
// },
jumpHost: {
host: 'jump.example.com',
port: 22,
username: 'your-username',
keyPath: '/path/to/jump-host-key.pem', // Path to PEM key file for jump host
passphrase: 'key-passphrase-if-needed', // Optional, only if key is encrypted
},
remoteResource: {
host: 'internal-server.example.com',
port: 3306, // e.g., MySQL: 3306, PostgreSQL: 5432, HTTP: 80
// For SSH-to-SSH tunneling, uncomment:
// useSSH: true,
// username: 'remote-username',
// keyPath: '/path/to/remote-resource-key.pem', // Different key for remote resource
},
local: {
host: '127.0.0.1',
port: 3306,
}
};- Run the script:
pnpm start
# or
node index.js- Connect to the local port as if it were the remote resource:
# Example: Connect to MySQL through the tunnel
mysql -h 127.0.0.1 -P 3306 -u username -p
# Example: Connect to PostgreSQL through the tunnel
psql -h 127.0.0.1 -p 5432 -U username -d databaseIf you need to route through a SOCKS proxy before reaching the jump host, you can configure it:
socksProxy: {
host: 'socks-proxy.example.com',
port: 1080, // SOCKS proxy port (default: 1080)
type: 'socks5', // 'socks4', 'socks4a', or 'socks5' (default: 'socks5')
// For SOCKS5 authentication (optional):
username: 'proxy-username',
password: 'proxy-password',
// For SOCKS4 authentication (optional):
// userId: 'user-id',
}Note: The SOCKS proxy is optional. If not specified, the script will connect directly to the jump host.
jumpHost: {
host: 'jump.example.com',
username: 'user',
keyPath: '/path/to/jump-host-key.pem',
passphrase: 'key-passphrase-if-needed', // Optional, only if key is encrypted
}jumpHost: {
host: 'jump.example.com',
username: 'user',
privateKey: require('fs').readFileSync('/path/to/jump-host-key.pem'),
passphrase: 'key-passphrase-if-needed', // Optional
}jumpHost: {
host: 'jump.example.com',
username: 'user',
password: 'your-password',
}For TCP forwarding (most common use case - databases, HTTP services, etc.), you don't need authentication for the remote resource. Only the jump host key is required.
For SSH-to-SSH tunneling (advanced use case), you can specify a different key for the remote resource:
remoteResource: {
host: 'internal-server.example.com',
port: 22, // SSH port
useSSH: true,
username: 'remote-username',
keyPath: '/path/to/remote-resource-key.pem', // Different key than jump host
passphrase: 'remote-key-passphrase-if-needed', // Optional
targetPort: 3306, // Port to forward on the remote SSH server
}- Accessing internal databases from your local machine
- Connecting to services behind a firewall
- Securely accessing remote resources without exposing them publicly
- Development and debugging of remote services
- Connect through SOCKS Proxy (if configured): Establishes a connection through the SOCKS proxy to the jump host
- Connect to Jump Host: Establishes an SSH connection to the jump host (via SOCKS if configured)
- Create Local Listener: Starts a local TCP server on the specified port
- Forward Connections: For each incoming connection, creates a forwarded connection through the jump host using
forwardOut() - Pipe Data: Bidirectionally pipes data between the local connection and the remote connection
The script exports a createTunnelThroughJumpHost function that can be used programmatically:
const { createTunnelThroughJumpHost } = require('./index.js');
const tunnel = await createTunnelThroughJumpHost({
jumpHost: { /* ... */ },
remoteResource: { /* ... */ },
local: { /* ... */ }
});
// Later, close the tunnel
tunnel.close();- The tunnel will remain active until you press Ctrl+C or call
tunnel.close() - Make sure the jump host has network access to the remote resource
- The local port must be available (not already in use)
- Ensure your SSH credentials for the jump host are correct
- If using a SOCKS proxy, ensure the proxy has network access to the jump host
- SOCKS proxy authentication is optional but recommended for security