題 通過多跳的SSH隧道


通過SSH隧道傳輸數據非常簡單:

ssh -D9999 username@example.com

設置端口9999就可以了 localhost 作為通往隧道的 example.com,但我有更具體的需求:

  • 我在當地工作 localhost
  • host1 可以訪問 localhost
  • host2 只接受來自的連接 host1
  • 我需要創建一個隧道 localhost 至 host2

實際上,我想創建一個“多跳”SSH隧道。我怎樣才能做到這一點?理想情況下,我想這樣做而不需要成為超級用戶 任何 的機器。


299
2018-01-16 05:58


起源


你用它做什麼用的?我想用它來代替襪子代理。它會起作用嗎? - prongs
是的,您應該能夠將隧道連接用作SOCKS代理,除非 host2 否認轉發 - Mala
我正在考慮通過SSH創建一個包裝器,它將使用多個ProxyCommand來設置它。 - Pavel Šimerda
@prongs你有沒有設法將它用於SOCKS代理(那些年前)? - Drux


答案:


你基本上有三種可能性:

  1. 隧道從 localhost 至 host1

    ssh -L 9999:host2:1234 -N host1
    

    如上所述,連接來自 host1 至 host2 不會得到保障。

  2. 隧道從 localhost 至 host1 從 host1 至 host2

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

    這將打開一條隧道 localhost 至 host1 和另一條隧道 host1 至 host2。不過端口 9999 至 host2:1234 任何人都可以使用 host1。這可能是也可能不是問題。

  3. 隧道從 localhost 至 host1 從 localhost 至 host2

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

    這將打開一條隧道 localhost 至 host1 通過SSH服務 host2 可以使用。然後打開第二條隧道 localhost 至 host2 穿過第一條隧道。

通常情況下,我會選擇選項1.如果連接來自 host1 至 host2 需要保護,使用選項2.選項3主要用於訪問服務 host2 這只能從 host2 本身。


281
2018-01-17 21:31



選項3是我想要的,謝謝! - Mala
我想以這種方式瀏覽。哪一個最好?我試過第一個,但它沒有用。我在我的瀏覽器localhost:1234中設置了一個socks代理,但沒有運氣。 :( 請幫忙.. - prongs
@prongs嘗試選項3 - Mala
有沒有辦法將我的公鑰從localhost,通過主機1的隧道轉發到host2? - Noli
@Noli如果你使用ssh-agent(你應該使用),你可以使用它轉發連接 -A ssh的選項。 - Mika Fischer


有一個 優秀的答案解釋了使用 ProxyCommand SSH的配置指令

將此添加到您的 ~/.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

141
2017-08-01 17:10



有條件地做這個嗎?我有時只想這樣做。此外,這是專門用於命令,但我正在尋找所有端口22(ssh,sftp等)的東西。 - Stephane
@Stephane你是什麼意思 專門用於命令?您的SSH配置被任何使用的東西使用 ssh, 包含 git, sftp 等afaik。 - kynan
@Stephane我不知道有條件地啟用此方法(例如,只有當您在目標主機的網絡之外時)。我在配置塊中為所有相關主機設置此選項,然後根據需要(un)註釋該行。不完美,但它的工作原理。 - kynan
@Stephane肯定: ssh -F /path/to/altconfig。注意這將忽略系統範圍 /etc/ssh/ssh_config。 - kynan
使設置“有條件”的簡單方法是在.ssh / config中定義兩個具有相同HostName的不同主機。如果需要隧道,請連接到host2-tunnel,否則連接到host2。 - Steve Bennett


我們的私人網絡有一個ssh網關。如果我在外面並想在專用網絡內的機器上使用遠程shell,我將不得不進入網關並從那裡進入私人機器。

要自動執行此過程,請使用以下腳本:

#!/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導入私有機器。 (&& operater確保這一點)。
  3. 關閉私有ssh會話後,我也想關閉ssh隧道。這是通過“睡眠10”技巧完成的。通常,第一個ssh命令將在10秒後關閉,但在此期間,第二個ssh命令將使用隧道建立連接。因此,第一個ssh命令使隧道保持打開狀態,直到滿足以下兩個條件:完成睡眠10並且不再使用隧道。

20
2018-01-24 18:47



非常聰明!!!愛它! - Hendy Irawan


閱讀完上述內容並將所有內容粘合在一起後,我創建了以下Perl腳本(將其保存為/ usr / 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-forwarding:

mssh HOSTA HOSTB HOSTC -X

通過HOSTA和HOSTB訪問HOSTC上的端口8080:

mssh HOSTA HOSTB -L8080:HOSTC:8080

18
2018-01-11 11:02



這太棒了 - Mala
我真的不能夠感謝你,這個劇本讓我的日常工作變得更輕鬆。我唯一改變的是將int(rand(1000))添加到iport,以允許多個實例同時運行。我肯定欠你一杯啤酒。 - Mala
這非常有效。進一步的改進是使用localhost的/ etc / hosts和〜/ .ssh / config解決HOSTB,HOSTC等問題。 - Steve Bennett
我也是馬累的評論。沒有隨機端口,如果你那麼嘗試 mssh HOSTA HOSTD 你真的會在HOSTB結束(也許不會意識到......) - Steve Bennett


OpenSSH v7.3以後版本支持a -J 開關和 ProxyJump 選項,允許一個或多個以逗號分隔的跳轉主機,因此,您現在可以執行此操作:

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

18
2017-08-10 09:11



ssh -J user1 @ host1 -YC4c arcfour,blowfish-cbc user2 @ host2 firefox -no-remote這將加快firefox從host2到localhost的速度。 - Jaur


這個答案類似於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

會做你所要求的。

我來到這裡尋找我讀這個技巧的原始地方。當我找到它時,我會發布一個鏈接。


8
2018-03-13 09:57



我相信這是訣竅的起源: wiki.gentoo.org/wiki/SSH_jump_host - slm
@slm是的,就是這樣!謝謝! - silviot


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


4
2018-01-19 02:03



製作隧道的最簡單,最簡單的答案,以便您可以直接從localhost:9999訪問host2上的應用程序 - dvtoever
按照這個答案,我得到一個 channel 3: open failed: administratively prohibited: open failed  錯誤信息。 - Franck Dernoncourt


您應該能夠使用端口轉發來訪問服務 host2 從 localhost。一個很好的指南位於 這裡。摘抄:

端口轉發有兩種:本地轉發和遠程轉發。它們也分別稱為傳出和傳入隧道。本地端口轉發將到達本地端口的流量轉發到指定的遠程端口。

例如,如果您發出命令

ssh2 -L 1234:localhost:23 username@host

進入客戶端端口1234的所有流量將被轉發到服務器(主機)上的端口23。請注意,在建立連接後,sshdserver將解析localhost。在這種情況下,localhost因此指的是服務器(主機)本身。

遠程端口轉發正好相反:它將來自遠程端口的流量轉發到指定的本地端口。

例如,如果您發出命令

ssh2 -R 1234:localhost:23 username@host

到達服務器(主機)上的端口1234的所有流量將被轉發到客戶端(localhost)上的端口23。

在你的演員表中,替換 localhost 在示例中 host2 和 host 同 host1


2
2018-01-16 06:34



根據那篇文章,只有中間機器(host1)才能保證連接。有沒有辦法確保整個事情保持安全? - Mala
我從來沒有嘗試過這個,但是如果host1和host2都是ssh服務器,你可以設置從host1到host2的隧道,然後為localhost設置從同一服務到host1的隧道(獲取你的本地和遠程服務)港口右)。我不知道在localhost的一個命令中是否可以。 - fideli


我做了我 認為 你想做什麼

ssh -D 9999 -J host1 host2

我被提示輸入兩個密碼,然後我可以使用localhost:9999作為host2的SOCKS代理。這是我能想到的最接近你所展示的例子。


2
2017-11-21 11:06





在這個答案中,我將通過一個具體的例子。您只需要更換計算機的主機名,用戶名和密碼。

問題陳述

假設我們有以下網絡拓撲:

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

為了具體起見,我們假設我們有以下計算機的主機名,用戶名和密碼:

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

目標:我們想要設置一個偵聽端口的SOCKS代理 9991 的 LocalPC 這樣每次連接就可以了 LocalPC 是從港口發起的 9991 它經歷了 mit.edu 然後 hec.edu

用例示例: hec.edu 有一個只能訪問的HTTP服務器 http://127.0.0.1:8001,出於安全原因。我們希望能夠訪問 http://127.0.0.1:8001 通過打開Web瀏覽器 LocalPC


組態

LocalPC, 加入 ~/.ssh/config

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

然後在終端 LocalPC, 跑:

ssh -D9991 HEC

它會問你的密碼 bob 上 mit.edu (即, dylan123),然後它會問你的密碼 john 上 hec.edu (即, doe456)。

此時,SOCKS代理現在在端口上運行 9991 的 LocalPC

例如,如果您想訪問網頁 LocalPC 使用SOCKS代理,您可以在Firefox中執行:

enter image description here

一些評論:

  • ~/.ssh/configHEC 是連接名稱:您可以將其更改為您想要的任何內容。
  • -D9991 告訴 ssh 在端口上設置SOCKS4代理 9991

1
2018-03-18 20:13