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@hostA
porque
- ou tudo o que escuta em B
10001
se liga apenas à interface local, - ou o firewall em B não permite a conexão
hostB:10001
de 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 ssh
quase imediatamente, mas é falho:
(ssh…&) && ssh…
É falho porque o primeiro ssh
nã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 ssh
tem -f
opção. Deman 1 ssh
:
-f
Solicitassh
para 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 -N
com o segundo ssh
ou não. Agora o truque são as primeiras ssh
bifurcaçõ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" ssh
processos. 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 ssh
sairá 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 ssh
sai, o primeiro (em segundo plano) permanece. A vantagem é que você pode executar um segundo ssh
depois. 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 ssh
ainda estiver em execução, o novo falhará porque não será capaz de se conectar à porta. O segundo ssh
será executado independentemente, usará o primeiro túnel e não importa a idade do túnel.
Pode acontecer que o antigo não exista ssh
e o primeiro ssh
falhe 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 ssh
processo "inativo", você pode querer encerrá-lo automaticamente após a segunda ssh
saída. Obviamente killall ssh
pode causar danos colaterais. Vamos encontrar algo mais sutil:
-M
Coloca ossh
cliente 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 ssh
terminar, o terceiro dirá ao primeiro para sair. No terceiro ssh
comando é o soquete que importa, não o nome do host; ainda assim você precisa fornecer algum nome de host, portanto dummy
. O primeiro ssh
irá remover o soquete antes de sair, não deve sobrar lixo.
Ainda existem algumas preocupações:
- Se o script for interrompido, o primeiro
ssh
permanecerá. Solte&&
ou envie-O exit
logo no início, apenas para garantir (seria o zerossh
). Se o primeiro
ssh
morrer abruptamente, o soquete pode permanecer. Isso fará com que qualquer novo primeirossh
falhe. Uma boa ideia pode ser expandir o zerossh
assim:ssh -S "$socket" -O exit dummy || rm "$socket"
/tmp/hostA.socket
pode não ser o melhor local para o soquete. Nossh_config
equivalente à-S
opção de linha de comando éControlPath
. Veja o queman 5 ssh_config
diz sobre isso:Recomenda-se que qualquer conexão
ControlPath
usada 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 sejasystemd
o caso).