Encadeie dois portforwards ssh

Encadeie dois portforwards ssh

Estive navegando pelas respostas aqui no superusuário, mas nenhuma resposta que encontrei aborda meu caso de uso.

Quero encadear dois comandos que uso que me permitem criar um túnel através de um túnel:

ssh -L 22:hostB:22 user@hostA

e

ssh -L 10002:localhost:10001 user@localhost

Agora o que faço é inserir o primeiro comando, abrir outro terminal e inserir o segundo. Se eu fizer algo como

ssh … && ssh …

o segundo comando é executado somente depois que eu "sair" da primeira conexão.

Opções ssh mais avançadas, como jumphost, não funcionam devido à configuração do servidor no hostA, então tenho certeza que esta é a única opção para ter a porta 10001 acessível assim.

Existe uma maneira de encadear esses dois comandos em um alias ou usar algum script bash simples com tempos limite, etc.?

Responder1

Presumo que você não pode fazer isso só ssh -L 10002:hostB:10001 user@hostAporque

  • ou tudo o que escuta em B 10001se liga apenas à interface local,
  • ou o firewall em B não permite a conexão hostB:10001de A,
  • como queiras.

ssh … && ssh …

o segundo comando é executado somente depois que eu "sair" da primeira conexão.

Sim, isso é normal. A parte anterior &&deve terminar e retornar o status de saída antes que o próximo comando seja executado (ou não, dependendo do status).

Isso executará o segundo sshquase imediatamente, mas é falho:

(ssh…&) && ssh…

É falho porque o primeiro sshnão tem impacto no segundo. O segundo provavelmente será executado muito antes do primeiro estabelecer o túnel. Pedir senhas também pode ser problemático.

Para este caso de uso sshtem -fopção. Deman 1 ssh:

-f
Solicita sshpara ir para segundo plano logo antes da execução do comando. […]

Portanto, o comando básico pode ser:

ssh -fNL 22:hostB:22 user@hostA && ssh -NL 10002:localhost:10001 user@localhost

Cabe a você decidir se deseja usar -Ncom o segundo sshou não. Agora o truque são as primeiras sshbifurcações para segundo plano após solicitar a senha (se aplicável) e após estabelecer o primeiro túnel, portanto, temporariamente, existem dois "primeiros" sshprocessos. O que está ao fundo começa a lidar com o túnel; o que está em primeiro plano sai, isso permite &&trabalhar.

Você provavelmente quer -o ExitOnForwardFailure=yes. Se o primeiro túnel não puder ser estabelecido, o primeiro sshsairá sem sucesso e o segundo não será executado. O comando aprimorado é:

ssh -fNL 22:hostB:22 \
    -o ExitOnForwardFailure=yes \
    user@hostA &&
ssh -NL 10002:localhost:10001 user@localhost

Observe que quando o segundo sshsai, o primeiro (em segundo plano) permanece. A vantagem é que você pode executar um segundo sshdepois. A desvantagem é que você precisará matar o primeiro manualmente se não quiser que ele permaneça. Com isso em mente, considere a seguinte abordagem:

ssh -fNL 22:hostB:22 \
    -o ExitOnForwardFailure=yes \
    user@hostA
ssh -NL 10002:localhost:10001 user@localhost

Observe que não há &&. Agora, se o primeiro antigo sshainda estiver em execução, o novo falhará porque não será capaz de se conectar à porta. O segundo sshserá executado independentemente, usará o primeiro túnel e não importa a idade do túnel.

Pode acontecer que o antigo não exista sshe o primeiro sshfalhe por qualquer motivo. Nesse caso, o segundo será executado e provavelmente falhará (conexão recusada) porque não há nada escutando na porta 22.

Ainda assim, você pode não querer deixar um sshprocesso "inativo", você pode querer encerrá-lo automaticamente após a segunda sshsaída. Obviamente killall sshpode causar danos colaterais. Vamos encontrar algo mais sutil:

-M
Coloca o sshcliente no modo “mestre” para compartilhamento de conexão. […]

[…]

-O ctl_cmd
Controle um processo mestre de multiplexação de conexão ativa. […]

[…]

-S ctl_path
Especifica a localização de um soquete de controle para compartilhamento de conexão [...]

#!/bin/sh

socket=/tmp/hostA.socket

ssh -fNL 22:hostB:22 \
    -o ExitOnForwardFailure=yes \
    -MS "$socket"
    user@hostA &&
{
ssh -NL 10002:localhost:10001 user@localhost
ssh -S "$socket" -O exit dummy
}

Depois que o segundo sshterminar, o terceiro dirá ao primeiro para sair. No terceiro sshcomando é o soquete que importa, não o nome do host; ainda assim você precisa fornecer algum nome de host, portanto dummy. O primeiro sshirá remover o soquete antes de sair, não deve sobrar lixo.

Ainda existem algumas preocupações:

  • Se o script for interrompido, o primeiro sshpermanecerá. Solte &&ou envie -O exitlogo no início, apenas para garantir (seria o zero ssh).
  • Se o primeiro sshmorrer abruptamente, o soquete pode permanecer. Isso fará com que qualquer novo primeiro sshfalhe. Uma boa ideia pode ser expandir o zero sshassim:

    ssh -S "$socket" -O exit dummy || rm "$socket"
    
  • /tmp/hostA.socketpode não ser o melhor local para o soquete. No ssh_configequivalente à -Sopção de linha de comando é ControlPath. Veja o queman 5 ssh_configdiz sobre isso:

    Recomenda-se que qualquer conexão ControlPathusada para compartilhamento de conexão oportunista inclua pelo menos %h, %p, e %r(ou alternativamente %C) e seja colocada em um diretório que não seja gravável por outros usuários.

    Um "diretório que não pode ser gravado por outros usuários" pode ser /run/user/$UID, se o seu sistema operacional suportar (acredito que seja systemdo caso).

informação relacionada