...

Funcionalidades do Linux: distribuir os direitos de root de forma segura e granular

Com o Linux Capabilities, divido os direitos de root em privilégios pequenos e claramente definidos, reduzindo assim drasticamente o risco. Desta forma, controlo de forma específica quais os processos que podem executar ações específicas e limito a superfície de ataque de cada aplicação.

Pontos centrais

  • De granulação fina Em vez de ser todo-poderoso: dividir os direitos de root em privilégios mais específicos.
  • Capacidades dos ficheiros Em vez de Set-UID: associar os direitos necessários diretamente aos ficheiros binários.
  • Conjuntos de capacidades Configurar de forma específica os parâmetros: Permitted, Effective, Inheritable e Bounding.
  • Separação de privilégios: Separar rigorosamente os serviços, as ferramentas e as tarefas.
  • Defesa em profundidade: Complementar as capacidades com o sudo, funções e registos.

Por que é que se deve desativar os direitos de root?

Uma conta de root concede Acesso total no ambiente de ficheiros e processos, mas é precisamente isso que dá azo a erros com consequências graves. Basta um comando errado ou um exploit para que toda a instalação entre em colapso. Por isso, limito as ações de grande alcance ao estritamente necessário, reduzindo assim a extensão dos danos e o tempo de recuperação. O princípio dos direitos mínimos mantém os serviços compactos e controláveis. Desativo o início de sessão direto como root, recorro a funções e mantenho registos completos.

Uma breve explicação sobre as capacidades do Linux

As capacidades do Linux dividem os poderes clássicos do utilizador «root» em categorias claramente definidas Privilégios. Cada processo recebe apenas os componentes de que realmente necessita para a sua tarefa, como a ligação a portas inferiores a 1024 ou o envio de sinais especiais. Desta forma, contorno o antigo princípio de «tudo ou nada». O kernel gere estes componentes por processo e aplica-os de forma rigorosa. Desta forma, o controlo mantém-se detalhado e transparente.

Do ponto de vista técnico, associo as competências a Processos (através dos seus conjuntos de capacidades) ou em Arquivos (como atributos estendidos capacidade de segurança nos ficheiros binários ELF). No execve()-Ao iniciar, o kernel combina as capacidades do ficheiro com os conjuntos de direitos do processo: em termos simples, as capacidades permitidas do atributo do ficheiro, juntamente com os direitos herdáveis do processo que o invoca, são combinadas para formar o novo conjunto «Permitted» e — se assim estiver definido — ativadas simultaneamente no conjunto «Effective». Isto evita desvios relacionados com o Set-UID e mantém os privilégios visíveis e verificáveis.

Compreender os conjuntos de capacidades no contexto do processo

Cada processo possui vários conjuntos de direitos, que eu defino de forma específica controlo. O «Permitted-Set» define o que um processo pode, em princípio, possuir. O «Effective-Set» determina o que está ativo no momento. O «Inheritable-Set» regula quais os privilégios que podem ser transmitidos aos processos filhos. O «Bounding-Set» estabelece um limite máximo rígido e impede que os processos o ultrapassem.

Ambient Capabilities e Securebits

Para além dos conjuntos já conhecidos, existe o Conjunto de música ambiente, que no execve() não expira automaticamente. Utilizo-o quando um processo sem privilégios necessita especificamente de direitos mínimos em vários executar-deve ignorar esses saltos (por exemplo, ao chamar programas auxiliares externos). Os direitos «ambient» só são tidos em conta nos direitos efetivos se o próprio ficheiro chamado não definir quaisquer capacidades de ficheiro – é assim que evito uma escalada indesejada.

Com os Securebits controlo os detalhes das transições, por exemplo, se um processo pode manter as capacidades que tinha definidas anteriormente após a alteração do UID (keepcaps) ou se, de um modo geral, não lhe é permitido obter novos privilégios (no_new_privs). Na prática, configuro o Securebits de forma restritiva e abdico do conforto para interromper cadeias de explorações.

Capacidades de ficheiro em vez de Set-UID

Substituo os ficheiros binários com Set-UID por capacidades de ficheiro, para reduzir o risco de baixar. Em vez de conceder direitos de root a um programa, limito-me a atribuir apenas os direitos necessários. Uma alteração típica é a seguinte: setcap 'cap_net_bind_service=+ep' /usr/bin/meinserver. Com getcap -r / verifico quais são os ficheiros que contêm competências. Isso reduz sensivelmente as vias de escalamento.

É importante que as capacidades dos ficheiros se apliquem apenas a Ficheiros binários ELF funcionam. Os scripts de interpretador (por exemplo, Python, Bash) não os herdam de forma fiável. Nesses casos, encapsulo a ação privilegiada num pequeno programa auxiliar verificado estaticamente ou utilizo a ativação por socket, para que o meu serviço nem sequer tenha de estabelecer a ligação por si próprio. Além disso, mantenho-me atento aos direitos de ficheiro: as capacidades conferem direitos especiais em relação ao kernel, mas substituem nenhum ACLs habituais ou permissões POSIX.

Ao copiar ou empacotar, as competências perdem-se rapidamente: cp sem suporte para XATTR, definido incorretamente umask ou eliminar um artefacto de compilação num sistema de ficheiros sem atributos estendidos capacidade de segurança de forma implícita. Por isso, trabalho de forma reproduzível e utilizo: cp --preserve=xattr ..., tar --xattrs, rsync -X. Nas compilações de pacotes, defino explicitamente as capacidades dos ficheiros no script de instalação, testo a instalação numa VM limpa e verifico getcap no CI.

Separação de privilégios com cenários realistas

Um servidor Web precisa de acesso às portas 80/443, mas não a módulos do kernel nem a reinicializações do sistema; por isso, defino CAP_NET_BIND_SERVICE e nada mais. Um agente de cópia de segurança pode ler e gravar ficheiros, mas não pode alterar a configuração da rede. Uma ferramenta de monitorização tem acesso de leitura aos indicadores, mas não possui direitos de alteração. Estas restrições limitam os ataques ao âmbito local, em vez de lhes permitir afetar todo o sistema. É precisamente esta separação que mantém os serviços controláveis e mantém as falhas de configuração sob controlo.

Combinar com o sudo e as funções

As capacidades não substituem uma abordagem bem organizada Estrutura de funções, complementam-nas. Concedo direitos de sudo com rigor, utilizo caminhos completos de comandos e evito regras genéricas como „ALL=(ALL) ALL“. Registo cada concessão de acesso. Os grupos agrupam responsabilidades, enquanto as capacidades estabelecem limites técnicos nos processos. Desta forma, criam-se competências claras sem direitos excessivos.

Armadilhas frequentes e melhores práticas

  • Não utilizar CAP_SYS_ADMIN como abreviatura: Este direito é um conceito abrangente. Substituo-o por alternativas mais específicas (por exemplo,. CAP_SYS_CHROOT, CAP_SYS_TIME, CAP_SYS_NICE) ou não o faças de todo.
  • Os direitos sobre os ficheiros permanecem rigorosos: As capacidades não anulam o DAC de forma geral. Sem CAP_DAC_OVERRIDE O kernel continua a respeitar os bits de proprietário e de modo. Por isso, continuo a atribuir direitos de leitura de forma mínima.
  • Endurecimento por via: Se eu atribuir capacidades de ficheiro a um ficheiro binário, evito a falsificação do PATH (caminhos absolutos em sudoers, direitos de escrita bloqueados em diretórios no caminho de pesquisa).
  • Começa cedo, começa com frequência: Os processos podem, se for o caso, ser iniciados com mais direitos do que o necessário. Retiro as autorizações desnecessárias imediatamente após a etapa crítica (prctl()/libcap) e defina no_new_privs, sempre que possível.
  • Limitar a herança: Mantenho os conjuntos «Inheritable» e «Ambient» reduzidos. Os processos filhos não podem abrir novas portas.
  • Verificar o pipeline de compilação e implementação: Confirmo que capacidade de segurança seja mantido e que nenhuma etapa de preparação (camadas de contentores, NFS, scanner de artefactos) remova os XATTRs.

Visão geral das principais capacidades e riscos

Antes de atribuir os privilégios, defino claramente os privilégios necessários e avalio o risco que representam. A tabela seguinte apresenta exemplos típicos, com os respetivos efeitos e classificação. Tenho sempre em conta alternativas, para evitar direitos excessivos. Especialmente CAP_SYS_ADMIN Concedo-os com extrema moderação. Sempre que possível, substituo privilégios de âmbito alargado por variantes específicas e restritas.

Capacidade Objetivo Risco Exemplo
CAP_NET_BIND_SERVICE Ligar a portas < 1024 Baixo a médio Servidor Web nas portas 80/443
CAP_SYS_BOOT Reiniciar o sistema Elevado Reinício programado
CAP_SYS_MODULE Carregar/remover módulos do kernel Muito elevado Gestão de controladores
CAP_SYS_ADMIN Operações administrativas versáteis Muito elevado Várias tarefas de manutenção
CAP_SETUID / CAP_SETGID Alterar o UID/GID Médio a elevado Transferência de direitos no serviço

Para além da tabela, estou neste momento a avaliar CAP_SYS_PTRACE (Depurar processos), CAP_NET_ADMIN (parametrização da rede) e CAP_DAC_OVERRIDE (Contornar restrições de acesso a ficheiros) é extremamente crítico. Muitas vezes, existem padrões que evitam esses direitos: pontos finais de métricas dedicados em vez de «process snooping», ativação de sockets ou reencaminhamento de portas em vez de direitos de ligação e direitos de ficheiros bem definidos em vez de contornar o DAC de forma generalizada.

Fortalecimento em contentores e alojamento

Em ambientes multi-tenant, considero que as competências são radicalmente pequeno e evito a herança em processos filhos. Os contentores beneficiam significativamente assim que o conjunto delimitador (bounding set) estiver bem definido. Combino isto com espaços isolados do sistema de ficheiros e dos processos. Para ter uma visão geral das abordagens de isolamento, esta introdução ajuda-me a Isolamento do processo. Desta forma, os serviços permanecem separados, mesmo que uma aplicação apresente falhas.

Na prática, defino os contentores, por predefinição, para „eliminar tudo, adicionar seletivamente“: --cap-drop=ALL --cap-add=NET_BIND_SERVICE para serviços Web, sem direitos de montagem, sem SYS_ADMIN. Em ambientes orquestrados, mantenho o perfil centralizado e verifico-o nas políticas. Importante: não confio nas capacidades dos ficheiros na imagem, mas atribuo direitos em tempo de execução no Orchestrator – de forma reproduzível e auditável.

Interação com o SELinux e o AppArmor

As capacidades determinam o que um processo pode fazer, enquanto os perfis MAC definem a que recursos ele tem acesso, e ambos funcionam em sintonia bom. Defino as capacidades de forma restrita e deixo que o SELinux ou o AppArmor limitem o acesso a ficheiros e sockets. Isto cria uma proteção em camadas que coloca vários obstáculos aos exploits. Encontro aqui uma comparação rápida: SELinux vs. AppArmor. Desta forma, um serviço comprometido fica isolado e pode causar menos danos.

Na prática: proceder passo a passo

Começo por fazer um inventário de todos os serviços e das suas Requisitos. Em seguida, elimino os ficheiros binários Set-UID desnecessários ou substituo-os por capacidades de ficheiro específicas. Configuro o `sudo` de forma restritiva e documento cada entrada. Atribuo tarefas a papéis e grupos e mantenho os direitos ao mínimo. Por fim, realizo testes sob carga e verifico as entradas de registo para detetar rejeições inesperadas.

Uma lista de verificação concisa ajuda-me na transição:

  • Definir por escrito os requisitos para cada serviço (apenas o que é realmente necessário).
  • Fazer um inventário dos direitos especiais existentes (find / -perm -4000, getcap -r /).
  • Substituir de forma seletiva: eliminar o Set-UID, definir as capacidades dos ficheiros, retirar os direitos numa fase inicial.
  • Fechar heranças: otimizar o conjunto delimitador, minimizar o Inheritable/Ambient.
  • Proteger os perfis do Systemd e dos contentores (CapabilityBoundingSet=, NoNewPrivileges=yes).
  • Realizar testes sob carga, verificar os registos e as entradas de auditoria, documentar as exceções.

Monitorização, espaços de nomes e auditorias contínuas

Monitorizo ficheiros de registo, alarmes e chamadas de sistema, para que quaisquer ações indesejadas sejam imediatamente destacar-se. Verifico regularmente as alterações nas capacidades, nas regras do sudo e nas funções. Sempre que faz sentido, isolo adicionalmente as cargas de trabalho através de mecanismos de isolamento do kernel. Esta visão geral sobre o assunto constitui um bom ponto de partida para Espaços de nomes e cgroups. Assim, consigo detetar anomalias atempadamente e manter o ambiente limpo.

No dia-a-dia, recorro a testes simples: capsh --print mostra-me o conjunto atual de competências, getpcaps enumera os direitos processuais e em /proc//status leio CapEff, CapPrm, CapBnd. Com auditado Acompanho as alterações no estado da capacidade (por exemplo, regra em capset), correlaciono eventos com implementações e defino alertas caso surjam, de repente, direitos de acesso elevados. Em casos complexos, o que me ajuda é strace -e capget,capset, para tornar visíveis as manipulações de direitos.

Exemplos práticos do Systemd e de contentores

Muitos serviços são executados por mim como unidades do systemd e é aí que encapsulo os direitos:

  • CapabilityBoundingSet=CAP_NET_BIND_SERVICE reduz a janela de direitos acessíveis ao mínimo indispensável.
  • AmbientCapabilities=CAP_NET_BIND_SERVICE concede ao serviço o direito de se ligar às portas 80/443 sem «file-capabilities».
  • NoNewPrivileges=yes impede futuras extensões de direitos.
  • Utilizador=, Grupo=, ProtectSystem=strict, PrivateTmp=sim completam o isolamento.

Nos contentores, inicio os processos da forma mais minimalista possível: docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE --read-only. Para tarefas de curta duração, utilizo as capacidades de tempo de execução em vez das capacidades de ficheiro na imagem, para que as compilações se mantenham reproduzíveis e os direitos fiquem associados ao ambiente.

Casos concretos de migração na prática

  • ping sem Set-UID: Em vez de setuid root coloco setcap 'cap_net_raw=+ep' /bin/ping. Desta forma, qualquer utilizador pode abrir sockets ICMP sem ter direitos de root totais. Verifico regularmente com getcap /bin/ping, se o atributo foi mantido.
  • Serviço Web nas portas 80/443: Executo o meu serviço como utilizador sem privilégios e apenas forneço cap_net_bind_service. Se o serviço já estiver ligado a um proxy reverso, posso, em alternativa, ligá-lo às portas 80/443 nesse proxy e utilizar internamente uma porta de alto número – sem necessidade de conhecimentos adicionais.
  • Alteração das partes no processo: Para ferramentas que necessitam temporariamente de direitos de acesso alargados (por exemplo, para definir níveis de prioridade), defino cap_sys_nice, realiza a ação logo no início e, em seguida, desativa a habilidade. Evito aumentar os direitos de forma permanente.

Limites e alternativas

Nem todos os casos de utilização exigem «capabilities». Muitas vezes, existem alternativas seguras com menor risco:

  • Ativação do soquete: O serviço de inicialização (por exemplo, o systemd) abre sockets privilegiados e passa-os para o processo. Assim, o meu serviço não precisa de direitos de ligação.
  • Reencaminhamento de portas: Através de regras de firewall, redireciono as portas 80/443 para uma porta alta. O serviço continua sem privilégios e o comportamento do sistema não se altera.
  • Portas de baixo nível não privilegiadas: Sempre que for adequado, posso aumentar o limite para as portas não privilegiadas. No entanto, isso alarga a margem de manobra para todos os processos – por isso, avalio cuidadosamente o risco e a conveniência.
  • Pequenos ajudantes em vez de «faz-tudo»: Prefiro um binário minúsculo e auditado com exatamente uma funcionalidade do que um grande monólito com um vasto leque de funcionalidades.

Brevemente resumido

Com Funcionalidades do Linux Divido os poderes de root em pequenos privilégios fáceis de controlar. As capacidades de ficheiro substituem os binários Set-UID de risco e reduzem as consequências de um ataque. Em combinação com regras rigorosas do sudo, funções e perfis MAC, cria-se uma proteção em camadas com limites claros. Os conjuntos «Bounding» e «Inheritable» limitam a herança e mantêm os processos no caminho certo. Quem procede desta forma reduz significativamente as vulnerabilidades e mantém a carga administrativa a um nível razoável.

Artigos actuais