Hardening com SELinux no RHEL 10, Rocky Linux e AlmaLinux: Guia Prático com Políticas Personalizadas em 2026
Guia prático de hardening com SELinux no RHEL 10, Rocky Linux e AlmaLinux em 2026: política targeted, booleans, contextos com semanage e restorecon, módulos customizados com audit2allow, isolamento de contêineres Podman com MCS, e troubleshooting via sealert.
O hardening com SELinux no RHEL 10, Rocky Linux 10 e AlmaLinux 10 consiste em manter a política targeted em modo enforcing, ajustar booleans para o seu workload, redefinir contextos de arquivo com semanage fcontext e restorecon, e empacotar exceções em módulos de política personalizados gerados a partir de logs AVC, em vez de desabilitar o subsistema. Na prática, é a diferença entre confinar um processo comprometido ao seu próprio diretório ou deixar a blast radius escapar para todo o filesystem. Este guia mostra como tratar o SELinux como uma camada arquitetural, não como um obstáculo a ser silenciado.
Confesso uma coisa antes de começar: passei boa parte da minha primeira década como SRE rodando setenforce 0 no susto. Foi só depois de investigar um incidente onde o SELinux teria bloqueado o que aconteceu que parei de tratá-lo como inimigo.
RHEL 10, Rocky Linux 10 e AlmaLinux 10 mantêm a política targeted como padrão, com setools-console e policycoreutils-python-utils como ferramentas de primeira linha para auditoria.
Nunca desabilite o SELinux para "fazer a aplicação funcionar". Mude para permissive, colete os AVCs e gere um módulo de política com audit2allow.
Contextos errados de arquivo são responsáveis pela maioria dos falsos positivos: semanage fcontext -a seguido de restorecon -Rv resolve 80% dos incidentes.
Booleans permitem habilitar comportamentos seguros (ex.: httpd_can_network_connect) sem reescrever política, e persistem com a flag -P.
Em contêineres, container_t e a categoria MCS isolam cada Pod, mas exigem rótulos corretos nos volumes (:Z ou :z no Podman) para não quebrar.
O troubleshooting eficaz começa em /var/log/audit/audit.log com ausearch -m AVC -ts recent e termina com sealert traduzindo o evento para humanos.
O que é o SELinux e por que ele continua relevante em 2026
O SELinux (Security-Enhanced Linux) é uma implementação de Mandatory Access Control (MAC) no kernel Linux que rotula cada processo, arquivo, socket e porta com um contexto de segurança, e decide cada acesso comparando esse contexto com uma política compilada em memória. Diferente do modelo DAC (proprietário e permissões), a política do SELinux não pode ser sobrescrita pelo dono do arquivo nem pelo próprio processo. Ela é aplicada pelo kernel antes que open(), execve() ou bind() retornem ao userland.
Em 2026, esse modelo continua relevante porque a maioria dos incidentes em servidores Linux não começa com escalada de privilégio direta para root. Começa com um worker do Nginx ou um runtime de contêiner sendo enganado para ler arquivos fora do seu escopo. Quando o SELinux está em enforcing com política targeted, esse worker tem um type (httpd_t) que não pode ler /etc/shadow nem se conectar a portas arbitrárias, independentemente de o processo rodar como root ou de o arquivo ter permissão 644. A blast radius do comprometimento fica contida ao domínio do serviço, e é exatamente esse comportamento que justifica manter a complexidade.
RHEL 10, Rocky Linux 10 e AlmaLinux 10, lançados em 2025 com kernel 6.12 LTS, trouxeram melhorias importantes: melhor performance do AVC cache, suporte ampliado a userspace_initial_context em systemd, e novos types para workloads de contêineres rootless. A documentação oficial do RHEL 10 sobre SELinux é a referência canônica para mudanças entre versões.
Verificar o status do SELinux e entender os modos
O primeiro comando em qualquer servidor recém-provisionado é sestatus. Ele resume modo atual, modo configurado em disco, política carregada e versão de policy. Em um RHEL 10 padrão você verá algo assim:
enforcing: a política nega acessos não autorizados e registra cada negação como evento AVC. Este é o único modo aceitável em produção.
permissive: nada é negado, mas todas as violações são logadas. Útil para coletar AVCs ao introduzir um serviço novo sem quebrar o ambiente.
disabled: o subsistema não é carregado. A partir do RHEL 9, desabilitar via /etc/selinux/config está deprecated. A forma correta é o parâmetro de kernel selinux=0, e fortemente desencorajado fora de bare metal de troubleshooting.
Para alternar temporariamente entre enforcing e permissive sem reboot:
# setenforce 0 # vira permissive
# setenforce 1 # volta para enforcing
# getenforce
Enforcing
Política targeted vs MLS: qual escolher no RHEL 10
O RHEL 10 entrega três políticas no pacote selinux-policy-*: targeted (padrão), mls (Multi-Level Security) e minimum. A diferença não é cosmética: define quais domínios são confinados.
Característica
targeted
mls
minimum
Domínios confinados
Serviços de rede comuns (httpd, sshd, dns, ntpd...)
Todos, com classificações Bell-LaPadula
Apenas alvos selecionados pelo administrador
Usuários não-confinados
Sim (unconfined_t)
Não
Sim
Caso de uso
Servidores e workstations gerais
Governo, defesa, ambientes classificados
Embarcados e troubleshooting
Overhead de gestão
Baixo
Alto, exige role engineering
Mínimo
Compatível com MCS p/ contêineres
Sim
Sim
Limitado
Para 99% dos workloads (incluindo Kubernetes, Podman, bancos de dados e clusters HPC) a política targeted é a escolha correta. Ela confina os serviços expostos enquanto deixa shells interativos em unconfined_t, evitando que cada SRE precise virar policy engineer só para reiniciar o serviço. Reserve mls para ambientes onde a classificação de dados (TOP SECRET, SECRET, CONFIDENTIAL) é um requisito legal, e nesse caso planeje semanas de modelagem de papéis, não horas.
Para verificar e trocar a política:
# cat /etc/selinux/config | grep -E '^(SELINUX|SELINUXTYPE)='
SELINUX=enforcing
SELINUXTYPE=targeted
# Mudar para mls (exemplo, exige relabel completo no próximo boot)
# sed -i 's/^SELINUXTYPE=.*/SELINUXTYPE=mls/' /etc/selinux/config
# touch /.autorelabel && reboot
Contextos de arquivo: semanage fcontext e restorecon na prática
A causa mais comum de "minha aplicação não funciona com SELinux" é simples: arquivos novos foram criados em diretórios não-padrão e herdaram o contexto errado. A política targeted espera que conteúdo web esteja em /var/www com type httpd_sys_content_t. Se você serve a partir de /srv/sites, o Nginx vai bater em AVC denied no primeiro request. (Eu já perdi uma tarde inteira nisso antes de aprender a olhar ls -lZ primeiro.)
Aqui o processo está em httpd_t e os arquivos em var_t, e a política não permite essa combinação para leitura. A correção tem dois passos: registrar a regra persistente e aplicar nos arquivos existentes.
# Registrar a regra (sobrevive a relabel)
# semanage fcontext -a -t httpd_sys_content_t "/srv/sites(/.*)?"
# Aplicar nos arquivos atuais
# restorecon -Rv /srv/sites
Relabeled /srv/sites/app from unconfined_u:object_r:var_t:s0 to unconfined_u:object_r:httpd_sys_content_t:s0
...
# Confirmar
# matchpathcon /srv/sites/app/index.html
/srv/sites/app/index.html system_u:object_r:httpd_sys_content_t:s0
Para portas (ex.: rodar httpd em 8443):
# semanage port -a -t http_port_t -p tcp 8443
# semanage port -l | grep http_port_t
Booleans SELinux: ajuste fino sem reescrever política
Booleans são chaves binárias dentro da política targeted que ligam ou desligam comportamentos opcionais. Em vez de reescrever regras quando o Apache precisa fazer requisições saindo do servidor para uma API externa, você liga um boolean. Honestamente, é o caminho mais subutilizado da stack.
# Listar booleans relacionados ao httpd
# getsebool -a | grep httpd
httpd_can_network_connect --> off
httpd_can_network_connect_db --> off
httpd_can_sendmail --> off
httpd_enable_homedirs --> off
httpd_use_nfs --> off
...
# Habilitar persistente (sobrevive a reboot e a reload de policy)
# setsebool -P httpd_can_network_connect on
# Confirmar
# getsebool httpd_can_network_connect
httpd_can_network_connect --> on
Sempre prefira um boolean a uma regra customizada quando o comportamento desejado já está modelado upstream. Os mais usados em 2026 incluem container_manage_cgroup, virt_sandbox_use_all_caps, nis_enabled, ssh_keysign e selinuxuser_execmod. Para descobrir o que cada boolean faz:
# semanage boolean -l | grep httpd_can_network_connect
httpd_can_network_connect (off , off) Allow HTTPD scripts and modules to connect to the network using TCP.
A flag -P em setsebool é não-negociável em produção. Sem ela, sua mudança evapora no próximo reboot e você acorda com um pager às 3 da manhã (já vivi essa, recomendo evitar). Para auditoria contínua, integre semanage boolean -l -C (apenas modificados) ao seu pipeline de configuração, uma abordagem que combina bem com a auditoria de conformidade com Lynis e CIS Benchmarks que documentei anteriormente.
Como criar uma política SELinux personalizada com audit2allow
Quando nem boolean nem rótulo resolvem (por exemplo, um daemon proprietário que precisa escrever em um pipe nomeado em /run/myapp), você gera um módulo customizado a partir dos AVCs reais. Esse é o caminho profissional. Copiar e colar regras de Stack Overflow é o caminho que vira incidente.
Passo 1, colocar em permissive apenas o domínio do serviço:
SELinux em contêineres: Podman, container_t e workloads confinados
A intersecção SELinux + contêineres é onde a maior parte das equipes tropeça em 2026. Quando você roda podman run -v /data:/data nginx com SELinux ativo, o processo dentro do contêiner roda como container_t com uma categoria MCS única (ex.: s0:c123,c456), e o diretório /data no host tem outro contexto. Resultado: permission denied mesmo com chmod 777.
O Podman tem duas opções para relabel automático de volumes:
:z rotula o volume como container_file_t compartilhado entre contêineres.
:Z rotula com categoria MCS privada, exclusiva daquele contêiner.
# Compartilhado entre vários contêineres
# podman run -d -v /data:/data:z docker.io/library/nginx
# Privado, isolado por MCS
# podman run -d -v /data/db:/var/lib/postgresql/data:Z docker.io/library/postgres:17
Em ambientes Kubernetes (k3s, OpenShift), o seLinuxOptions no securityContext do Pod controla o nível MCS por workload. Combinado com Pod Security Admission, isso bloqueia contêineres tentando rodar como spc_t (super privileged container). Para defesa em profundidade no runtime, o SELinux complementa muito bem ferramentas eBPF. Abordo essa camada com mais detalhe no comparativo entre Falco e Tetragon para segurança de runtime no Kubernetes.
O NIST SP 800-190 classifica isolamento MAC (categoria 3.1.3) como controle obrigatório para qualquer ambiente que rode múltiplos tenants no mesmo host. Sem SELinux ou equivalente, você está implicitamente confiando no kernel namespace de contêiner como única barreira, e CVEs como o CVE-2022-0185 já mostraram que isso não basta.
SELinux vs AppArmor: quando usar cada um
Ambos implementam MAC, ambos confinam processos, mas os modelos divergem em filosofia. Cobri o ecossistema AppArmor em detalhe no guia de hardening com AppArmor para Ubuntu e Debian. Aqui vai o resumo prático para a escolha.
Dimensão
SELinux
AppArmor
Modelo
Type Enforcement + RBAC + MCS/MLS
Path-based profiles
Distribuições padrão
RHEL, Rocky, AlmaLinux, Fedora, CentOS Stream
Ubuntu, Debian, openSUSE
Curva de aprendizado
Íngreme (tipos, atributos, contextos)
Suave (perfis legíveis por humano)
Granularidade
Por inode (sobrevive a rename, mount, bind)
Por caminho (vulnerável a path manipulation)
Suporte a MLS/MCS
Sim, nativo
Não
Comunidade de policy
Vasta (refpolicy)
Menor, mas crescente
Melhor cenário
Multi-tenant, contêineres, compliance regulatória
Servidores single-tenant, edge, IoT
Minha regra de bolso: se o sistema roda workloads de múltiplos times no mesmo kernel, ou se compliance (PCI-DSS, FedRAMP, ISO 27001) menciona MAC nominalmente, use SELinux. Se é um appliance single-purpose onde a maior preocupação é confinar aquele daemon específico, o AppArmor é mais barato de operar e suficiente. Rodar os dois simultaneamente no mesmo kernel não é suportado: o LSM stack permite múltiplos módulos, mas as distribuições só ativam um por vez.
Solução de problemas: sealert, ausearch e logs AVC
Toda decisão de SELinux que importa termina em /var/log/audit/audit.log. Quando o auditd está rodando (padrão no RHEL 10), cada negação vira um evento AVC com timestamp, syscall, contexto de origem e destino. As três ferramentas que você vai usar todo dia:
# Eventos AVC dos últimos 10 minutos
# ausearch -m AVC,USER_AVC -ts recent
# Tradução humanizada dos AVCs (pacote setroubleshoot-server)
# sealert -a /var/log/audit/audit.log
# Resumo agregado por syscall/comando
# aureport -a --summary
Um exemplo real de saída do sealert:
SELinux is preventing /usr/sbin/nginx from getattr access on the file
/srv/sites/app/index.html.
***** Plugin catchall_labels (83.8 confidence) suggests ********
If you want to allow nginx to have getattr access on the index.html file
Then you need to change the label on /srv/sites/app/index.html
Do
# semanage fcontext -a -t httpd_sys_content_t '/srv/sites/app/index.html'
# restorecon -v '/srv/sites/app/index.html'
O sealert normalmente acerta na primeira sugestão para 70-80% dos AVCs. Para os outros, leia o evento bruto e pergunte: qual processo, em qual domínio, tentou qual operação em qual contexto? Essa é a única pergunta. Se a resposta envolve um processo confinado tentando ler segredos ou abrir sockets de rede inesperados, você acabou de detectar uma intrusão, não uma falha de configuração. Esse é o ponto onde o SELinux deixa de ser "incômodo" e vira sensor de segurança, e onde a integração com pipelines de detecção de intrusões com Wazuh, Auditd e Osquery paga todos os dividendos. Combine isso com hardening de SSH e você fechou a maior parte das portas de entrada que um operador remoto consegue empurrar.
Perguntas frequentes
Como desabilitar o SELinux temporariamente sem reboot?
Execute setenforce 0 para mudar para permissive. O SELinux continua carregado, registra AVCs, mas não bloqueia nada. Para reativar enforcement: setenforce 1. Essa abordagem é preferível a desabilitar completamente porque não exige reboot nem relabel posterior.
O SELinux atrapalha o desempenho do servidor?
Em workloads típicos de servidor, o overhead do SELinux fica abaixo de 7% em syscalls intensivas e é praticamente invisível em throughput de rede ou disco. O AVC cache do kernel 6.12 LTS reduz ainda mais esse custo. Desabilitar SELinux por motivo de performance raramente se justifica fora de cenários HFT.
O que fazer quando uma aplicação não funciona com SELinux?
Não desabilite. Coloque o domínio específico em permissive com semanage permissive -a <domain_t>, exercite a aplicação, colete AVCs com ausearch -m AVC -ts recent e gere um módulo de política com audit2allow -M nome-modulo. Revise cada regra antes de instalar com semodule -i e remova o permissive.
Posso usar SELinux junto com AppArmor no mesmo servidor?
Tecnicamente o LSM stack do kernel Linux suporta múltiplos módulos MAC carregados simultaneamente desde 5.1, mas nenhuma distribuição mainstream entrega ambos ativos. Escolha um por host conforme a distribuição e o caso de uso. Operar os dois em paralelo significaria manter duas políticas em sincronia, e o custo operacional não compensa.
Como saber qual boolean SELinux preciso ativar para meu serviço?
Liste todos os booleans com descrição usando semanage boolean -l e filtre pelo nome do serviço (ex.: | grep httpd). A coluna final é a descrição do que cada boolean libera. Se nenhum cobre seu cenário, é sinal de que você precisa de um módulo customizado, não de um boolean.
Hardening completo de serviços systemd com systemd-analyze security, sandboxing seccomp, cgroups v2, DynamicUser e drop-ins não-invasivos, com exemplos para RHEL 10, Ubuntu 24.04 e Debian 13.
Aprenda a implementar AppArmor no Linux com perfis personalizados, modos enforce e complain, integração com systemd, Docker e auditoria de logs. Guia completo para Ubuntu 24.04 LTS e Debian 12 atualizado para 2026.