SSH como tecnologia substituta do sudo
(whynothugo.nl)sudoedoasdependem de binários setuid e de elevação de privilégios, então esta é uma experiência que delega a execução de comandos como root a umsshdlocal vinculado a um socket de domínio Unix- O objetivo é fazer com que apenas usuários autorizados executem comandos como root, sem deixar a capacidade de elevação de privilégios disponível para toda a sessão do usuário
- A implementação consiste em um arquivo de chave SSH exclusivo para root, um
/run/sshd/sshd.sockcom acesso restrito e uma instância separada desshdcom as opçõesAuthorizedKeysFileePermitRootLogin=yes - Como o
sshnão tem uma opção para receber diretamente um socket já existente, no início foram usadosProxyCommandesocat; depois, ProxyUseFdpass e um pequeno script em Python passaram o descritor de arquivo do socket - Essa abordagem funciona, e o tratamento de segurança depende principalmente do OpenSSH, mas para uso diário é mais adequado transformar o
passfd.pyem um pequeno executável e envolver todo o comandosshem um wrapper
Restrições estruturais do sudo e do doas
sudoedoasdependem de bináriossetuide de elevação de privilégios para executar comandos como root- Esse desenho tem algumas limitações
- Toda a sessão do usuário precisa manter a capacidade de realizar elevação de privilégios
- Não funciona ao executar toda a sessão do usuário dentro de um namespace de usuário restrito
- Binários
setuidimpõem restrições à configuração de segurança do sistema como um todo
s6-sudodé uma alternativa que divide o programa entre um servidor com privilégios e um cliente sem privilégios
Objetivo do experimento
- Usar
sshlocalmente para cumprir o papel dosudo, mas sem expor essa instância desshdà rede - Há duas condições centrais
- Somente usuários autorizados podem executar comandos como root
- Não usar elevação de privilégios
Configuração do sshd para login local como root
- É criada uma chave SSH dedicada apenas para autenticação de root, armazenada em
/root/.ssh/local_keysem vez doauthorized_keyscomum - Uma instância separada de
sshdé vinculada a um socket de domínio Unix, e as permissões de/run/sshd/são restringidas para impedir que usuários não autorizados acessem o socket - O exemplo de execução cria
/run/sshd/sshd.sockcoms6-ipcservere passa aosshdas seguintes opçõesAuthorizedKeysFile=/root/.ssh/local_keysPermitRootLogin=yes
- O
/etc/ssh/sshd_confignão é alterado- Porque não se quer permitir login de root no
sshdjá existente, vinculado à rede - Na configuração atual,
PermitRootLogin noé mantido
- Porque não se quer permitir login de root no
Bloqueio da conta root e tratamento do login por senha
- A conta root estava bloqueada para impedir login por qualquer meio, usando um
!antes do hash da senha - O
sshdinterpreta esse prefixo!como conta bloqueada e não permite login de root - O valor da senha de root em
/etc/passwdé alterado, trocando!por*- O
sshdnão interpreta*como um valor especial de bloqueio - Como
*não corresponde a nenhum hash de senha, o login por senha continua efetivamente desativado
- O
- Além disso,
PasswordAuthentication noestá definido nosshd_config, e é mais seguro desabilitar a autenticação por senha nosshd
Conectando ao socket Unix com ssh
- O
sshdtem a flag-ipara receber um socket já existente, mas osshnão possui uma flag equivalente - No início, usou-se
socatcomProxyCommandpara conectar a/run/sshd/sshd.sock - O comando de conexão era composto pelos seguintes elementos
ProxyCommand='socat STDIO UNIX-CONNECT:/run/sshd/sshd.sock'- A chave root dedicada é especificada com
-i .ssh/root-key.pub - Conexão para
root@root - Mudança para o diretório atual e, em seguida, execução de um shell de login
- A chave SSH usada era uma chave vinculada a hardware, então era preciso tocar fisicamente o dispositivo para aprovar a conexão
- Um
ssh-agentque só expõe a chave após aprovação explícita, como ohissh-agent, também pode ser uma alternativa
Overhead do socat e ProxyUseFdpass
- O
socatlê toda a entrada dosshe só então escreve no socket, então o overhead da conexão acaba sendo praticamente duplicado ProxyUseFdpassfaz com que o comando envie o descritor de arquivo do socket aosshporstdout, e então osshse conecta usando esse socket- Com base em um exemplo de uso do OpenSSH ProxyUseFdpass de 2016, foi escrito o script em Python
passfd.py- Ele se conecta ao socket de domínio Unix
/run/sshd/sshd.sock - Usa
sendmsgeSCM_RIGHTSpara passar o descritor de arquivo
- Ele se conecta ao socket de domínio Unix
- Depois disso, o comando de conexão passou a usar
passfd.pyemProxyCommande adicionarProxyUseFdpass=yes nc -FU /run/sshd/sshd.socktambém parece possível, mas o manual diz explicitamente que-Fnão pode ser usado junto com-U
Conclusão e forma de uso no dia a dia
- Essa técnica funciona, e o tratamento sensível de segurança fica principalmente a cargo do OpenSSH
- O OpenSSH tem um bom histórico e permite usar vários métodos de autenticação, inclusive chaves SSH baseadas em hardware
- O processo de configuração em um novo host não envolve etapas complexas, e o comando
ipcserverpode ser executado por meio do gerenciador de serviços do sistema - O
passfd.pyé mais um hack rápido para viabilizar o experimento - Para uso diário, é mais adequado criar um pequeno executável com a mesma função, colocá-lo em
/usr/local/bine também envolver todo o comandosshem um pequeno wrapper
1 comentários
Opiniões do Hacker News
O principal motivo para ser contra essa abordagem é o aumento da complexidade. Em vez de um único binário
suidque lê um arquivo de configuração e chamaexec(), passa a haver um binário rodando como root e escutando em um socket UNIX, e outro binário que fala com esse socket; ambos ainda precisam lidar com criptografia assimétricaSe a crítica central ao sudo/doas é “há um binário
suidacessível por todos os usuários e, se houver um bug, ele pode ser explorado para escalonamento de privilégios”, basta fazer algo como abaixochgrp wheel /usr/bin/sudochmod o-rwx /usr/bin/sudoAo colocar usuários do sudo no grupo
wheel, apenas usuários que podem usar sudo conseguem ler os bytes do arquivo no disco e também executá-lo. Em termos de controle de acesso, a segurança é quase a mesma que na abordagem com sshd, em que só usuárioswheelpodem acessar o socket UNIX, mas a complexidade é muito menorAlém disso, a abordagem com sshd não consegue limitar o acesso root a comandos específicos como o sudo faz; portanto, mesmo que exista um bug que contorne as restrições de comando do sudo, isso não concederia mais privilégios do que a abordagem com sshd
Se a preocupação é que o gerenciador de pacotes estrague as permissões de
/usr/bin/sudo, dá para corrigi-las periodicamente com cron, ou remover o sudo completamente e instalá-lo manualmente a partir do código-fonte em outro local. Claro que, nesse caso, a manutenção e as atualizações também ficam por sua conta/etc. Mudanças feitas por instalação/atualização de software ou por pessoas ficam registradas, e ao atualizar para uma nova versão também é útil poder gerar um patch das mudanças locais, aplicá-lo sobre uma instalação limpa e verificar conflitos com um merge de 3 viashttps://etckeeper.branchable.com/
wheelpara que apenas usuários com sudo possam ler e executar o arquivo parece bem razoável. Fico curioso por que isso não é a configuração padrão em todos os lugares/usr/bin/sudoimmutable? Acho que isso ajudaria a impedir que o gerenciador de pacotes mexesse neleNão é isso que o systemd run0 faz hoje? Há uma nova ferramenta no systemd chamada
run0, mas na verdade ela não é exatamente uma ferramenta totalmente nova; é mais um modo em que o antigosystemd-run, quando chamado por um link simbólico com o nomerun0, se comporta como um substituto do sudoA diferença central é que, de fato, ele não é
SUID. Ele pede ao gerenciador de serviços que execute um comando ou shell com o UID do usuário de destino, aloca um novo PTY e então encaminha dados entre o TTY original e esse PTYEm outras palavras, o comando de destino não herda o contexto do cliente; ele roda em um contexto de execução isolado, recém-forkado a partir do PID 1.
$TERMé repassado, mas isso é uma exceção explícita, mais próxima de uma lista de permissões do que de uma lista de bloqueioEm vários aspectos, dá para dizer que
run0se comporta mais comosshdo que comosudohttps://mastodon.social/@pid_eins/112353324518585654
Será que estou deixando passar alguma coisa? Não entendo como fazer login SSH como root seria mais seguro do que usar sudo. Sempre aprendi que isso não deve ser permitido, então também não sei ao certo o quão perigoso isso realmente é
Aqui parece haver uma preocupação em bloquear usuários remotos, então não estou falando desse aspecto de segurança
Pode ter relação com a limitação nº 3 do sudo, mas, pelo menos em comparação com a nº 1, não vejo vantagem
Entendo que é um experimento, mas parece que seria mais vulnerável, não menos, do que o sudo. Um proxy de socket aberto parece vulnerável a ataques man-in-the-middle
Ainda assim, aprendi algumas técnicas que dá para fazer com ferramentas antigas, e foi bom ver algo novo
sudoé suid root e, por ser um binário privilegiado, fica diretamente exposto a usuários não confiáveis. Se algo der errado dentro do sudo, todo o ambiente do usuário vira superfície de ataque e pode ser exploradoA abordagem com ssh não expõe um binário
suide usa a camada de rede do ssh. Portanto, não é menos segura do que acessar ssh pela rede, algo considerado bastante segurosudo bash, você pode executar comandos individuais com sudo e aumentar a auditabilidadeEsse daemon também pode ter erros de configuração ou vulnerabilidades, e reduz várias camadas de autorização baseadas em usuário a um único nível root
Mesmo assim, parece ser considerada mais segura. De uma perspectiva de segurança razoável, não dá para chamá-la de mais segura; é apenas uma abordagem diferente
Na prática, fazer login SSH diretamente como root é estritamente mais seguro do que entrar como usuário via SSH remoto e depois usar
sudoSe
user@homefaz ssh pararoot@server,root@serversó é comprometido seuser@homefor invadidoPor outro lado, se
user@homefaz ssh parauser@servere depois usa sudo para virarroot@server, basta queuser@homeouuser@serverseja invadido para que o root seja comprometido. Em especial, emuser@server, outros softwares como daemons ou tarefas cron costumam estar rodandoNão se deve dar a alguém que conseguiu infectar por esse caminho uma escalada de privilégios root de graça, nem o movimento lateral que ocorre com frequência por causa da reutilização de senhas
Claro, isso não se aplica quando o sudo é usado em modo restrito a comandos em lista de permissões e não recebe senhas ou credenciais totalmente acessíveis a partir do host remoto
O que acontece se o ssh não iniciar durante o boot? Pelo que lembro, numa configuração comum ele começa dependendo da rede. Então também não dá para fazer login nem no console failsafe
Não sei o que se ganha de fato em comparação com sudo ou su. Em vez de evitar um binário setuid, você acaba executando um serviço de rede como root, mesmo que ele só se conecte a um socket
E se o SSD morrer? Nesse caso também não dá para fazer login no console failsafe
Em 30 anos usando Linux, discos rígidos morreram muito mais vezes do que o daemon sshd deixou de iniciar; em termos de proporção, é quase uma divisão por zero
Se o daemon sshd do sistema operacional parar de iniciar aleatoriamente, eu consideraria isso um sinal de que é hora de migrar para um sistema operacional mais estável
O que se ganha em relação a sudo ou su é que exploits locais de escalonamento de privilégios ficam muito mais difíceis
ttyS*de porta serial e IPMI SoL — não usa sudo nem suEsses consoles usam programas como
gettyou gerenciadores de janelas, que são programas não suid iniciados como rootFaz sentido manter uma senha de root configurada para login pelo console
kubectlem vez de sshSe alguém consegue fazer login, deixo essa pessoa poder fazer setuid para root. Se for uma caixa k8s, isso é a equipe de infraestrutura da plataforma, e o acesso aos serviços em cima dela passa pelo provedor de permissões do k8s
Do ponto de vista da equipe de infraestrutura da plataforma, se só forem necessários métricas e logs, eles já estão fora da caixa; se for preciso disparar alguma tarefa ou workflow, usa-se um pipeline
Ainda assim, se alguém fizer login e executar trabalho como root, quero que fique um log de auditoria
Não consigo pensar em nenhuma caixa minha em que alguém com permissão de login não tenha privilégios root completos
Claro que entendo casos em que um serviço faz setuid, mas, no caso de serviços, normalmente o systemd faz setuid para reduzir privilégios, não para elevá-los
systems.unit=emergency.target,init=/bin/bash,rd.break=pre-pivotou inicializar por um ambiente de live CD. Todas as opções comuns de recuperação de emergência continuam funcionandoNuma emergência menos catastrófica, também não vejo motivo para essa instância do sshd precisar estar vinculada à rede
É uma pena que o texto não inclua todas as desvantagens dessa abordagem. O sudo permite controlar quais grupos podem executar quais comandos, quais argumentos esses comandos podem receber, se é permitido criar subshells etc.
Esse método perde muito desse controle granular e depende de chaves confiáveis que são mais difíceis de administrar do que editar o arquivo sudoers
Recomendo muito o livro Sudo Mastery para ver as coisas surpreendentes que o sudo consegue fazer
A ideia é parecida com o run0 do Systemd: https://news.itsfoss.com/systemd-run0/
Um dos problemas do ssh é que a criação de processos não faz parte do protocolo. E, por ser um protocolo remoto, não dá para passar recursos locais para o processo filho
Por isso não é possível passar um array de argumentos separado por null, passar descritores de arquivo adicionais ou especificar o executável
Em vez disso, ele passa uma única string para o shell configurado no servidor. Isso exige escape de shell, e você também precisa saber qual shell está rodando no lado do servidor
Para usar SSH como um substituto adequado do sudo, seria necessária uma extensão com algo próximo de
posix_spawnhttps://bugzilla.mindrot.org/show_bug.cgi?id=2283
Concordo totalmente. Eu faço algo parecido e já expliquei isso antes em um comentário no HN
Minha abordagem é um pouco diferente. Uso uma máquina dedicada como console SSH físico, e essa máquina fica numa LAN privada separada das outras máquinas de casa. Não é um switch gerenciável, é um switch comum; usa cabos Ethernet e não há trunk
O login só é possível por SSH, e conectei uma Yubikey a ela
O PC desktop tem seu próprio firewall e só permite tráfego SSH vindo do endereço IP/MAC desse console SSH. Isso vale apenas nessa LAN privada compartilhada entre os dois; em outra LAN física, o desktop consegue acessar a internet
O daemon sshd permite apenas login por chave pública/privada e bloqueia login por senha
Quando preciso de root, inicializo o “console SSH”. Como é uma máquina com quase nada, liga muito rápido. Faço login, uso a seta para cima para recuperar a linha
ssh root@..., pressiono Enter e toco na YubikeyEsse console SSH e o teclado ficam na mesa, sempre ao alcance da mão
Se iptables/nftables + sshd numa LAN privada, ainda por cima fisicamente separada de outras LANs privadas, é mais ou menos seguro do que o binário sudo ou o su, cada um que julgue
Se perguntarem “por quê?”, minha resposta é “porque dá”. Configurei isso há tanto tempo que nem lembro quando foi. Provavelmente comecei a brincar com essa ideia uns 2 anos atrás e venho usando desde então. Nunca tive problema algum
É uma solução elegante para esse problema. Não é preciso tratar usuários como crianças, mas ao mesmo tempo é bom evitar possíveis erros com padrões razoáveis
Se precisar de root, basta fazer login como root no console, então acho que nem
sué necessário. Essa abordagem é o mais próximo possível de fazer login como root num tty de consolePrimeiro, ao contrário do sudo, todos os usuários precisam saber a senha de root
Segundo, se todo mundo simplesmente fizer login como root, não há como auditar quem realmente entrou e fez o quê
Fiz algo parecido há 10 anos. Não havia a parte de socket UNIX; eu rodava um
sshdseparado que escutava apenas no localhost, e também não precisei lidar comSCM_RIGHTSNão houve nenhum resultado particularmente bom ou ruim; simplesmente perdi o interesse e não levei a configuração para a máquina seguinte