- SSH é uma ferramenta que vai além do acesso remoto, cobrindo também encaminhamento de portas, jump hosts, arquivo de configuração e gerenciamento de chaves; os exemplos com servidor web podem ser aplicados da mesma forma a outros serviços como RDP e SQL
- Encaminhamento de porta local, remoto e dinâmico são configurados com
-L, -R e -D, respectivamente, e a principal diferença é em que lado a porta é aberta e para onde o tráfego flui
- Em redes onde o acesso direto é bloqueado, é possível chegar ao destino conectando vários intermediários SSH com jump hosts via
-J e ProxyJump
ssh-agent e o encaminhamento de agente com -A permitem usar suas chaves locais também em hosts remotos, o que é conveniente, mas primeiro é preciso avaliar os riscos de segurança do uso indevido do agente
- Usar
~/.ssh/config, ssh-copy-id, ssh-keygen e o console do SSH ~?·~C em conjunto reduz a repetição de opções, facilita adicionar forwards durante a sessão, distribuir chaves públicas e gerar ou inspecionar chaves
Premissas para entender o encaminhamento de portas no SSH
- O encaminhamento de portas no SSH é difícil de entender só com diagramas; fica mais fácil quando se vê os comandos reais junto com cenários de rede
- Os exemplos usam acesso a servidor web, mas a mesma abordagem pode ser aplicada a RDP, SQL e praticamente qualquer serviço
- As opções usadas repetidamente têm os seguintes significados
-N: não executa comando no servidor remoto e também não abre shell
-f: envia o SSH para o background
root@host: faz login no host com o usuário que criará o túnel
Encaminhamento de porta local -L
- O encaminhamento de porta local redireciona uma porta da máquina local para uma porta no servidor remoto
- Situação de exemplo
internal-web.int hospeda uma página web acessível apenas pela interface de loopback
campfire.int consegue acessar internal-web.int via SSH
- Deseja-se acessar o servidor web de
internal-web.int por meio de uma porta local em campfire.int
- Comando usado
ssh -N -f -L 1337:127.0.0.1:80 root@internal-web.int
- Interpretação das opções
-L: define encaminhamento local
1337:127.0.0.1:80: vincula a porta local 1337 ao 127.0.0.1:80 remoto
- Depois que o túnel é criado, é possível enviar requisições para a porta local
1337 em campfire.int e interagir com a porta 80 de internal-web.int
- O ponto a lembrar é que, em
-L, a porta local fica à esquerda no endereço
Encaminhamento de porta remoto -R
- O encaminhamento de porta remoto expõe, no servidor remoto, uma porta acessível localmente
- Situação de exemplo
internal-web.int hospeda uma página web acessível apenas por loopback
campfire.int não consegue acessar internal-web.int diretamente por causa do firewall
vuln-server.int pode ser acessado tanto por campfire.int quanto por internal-web.int
- Comando usado
ssh -N -f -R 3000:127.0.0.1:80 root@vuln-server.int
- Interpretação das opções
-R: define encaminhamento remoto
3000:127.0.0.1:80: vincula a porta 3000 de vuln-server.int ao 127.0.0.1:80 local
- Depois disso, ao enviar uma requisição
curl para vuln-server.int:3000, é possível acessar a página web interna que roda na porta 80 de internal-web.int
- O ponto a lembrar é que, em
-R, a porta local fica à direita no endereço
Encaminhamento de porta dinâmico -D e proxy SOCKS
- O encaminhamento de porta dinâmico cria um proxy SOCKS com a opção
-D e envia o tráfego por um intermediário SSH
- Situação de exemplo
internal-web.int hospeda uma aplicação web acessível apenas pela rede interna
vuln-server.int está na mesma rede interna e consegue acessar internal-web.int
- Deseja-se fazer proxy do tráfego a partir de
campfire.int por meio de vuln-server.int
- A configuração de
/etc/proxychains.conf precisa corresponder à porta do comando SSH
socks5: faz o proxychains usar SOCKS5
127.0.0.1: usa localhost
8080: deve corresponder à porta definida em -D no SSH
- Comando usado
ssh -N -f -D 8080 root@vuln-server.int
- Depois de criar o forward, ao configurar
socks5 127.0.0.1 8080, é possível acessar a página web interna com proxychains curl 192.168.1.185
- O DNS via SOCKS pode não funcionar bem dependendo do ambiente, por isso o exemplo usa endereço IP em vez de nome de host
- No Firefox também é possível usar o proxy SOCKS com configuração manual
- Caminho: Settings → Privacy & Security → Network Settings
- Selecionar Manual proxy configuration
- Marcar “Proxy DNS when using SOCKS V5”
- Definir SOCKS host como
127.0.0.1 e port como 8080
Jump host -J
- Um jump host permite se conectar a um destino que não é acessível diretamente a partir do host atual, passando por vários intermediários SSH
- A cadeia de exemplo é
campfire.int → vuln-server.int → internal-web.int → dns.int
- Comando usado
ssh -J root@vuln-server.int,root@internal-web.int root@dns.int
- Vários destinos de salto são separados por vírgula
Encaminhamento de agente -A
ssh-agent permite adicionar uma chave privada ou identidade na máquina local com ssh-add <private_key_file>
- As chaves adicionadas podem ser verificadas com
ssh-add -l
- Ao adicionar uma chave ao
ssh-agent, é possível fazer conexões SSH com essa chave sem redigitar a senha, o que é útil tanto para pessoas quanto para contas de serviço
- O encaminhamento de agente com
-A permite usar, na máquina remota à qual você se conectou, as chaves que estão no agente local
- Antes de usar, é preciso verificar os riscos de segurança em Zero Effort Private Key Compromise: Abusing SSH-Agent for Lateral Movement
- Comando de exemplo
ssh -A -J root@vuln-server.int root@internal-web.int
- Esse comando acessa
internal-web.int passando por vuln-server.int e permite usar as chaves do agente SSH local de campfire.int
- Depois disso, ao executar
ssh root@dns.int em internal-web.int, é possível se conectar sem especificar chave privada nem informar credenciais
Alocação de TTY -t
- A opção
-t é útil para executar rapidamente no servidor remoto comandos que exigem interação
- O exemplo são comandos que precisam de TTY, como
Vim ou top
- Comando usado
ssh root@internal-web.int -t top
- Ao executar, você recebe um TTY no servidor remoto com o comando
top
Permitir que hosts externos também usem a porta do forward local com -g
- A opção
-g permite que hosts remotos se conectem à porta encaminhada localmente
- É parecida com o encaminhamento local
-L, mas com a diferença de que máquinas externas também podem usar essa porta “local”
- Situação de exemplo
- Há acesso shell a
vuln-server.int
- Deseja-se fazer proxy das conexões que chegam à porta
2222 de vuln-server.int para a porta 22 de internal-web.int
- Comando usado
ssh -N -f -g -L 2222:localhost:22 root@internal-web.int
- Interpretação das opções
-g: permite que hosts remotos se conectem à porta de encaminhamento local
-L: define encaminhamento local
- Mesmo conectando por SSH à porta
2222 de vuln-server.int, o shell real estará em internal-web.int
Console do SSH ~? e encaminhamento durante a sessão
- O console do SSH é um recurso oculto que permite controlar o próprio SSH sem interagir diretamente com o sistema remoto
- É útil quando o shell travou ou quando é preciso controlar a própria sessão SSH
- O console de ajuda pode ser aberto com
~?
- Opções úteis
~.: encerra a sessão SSH atual
~C: abre o console do SSH para adicionar opções de encaminhamento
- Mesmo já conectado a
vuln-server.int com um comando ssh comum, ao pressionar ~C e digitar -D 8080, é possível passar a usar essa sessão como uma sessão de encaminhamento dinâmico
- Se
/etc/proxychains.conf em campfire.int estiver configurado para usar a porta 8080, será possível usar o proxychains como se a sessão tivesse sido iniciada com ssh -D desde o começo
Arquivo de configuração do SSH ~/.ssh/config
- O arquivo de configuração do SSH fica em
~/.ssh/config e permite salvar opções usadas com frequência para economizar tempo
- Ao iniciar uma conexão SSH, esse arquivo é analisado e, se houver uma configuração
host compatível com o destino, essas opções serão usadas
- Argumentos de linha de comando têm prioridade sobre o arquivo de configuração
- Por exemplo, mesmo que o arquivo defina o usuário de
internal-web.int como root, ao executar ssh graham@internal-web.int a tentativa de login será com graham
- Exemplo de configuração básica
# You can put comments with a `#` at the beginning of the line only.
host internal-web.int
User root
IdentityFile /home/smores/ssh_agent/internal-web-no-pw
Port 2222
- Fluxo de processamento ao executar
ssh internal-web.int
- Faz a correspondência entre
internal-web.int na linha de comando e host internal-web.int em ~/.ssh/config
- Se houver correspondência, busca no arquivo de configuração as opções não especificadas na linha de comando
- Se não houver correspondência, usa apenas as opções definidas na linha de comando
Palavras-chave comuns do SSH config
IdentityFile /path/to/private_key
- Define a chave privada a ser usada para o host
- Equivale a
ssh -i
ForwardAgent
ProxyJump root@internal-web.int
- Define o servidor pelo qual o tráfego será proxied
- Equivale à opção
-J
- No exemplo, isso mostra que o tráfego passa por
vuln-server.int, pois a autenticação nesse host é exigida primeiro
Match
- Aplica palavras-chave do SSH config com base em condições
- No exemplo, se o código de saída do comando
export | grep PROXYME=TRUE for 0, o ProxyJump abaixo do bloco Match será usado
- Se a variável de ambiente
PROXYME não existir, apenas o bloco normal host internal-web.int será usado
- Após definir
export PROXYME=TRUE, ao executar o mesmo ssh internal-web.int, você passará pela autenticação em vuln-server.int antes de receber o shell de internal-web.int
scp e alguns utilitários baseados em SSH normalmente também podem usar o SSH config
- Em ambientes que não o usam automaticamente, é possível especificar explicitamente com
-F ~/.ssh/config
Cópia de chave pública ssh-copy-id
ssh-copy-id é um pequeno utilitário para enviar rapidamente uma chave pública ao servidor
- Comando usado
ssh-copy-id -i internal-web root@internal-web.int
- Interpretação das opções
-i internal-web: define o nome da chave privada a ser usada na autenticação com o servidor
root@internal-web.int: define o servidor para o qual a chave pública será enviada
Geração e inspeção de chaves ssh-keygen
ssh-keygen é o utilitário para gerar pares de chave privada e pública
- Em geral, recomenda-se usar a opção
-b para especificar um tamanho de chave maior
- No ambiente de exemplo, o tamanho padrão da chave era
3072
- O algoritmo padrão é RSA, mas é possível escolher outro com a flag
-t
- Exemplo:
ssh-keygen -t ecdsa -b 521
- A impressão digital e o tamanho em bytes da chave podem ser verificados com o comando abaixo
ssh-keygen -lf <file-name>
1 comentários
Comentários do Hacker News
Falta aqui uma diretiva surpreendentemente simples: basta configurar algo como
AuthorizedKeysCommand /usr/bin/php /etc/ssh/auth.php %uemsshd_confige, no script, buscarhttps://github.com/{$user}.keysno GitHubClaro, não é código com qualidade de produção, mas mostra a ideia central
Depois de verificar se a pessoa pertence a uma organização/grupo do GitHub, se o usuário existir e estiver mapeado por algo como
nss-ato, você pode permitir login no servidorAo fazer onboarding/offboarding de pessoas, basta adicioná-las/removê-las de um grupo do GitHub para conceder ou revogar acesso aos equipamentos, reduzindo o trabalho
No Amazon Linux 2 e anteriores, ele chama a linha de comando do
opensslpara verificar o formato de cada chave no arquivoauthorized_keys, mas isso é hardcoded para RSA; então, mesmo que a versão do OpenSSH suporteed25519, não era possível autenticar em hosts Amazon Linux 2 usandoed25519Em teoria, é bom porque permite um recurso bacana¹, mas, na prática, quebra a funcionalidade básica até para quem não usa esse recurso, o que me faz confiar menos no Amazon Linux
Quando encontrei esse problema pela primeira vez, eu estava tentando acessar via SSH uma máquina de um colega de DevOps cloud-first e, como não podia mexer diretamente nela, foi difícil diagnosticar
Ele conhecia bem a AWS, mas não tanto Linux, então não sabia onde procurar; escolheu Amazon Linux por ser uma distribuição feita pelo dono da plataforma de nuvem e achou que seria “mais compatível”, mas aqui “mais compatível” na verdade significava “com mais surpresas idiotas”
¹ https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/connect-...
Como “a rede não está funcionando direito” é o escopo do trabalho, acabo perdendo esses recursos legais
Muita gente provavelmente não sabe que o parser de configuração do OpenSSH ignora diretivas duplicadas e que, entre diretivas iguais, só a primeira tem efeito
Isso é bem contraintuitivo, porque em parsers de configuração ou motores de regras normalmente a diretiva posterior sobrescreve a anterior
Pode parecer um detalhe pequeno, mas, ao mudar valores padrão como os de
/etc/ssh/sshd_config, pessoas e softwares tendem a acrescentar as mudanças ao fim do arquivo ou do bloco de diretivas, esperando que elas sejam aplicadasEmpresas e organizações de segurança, vários produtos de bastion SSH, também erram isso, e as recomendações do CIS Benchmarks e a maioria das ferramentas terceiras de auditoria CIS não consideram a prioridade ou a tratam incorretamente
Para verificar se as diretivas de configuração do OpenSSH foram definidas como esperado, não examine manualmente o arquivo de configuração; despeje a configuração interna derivada com
sshd -Toussh -Gsudoersfunciona da mesma formaEm software de autenticação/autorização de usuários, considero esse comportamento desejável
Isso porque fica fácil criar configurações que não podem ser sobrescritas simplesmente adicionando um novo arquivo em um diretório
whatever.conf.dBasta definir essa configuração no arquivo principal antes de carregar
whatever.conf.d/*e aplicar proteções especiais a esse arquivoMesmo sem alguém tentando burlar controles, isso é vantajoso para gerenciamento de configuração, porque a configuração-base mantém prioridade mesmo que uma pessoa novata, sem entender todo o contexto, adicione um novo arquivo, ou que algum pacote instale padrões estranhos para seu próprio serviço
O motivo de vermos o comportamento oposto com mais frequência em outros contextos é que o que se quer não é uma “configuração-base”, mas sim valores padrão no sentido estrito, usados quando usuários/desenvolvedores/administradores não configuraram algo explicitamente
AllowUsersSó que ontem o NixOS mudou de repente a ordem de mesclagem, e meu
AllowUsersacabou ficando abaixo de umMatchem outro arquivo, me deixando bloqueadoA frase mais importante e concisa do texto é a explicação para lembrar que, no encaminhamento local com
-L, o local fica à esquerda do endereço, e, no encaminhamento remoto com-R, a porta local fica à direita do endereçoDesde o começo eu me confundia com
-Le-R, e é bem irritante que a instância de porta que é local mude conforme L/REntendo que
-Le-Rmudam a direção pretendida, ou seja, onde estão o iniciador e o respondente, mas acho que também teria sido razoável fazerport:address:portsignificar semprelocal:binding:remotee deixar-L/-Rdeterminar qual lado escuta e qual lado envia-Lescuta na porta local cujo número vem logo depois, e-Rescuta na porta remota cujo número vem logo depoisO restante,
host:port, é apenas o formato comum que informa para onde conectarComo se trata de encaminhamento de porta por meio de um túnel SSH, também decorre naturalmente que o host é contatado do lado oposto do túnel em relação ao lado onde está a porta de escuta
Um recurso pouco conhecido, mas útil, do SSH é a multiplexação de conexões
Em vez de criar uma nova conexão TCP e passar de novo pelo processo de autenticação, você pode reutilizar uma conexão existente
O próprio protocolo tem o conceito de canais, e cada quadro de dados recebe metadados que distinguem diferentes streams; esse recurso usa isso
A grande vantagem é que, nas sessões seguintes, não é preciso refazer toda a autenticação
É especialmente bom quando não há
tmuxou algo do tipo no remoto e você usa vários painéis em várias janelas de terminal; se a autenticação envolver etapas que levam alguns segundos, como uma frase secreta ou tocar em um HSM, dá para sentir ainda mais a diferençaTambém há uma configuração de “persistência de conexão”, para que você não precise se autenticar toda vez que alternar entre alguns servidores
No geral, vejo como um recurso bom de ter, mas não a ponto de mudar a vida
Em alguns servidores ele fica desativado; a ausência é mais perceptível quando está desativado do que a presença quando está ativado
Veja mais: https://en.wikibooks.org/wiki/OpenSSH/Cookbook/Multiplexing
Ao contrário do TLS, que é otimizado para reduzir o número de idas e voltas, o SSH é um protocolo bem “falante”, e essa opção de multiplexação é praticamente a única exceção
ProxyJumpSe você tem muitos hosts em
~/.ssh/config, pode evitar que o arquivo fique bagunçado demais usando a diretivaInclude, que aceita curingasPor exemplo, você pode colocar
Include config.d/*.confem~/.ssh/confige separar as configurações dehost,hostnameeuserem arquivos como~/.ssh/config.d/work.confouclient1.confHosttambém aceita curingasPor exemplo, você pode adicionar
host *_worke colocar ali configurações comuns a todos os hosts de trabalho, comohost1_workIncludedentro de diretivasHost/MatchPor exemplo, se você tiver
Host proj1.*.corpeInclude ~/.ssh/proj1.confem~/.ssh/config, pode manter os matches por projeto perto do topo e reduzir a necessidade de vasculhar inúmeros arquivos individuais ao revisarAo fazer forwarding, quase nunca uso
-fEle pode virar uma arma apontada para o próprio pé, porque dificulta saber quais forwardings ainda estão abertos e em funcionamento
-té um truque bacana, e era um recurso que eu não conheciaUm ponto importante que é fácil deixar passar na lista de comandos de escape com
~é que você também pode aninhar escapes dentro de sessões aninhadasIsso é útil quando, por algum motivo, você não usa
-JA lista combina bem com coisas úteis, e também aprendi mais algumas
-té excelente. Eu uso algo comossh -t my-dev-vps 'tmux new-session -A -s main'para voltar direto ao ponto anterior da sessão tmux toda vez que executoSeria bom se o SSH expusesse o estado do forwarding de uma forma razoável, em vez de obrigar a vasculhar manualmente a lista de processos
Há um pull request atual que adiciona suporte a
AF_UNIX; se ele entrar, vai permitir todo tipo de forwarding interessanteIsso porque ficará mais fácil fazer proxy de conexões SSH por meio de processos locais arbitrários, e esse processo poderá cuidar, como quiser, de encaminhar os dados até a outra ponta remota
https://github.com/openssh/openssh-portable/pull/431
-DusandoAF_UNIX, mas é bom que tudo possa passar a funcionar sobreAF_UNIXParece que, desde cerca de um ano atrás, o
curlconsegue usar SOCKS sobreAF_UNIXpor meio da sintaxeALL_PROXYsocks5://localhost/pathousocks5hAparentemente isso foi adicionado porque o Tor usa proxy SOCKS em
AF_UNIXSeria bom poder configurar acesso à rede com permissões Unix padrão; pessoalmente, seria ainda melhor se desse para expulsar completamente o TCP/IP do kernel
Fiquei chocado quando vi o console do SSH pela primeira vez
Um colega me mostrou
~#, e foi como descobrir um menu secreto de cheats que poderia aparecer em um jogo do SEGA GenesisPor que til? Porque
rloginershusavamPor que
rloginershusavam til? PorquecuusavaPor que
cu? Quando havia um modem ou uma linha serial, você se comunicava comcue precisava enviar códigos Hayes; se usasse a sequência de escape dos códigos Hayes, cairia no modem, então era necessário um sinal separado para escapar docuPor que não
^[? Porque isso é do telnetPortanto, se você se conectasse a um host via telnet e depois usasse
cupara se conectar a um modem, precisava de uma sintaxe de escape separada para voltar aocusem escapar para o telnetNo fim, é uma estrutura de sintaxes de escape empilhadas sem fim
E, na prática, não é til; é til
CR, til,.; será que eu estava usando errado esse tempo todo?A seção sobre
ssh-copy-idcomeça explicando que o comando envia a chave pública, mas de repente passa a dizer que envia a chave privada; parece ser um erro de digitaçãoAlém disso, esse comando não apenas faz upload da chave: ele a acrescenta a
~/.ssh/authorized_keys, o que o torna muito mais útilPor fim, na seção sobre
ssh-keygen, pelo que tenho lido ultimamente,ed25519é preferido aecdsa