通过多跳的 SSH 隧道

Mal*_*ala 423 ssh tunnel

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

ssh -D9999 username@example.com
Run Code Online (Sandbox Code Playgroud)

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

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

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

Mik*_*her 390

你基本上有三种可能性:

  1. localhost到 的隧道host1

    ssh -L 9999:host2:1234 -N host1
    
    Run Code Online (Sandbox Code Playgroud)

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

  2. localhosthost1和从host1到的隧道host2

    ssh -L 9999:localhost:9999 host1 ssh -L 9999:localhost:1234 -N host2
    
    Run Code Online (Sandbox Code Playgroud)

    这将打开一个从localhostto的隧道host1和另一个从host1to 的隧道host2。然而端口9999host2:1234可以通过任何人使用host1。这可能是也可能不是问题。

  3. localhosthost1和从localhost到的隧道host2

    ssh -L 9998:host2:22 -N host1
    ssh -L 9999:localhost:1234 -N -p 9998 localhost
    
    Run Code Online (Sandbox Code Playgroud)

    这将打开一个隧道localhosthost1通过该隧道host2可以使用SSH 服务。然后第二隧道从打开localhosthost2通过第一隧道。

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

  • @Noli 如果您使用 ssh-agent(您应该使用),则可以使用 `-A` 选项通过连接将其转发到 ssh。 (6认同)
  • @prongs 尝试选项 3 (2认同)
  • @musically_ut - 没错,第二个 ssh 命令在 host1 上是后台的,所以如果你想在本地重新连接到它,你只需要再次重新运行第一部分。如果您添加 `-f`,它将立即在本地后台运行,因此如果您在第一种情况下按 ctrl-c,`ssh -f -L 9999:localhost:9999 host1` 将重新连接。或者在第一次运行时将 `-f` 添加到原始双 ssh 命令中,立即将所有内容作为后台运行。 (2认同)

kyn*_*nan 173

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

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

Host host2
  ProxyCommand ssh host1 -W %h:%p
Run Code Online (Sandbox Code Playgroud)

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

这也适用于一整类主机,例如由域标识:

Host *.mycompany.com
  ProxyCommand ssh gateway.mycompany.com -W %h:%p
Run Code Online (Sandbox Code Playgroud)

更新

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

Host host2
  ProxyJump host1
Run Code Online (Sandbox Code Playgroud)

  • 使设置“有条件”的一种简单方法是在 .ssh/config 中定义两个不同的主机,它们具有相同的主机名。需要隧道时连接到 host2-tunnel,不需要时连接到 host2。 (20认同)
  • 有没有办法有条件地做到这一点?有时我只想这样做。此外,这是专门针对命令的,但我正在寻找适用于所有端口 22(ssh、sftp 等)的内容。 (3认同)
  • @Stephane 确定:`ssh -F /path/to/altconfig`。请注意,这将忽略系统范围内的“/etc/ssh/ssh_config”。 (2认同)

nik*_*lay 54

OpenSSH v7.3 以后支持一个-J开关和一个ProxyJump选项,允许一个或多个逗号分隔的跳转主机,所以,你现在可以简单地这样做:

ssh -J jumpuser1@jumphost1,jumpuser2@jumphost2,...,jumpuserN@jumphostN user@host
Run Code Online (Sandbox Code Playgroud)


小智 20

我们有一个 ssh 网关进入我们的专用网络。如果我在外面并且想要在专用网络内的机器上使用远程 shell,则必须通过 ssh 进入网关并从那里连接到专用机器。

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

#!/bin/bash
ssh -f -L some_port:private_machine:22 user@gateway "sleep 10" && ssh -p some_port private_user@localhost
Run Code Online (Sandbox Code Playgroud)

怎么了:

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


Bob*_*ler 19

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

#!/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($_);
}
Run Code Online (Sandbox Code Playgroud)

用法:

通过 HOSTA 和 HOSTB(同一用户)访问 HOSTC:

mssh HOSTA HOSTB HOSTC
Run Code Online (Sandbox Code Playgroud)

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

mssh user1@HOSTA:1234 user2@HOSTB:1222 user3@HOSTC:78231
Run Code Online (Sandbox Code Playgroud)

通过 HOSTA 和 HOSTB 访问 HOSTC 并使用 X-forwarding:

mssh HOSTA HOSTB HOSTC -X
Run Code Online (Sandbox Code Playgroud)

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

mssh HOSTA HOSTB -L8080:HOSTC:8080
Run Code Online (Sandbox Code Playgroud)


小智 10

我做了我认为你想做的事

ssh -D 9999 -J host1 host2
Run Code Online (Sandbox Code Playgroud)

系统提示我输入两个密码,然后我可以使用 localhost:9999 作为到 host2 的 SOCKS 代理。这是我能想到的最接近你首先展示的例子。


sil*_*iot 8

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

如果你在你的 hop 机器上安装了 netcat,你可以把这个片段添加到你的 ~/.ssh/config 中:

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

然后

ssh -D9999 host1+host2 -l username
Run Code Online (Sandbox Code Playgroud)

会做你问的。

我来这里是为了寻找我阅读这个技巧的原始地方。当我找到它时,我会发布一个链接。

  • 我相信这是这个技巧的起源:https://wiki.gentoo.org/wiki/SSH_jump_host (2认同)

Ste*_*uan 7

我的答案与此处的所有其他答案完全相同,但是,我想澄清~/.ssh/configProxyJump 和 ProxyJump的用处。

假设我需要在 3 跳内到达目的地,并且对于每一跳,我需要一个特定的用户名、主机、端口和身份。由于身份标准,这只能通过~/.ssh/config配置文件完成:

Host hop1
    User user1
    HostName host1
    Port 22
    IdentityFile ~/.ssh/pem/identity1.pem

Host hop2
    User user2
    HostName host2
    Port 22
    IdentityFile ~/.ssh/pem/identity2.pem
    ProxyJump hop1

Host hop3
    User user3
    HostName host3
    Port 22
    IdentityFile ~/.ssh/pem/identity3.pem
    ProxyJump hop2
Run Code Online (Sandbox Code Playgroud)

在您的计算机上,您可以单独测试每个跳跃,即

Host hop1
    User user1
    HostName host1
    Port 22
    IdentityFile ~/.ssh/pem/identity1.pem

Host hop2
    User user2
    HostName host2
    Port 22
    IdentityFile ~/.ssh/pem/identity2.pem
    ProxyJump hop1

Host hop3
    User user3
    HostName host3
    Port 22
    IdentityFile ~/.ssh/pem/identity3.pem
    ProxyJump hop2
Run Code Online (Sandbox Code Playgroud)

~/.ssh/config文件的另一个很酷的事情是,这也将允许sftp通过任何一系列的跃点传输文件,例如

[yourpc] $ ssh hop1 # will go from your PC to host1 in a single step
[host1] $ exit
[yourpc] $ ssh hop2 # will go from your PC to host2 via host1 (i.e. two steps)
[host2] $ exit
[yourpc] $ ssh hop3 # will go from your PC to host3 via host1 and host2 (i.e. three steps)
[host3] $ exit
[yourpc] $ 
Run Code Online (Sandbox Code Playgroud)