2010-01-16 05:58:48 +0000 2010-01-16 05:58:48 +0000
354
354

Um túnel SSH através de múltiplos lúpulos

Os dados de túnel sobre SSH são bastante simples:

ssh -D9999 username@example.com

estabelece o porto 9999 no seu &0099 como um túnel para localhost, mas eu tenho uma necessidade mais específica:

  • Estou a trabalhar localmente em example.com
  • localhost é acessível a host1
  • localhost só aceita ligações de host2
  • Preciso de criar um túnel de host1 para localhost

Efetivamente, quero criar um túnel SSH “multi-hop”. Como posso fazer isto? Idealmente, gostaria de fazer isto sem precisar de ser super-utilizador em qualquer das máquinas.

Respostas (15)

341
341
341
2010-01-17 21:31:56 +0000

Basicamente tem três possibilidades:

  1. Túnel de localhost a host1:

  2. Túnel de localhost a host1 e de host1 a host2:

  3. Túnel de localhost a host1 e de localhost a host2:

Normalmente, eu escolheria a opção 1. Se a ligação de host1 a host2 precisar de ser assegurada, opte pela opção 2. A opção 3 é principalmente útil para aceder a um serviço em host2 que só é acessível a partir do próprio host2.

158
158
158
2010-08-01 17:10:27 +0000

Existe uma excelente resposta que explica o uso da directiva de configuração ProxyCommand para SSH :

Adicione isto ao seu ~/.ssh/config (veja man 5 ssh_config para mais detalhes):

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

Então o ssh host2 passará automaticamente pelo host1 (também funciona com o reencaminhamento X11, etc. ).

Isto também funciona para toda uma classe de hosts, por exemplo, identificados por domínio:

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

Actualização

OpenSSH 7.3 introduz a ProxyJump directiva, simplificando o primeiro exemplo para

Host host2
  ProxyJump host1
35
35
35
2016-08-10 09:11:34 +0000

O OpenSSH v7.3 suporta um interruptor -J e uma opção ProxyJump, que permitem um ou mais hospedeiros de salto separados por vírgula, portanto, pode simplesmente fazer isto agora:

ssh -J jumpuser1@jumphost1,jumpuser2@jumphost2,...,jumpuserN@jumphostN user@host
20
20
20
2010-01-24 18:47:37 +0000

Temos um ssh gateway para a nossa rede privada. Se eu estiver no exterior e quiser uma shell remota numa máquina dentro da rede privada, teria de entrar na gateway e de lá para a máquina privada.

Para automatizar este procedimento, uso o seguinte guião:

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

O que está a acontecer:

  1. Estabelecer um túnel para o protocolo ssh (porto 22) até à máquina privada.
  2. Apenas se for bem sucedido, ssh para a máquina privada utilizando o túnel. (o &&operador assegura isto).
  3. Depois de fechar a sessão privada ssh, quero que o túnel ssh também feche. Isto é feito através do truque “dormir 10”. Normalmente, o primeiro comando ssh fecharia após 10 segundos, mas durante este tempo, o segundo comando ssh terá estabelecido uma ligação utilizando o túnel. Como resultado, o primeiro comando ssh mantém o túnel aberto até que as duas condições seguintes sejam satisfeitas: o sono 10 está terminado e o túnel já não é utilizado.
18
18
18
2012-01-11 11:02:06 +0000

Depois de ler o acima exposto e colar tudo junto, criei o seguinte script Perl (guardar como mssh in /usr/bin e torná-lo executável):

#!/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($_);
}

Utilização:

Para aceder ao HOSTC via HOSTA e HOSTB (mesmo utilizador):

mssh HOSTA HOSTB HOSTC

Para aceder ao HOSTC através do HOSTA e do HOSTB e utilizar números SSH não predefinidos e diferentes utilizadores:

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

Para aceder ao HOSTC através do HOSTA e do HOSTB e utilizar o encaminhamento X:

mssh HOSTA HOSTB HOSTC -X

Para aceder ao porto 8080 no HOSTC através do HOSTA e do HOSTB:

mssh HOSTA HOSTB -L8080:HOSTC:8080
8
8
8
2013-03-13 09:57:28 +0000

Esta resposta é semelhante ao kynan, uma vez que envolve o uso do ProxyCommand. Mas é mais conveniente usar o IMO.

Se tem o netcat instalado nas suas máquinas de salto pode adicionar este snippet ao seu ~/.ssh/config:

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

Então

ssh -D9999 host1+host2 -l username

fará o que pediu.

Vim aqui à procura do local original onde li este truque. Vou colocar um link quando o encontrar.

6
6
6
2017-11-21 11:06:21 +0000

Eu fiz o que eu pensei que você queria fazer com

ssh -D 9999 -J host1 host2

Eu sou solicitado para ambas as senhas, então eu posso usar o localhost:9999 para um proxy SOCKS para hospedar2. É o mais próximo que consigo pensar do exemplo que mostrou em primeiro lugar.

4
4
4
2010-01-19 02:03:35 +0000
ssh -L 9999:host2:80 -R 9999:localhost:9999 host1

-L 9999:host2:80

Significa que qualquer pacote recebido pelo host1:9999 o reencaminha para o host2:80

-R 9999:localhost:9999

Significa que qualquer pacote recebido pelo host1:9999 o reencaminha para o host local:9999

2
2
2
2010-01-16 06:34:17 +0000

deve poder utilizar o reencaminhamento de portos para aceder a um serviço em host2 a partir de localhost. Um bom guia está localizado em aqui . Excerto:

Existem dois tipos de encaminhamento de portas: encaminhamento local e encaminhamento remoto. São também chamados túneis de saída e de entrada, respectivamente. O reencaminhamento de porta local encaminha o tráfego que chega a uma porta local para uma porta remota especificada.

Por exemplo, se emitir o comando

ssh2 -L 1234:localhost:23 username@host

todo o tráfego que chega à porta 1234 no cliente será encaminhado para a porta 23 no servidor (host). Note que o localhost será resolvido pelo sshdserver após a conexão ser estabelecida. Neste caso, o localhost refere-se ao próprio servidor (host).

Remote port forwarding faz o contrário: encaminha o tráfego que vem para uma porta remota para uma porta local especificada.

Por exemplo, se emitir o comando

ssh2 -R 1234:localhost:23 username@host

todo o tráfego que chega à porta 1234 no servidor (host) será encaminhado para a porta 23 no cliente (localhost).

No seu elenco, substitua localhost no exemplo por host2 e host por host1.

1
1
1
2019-12-18 04:08:37 +0000

A minha resposta é realmente a mesma de todas as outras respostas aqui, mas, eu queria esclarecer a utilidade do ~/.ssh/config e do ProxyJump.

Digamos que eu preciso chegar a um destino em 3 lúpulos, e, para cada lúpulo, eu precisava de um nome de utilizador, anfitrião, porto e identidade específicos. Devido aos critérios de identidade, isto só pode ser feito com o ficheiro de configuração ~/.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

A partir do seu computador, pode testar cada salto individualmente, ou seja,

$ ssh hop1 # will go from your PC to the host1 in a single step
$ ssh hop2 # will go from your PC to host1, then from host1 to host2, i.e. in two steps
$ ssh hop3 # will go from your PC to host1, then to host2, then to host3, i.e. in three steps

Outra coisa interessante sobre o ficheiro ~/.ssh/config é que isto também vai permitir transferências de ficheiros sftp, por exemplo

$ sftp hop1 # will connect your PC to host1 (i.e. file transfer between your PC and host1)
$ sftp hop2 # will connect your PC to host1 to host2 (i.e. file transfer between your PC and host2)
$ sftp hop3 # will connect your PC to host1 to host2 to host3 (i.e. file transfer between your PC and host3)
1
1
1
2017-03-18 20:13:02 +0000

Nesta resposta, passo por um exemplo concreto. Basta substituir os nomes das máquinas, nomes de utilizador e palavras-passe dos computadores pelos seus.

Declaração de problema

Vamos assumir que temos a seguinte topologia de rede:

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

Por uma questão de concretude, vamos assumir que temos os seguintes nomes de hosts, nomes de utilizador e palavras-passe dos computadores:

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

Objectivo: queremos criar um proxy SOCKS que ouça no porto 9991 do LocalPC de modo a que cada vez que uma ligação no LocalPC é iniciada a partir do porto 9991 passe pelo mit.edu e depois pelo hec.edu.

Exemplo de caso de uso: hec.edu tem um servidor HTTP que só está acessível em http://127.0.0.1:8001 , por razões de segurança. Gostaríamos de poder visitar http://127.0.0.1:8001 abrindo um web browser em LocalPC.


Configuração

Em LocalPC, adicione em ~/.ssh/config:

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

Depois, no terminal de LocalPC, corra:

ssh -D9991 HEC

Pedir-lhe-á a password de bob em mit.edu (ou seja dylan123), depois pedir-lhe-á a palavra-passe de john em hec.edu (ou seja, doe456 em 9991), LocalPC).

Nessa altura, o proxy SOCKS está agora a funcionar no porto LocalPC do ~/.ssh/config.

Por exemplo, se quiser visitar uma página web em HEC usando o proxy SOCKS, pode fazer no Firefox:

Algumas observações:

  • em -D9991, ssh é o nome da ligação: pode alterá-la para o que quiser.
  • O 9991 diz ao &007 para configurar um proxy SOCKS4 na porta &007.
0
0
0
2020-02-22 07:13:01 +0000

Só isto me ajudou em mais de dois anfitriões:

ssh -L 6010:localhost:6010 user1@host1 \
-t ssh -L 6010:localhost:6010 user2@host2 \
-t ssh -L 6010:localhost:6010 user3@host3

Vai dar-lhe três palavras-passe. Inspirado por esta resposta

0
0
0
2018-07-26 05:24:38 +0000

No meu caso fiz

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

onde o host2:8890 está a correr num caderno Jupyter.

Depois configurei o Firefox para usar o localhost:9999 como anfitrião SOCKS.

Agora tenho o caderno a correr no host2 acessível pelo Firefox no localhost:8890 na minha máquina.

0
0
0
2017-10-05 08:43:30 +0000

A opção 2 da melhor resposta poderia ser utilizada com diferentes utilizadores ssh do que a actual, também conhecida como: 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
0
0
2019-08-20 19:09:55 +0000

As três opções mencionadas na resposta aceite não funcionaram de todo para mim. Uma vez que não tenho muita permissão sobre ambos os anfitriões, e parece que a nossa equipa DevOps tem regras bastante rígidas quando se trata de autenticação e está a fazer AMF. De alguma forma os comandos acima não podem jogar bem com a nossa autenticação.

O contexto é realmente semelhante às respostas acima: não posso entrar directamente no servidor de produção, e tenho de fazer 1 salto usando um servidor de salto.

Mais uma solução - uma solução ingénua

Acabei por o fazer de uma forma muito ingénua: em vez de tentar correr todos os comandos no meu portátil, corro os comandos ** em cada uma das máquinas** , como abaixo:

  1. SSH no seu servidor de salto, depois execute o ssh -v -L 6969:localhost:2222 -N your-protected.dest.server. Se lhe for pedido qualquer palavra-passe, digite-a.
  2. Agora no seu portátil, corra o ssh -v -L 6969:localhost:6969 -N your-jump-server.host.name. Isto irá encaminhar qualquer um dos seus pedidos na porta 6969 do seu portátil, para o servidor de salto. Depois, por sua vez, como configurámos no nosso passo anterior, o servidor de salto reencaminhará novamente os pedidos da porta 6969 para a porta 2222 no servidor de destino protegido.

Deverá ver aí o comando “pendurado” depois de imprimir alguma mensagem - significa que estão a funcionar! Uma excepção - não deve ver mensagem de erro como Could not request local forwarding., se vir isso, então ainda não está a funcionar :(. Pode agora tentar disparar o pedido na porta 6969 do seu portátil, e ver se está a funcionar.

Esperemos que se for alguém que falhou em todos os métodos acima mencionados, talvez possa tentar isto.