通过多跳的SSH隧道


332

通过SSH隧道传输数据非常简单:

ssh -D9999 username@example.com

在您的上将端口9999设置localhost为的隧道example.com,但我有一个更具体的需求:

  • 我在当地工作 localhost
  • host1 可访问 localhost
  • host2 仅接受来自 host1
  • 我需要创建一个从localhost到的隧道host2

实际上,我想创建一个“多跳” SSH隧道。我怎样才能做到这一点?理想情况下,我想执行此操作而无需成为任何计算机上的超级用户。


2
你是用来干什么的 我想将其用于袜子代理。能行吗
插脚

2
是的,除非您host2拒绝转发,否则您应该能够将隧道连接用作SOCKS代理
Mala 2012年

我正在考虑通过SSH创建包装程序,该包装程序将通过多次使用ProxyCommand进行设置。
2013年

@prongs(所有年前)您是否已将其用于SOCKS代理?
Drux

Answers:


324

您基本上有三种可能性:

  1. localhost到的隧道host1

    ssh -L 9999:host2:1234 -N host1
    

    如上所述,将不会确保从host1到的连接host2

  2. 从隧道localhosthost1host1host2

    ssh -L 9999:localhost:9999 host1 ssh -L 9999:localhost:1234 -N host2
    

    这将打开从隧道localhosthost1从另一个隧道host1host2。但是9999to 的端口host2:1234可以由上的任何人使用host1。这可能是问题,也可能不是问题。

  3. 从隧道localhosthost1localhosthost2

    ssh -L 9998:host2:22 -N host1
    ssh -L 9999:localhost:1234 -N -p 9998 localhost
    

    这将打开一条隧道localhost,以host1通过SSH服务上host2都可以使用。然后,第二条隧道从localhosthost2第一条隧道打开。

通常,我会选择选项1。如果需要保护从host1至的连接host2,请选择选项2。方法3主要用于访问host2只能从host2自身访问的服务。


17
选项3是我一直在寻找的东西,谢谢!
马拉2010年

1
我想以这种方式浏览。哪一个最好?我尝试了第一个,但是没有用。我在浏览器localhost:1234中设置了一个袜子代理,但是没有运气。:(请帮助..
五爪

1
@prongs尝试选项3
马拉

6
@Noli如果使用ssh-agent(应使用),则可以使用-Assh选项通过连接转发它。
米卡·菲舍尔

2
@musically_ut-正确,第二个ssh命令在host1上后台运行,因此,如果要在本地重新连接它,则只需要再次运行第一部分即可。如果添加-f,它将立即在本地后台运行,因此,ssh -f -L 9999:localhost:9999 host1在第一种情况下按ctrl-c将重新连接。或-f在首次立即运行它以使所有背景立即运行时,将其添加到原始的double ssh命令。
马克·费舍尔

153

有一个很好的答案解释了ProxyCommandSSH 的配置指令的用法

将此添加到您的~/.ssh/config(请参阅man 5 ssh_config有关详细信息):

Host host2
  ProxyCommand ssh host1 -W %h:%p

然后ssh host2将自动通过隧道host1(也适用于X11转发等)。

这也适用于整个主机类别,例如按域标识:

Host *.mycompany.com
  ProxyCommand ssh gateway.mycompany.com -W %h:%p

更新资料

OpenSSH 7.3引入了一个ProxyJump指令,将第一个示例简化为

Host host2
  ProxyJump host1

3
有条件地做到这一点的方法吗?有时候我只想这样做。另外,这是专门针对命令的,但是我正在为22端口(ssh,sftp等)寻找所有东西。
Stephane 2013年

1
@Stephane 专门用于命令是什么意思?任何使用ssh,包括gitsftp等等的afaik 都会使用您的SSH配置。
kynan 2013年

1
@Stephane我不知道有条件地启用此功能的方法(例如,仅当您不在目标主机的网络范围内时)。我在配置块中为所有有问题的主机设置了此选项,然后根据需要(取消)注释该行。并不完美,但是可以。
kynan 2013年

2
@Stephane确定:ssh -F /path/to/altconfig。当心,这将忽略整个系统/etc/ssh/ssh_config
kynan 2013年

19
一种使设置“有条件”的简单方法是在.ssh / config中定义两个具有相同HostName的不同主机。当需要隧道时,连接到host2-tunnel,而当不需要隧道时,连接到host2。
史蒂夫·贝内特

25

OpenSSH v7.3 -JProxyJump更高版本支持一个开关和一个选项,这些选项和选项允许一个或多个逗号分隔的跳转主机,因此,您现在就可以执行以下操作:

ssh -J jumpuser1@jumphost1,jumpuser2@jumphost2,...,jumpuserN@jumphostN user@host

ssh -J user1 @ host1 -YC4c arcfour,blowfish-cbc user2 @ host2 firefox -no-remote这将加快将firefox从host2获取到本地主机的速度。
Jaur

21

我们有一个进入我们专用网络的ssh网关。如果我在外面,并且想要在专用网络内部的计算机上使用远程外壳,则必须SSH进入网关,然后从那里进入专用计算机。

要自动执行此过程,我使用以下脚本:

#!/bin/bash
ssh -f -L some_port:private_machine:22 user@gateway "sleep 10" && ssh -p some_port private_user@localhost

怎么了:

  1. 为ssh协议(端口22)建立到专用计算机的隧道。
  2. 仅在成功的情况下,才使用隧道将其SSH到专用计算机中。(&&操作员确保这一点)。
  3. 关闭私有ssh会话后,我也希望ssh隧道也关闭。这是通过“睡眠10”技巧完成的。通常,第一个ssh命令将在10秒后关闭,但是在此期间,第二个ssh命令将使用隧道建立连接。结果,第一个ssh命令使隧道保持打开状态,直到满足以下两个条件:sleep 10完成并且不再使用隧道。

1
非常聪明!!!爱它!
Hendy Irawan

18

阅读以上内容并将所有内容粘合在一起之后,我创建了以下Perl脚本(将其保存为/ sh / bin中的mssh并使其可执行):

#!/usr/bin/perl

$iport = 13021;
$first = 1;

foreach (@ARGV) {
  if (/^-/) {
    $args .= " $_";
  }
  elsif (/^((.+)@)?([^:]+):?(\d+)?$/) {
    $user = $1;
    $host = $3;
    $port = $4 || 22;
    if ($first) {
      $cmd = "ssh ${user}${host} -p $port -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no";
      $args = '';
      $first = 0;
    }
    else {
      $cmd .= " -L $iport:$host:$port";
      push @cmds, "$cmd -f sleep 10 $args";
      $cmd = "ssh ${user}localhost -p $iport -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no";
      $args = '';
      $iport ++;
    }
  }
}
push @cmds, "$cmd $args";

foreach (@cmds) {
  print "$_\n";
  system($_);
}

用法:

要通过HOSTA和HOSTB(同一用户)访问HOSTC,请执行以下操作:

mssh HOSTA HOSTB HOSTC

要通过HOSTA和HOSTB访问HOSTC并使用非默认SSH端口号和其他用户:

mssh user1@HOSTA:1234 user2@HOSTB:1222 user3@HOSTC:78231

要通过HOSTA和HOSTB访问HOSTC并使用X转发:

mssh HOSTA HOSTB HOSTC -X

要通过HOSTA和HOSTB访问HOSTC上的端口8080:

mssh HOSTA HOSTB -L8080:HOSTC:8080

1
这太棒了
Mala 2012年

1
我非常感谢您,此脚本每天使我的生活更加轻松。我唯一更改的是将int(rand(1000))添加到iport,以允许多个实例同时运行。我绝对欠你一杯啤酒。
2012年

这真的很好。进一步的改进是使用本地主机的/ etc / hosts和〜/ .ssh / config解决HOSTB,HOSTC等问题
Steve Bennett

我也赞同马拉的评论。如果没有随机端口,那么如果您尝试这样mssh HOSTA HOSTD做,实际上将最终到达HOSTB(并且可能不会意识到..)
Steve Bennett

8

这个答案与kynan相似,因为它涉及到ProxyCommand的使用。但是使用IMO更方便。

如果您的跃点计算机中安装了netcat,则可以将此代码段添加到〜/ .ssh / config中:

Host *+*
    ProxyCommand ssh $(echo %h | sed 's/+[^+]*$//;s/\([^+%%]*\)%%\([^+]*\)$/\2 -l \1/;s/:/ -p /') nc $(echo %h | sed 's/^.*+//;/:/!s/$/ %p/;s/:/ /')

然后

ssh -D9999 host1+host2 -l username

将按照您的要求进行。

我来这里是寻找这个技巧的原始地方。找到链接后,我会发布一个链接。


2
我相信这是骗局的源头
slm

5

我做了什么,我你想和做

ssh -D 9999 -J host1 host2

提示我输入两个密码,然后可以使用localhost:9999作为host2的SOCKS代理。我能想到的最接近您最初显示的示例。


这很好用!
亚瑟席尔瓦

4
ssh -L 9999:host2:80 -R 9999:localhost:9999 host1

-L 9999:host2:80

意味着绑定到localhost:9999,任何发送到localhost:9999的数据包都将其转发到host2:80

-R 9999:localhost:9999

表示host1:9999收到的任何数据包都将其转发回localhost:9999


创建隧道的最
出色

按照此答案,我收到一条channel 3: open failed: administratively prohibited: open failed 错误消息。
Franck Dernoncourt

2

你应该可以使用端口转发到访问服务host2localhost。一个很好的指南位于这里。摘抄:

端口转发有两种:本地转发和远程转发。它们分别也称为传出隧道和传入隧道。本地端口转发将进入本地端口的流量转发到指定的远程端口。

例如,如果您发出命令

ssh2 -L 1234:localhost:23 username@host

到达客户端上的端口1234的所有流量都将转发到服务器(主机)上的端口23。请注意,建立连接后,sshdserver将解析localhost。因此,在这种情况下,localhost是指服务器(主机)本身。

远程端口转发的功能与此相反:它将来自远程端口的流量转发到指定的本地端口。

例如,如果您发出命令

ssh2 -R 1234:localhost:23 username@host

服务器(主机)上到达端口1234的所有流量都将转发到客户端(本地主机)上的端口23。

在你的施法,取代localhost与例子host2hosthost1


根据该文章,连接将仅在中间机器(host1)之前得到保护。有没有办法确保整个事情安全?
马拉2010年

我从来没有尝试过,但是如果host1和host2都是ssh服务器,则可以设置从host1到host2的隧道,然后为同一服务设置从localhost到host1的隧道(获取本地和远程)端口正确)。我不知道在本地主机的一个命令中是否可行。
fideli 2010年

1

在这个答案中,我将举一个具体的例子。您只需要用您的计算机替换主机名,用户名和密码。

问题陈述

假设我们具有以下网络拓扑:

our local computer <---> server 1 <---> server 2

为了具体起见,假设我们具有以下计算机的主机名,用户名和密码:

LocalPC            <--->  hostname: mit.edu         <---> hec.edu
                          username: bob                   username: john 
                          password: dylan123              password: doe456

目标:我们要设置一个端口侦听SOCKS代理9991LocalPC,这样每次在连接LocalPC开始从端口 9991它通过mit.edu然后hec.edu

用例示例:出于安全原因,hec.edu拥有一个只能在http://127.0.0.1:8001上访问的HTTP服务器 。我们希望能够通过打开Web浏览器访问 http://127.0.0.1:8001LocalPC


组态

在中LocalPC,添加~/.ssh/config

Host HEC
    HostName hec.edu
    User john
    ProxyCommand ssh bob@mit.edu -W %h:%p

然后在的终端中LocalPC,运行:

ssh -D9991 HEC

它将询问您bobon 的密码mit.edu(即dylan123),然后询问您johnon 的密码hec.edu(即doe456)。

那时,SOCKS代理现在在的端口9991上运行LocalPC

例如,如果您想LocalPC使用SOCKS代理访问网页,则可以在Firefox中进行:

在此处输入图片说明

一些说明:

  • ~/.ssh/configHEC是连接名称:您可以将其更改为你想要的任何东西。
  • -D9991通知ssh以设立端口SOCKS4代理9991

0

如果可以同时在两台计算机上使用SSH,请查看ssh的ProxyCommand指令。这将使您直接从localhost进入host2(如果使用公共密钥,只需一个简单的命令!)。然后,您可以使用host2做任何您想做的事情。

http://www.statusq.org/archives/2008/07/03/1916/


0

最佳答案的选项2 可以用于与当前aka用户不同的ssh用户:user @ host

    export local_host_port=30000
    export host1_user=xyz
    export host1=mac-host
    export host1_port=30000
    export host2=192.168.56.115
    export host2_user=ysg
    export host2_port=13306

    # Tunnel from localhost to host1 and from host1 to host2
    # you could chain those as well to host3 ... hostn
    ssh -tt -L $local_host_port:localhost:$host1_port $host1_user@$host1 \
    ssh -tt -L $host1_port:localhost:$host2_port $host2_user@$host2

0

就我而言

localhost$ ssh -D 9999 host1
host1$ ssh -L 8890:localhost:8890 host2

host2:8890Jupyter Notebook上运行的位置。

然后,我将Firefox配置为localhost:9999用作SOCKS主机。

因此,现在我可以在计算机上host2的Firefox localhost:8890上运行该笔记本了。


0

接受的答案中提到的三个选项对我根本不起作用。由于我对这两个主机的权限不高,并且看来我们的DevOps团队在进行身份验证和执行MFA时具有非常严格的规则。上面的命令以某种方式无法与我们的身份验证完美配合。

上下文实际上与上面的答案类似:我无法直接SSH进入生产服务器,而必须使用跳转服务器进行1跳。

另一个解决方案-天真的解决方案

我最终以一种非常幼稚的方式完成了此操作:我没有尝试在笔记本电脑上运行所有命令,而是在每台计算机上运行命令,如下所示:

  1. SSH进入您的跳转服务器,然后运行ssh -v -L 6969:localhost:2222 -N your-protected.dest.server。如果系统提示您输入任何密码,请输入密码。
  2. 现在,在您的笔记本电脑上,运行ssh -v -L 6969:localhost:6969 -N your-jump-server.host.name。这会将您在笔记本电脑上端口6969上的任何请求转发到跳转服务器。然后,由于我们在上一步中进行了配置,因此跳转服务器将再次将端口6969的请求转发到受保护的目标服务器上的端口2222。

打印一些消息后,您应该在此看到“挂起”命令-这意味着它们正在工作!一个例外-您不应看到类似的错误消息Could not request local forwarding.,如果看到该错误消息,则它仍然不起作用:(。您现在可以尝试从笔记本电脑向端口6969发出请求,并查看它是否正常工作。

希望如果您是上述所有方法均未通过,可以尝试一下。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.