Um túnel SSH através de vários saltos

332

Os dados de encapsulamento sobre SSH são bastante diretos:

ssh -D9999 [email protected]

configura a porta 9999 no seu localhostcomo um túnel para example.com, mas tenho uma necessidade mais específica:

  • Estou trabalhando localmente em localhost
  • host1 é acessível para localhost
  • host2 só aceita conexões de host1
  • Eu preciso criar um túnel de localhostparahost2

Efetivamente, eu quero criar um túnel SSH "multi-hop". Como posso fazer isso? Idealmente, eu gostaria de fazer isso sem precisar ser superusuário em nenhuma das máquinas.

Mala
fonte
2
Para que você o usou? Eu quero usá-lo para meias proxy. será que vai dar certo?
29512 prongs
2
Sim, você deve ser capaz de usar a conexão encapsulada como um proxy SOCKS, a menos que host2nega o encaminhamento
Mala
Eu estava pensando em criar um wrapper sobre SSH que configuraria isso usando o uso múltiplo do ProxyCommand.
Pavel Šimerda 19/10/2013
@prongs Você conseguiu usar isso para o proxy SOCKS (todos esses anos atrás)?
Drux

Respostas:

324

Você basicamente tem três possibilidades:

  1. Túnel de localhostpara host1:

    ssh -L 9999:host2:1234 -N host1
    

    Como observado acima, a conexão de host1para host2não será protegida.

  2. Túnel de localhostpara host1ea partir host1de host2:

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

    Isso abrirá um túnel de localhostpara host1e outro túnel de host1para host2. No entanto, a porta 9999para host2:1234pode ser usado por qualquer pessoa na host1. Isso pode ou não ser um problema.

  3. Túnel de localhostpara host1ea partir localhostde host2:

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

    Isso abrirá um túnel localhostpara o host1qual o serviço SSH host2pode ser usado. Em seguida, um segundo túnel é aberto de localhostpara host2através do primeiro.

Normalmente, eu usaria a opção 1. Se a conexão de host1para host2precisar ser protegida, a opção 2. A opção 3 é principalmente útil para acessar um serviço host2que só pode ser acessado por host2si.

Mika Fischer
fonte
17
opção 3 era o que eu estava procurando, obrigado!
Mala
1
Eu quero navegar dessa maneira. Qual é o melhor? Eu tentei o primeiro, mas não funcionou. Eu configurei um proxy de meias no meu host localhost: 1234, mas sem sorte. :( por favor me ajude ..
pinos
1
@prongs tente a opção 3
Mala
6
@Noli Se você usar o ssh-agent (o que você deve), poderá enviá-lo pelas conexões usando a -Aopção para ssh.
Mika Fischer
2
@musically_ut - está correto, o segundo comando ssh está em segundo plano no host1, portanto, se você deseja se reconectar localmente, precisará executar novamente a primeira parte novamente. Se você adicionar -f, ele entrará imediatamente em segundo plano localmente, assim ssh -f -L 9999:localhost:9999 host1será reconectado se você pressionar ctrl-c no primeiro caso. Ou adicione -fao comando double ssh original ao executá-lo pela primeira vez em segundo plano tudo imediatamente.
Mark Fisher
153

Há uma excelente resposta explicando o uso da ProxyCommanddiretiva de configuração para SSH :

Adicione isso ao seu ~/.ssh/config(veja man 5 ssh_configpara detalhes):

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

Em seguida ssh host2, o túnel passa automaticamente host1(também funciona com o encaminhamento do X11 etc.).

Isso também funciona para uma classe inteira de hosts, por exemplo, identificados pelo domínio:

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

Atualizar

O OpenSSH 7.3 introduz uma ProxyJumpdiretiva, simplificando o primeiro exemplo para

Host host2
  ProxyJump host1
kynan
fonte
3
Existe uma maneira de fazer isso condicionalmente? Eu só quero fazer isso às vezes. Além disso, isso é especificamente para comandos, mas estou procurando algo para toda a porta 22 (ssh, sftp, etc).
Stephane
1
@ Stephanie, o que você quer dizer com especificamente para comandos ? Sua configuração de SSH é usada por qualquer coisa usando ssh, inclusive git, sftpetc.
Kynan
1
@ Stephanie Não conheço uma maneira de habilitar isso condicionalmente (por exemplo, apenas quando você estiver fora da rede do host de destino). Defino esta opção para todos os hosts em questão em um bloco de configuração e, em seguida, (des) comento a linha, conforme necessário. Não é perfeito, mas funciona.
Kynan
2
@Stephane certeza: ssh -F /path/to/altconfig. Cuidado, isso ignorará todo o sistema /etc/ssh/ssh_config.
Kynan
19
Uma maneira fácil de tornar as configurações "condicionais" é definir dois hosts diferentes em .ssh / config, que possuem o mesmo HostName. Conecte-se ao host2-tunnel quando desejar o túnel e ao host2 quando não desejar.
21813 Steve Bennett
25

O OpenSSH v7.3 em diante suporta um -Jswitch e uma ProxyJumpopção, que permitem um ou mais hosts de salto separados por vírgula; portanto, você pode simplesmente fazer isso agora:

ssh -J jumpuser1@jumphost1,jumpuser2@jumphost2,...,jumpuserN@jumphostN user@host
Nikolay
fonte
ssh -J usuário1 @ host1 -YC4c arcfour, blowfish-cbc usuário2 @ host2 firefox -no-remote Isso irá acelerar a obtenção do firefox do host2 para o host local.
Jaur
21

Temos um gateway ssh em nossa rede privada. Se eu estiver do lado de fora e quiser um shell remoto em uma máquina dentro da rede privada, eu precisaria ssh no gateway e daí para a máquina privada.

Para automatizar esse procedimento, eu uso o seguinte script:

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

O que está acontecendo:

  1. Estabeleça um túnel para o protocolo ssh (porta 22) para a máquina privada.
  2. Somente se for bem-sucedido, faça o ssh na máquina privada usando o túnel. (o operador && garante isso).
  3. Depois de fechar a sessão ssh privada, quero que o túnel ssh também feche. Isso é feito através do truque "sleep 10". Normalmente, o primeiro comando ssh fecha após 10 segundos, mas durante esse período, o segundo comando ssh estabelecerá uma conexão usando o túnel. Como resultado, o primeiro comando ssh mantém o túnel aberto até que as duas condições a seguir sejam atendidas: sleep 10 é concluído e o túnel não é mais usado.
Bernhard Kausler
fonte
1
Muito esperto!!! ADORO!
Hendy Irawan
18

Depois de ler o texto acima e colar tudo juntos, criei o seguinte script Perl (salve-o como mssh em / usr / bin e torne-o 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($_);
}

Uso:

Para acessar o HOSTC via HOSTA e HOSTB (mesmo usuário):

mssh HOSTA HOSTB HOSTC

Para acessar o HOSTC via HOSTA e HOSTB e usar números de porta SSH não padrão e usuários diferentes:

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

Para acessar o HOSTC via HOSTA e HOSTB e usar o encaminhamento X:

mssh HOSTA HOSTB HOSTC -X

Para acessar a porta 8080 no HOSTC via HOSTA e HOSTB:

mssh HOSTA HOSTB -L8080:HOSTC:8080
Bob Muller
fonte
1
isto é incrível
Mala
1
Eu realmente não posso agradecer o suficiente, esse script facilita minha vida diariamente. A única coisa que mudei foi adicionar int (rand (1000)) ao iport, para permitir que várias instâncias rodassem ao mesmo tempo. Eu definitivamente te devo uma cerveja.
Mala
Isso funciona muito bem. Outra melhoria seria para resolver HostB, HostC etc usando localhost / etc / hosts e ~ / .ssh / config
Steve Bennett
Também eu segundo comentário de Mala. Sem a porta randomizado, se você tente mssh HOSTA HOSTDvocê vai realmente acabar em HostB (e talvez não vai perceber ..)
Steve Bennett
8

Essa resposta é semelhante ao kynan, pois envolve o uso do ProxyCommand. Mas é mais conveniente usar o IMO.

Se você possui o netcat instalado em suas máquinas hop, você pode adicionar este trecho ao seu ~ / .ssh / config:

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

Então

ssh -D9999 host1+host2 -l username

fará o que você pediu.

Eu vim aqui procurando o lugar original onde li esse truque. Vou postar um link quando o encontrar.

silviot
fonte
2
Eu acredito que esta é a origem do truque: wiki.gentoo.org/wiki/SSH_jump_host
slm
5

Eu fiz o que acho que você queria fazer com

ssh -D 9999 -J host1 host2

As duas senhas são solicitadas e, em seguida, posso usar o host local: 9999 para um proxy SOCKS para o host2. É o mais próximo que consigo pensar no exemplo que você mostrou em primeiro lugar.

Cheryl
fonte
Isso funcionou perfeitamente!
Arthur Silva
4
ssh -L 9999:host2:80 -R 9999:localhost:9999 host1

-L 9999: host2: 80

Significa ligar ao localhost: 9999 e qualquer pacote enviado ao localhost: 9999 encaminhá-lo para o host2: 80

-R 9999: localhost: 9999

Significa que qualquer pacote recebido pelo host1: 9999 o encaminha para o localhost: 9999

chinmaya
fonte
Brilliant resposta, mais simples de fazer um túnel para que possa aceder à aplicação em host2 diretamente de localhost: 9999
dvtoever
Após esta resposta, recebo uma channel 3: open failed: administratively prohibited: open failed mensagem de erro.
Franck Dernoncourt 18/03/19
2

você poderá usar o encaminhamento de porta para acessar um serviço a host2partir de localhost. Um bom guia está localizado aqui . Excerto:

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

Por exemplo, se você 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). Observe que o host local será resolvido pelo sshdserver depois que a conexão for estabelecida. Nesse caso, localhost se refere ao próprio servidor (host).

O encaminhamento de porta remota faz o oposto: encaminha o tráfego que chega a uma porta remota para uma porta local especificada.

Por exemplo, se você 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 (host local).

Em seu elenco, substitua localhostno exemplo por host2e hostcom host1.

fideli
fonte
de acordo com esse artigo, a conexão será protegida apenas até a máquina do meio (host1). Existe uma maneira de garantir que tudo fique seguro?
Mala
Eu nunca tentei isso, mas se host1 e host2 são servidores ssh, você poderá configurar um encapsulamento do host1 para o host2 e, em seguida, configurar um encapsulamento do localhost para o host1 para o mesmo serviço (obtendo seu local e remoto portas à direita). Não sei se isso é possível em um comando do localhost.
Fideli
1

Nesta resposta, passarei por um exemplo concreto. Você só precisa substituir os nomes de host, nomes de usuário e senhas dos computadores pelos seus.

Declaração do problema

Vamos supor que temos a seguinte topologia de rede:

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

Por uma questão de concretude, vamos supor que temos os nomes de host, nomes de usuário e senhas dos seguintes computadores:

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

Objetivo: queremos configurar um proxy SOCKS que escute na porta 9991de LocalPCforma que, toda vez que uma conexão LocalPCseja iniciada a partir da porta, 9991ela seja mit.eduexecutada hec.edu.

Exemplo de caso de uso: hec.edupossui um servidor HTTP acessível apenas em http://127.0.0.1:8001 , por motivos de segurança. Gostaríamos de poder visitar http://127.0.0.1:8001 abrindo um navegador na Web LocalPC.


Configuração

Em LocalPC, adicione ~/.ssh/config:

Host HEC
    HostName hec.edu
    User john
    ProxyCommand ssh [email protected] -W %h:%p

Em seguida, no terminal de LocalPC, execute:

ssh -D9991 HEC

Ele solicitará a senha do bobon mit.edu(ie dylan123), e depois a senha do johnon hec.edu(ie doe456).

Nesse ponto, o proxy SOCKS agora está sendo executado na porta 9991de LocalPC.

Por exemplo, se você quiser visitar uma página da Web sobre o LocalPCuso do proxy SOCKS, faça no Firefox:

insira a descrição da imagem aqui

Algumas observações:

  • in ~/.ssh/config, HECé o nome da conexão: você pode alterá-lo para o que quiser.
  • O -D9991instrui sshpara configurar um proxy SOCKS4 na porta 9991.
Franck Dernoncourt
fonte
0

Se você puder fazer o SSH nas duas máquinas, dê uma olhada na diretiva ProxyCommand do ssh. Isso permitirá que você vá direto do localhost para o host2 (em um comando fácil, se você usar chaves públicas !!). Então você pode fazer o que quiser com o host2.

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


fonte
0

A opção 2 da melhor resposta pode ser usada com usuários ssh diferentes do atual, também conhecido 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
Yordan Georgiev
fonte
0

No meu caso eu fiz

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

onde host2:8890está sendo executado em um notebook Jupyter.

Então eu configurei o Firefox para usar localhost:9999como host SOCKS.

Então agora eu tenho o notebook rodando host2acessível pelo Firefox na localhost:8890minha máquina.

amarion
fonte
0

As três opções mencionadas na resposta aceita não funcionaram para mim. Como não tenho muita permissão sobre os dois hosts, e parece que nossa equipe de DevOps tem regras bastante rígidas quando se trata de autenticação e está realizando MFA. De alguma forma, os comandos acima não podem funcionar bem com nossa autenticação.

Porém, o contexto é realmente semelhante às respostas acima: Eu não posso fazer o ssh diretamente no servidor de produção e preciso fazer um salto usando um servidor de salto.

Outra solução - ingênua

Acabei fazendo isso de uma maneira muito ingênua: em vez de tentar executar todos os comandos no meu laptop, executei os comandos em cada uma das máquinas , como abaixo:

  1. SSH no servidor de salto e, em seguida, execute ssh -v -L 6969:localhost:2222 -N your-protected.dest.server. Se você for solicitado com alguma entrada de senha, digite-a.
  2. Agora no seu laptop, corra ssh -v -L 6969:localhost:6969 -N your-jump-server.host.name. Isso encaminhará qualquer solicitação na porta 6969 do seu laptop para o servidor de salto. Por sua vez, como configuramos em nossa etapa anterior, o servidor de salto encaminhará novamente as solicitações da porta 6969 para a porta 2222 no servidor de destino protegido.

Você verá o comando "travar" depois de imprimir alguma mensagem - significa que eles estão funcionando! Uma exceção - você não verá uma mensagem de erro como Could not request local forwarding., se a vir, ainda não está funcionando :(. Agora você pode tentar acionar a solicitação na porta 6969 do seu laptop e verificar se está funcionando.

Espero que se você falhou em todos os métodos acima, talvez você possa tentar isso.

Shaung Cheng
fonte