You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现支持远程端口转发的Node SSH2服务器?

我来帮你解决这个远程端口转发的问题!你之前的代码里踩了一个常见的坑——client.forwardIn是SSH客户端用来请求本地转发(-L参数)的方法,服务器端根本不该这么用。咱们换个正确的思路来实现远程端口转发(-R参数)的功能:

核心逻辑梳理

远程端口转发的本质是:客户端让你的Node SSH服务器监听指定端口,当有外部连接打到这个端口时,服务器通过SSH通道把连接数据流传给客户端,客户端再把数据转发到它指定的目标服务(比如你的localsite.tld:80)。所以服务器需要自己监听端口,再把新连接和SSH通道做双向桥接。

修正后的完整代码

var fs = require('fs');
var inspect = require('util').inspect;
var ssh2 = require('ssh2');
var net = require('net'); // 新增引入net模块,用来创建TCP监听服务器

new ssh2.Server({
  hostKeys: [fs.readFileSync('/etc/ssh/ssh_host_rsa_key')]
}, function(client) {
  console.log('Client connected!');

  client.on('authentication', function(ctx) {
    if (ctx.method === 'password' && ctx.username === 'foo' && ctx.password === 'bar')
      ctx.accept();
    else
      ctx.reject();
  }).on('ready', function() {
    console.log('Client authenticated!');

    // 保留你原来的session/exec处理逻辑(如果需要的话)
    client.on('session', function(accept, reject) {
      var session = accept();
      session.once('exec', function(accept, reject, info) {
        console.log('Client wants to execute: ' + inspect(info.command));
        var stream = accept();
        stream.stderr.write('Oh no, the dreaded errors!\n');
        stream.write('Just kidding about the errors!\n');
        stream.exit(0);
        stream.end();
      });
    });

    // 处理远程端口转发请求
    client.on('request', function(accept, reject, name, info) {
      console.log(`Received request: ${name}`, info);
      if (name === 'tcpip-forward') {
        accept(); // 同意客户端的远程转发请求
        
        // 创建TCP服务器,监听客户端要求的端口
        const forwardServer = net.createServer((externalConn) => {
          console.log(`Incoming connection to forwarded port ${info.bindPort}`);
          
          // 向客户端发起direct-tcpip通道请求,把外部连接的信息传给客户端
          client.openChannel('direct-tcpip', {
            originAddr: externalConn.remoteAddress,
            originPort: externalConn.remotePort,
            remoteAddr: info.originAddr, // 客户端指定的目标地址(localsite.tld)
            remotePort: info.originPort  // 客户端指定的目标端口(80)
          }, (err, channel) => {
            if (err) {
              console.error('Failed to open SSH channel:', err);
              externalConn.destroy();
              return;
            }
            
            // 双向桥接外部连接和SSH通道的数据流
            externalConn.pipe(channel).pipe(externalConn);
            
            // 处理关闭/错误事件,避免资源泄漏
            channel.on('close', () => externalConn.destroy());
            externalConn.on('close', () => channel.close());
            channel.on('error', (err) => {
              console.error('Channel error:', err);
              externalConn.destroy();
            });
            externalConn.on('error', (err) => {
              console.error('Connection error:', err);
              channel.close();
            });
          });
        });
        
        // 启动监听
        forwardServer.listen(info.bindPort, info.bindAddr, () => {
          console.log(`Forwarding server running on ${info.bindAddr}:${info.bindPort}`);
        });
        
        // 客户端断开时,自动关闭转发服务器
        client.once('close', () => {
          console.log('Client disconnected, shutting down forward server');
          forwardServer.close();
        });
        
        // 监听失败时的错误处理
        forwardServer.on('error', (err) => {
          console.error('Forward server failed to start:', err);
          reject();
        });
      } else {
        reject(); // 拒绝其他未知请求
      }
    });

  }).on('close', function() {
    console.log('Client disconnected');
  }).on('error', function(err) {
    console.error('Client error:', err);
  });
}).listen(21, '0.0.0.0', function() {
  console.log('SSH server listening on port ' + this.address().port);
});

关键修改点说明

  1. 新增net模块:用来创建自己的TCP服务器,监听客户端要求转发的端口(比如8100)。
  2. 正确处理tcpip-forward请求:不再调用错误的client.forwardIn,而是同意请求后启动本地TCP监听。
  3. 桥接数据流:当有外部连接进来时,通过client.openChannel发起direct-tcpip通道请求,把外部连接和SSH通道双向绑定,让数据能在两者之间流转。
  4. 资源清理:添加了客户端断开时关闭转发服务器的逻辑,以及各类错误/关闭事件的处理,避免资源泄漏。

测试步骤

  1. 确保你的服务器上有/etc/ssh/ssh_host_rsa_key密钥文件,如果没有可以用命令生成:
    ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key -N ""
    
  2. 运行上述Node.js服务器。
  3. 在客户端执行你想要的命令:
    ssh -R 8100:localsite.tld:80 foo@sub.distantserver.com -p 21
    
  4. 输入密码bar后,尝试访问sub.distantserver.com:8100,流量就会被转发到localsite.tld:80了。

注意事项

  • 确保服务器的21端口和8100端口没有被其他服务占用,且防火墙允许这两个端口的入站连接。
  • 如果你的服务器上有FTP服务(默认用21端口),建议把SSH服务器改成其他端口,比如2221。

内容的提问来源于stack exchange,提问作者Doubidou

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 04:10:18