Eu tenho 3 hosts:
- Meu computador.
- Um Jump Host (também conhecido como bastião) com um IP estático.
- Um servidor com IP dinâmico; atrás de um roteador/firewall NAT, sem portas de entrada abertas.
O servidor atualmente se conecta ao Jump Host e estabelece um túnel SSH via -R "0:localhost:22"
, de modo que a porta é alocada dinamicamente pelo Jump Host (isso tem sido muito confiável até agora).
Com algummagia de soquete, tenho o número da porta alocada dinamicamente registrado em um arquivo no Jump Host.
Com isso, posso usar SSH para o Jump Host e executarssh -p $(cat /path/to/port-file) localhost
Mas é possível pular esta etapa extra?
Isso seria útil para Ansible, onde inventory.yml
preciso atualizar o número da porta:
server:
# /usr/bin/ssh jh cat /path/to/port-file
ansible_port: "34625"
ansible_host: "localhost"
ansible_ssh_common_args: "-o ProxyCommand='ssh -q -W %h:%p jh' -o StrictHostKeyChecking=no"
Talvez seja possível usar ProxyCommand
:
Ele pode (mais ou menos) usar a substituição de comandos no ~/.ssh/config
arquivo do meu computador:
Host server
ProxyCommand ssh -q -W localhost:$(echo "34625") jh
Isso funciona (mesmo sem echo -n
) onde a substituição parece acontecer no meu computador local.
E apesar da ineficiência de usar SSH para obter primeiro o número da porta, isso não funciona:
Host server
ProxyCommand ssh -q -W localhost:$(ssh jh cat /path/to/port-file) jh
Isso resulta em:
Bad packet length 1349676916.
ssh_dispatch_run_fatal: Connection to UNKNOWN port 65535: message authentication code incorrect
E estranhamente, o servidor (no final da cadeia) anota isso em seus logs de autenticação:
sshd[5558]: Bad protocol version identification '' from ::1 port 36048
O que implica que o número da porta está sendo retornado. Mas não tenho ideia de por que ele quebra neste momento.
E ssh -vvv
mostra que identifica a chave do host no meu arquivo ~/.ssh/known_hosts
.
Também tentei criar um arquivo "ssh_config" personalizado no Jump Host, usando ssh -F /path/to/ssh_config
, com o conteúdo:
Host tunnel.server
HostName localhost
Port 34625
Mas acho que não posso usar isso com o ProxyCommand
.
Eu também suspeito StrictHostKeyChecking=no
que talvez seja necessário introduzi-lo em algum momento, quando o número da porta for alterado.
Responder1
Segunda solução parcial, inspirada em @anx...
Crie um arquivo de soquete
ssh -R '/path/to/socket-file:localhost:22' tunnel@jh
Então, para usar este soquete (do Jump Host), posso usar socat
:
ssh -o "ProxyCommand socat - UNIX-CLIENT:/path/to/socket-file" localhost
O uso de socat
parece uma etapa desnecessária, onde tenho certeza de que deve haver uma maneira de fazer com que o ssh
comando use o arquivo de soquete diretamente, mas ainda não consigo encontrá-lo.
Também não descobri como usar esse arquivo de soquete no meu computador (já que o ProxyCommand é executado no host local, não no JumpHost).
Devo também observar; como a tunnel
conta (no Jump Host) é muito restrita (só existe para estabelecer essas conexões de túnel), preciso configurar StreamLocalBindMask=0111
para que minha conta no Jump Host possa usar esse arquivo de soquete. Da mesma forma, o arquivo de soquete antigo deve ser removido se uma nova conexão for estabelecida, via StreamLocalBindUnlink=yes
.
Ambas as opções precisam ser definidas no Jump Host, em "/etc/ssh/sshd_config":
Match User tunnel
StreamLocalBindMask 0111
StreamLocalBindUnlink yes
Infelizmente, Match
as regras são ignoradas em "/etc/ssh/sshd_config.d/tunnel.conf" antes do OpenSSH 8.4, lançado em 27 de setembro de 2020 (relatório de erro), e isso não está disponível atualmente no Ubuntu 20.04.1 LTS.
Responder2
Solução temporária, não ideal...
Adicione um cron job de hora em hora no meu computador, ele coleta os números das portas do Jump Host e cria um novo ~/.ssh/config_tunnels
arquivo.
#!/bin/bash
set -u;
config_path="${HOME}/.ssh/config_tunnels";
port=$(/bin/ssh jh /bin/cat /path/to/port-file 2> /dev/null | /bin/sed 's/[^0-9]//g');
if [[ "${port}" -lt 1024 ]]; then
exit; # Probably a blank/zero value (e.g. connection issue).
fi
{
echo "";
echo "Host server";
echo " ProxyCommand ssh -q -W localhost:${port} jh";
echo "";
} > "${config_path}";
chown user:group "${config_path}";
chmod 600 "${config_path}";
O arquivo principal ~/.ssh/config
pode então usar Include ~/.ssh/config_tunnels
.
Observe que estou usando sed
para ter certeza de que receberei um número de volta. Se meu Jump Host for comprometido, não quero que o arquivo de porta contenha alguma configuração SSH extra (malicioso).