...

Capacidades do Linux em vez de Root: princípio da minimalidade para serviços de servidor

Eu fixo capacidades do Linux para operar os serviços do servidor de acordo com o princípio da minimização e, assim, atribuir apenas os direitos parciais absolutamente necessários. Desta forma, reduzo a Superfície de ataque perceptível, sem bloquear as funcionalidades.

Pontos centrais

  • Menor privilégio De forma coerente: os serviços recebem apenas as competências estritamente necessárias.
  • De granulação fina Em vez do Root: cerca de 40 a 50 capacidades substituem o acesso total.
  • Separação sobre processos: a separação de privilégios reduz os danos causados por explorações.
  • Ficheiro Funcionalidades: Associar direitos diretamente aos ficheiros binários.
  • Auditável: O getcap fornece uma visão clara dos privilégios especiais.

Por que é que o acesso de root é arriscado – e como as capacidades mudam isso

Antigamente, quase todos os serviços de servidor funcionavam com Direitos de raiz, o que, em caso de violação, poderia levar imediatamente à tomada de controlo do sistema. Hoje, distribuo os direitos de forma seletiva, utilizando capacidades como CAP_NET_BIND_SERVICE atribuir direitos para portas inferiores a 1024 e retirar todos os outros direitos avançados. Desta forma, o servidor Web pode estabelecer ligações, mas não pode carregar módulos do kernel nem alterar os proprietários dos ficheiros, o que Segurança aumenta significativamente. Uma separação clara das tarefas torna os ataques menos eficazes, porque um processo comprometido só pode realizar ações limitadas. Quem quiser introduzir mais estrutura neste conceito pode definir os direitos de forma muito precisa dividir de forma granular e, assim, restringir sistematicamente as operações críticas. Deste modo, um serviço «root» monolítico transforma-se num conjunto de serviços com competências mínimas e claramente definidas.

É assim que funcionam os conjuntos de capacidades no kernel

Cada processo possui vários Conjuntos de capacidades, que o kernel verifica em ações sensíveis. O «Effective-Set» determina o que um processo pode fazer imediatamente, enquanto o «Permitted-Set» contém o conjunto de direitos possíveis. Através do «Inheritable-Set», posso controlar o que, no execve() que são transferidas para processos filhos, o que é particularmente importante no caso de wrappers e scripts de arranque. O Bounding-Set define um limite máximo rígido, de modo que determinadas capacidades nunca mais podem ser obtidas, mesmo em caso de erros na aplicação. Com o Ambient-Set, atribuo capacidades sem SUID a programas normais e mantenho o Caminho de ataque pequeno. Em conjunto, estes conjuntos permitem-me um controlo muito preciso, que vai muito além do clássico «tudo ou nada» do UID 0.

Conjunto Objetivo Utilização típica Risco de configuração incorrecta
Eficaz Competências agora efetivas Verificação de cada operação privilegiada O processo pode ser demasiado intenso logo de início
Permitido Conjunto de competências permitidas Fonte do conjunto efetivo As reservas desnecessárias continuam disponíveis
Hereditário Habilidades hereditárias Transferência controlada na função execve() As crianças herdam direitos desnecessariamente
Delimitação Limite máximo de todos os direitos Definir exclusões permanentes É possível recuperar direitos importantes
Ambient Partilha sem SUID Os programas regulares recebem capacidades Concessão de direitos mais abrangente e discreta

Na prática, há dois aspetos adicionais que são importantes: em primeiro lugar, é fundamental Securebits sobre se um processo, após uma alteração do ID de utilizador (por exemplo, através de setuid()) mantém as suas capacidades. Com PR_SET_KEEPCAPS isso pode ser controlado de forma específica – procedimento típico: iniciar temporariamente como Root, criar os sockets ou recursos necessários, mudar o UID para um utilizador sem privilégios e manter apenas as capacidades necessárias. Em segundo lugar, aplica-se o seguinte: O Conjunto delimitador está definitivamente na linha de processo atual. Quem remover aqui, numa fase inicial do percurso de inicialização, as capacidades desnecessárias, não poderá, mais tarde, obter direitos „proibidos“, mesmo que ocorra uma configuração incorreta.

Controlar os direitos dos ficheiros com as capacidades de ficheiro

Em vez de um serviço permanente Direitos especiais Para evitar isso, prefiro ligá-las diretamente ao ficheiro binário. Através de setcap cap_net_bind_service=+eip /usr/bin/node permito a atribuição de portas sem que o processo tenha de ser executado como root. Com getcap /usr/bin/node ou recursivamente getcap -r / 2>/dev/null verifico a atribuição e mantenho o controlo. A remoção é feita através de setcap -r /caminho/para/o/binary, pelo que retiro os direitos temporários após a implementação. Durante a cópia, as capacidades perdem-se frequentemente, pelo que as protejo explicitamente durante a implementação, para Regressões para evitar isso. Desta forma, as compilações permanecem reproduzíveis e os direitos ficam sempre devidamente documentados.

As capacidades dos ficheiros existem sob a forma de atributos alargados (capacidade de segurança) no sistema de ficheiros. Isto pressupõe um sistema de ficheiros compatível e opções de montagem adequadas. Ferramentas como alcatrão e rsync é necessário incluir explicitamente os XAttrs (por exemplo,. tar --xattrs, rsync -XA), caso contrário, os direitos desaparecem silenciosamente. Os gestores de pacotes podem definir capacidades nas etapas pós-instalação; prefiro definir isso no processo de compilação/lançamento, para evitar surpresas nas atualizações. Outro aspeto crítico é que os scripts de interpretador (por exemplo, com shebang) não herdam as capacidades de ficheiro tal como os binários ELF. Capacidades poderosas em Intérprete Fazer isso é, de qualquer forma, arriscado – prefiro separar as funções e trabalhar com pequenos binários auxiliares dedicados.

O princípio da minimalidade aplicado aos serviços de servidor na prática

Inicio o servidor Web como utilizador sem privilégios e concedo exclusivamente CAP_NET_BIND_SERVICE, para que o processo possa ligar-se ao 80/443 e não haja mais Privilégios que proporciona. Continuo a gerir ficheiros e diretórios através de direitos POSIX, bem como, opcionalmente, de perfis MAC, o que permite que a configuração e os conteúdos permaneçam protegidos separadamente. Os agentes de monitorização ou de registo recebem direitos de rede específicos e direitos de leitura sobre os registos, mas não têm quaisquer permissões para alterar o sistema. Em ambientes de contentores, reduzo ainda mais o conjunto de capacidades e combino-o com filtros de chamadas de sistema, para restringir rigorosamente o comportamento. Esta combinação diminui o impacto de explorações bem-sucedidas e aumenta a Transparência dos poderes efetivos. Os serviços continuam a funcionar, mas a margem de manobra continua a ser reduzida.

Em vez de atribuir «Capabilities», por vezes elimino-as por completo: a ativação do socket disponibiliza listeners privilegiados (por exemplo, 443/tcp) através do processo «Init» e apenas passa o descritor de ficheiro aberto ao serviço. O processo da aplicação não necessita, então, de nenhum CAP_NET_BIND_SERVICE mais. Da mesma forma, é possível realizar antecipadamente ações de root pontuais (por exemplo, criar o diretório PID) e, em seguida, devolver os direitos de forma sistemática. Quanto menos capacidades de todo estiverem em jogo, mais resistente o sistema se mantém face a erros em cadeia.

Implementar corretamente a separação de privilégios

Divido serviços de grande dimensão em vários Subprocessos, cada um dos quais possui apenas as capacidades necessárias. Um processo front-end estabelece ligações TLS e liga-se a portas, mas não possui direitos no sistema de ficheiros para efetuar alterações críticas. Um processo de back-end processa dados internamente, tem direitos mínimos de leitura sobre a configuração e comunica com bases de dados sem capacidades de rede próprias. Tarefas administrativas, como a rotação de registos ou a manutenção, são executadas através de ferramentas dedicadas com capacidades limitadas no tempo. Se um atacante atacar uma parte do sistema, o resto do sistema permanece intacto, porque a Autorizações são definidos de forma restrita. Assim, a segurança adapta-se à estrutura da aplicação, em vez de depender de direitos de sistema ilimitados.

Para esta divisão, recomenda-se uma orquestração inicial clara. Em configurações clássicas, essa tarefa é desempenhada por um supervisor; nos sistemas atuais, prefiro utilizar o systemd, porque integra diretamente capacidades, cgroups e namespaces. Desta forma, posso iniciar o front-end de rede, os workers e as ferramentas de administração, cada um na sua própria sandbox, limitar os recursos e, em caso de falha, reiniciá-los automaticamente — sem nunca conceder direitos de root de forma generalizada.

Combinar controlos de segurança: POSIX, MAC e capacidades

As capacidades funcionam melhor quando as combino com as técnicas clássicas Direitos sobre os ficheiros e sistemas MAC. O SELinux ou o AppArmor podem restringir ainda mais as ações, apesar das capacidades atribuídas, criando assim uma proteção múltipla. Assim, embora um processo possa ligar-se a uma porta, é impedido por uma política de aceder a ficheiros sensíveis. Quem quiser aprofundar a diferença entre estas abordagens encontrará uma comparação clara em SELinux vs. AppArmor e pode, assim, escolher uma estratégia de políticas adequada. Em suma, resulta numa rede de defesa que bloqueia os ataques a vários níveis e que Superfície de ataque reduzida ainda mais. A atribuição de direitos continua, assim, a ser verificável, repetível e consistente.

A situação torna-se particularmente restritiva quando, além disso, eu NoNewPrivileges Ativar: os processos e os filhos não poderão, assim, adquirir novos privilégios (por exemplo, através de SUID ou de capacidades de ficheiro recém-definidas). Em conjunto com uma lista rígida de limitação de capacidades, cria-se uma barreira de segurança que impede a expansão posterior de privilégios, mesmo em caso de configuração incorreta.

Distribuir e auditar as capacidades de forma segura

Considero que o Conjunto de competências o mais pequeno possível e evita tudo o que pareça um „segundo root“, como, por exemplo, CAP_SYS_ADMIN. Interpretadores como o Python, o Perl ou os shells não dispõem de funcionalidades avançadas, uma vez que o seu leque de funcionalidades pode ser facilmente utilizado de forma indevida. Através de auditorias regulares sobre getcap -r / 2>/dev/null deteto anomalias e corrijo-as. Os ficheiros binários com capacidades estão protegidos contra gravação, pertencem ao utilizador «root» e não se encontram em caminhos que os utilizadores normais possam alterar. Além disso, verifico os meus próprios ficheiros binários antes de cada lançamento e documento as alterações, para que Revisão e a reprodução funcionem de forma fiável. Desta forma, a atribuição de direitos mantém-se controlável e as alterações permanecem compreensíveis.

Durante a execução, verifico os processos através de /proc//status (Campos CapEff, CapPrm, CapInh). Isto fornece os valores hexadecimais dos conjuntos ativos e mostra imediatamente se uma aplicação tem capacidades superiores às previstas. Ferramentas como capsh --print ou getpcaps facilitam a depuração. Com o subsistema de auditoria do Linux, registo também as alterações nas capacidades ou em capacidade de segurança-atributos dos ficheiros, para rastrear manipulações. Quem trata as capacidades como um objeto de configuração e submete as alterações a uma revisão rigorosa torna as auditorias reproduzíveis e simplifica a comprovação da conformidade.

Armadilhas comuns e como as evitar

Uma armadilha típica: ao copiar, Atributos perdidas, o que faz com que os serviços deixem subitamente de iniciar ou, pelo contrário, não sejam suficientemente restringidos. Por isso, garanto explicitamente as capacidades na compilação ou atribuo-as automaticamente na etapa pós-instalação. Outro erro é a utilização excessiva de capacidades genéricas, que abrem mais do que o necessário. É preferível definir capacidades específicas, tais como CAP_NET_RAW ou CAP_CHOWN utilizá-los apenas onde desempenham uma função real. Também utilizo o conjunto «Ambient» com moderação, para que não surjam efeitos indesejados Transmissão difundido. Quem reduz de forma seletiva e verifica regularmente evita falhas de segurança decorrentes de erros de operação.

Outro aspeto importante: eliminar sistematicamente os ficheiros binários SUID. Nos casos em que anteriormente era necessário o SUID (por exemplo, para enviar ICMP), muitas vezes é possível resolver a situação com CAP_NET_RAW trabalhar – ou, melhor ainda, externalizar a função para um processo auxiliar o mais pequeno possível, com um caderno de encargos muito restrito. Além disso, evito colocar „capabilities“ em caminhos temporários ou graváveis pelos utilizadores. Um regime rigoroso de propriedade e implementação (Root:root, 0755/0555, caminhos imutáveis) impede a «perda» de direitos devido à substituição de ficheiros binários.

Funcionalidades em contentores e DevSecOps

Em ambientes de contentores, reduzo a Capacidades de forma agressiva e elimino tudo o que a carga de trabalho não necessita obrigatoriamente. Além disso, crio um Perfil Seccomp que bloqueia chamadas de sistema arriscadas, criando assim mais um obstáculo. Nas pipelines de compilação, defino as capacidades de forma declarativa, testo-as no ambiente de teste e registo-as com controlo de versões. A conformidade beneficia com isto, pois posso comprovar a aplicação do princípio do privilégio mínimo e documentar de forma exaustiva as alterações de direitos. Desta forma, os contentores mantêm-se rigorosamente controlados, sem que isso prejudique as suas tarefas, e a Superfície de ataque mantém-se pequeno. Em combinação com imagens que contêm apenas o estritamente necessário, a segurança aumenta ainda mais.

Importante no contexto dos contentores: as capacidades encontram-se em namespaces Relativo. Dentro de um espaço de nomes de utilizador, um processo pode ser „Root“, mas as suas capacidades só se aplicam aos espaços de nomes associados – o que reduz significativamente o alcance dos danos. Por outro lado, „--privilegiado“praticamente sempre um tabu: desativa o limite rígido de delimitação e abre muito mais do que o necessário. Por isso, por predefinição, inicio os contentores com a opção „soltar tudo, adicionar seletivamente“ e complemento NoNewPrivileges, limites de cgroup e montagens em modo de só leitura. Para serviços que apenas precisam de estar à escuta, utilizo a ativação por socket ou sidecars, para evitar completamente o uso de capacidades adicionais.

Exemplo do Systemd: restringir capacidades de forma declarativa

Nas unidades de serviço, defino os limites máximos de um processo – de forma clara, repetível e sujeita a controlo de versões. Um exemplo conciso de um serviço web que só pode ligar-se à porta 443 e que, para além disso, está sujeito a restrições rigorosas:

[Unit]
Description=Serviço Web mínimo sem direitos de root

[Service]
User=web
Group=web
ExecStart=/usr/bin/my-web
AmbientCapabilities=CAP_NET_BIND_SERVICE
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
ReadWritePaths=/var/lib/my-web
RestrictAddressFamilies=AF_INET AF_INET6
SystemCallFilter=@basic-io @network-io
LockPersonality=yes
MemoryDenyWriteExecute=yes

[Install]
WantedBy=multi-user.target

A combinação de Capacidades ambientais e uma dura Conjunto de Limites de Capacidade garante que o serviço receba apenas a capacidade necessária e nada mais. NoNewPrivileges impede a reclassificação posterior de privilégios, ProtectSystem e ReadWritePaths regulam o acesso de escrita, e um filtro rigoroso de chamadas de sistema impede pontos de entrada desnecessários no kernel.

Funcionalidades frequentemente utilizadas – e alternativas seguras

  • CAP_NET_BIND_SERVICE: Atribuição a portas <1024. Alternativa: ativação de sockets, colocar um proxy inverso a montante.
  • CAP_NET_RAW: Rohsockets (Ping, DHCP). Alternativa: um pequeno processo auxiliar em vez de direitos alargados do interpretador.
  • CAP_CHOWN/CAP_FOWNER: Proprietário/Ajustes de ACL. Alternativa: diretórios pré-configurados, ferramentas de manutenção dedicadas.
  • CAP_SYS_PTRACE: Depuração/Rastreio – apenas no ambiente de teste, nunca em produção a grande escala.
  • CAP_SYS_ADMIN: „Segunda raiz“ – evitar; especificar o que é realmente necessário.

Escolho sempre a quantidade mínima necessária para ativar exatamente a função pretendida. Caso uma capacidade abra várias vias de ataque (por exemplo, sockets RAW), encapsulo a função num processo separado e de curta duração e retiro os direitos assim que o trabalho estiver concluído.

Lista de verificação prática para capacidades robustas

  • O serviço arranca sem direitos de root? Se não: por que razão não – e será que isso se pode resolver com a ativação de sockets ou com pequenos binários auxiliares?
  • São todos As capacidades atribuídas são comprovadamente necessárias (comprovação de funcionalidades, casos de teste)?
  • O conjunto delimitador foi definido da forma mais restrita e o mais cedo possível?
  • Os XAttrs são mantidos de forma consistente na compilação, nas implementações e nos cópias de segurança (opções do rsync/tar, scripts de pacotes)?
  • Estou a evitar sistematicamente as «capabilities» nos interpretadores e nos ficheiros binários SUID?
  • Os direitos de propriedade e de ficheiro (Root:root, 0755/0555), bem como os caminhos, estão protegidos contra a substituição?
  • As medidas de controlo adicionais (NoNewPrivileges, Seccomp, perfis MAC) são eficazes?
  • As capacidades dos processos são auditadas durante a execução (/proc//status, getpcaps) e as alterações foram documentadas?
  • Os contentores estão configurados, por predefinição, com „drop all, add minimal“ e sem „privileged“?

Brevemente resumido

Linux As «Capabilities» dividem os direitos de root clássicos em pequenas unidades controláveis, implementando assim o princípio do mínimo de forma tecnicamente correta. Atribuo aos serviços apenas as capacidades de que realmente necessitam e combino isso com direitos POSIX e políticas MAC. As capacidades de ficheiro garantem que os direitos estejam diretamente associados aos binários e que as auditorias mostrem claramente quem tem permissão para fazer o quê. Com a separação de privilégios, direitos reduzidos nos contentores e filtros de chamadas de sistema, limito os danos caso uma vulnerabilidade seja explorada. Verificações regulares, direitos de propriedade e de escrita rigorosos e um processo de lançamento documentado mantêm a atribuição de direitos simplificada. Desta forma, o serviço do servidor permanece operacional, mas o Espaço de manobra é mantida sistematicamente baixa para os atacantes.

Artigos actuais

Sala de servidores com servidores Linux e ícone de aviso relativo à falha de segurança do kernel do GhostLock
Segurança

GhostLock CVE – Análise técnica da vulnerabilidade do kernel do Linux

O GhostLock CVE-2026-43499 é uma vulnerabilidade crítica do tipo «use-after-free» no kernel do Linux. Nesta análise sobre o GhostLock CVE, apresentamos a cadeia de exploração para a escalada de privilégios até ao nível de root e fornecemos recomendações concretas de segurança para os administradores.