複数のホップを介したSSHトンネル


332

SSHを介したデータのトンネリングは非常に簡単です。

ssh -D9999 username@example.com

localhostへのトンネルとして、ポート9999をセットアップしますexample.comが、より具体的なニーズがあります。

  • 私は地元で働いています localhost
  • host1 にアクセス可能です localhost
  • host2 からの接続のみを受け入れます host1
  • 私はからトンネルを作成する必要がありますlocalhosthost2

事実上、「マルチホップ」SSHトンネルを作成したいと思います。これどうやってするの?理想的には、どのマシンでスーパーユーザーになる必要なく、これを実行したいと思います。


2
何のために使いましたか?ソックスプロキシに使用したいです。機能しますか?
プロング

2
場合を除きはい、あなたは、SOCKSプロキシとしてトンネル接続を使用することができるはずhost2転送拒否した
マラ

ProxyCommandの複数の使用を使用してそれをセットアップするラッパーをSSHで作成することを考えていました。
パベルシメルダ

@prongs SOCKSプロキシにこれを使用することができましたか(すべての年前)
Drux

回答:


324

基本的に3つの可能性があります。

  1. トンネルlocalhosthost1

    ssh -L 9999:host2:1234 -N host1
    

    上述のように、からの接続host1にはhost2確保されません。

  2. トンネルlocalhosthost1とからhost1host2

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

    これは、トンネルから開くlocalhostまでhost1とから別のトンネルhost1へとhost2。しかし、ポートが9999するhost2:1234上で誰でも使用することができますhost1。これは問題になる場合もあれば、そうでない場合もあります。

  3. トンネルlocalhosthost1とからlocalhosthost2

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

    このからトンネルを開くlocalhosthost1SSHサービスには、そこを通ってhost2使用することができます。次いで、第2のトンネルから開放されるlocalhostまでhost2最初のトンネルを介し。

通常、オプション1を使用します。からの接続をセキュリティで保護host1するhost2必要がある場合は、オプション2を使用します。オプション3はhost2host2それ自体からのみ到達可能なサービスにアクセスする場合に主に役立ちます。


17
オプション3は私が探していたものでした、ありがとう!
マラ

1
この方法でブラウジングしたいです。どれが一番ですか?最初に試しましたが、うまくいきませんでした。ブラウザlocalhost:1234にソックスプロキシを設定しましたが、運はありません。:(助けてください
..-プロング

1
@prongsオプション3を試す
マラ

6
@Noli ssh-agent(使用する必要があります)を使用する-A場合、ssh のオプションを使用して接続を介して転送できます。
ミカフィッシャー

2
@musically_ut-それは正しい、2番目のsshコマンドはhost1でバックグラウンド化されているため、ローカルで再接続する場合は、最初の部分を再実行するだけです。を追加すると-f、すぐにローカルでバックグラウンドになります。したがってssh -f -L 9999:localhost:9999 host1、最初にctrl-cを押すと再接続されます。または-f、最初に実行したときに元のdouble sshコマンドに追加して、すぐにすべてをバックグラウンドにします。
マークフィッシャー

153

SSHの構成ディレクティブの使用について説明ProxyCommandする優れた回答があります

これを追加してください~/.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.3ProxyJumpディレクティブを導入し、最初の例を単純化して

Host host2
  ProxyJump host1

3
条件付きでこれを行う方法はありますか?私は時々これをしたいだけです。また、これは特にコマンド用ですが、ポート22のすべて(ssh、sftpなど)の何かを探しています。
ステファン

1
@Stephane コマンドの意味は何ですか?SSH configは、などsshを含むあらゆるもので使用されます。gitsftp
キナン

1
@Stephane条件付きでこれを有効にする方法を知りません(たとえば、ターゲットホストのネットワーク外にいる場合のみ)。構成ブロックで問題のすべてのホストに対してこのオプションを設定し、必要に応じて行をコメント解除します。完璧ではありませんが、機能します。
キナン

2
確か@Stephane: ssh -F /path/to/altconfig。これはシステム全体を無視することに注意してください/etc/ssh/ssh_config
キナン

19
設定を「条件付き」にする簡単な方法は、同じHostNameを持つ2つの異なるホストを.ssh / configで定義することです。トンネルが必要な場合はhost2-tunnelに接続し、不要な場合はhost2に接続します。
スティーブベネット

25

OpenSSH v7.3以降では、-JスイッチとProxyJumpオプションがサポートされており、1つ以上のカンマ区切りのジャンプホストが許可されているため、今すぐこれを行うことができます。

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

ssh -J user1 @ host1 -YC4c arcfour、blowfish-cbc user2 @ host2 firefox -no-remoteこれにより、host2からlocalhostへのfirefoxの取得が高速化されます。
Jaur

21

プライベートネットワークへのsshゲートウェイが1つあります。私が外にいて、プライベートネットワーク内のマシンにリモートシェルが必要な場合は、ゲートウェイに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トンネルも閉じたいです。これは「sleep 10」トリックを介して行われます。通常、最初のsshコマンドは10秒後に閉じますが、この間に2番目のsshコマンドはトンネルを使用して接続を確立します。その結果、最初のsshコマンドは、次の2つの条件が満たされるまでトンネルを開いたままにします。スリープ10が終了し、トンネルが使用されなくなります。

1
非常に賢い!!!大好きです!
ヘンディイラワン

18

上記を読んですべてを接着した後、次の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転送を使用するには:

mssh HOSTA HOSTB HOSTC -X

HOSTAおよびHOSTBを介してHOSTCのポート8080にアクセスするには:

mssh HOSTA HOSTB -L8080:HOSTC:8080

1
これは素晴らしいです
マラ

1
私は真剣に十分に感謝することはできません、このスクリプトは私の人生を日々楽にします。私が変更したのは、int(rand(1000))をiportに追加して、複数のインスタンスを同時に実行できるようにすることだけでした。私は間違いなくビールを借りています。
マラ

これは本当にうまくいきます。更なる改善がローカルホストの/ etc / hostsファイルとの〜/ .ssh / configにを使用してHOSTB、HOSTCなどを解決するだろう
スティーブ・ベネット

また、私はMalaのコメントを2番目にします。ランダム化されたポートがなければ、mssh HOSTA HOSTD実際にHOSTBに到達しようとすると(そして、おそらく実現しないかもしれません。)
スティーブベネット

8

この答えは、ProxyCommandの使用を伴うため、kynanに似ています。ただし、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
これがトリックの起源だと思います:wiki.gentoo.org/wiki/SSH_jump_host
slm

5

私はあなたがやりたいと思うことをした

ssh -D 9999 -J host1 host2

両方のパスワードの入力を求められたら、host2へのSOCKSプロキシにlocalhost:9999を使用できます。最初に示した例に最も近いと思います。


これは完璧に機能しました!
アーサーシルバ

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に転送することを意味します


localhost:9999
dvtoever

この回答に続いて、channel 3: open failed: administratively prohibited: open failed エラーメッセージが表示されます。
フランクデルノンクール

2

ポート転送を使用してhost2からサービスにアクセスできるはずですlocalhost。良いガイドがここにあります。抜粋:

ポート転送には、ローカル転送とリモート転送の2種類があります。これらは、それぞれ発信トンネルおよび着信トンネルとも呼ばれます。ローカルポートフォワーディングは、ローカルポートに着信するトラフィックを指定されたリモートポートに転送します。

たとえば、コマンドを発行した場合

ssh2 -L 1234:localhost:23 username@host

クライアントのポート1234に着信するすべてのトラフィックは、サーバー(ホスト)のポート23に転送されます。接続が確立された後、localhostはsshdserverによって解決されることに注意してください。したがって、この場合、localhostはサーバー(ホスト)自体を指します。

リモートポートフォワーディングはその逆を行います。リモートポートに着信するトラフィックを指定されたローカルポートに転送します。

たとえば、コマンドを発行した場合

ssh2 -R 1234:localhost:23 username@host

サーバー(ホスト)のポート1234に到達するすべてのトラフィックは、クライアント(ローカルホスト)のポート23に転送されます。

あなたのキャストでは、置き換えlocalhostで例にhost2してhosthost1


その記事によると、接続は中央のマシン(host1)までしか保護されません。すべてを安全に保つ方法はありますか?
マラ

私はこれを試したことはありませんが、host1とhost2が両方ともsshサーバーである場合、host1からhost2へのトンネルを設定し、同じサービスに対してlocalhostからhost1へのトンネルを設定できる可能性があります(ローカルとリモートを取得する右のポート)。localhostからの1つのコマンドでそれが可能かどうかはわかりません。
フィデリ

1

この回答では、具体的な例を紹介します。コンピューターのホスト名、ユーザー名、パスワードを自分のものに置き換えるだけです。

問題文

次のネットワークトポロジがあると仮定します。

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

具体的にするために、次のコンピューターのホスト名、ユーザー名、およびパスワードがあるとします。

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

目標:ポート9991でリッスンするSOCKSプロキシを設定して、ポートからLocalPC接続LocalPCが開始さ れるたび9991に通過するようmit.eduにしhec.eduます。

ユースケースの例:http://127.0.0.1:8001hec.eduのみアクセス可能なHTTPサーバーがあります 。これはセキュリティ上の理由からです。でウェブブラウザを開いて、http//127.0.0.18001にアクセスできるようにしたいと思い ます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プロキシの使用に関するWebページにアクセスする場合、Firefoxで次の操作を実行できます。

ここに画像の説明を入力してください

いくつかのコメント:

  • ~/.ssh/configHEC接続名です:あなたが欲しいものにそれを変更することがあります。
  • -D9991伝えsshポート上のSOCKS4プロキシを設定します9991

0

両方のマシンにSSH接続できる場合は、sshのProxyCommandディレクティブを見てください。これにより、localhostからhost2に直接アクセスできるようになります(公開キーを使用する場合は、1つの簡単なコマンドで!!)。その後、host2で何でもできます。

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


0

ベストアンサーのオプション2は、現在の別名とは異なる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ノートブック上で実行されています。

次にlocalhost:9999、SOCKSホストとして使用するようにFirefoxを構成しました。

だから今、私のマシンでhost2Firefox からアクセスできるノートブックを実行していますlocalhost:8890


0

受け入れられた答えに記載されている3つのオプションは、私にはまったく機能しませんでした。私は両方のホストに対する権限があまりないので、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に再び転送します。

メッセージを出力した後、コマンドが「ハング」するのが見えるはずです-それは、それらが機能していることを意味します!1つの例外-のようなエラーメッセージは表示されないはずCould not request local forwarding.ですが、それでもまだ動作していません:(

上記のすべての方法に失敗した人であれば、おそらくこれを試すことができます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.