【问题标题】:ssh -L forward multiple portsssh -L 转发多个端口
【发布时间】:2015-07-08 08:07:23
【问题描述】:

我目前正在运行一堆:

sudo ssh -L PORT:IP:PORT root@IP

其中 IP 是受保护机器的目标,PORT 表示我要转发的端口。

这是因为我使用了很多应用程序,如果没有此转发,我将无法访问。执行此操作后,我可以通过localhost:PORT 访问。

现在主要问题出现了,因为我实际上有 4 个端口需要转发。

我的解决方案是打开 4 个 shell 并不断向后搜索我的历史记录,以查找需要转发的确切端口等,然后运行此命令 - 每个 shell 中一个(必须填写密码等)。

要是我能做这样的事情就好了:

sudo ssh -L PORT1+PORT2+PORT+3:IP:PORT+PORT2+PORT3 root@IP

那么这已经很有帮助了。

有没有办法让它更容易做到这一点?

【问题讨论】:

    标签: ssh port portforwarding


    【解决方案1】:

    -L 选项可以在同一命令中多次指定。每次使用不同的端口。 IE。 ssh -L localPort0:ip:remotePort0 -L localPort1:ip:remotePort1 ...

    【讨论】:

    • 起初我不明白这个答案。所以在这里发布一个例子,以防有人遭受同样的痛苦。作者的意思是“ssh -L port0:ip:port0 -L port1:ip:port1 ...”
    • 当然,左边的端口是本地的,右边是远程的。前任。 ssh -L localPort0:IP:remotePort0 -L localPort1:IP:remotePort1
    • 反向隧道也可以做到吗?
    • @DarkStar1 是的!我刚刚对其进行了测试,它可以工作:)
    【解决方案2】:

    正如NaN 所回答的那样,您指定了多个 -L 参数。我一直这样做。下面是一个多端口转发的例子:

    ssh remote-host -L 8822:REMOTE_IP_1:22 -L 9922:REMOTE_IP_2:22
    

    注意:如果不指定localhost,则与 -L localhost:8822:REMOTE_IP_1:22 相同。

    现在有了这个,你现在可以(从另一个终端)做:

    ssh localhost -p 8822
    

    在端口22上连接到REMOTE_IP_1

    类似

    ssh localhost -p 9922
    

    连接到端口22上的REMOTE_IP_2

    当然,如果您有许多不同的主机/端口要转发到某些特定的主机/端口,则没有什么能阻止您将其包装到脚本中或将其自动化。

    希望这会有所帮助。

    【讨论】:

    • 对 Nan 的回答很好的补充。谢谢。
    • 注意这一点:“注意:如果你不指定本地主机,这与 -L localhost:8822:REMOTE_IP_1:22 相同。”仅当 GatewayPorts 设置为“否”时才适用,这是公认的默认设置。但是考虑到如果不是这样的影响,您应该验证设置,或者更好的是,明确并使用“-L localhost:8822 ...”。
    • 我同意@David By default, anyone (even on different machines) can connect to the specified port on the SSH client machine. However, this can be restricted to programs on the same host by supplying a bind address: ssh -L 127.0.0.1:80:intra.example.com:80 gw.example.com ssh.com/ssh/tunneling/example
    【解决方案3】:

    对于通过同一主机转发多个端口的人可以在他们的~/.ssh/config中设置类似的东西

    Host all-port-forwards Hostname 10.122.0.3 User username LocalForward PORT_1 IP:PORT_1 LocalForward PORT_2 IP:PORT_2 LocalForward PORT_3 IP:PORT_3 LocalForward PORT_4 IP:PORT_4

    它变成了一个简单的ssh all-port-forwards

    【讨论】:

    • 我喜欢这种方法。
    • 这是迄今为止最好的方法,我猜大多数用户将只有一台带有 nginx 代理的远程服务器来访问其 CGNAT 屏蔽的主端口等。
    • (当然,为了完整起见,我刚才提到的示例是使用 -R 而不是 -L 选项,即 RemoteForward 而不是 LocalForward )
    【解决方案4】:

    您可以使用以下 bash 函数(只需将其添加到您的 ~/.bashrc):

    function pfwd {
      for i in ${@:2}
      do
        echo Forwarding port $i
        ssh -N -L $i:localhost:$i $1 &
      done  
    }
    

    用法示例:

    pfwd hostname {6000..6009}
    

    【讨论】:

    • 使用-f在后台运行
    • Ehmmm...你为什么要那样做?
    【解决方案5】:

    jbchichoko 和 yuval 给出了可行的解决方案。但是 jbchichoko 的答案作为一个函数并不是一个灵活的答案,yuval 的答案打开的隧道不能被ctrl+c 关闭,因为它在后台运行。我在下面给出解决这两个缺陷的解决方案:

    ~/.bashrc~/.zshrc 中定义函数

    # fsshmap multiple ports
    function fsshmap() {
      echo -n "-L 1$1:127.0.0.1:$1 " > $HOME/sh/sshports.txt
      for ((i=($1+1);i<$2;i++))
      do
        echo -n "-L 1$i:127.0.0.1:$i " >> $HOME/sh/sshports.txt
      done
      line=$(head -n 1 $HOME/sh/sshports.txt)
      cline="ssh "$3" "$line
      echo $cline
      eval $cline
    }
    

    函数运行示例:

    fsshmap 6000 6010 hostname
    

    此示例的结果:

    你可以像hostname:6000~6009一样访问127.0.0.1:16000~16009

    【讨论】:

      【解决方案6】:

      在我的公司,我和我的团队成员都需要访问不可访问的“目标”服务器的 3 个端口,因此我创建了一个永久隧道(这是一个可以运行 的隧道在后台无限期地,查看从可访问服务器到目标服务器的参数-f-N)。在我执行的可达服务器的命令行上:

      ssh root@reachableIP -f -N  -L *:8822:targetIP:22  -L *:9006:targetIP:9006  -L *:9100:targetIP:9100
      

      我使用了用户 root,但您自己的用户可以使用。您必须输入所选用户的密码(即使您已经通过该用户连接到可访问的服务器)。

      现在可达机器的 8822 端口对应目标机器的 22 端口(用于 ssh/PuTTY/WinSCP),可达机器上的端口 9006 和 9100 对应目标机器的相同端口(它们托管两个 Web 服务就我而言)。

      【讨论】:

        【解决方案7】:

        使用端口转发登录服务器的好处之一是便于使用 Jupyter Notebook。这个link 提供了一个很好的描述。在这里我做一些总结和扩展,供大家参考。

        情况 1. 从名为 Host-A 的本地机器(例如您自己的笔记本电脑)登录到名为 Host-B 的远程工作机器。

        ssh user@Host-B -L port_A:localhost:port_B
        jupyter notebook --NotebookApp.token='' --no-browser --port=port_B
        

        然后您可以打开浏览器并输入:http://localhost:port_A/ 在 Host-B 上执行您的工作,但在 Host-A 中查看。

        情况 2. 从名为 Host-A 的本地机器(例如您自己的笔记本电脑)登录到名为 Host-B 的远程登录机器,然后从那里登录到名为 Host-C 的远程工作机器。这通常是大学内大多数分析服务器的情况,可以通过使用两个与-t 连接的ssh -L 来实现。

        ssh -L port_A:localhost:port_B user@Host-B -t ssh -L port_B:localhost:port_C user@Host-C
        jupyter notebook --NotebookApp.token='' --no-browser --port=port_C
        

        然后您可以打开浏览器并输入:http://localhost:port_A/ 在 Host-C 上执行您的工作,但在 Host-A 中查看。

        情况 3. 从名为 Host-A 的本地机器(例如您自己的笔记本电脑)登录到名为 Host-B 的远程登录机器,然后从那里登录到名为 Host-C 的远程工作机器,最后登录到远程工作机器主机-D。这通常不是这种情况,但有时可能会发生。它是情况 2 的扩展,同样的逻辑可以应用在更多机器上。

        ssh -L port_A:localhost:port_B user@Host-B -t ssh -L port_B:localhost:port_C user@Host-C -t ssh -L port_C:localhost:port_D user@Host-D
        jupyter notebook --NotebookApp.token='' --no-browser --port=port_D
        

        然后您可以打开浏览器并输入:http://localhost:port_A/ 在 Host-D 上执行您的工作,但在 Host-A 中查看。

        请注意,port_A、port_B、port_C、port_D 可以是随机数,除了列出的常见端口号here。情况1,port_A和port_B可以相同,以简化程序。

        【讨论】:

        • 提醒一下,不同服务器上的相同端口是不同的端口。因此,通过指定相同的端口号总是可以使事情变得更容易!
        【解决方案8】:

        我在 debian 上使用和工作的另一个衬垫:

        ssh user@192.168.1.10 $(for j in $(seq 20000 1 20100 ) ; do  echo " -L$j:127.0.0.1:$j " ; done | tr -d "\n")
        

        【讨论】:

        • 顺便说一句,我认为您最后不需要tr -d "\n"。对我来说,子shell 只是解析成新的参数。这样的东西对我有用(适用于 Jupyter 笔记本):ssh somewhere $(for i in $(seq 8888 8898); do echo "-L ${i}:localhost:${i}"; done)
        【解决方案9】:

        如果您想要一个在后台运行且易于杀死的简单解决方案 - 使用控制套接字

        # start
        $ ssh -f -N -M -S $SOCKET -L localhost:9200:localhost:9200 $HOST
        # stop
        $ ssh -S $SOCKET -O exit $HOST
        

        【讨论】:

          【解决方案10】:

          这是一个灵感来自于 Yuval Atzmon 的解决方案。

          与最初的解决方案相比,它有一些好处:

          • 首先它创建一个后台进程,而不是每个端口一个
          • 它会生成允许您终止隧道的别名
          • 它只绑定到更安全的 127.0.0.1

          您可以将其用作:

          • tnl your.remote.com 1234
          • tnl your.remote.com {1234,1235}
          • tnl your.remote.com {1234..1236}

          最后用tnlkill杀死他们。

          function tnl {
            TUNNEL="ssh -N "
            echo Port forwarding for ports:
            for i in ${@:2}
            do
              echo " - $i"
              TUNNEL="$TUNNEL -L 127.0.0.1:$i:localhost:$i"
            done
            TUNNEL="$TUNNEL $1"
            $TUNNEL &
            PID=$!
            alias tnlkill="kill $PID && unalias tnlkill"
          }
          

          【讨论】:

            【解决方案11】:

            另一种方法是使用-D 标志告诉ssh 作为SOCKS 代理工作。

            这样,只要客户端应用程序能够通过 SOCKS 代理(或使用类似 socksify 的东西),您就可以通过 ssh 服务器连接到可访问的任何远程网络地址/端口。

            【讨论】:

              【解决方案12】:

              我开发了loco 以获取 ssh 转发方面的帮助。它可用于在远程本地共享端口 5000 和 7000 的相同端口:

              pip install loco
              
              loco listen SSHINFO -r 5000 -r 7000
              

              【讨论】:

                【解决方案13】:

                首先可以通过xargs -P 0使用Parallel Execution来完成。

                创建一个用于绑定端口的文件,例如

                localhost:8080:localhost:8080
                localhost:9090:localhost:8080
                

                然后运行

                xargs -P 0  -I xxx ssh -vNTCL xxx <REMOTE>  < port-forward
                

                或者你可以做一个单线

                echo localhost:{8080,9090} | tr ' ' '\n' | sed 's/.*/&:&/' | xargs -P 0 -I xxx ssh -vNTCL xxx <REMOTE>
                

                pros 独立的 ssh 端口转发,它们是独立的 == 避免单点故障
                缺点 每个 ssh 端口转发都是单独分叉的,不知何故效率不高


                second 可以使用 bash 中的 大括号扩展 功能来完成

                echo "ssh -vNTC $(echo  localhost:{10,20,30,40,50} | perl -lpe 's/[^ ]+/-L $&:$&/g') <REMOTE>"
                # output
                ssh -vNTC -L localhost:10:localhost:10 -L localhost:20:localhost:20 -L localhost:30:localhost:30 -L localhost:40:localhost:40 -L localhost:50:localhost:50 <REMOTE>
                

                真实例子

                echo "-vNTC $(echo localhost:{8080,9090} | perl -lpe 's/[^ ]+/-L $&:$&/g') gitlab" | xargs ssh
                

                转发80809090到gitlab服务器。

                优点单个分叉 == 高效
                缺点 通过关闭此进程 (ssh) 所有转发都已关闭 == 单点故障

                【讨论】:

                  【解决方案14】:

                  你可以使用这个 zsh 函数(可能也适用于 bash)(把它放在~/.zshrc 中):

                  ashL () {
                      local a=() i
                      for i in "$@[2,-1]"
                      do
                          a+=(-L "${i}:localhost:${i}")
                      done
                      autossh -M 0 -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" -NT "$1" "$a[@]"
                  }
                  

                  例子:

                  ashL db@114.39.161.24 6480 7690 7477

                  ashL db@114.39.161.24 {6000..6050} # Forwards the whole range. This is simply shell syntax sugar.

                  【讨论】: