chroot falha no OpenVPN: regra do AppArmor ou capacidade?
Adicionar um caminho a um perfil AppArmor nunca concede CAP_SYS_CHROOT, e um chroot negado pode vir de três camadas separadas. Descubra qual delas mudar.
O daemon registra uma falha de chroot e o perfil já parece generoso, então a correção tentadora é ampliar uma regra de caminho e reiniciar. Isso quase nunca muda nada, porque o chroot é condicionado por um privilégio que o perfil nunca pôde conceder. Decidir qual camada tocar exige três verificações, e elas são baratas: o que o processo pode deter, o que a política permite e o que o lançador removeu antes que qualquer uma das duas importasse.
Duas portas separadas, e só uma pertence ao AppArmor
chroot exige CAP_SYS_CHROOT no namespace de usuário em que o processo está rodando. Esse é o privilégio convencional do Linux, verificado pelo kernel antes que a chamada faça qualquer coisa com caminhos. Separadamente, um perfil AppArmor em modo enforcing pode negar a capacidade mesmo quando o processo a detém, porque a política obrigatória é consultada além das credenciais, e não no lugar delas. A distinção importa na prática: as duas camadas vivem em arquivos diferentes e costumam ser mantidas por pessoas diferentes.
Essas duas portas falham com o mesmo errno. Ambas produzem EPERM, e ambas aparecem como uma queixa genérica de permissão no log do daemon, e é por isso que adivinhar o conteúdo do perfil desperdiça tanto tempo. A regra de caminho que você estava prestes a adicionar controla o acesso a arquivos depois que o chroot tem sucesso; ela não tem relação alguma com a permissão da operação.
Uma terceira porta fica a montante das duas. Se o gerenciador de serviços ou um wrapper iniciou o processo sem a capacidade em seu conjunto de limites, nenhuma mudança no AppArmor pode devolvê-la, e adicionar capacidades ao perfil apenas documenta uma intenção que o kernel não vai honrar.
Leia o estado de privilégios antes de editar a política
Comece pelo processo em execução, e não pela configuração que você acredita estar ativa. O arquivo de status expõe cinco máscaras de capacidade e o sinalizador no-new-privileges, e eles respondem a perguntas diferentes:
grep -E '^(CapInh|CapPrm|CapEff|CapBnd|CapAmb|NoNewPrivs):' /proc/$(pidof openvpn)/status
sudo aa-status | grep -i openvpn
CapEff é o conjunto que importa para a chamada: uma capacidade presente em CapBnd mas ausente de CapEff é permitida pelo teto e não está sendo detida no momento. CapAmb importa quando o binário não é privilegiado no sentido usual de setuid, porque capacidades ambientes são o modo como um serviço mantém um privilégio através do exec sem capacidades de arquivo. CapInh guarda bits que poderiam ser herdados através do exec, mas só quando o conjunto herdável do arquivo executado permite, então um bit ali não concede nada por si só.
Depois confirme qual perfil está de fato anexado. Um processo pode estar confinado por um nome de perfil que você não esperava, especialmente quando uma distribuição entrega sua própria unidade e seu próprio perfil juntos, e ler /proc/PID/attr/current resolve isso em uma linha. Comparar seu arquivo editado com o perfil que o kernel reporta é a diferença entre corrigir a causa e editar um arquivo que ninguém carrega.
Adicione a regra de capacidade só quando a trilha de auditoria disser isso
Registros do kernel e da auditoria nomeiam a camada que recusou. Procure uma negação que traga a operação capable e o nome sys_chroot, junto com o perfil que a produziu. Se esse registro existir, o processo já detinha a capacidade e o AppArmor a recusou, e a mudança direcionada é uma regra de capacidade dentro do perfil real:
capability sys_chroot,
Mantenha-a como fragmento no perfil que está genuinamente anexado, e confira a palavra-chave na referência de perfil do apparmor.d em vez de copiar uma regra de um tutorial sem relação. Colocá-la em um include compartilhado concede a todos os programas confinados que carregam esse arquivo, e mudar o perfil para o modo complain esconde a próxima negação em vez de resolver esta. A regra de perfil remove uma negação de política; ela não fabrica privilégio, então um processo que nunca teve CAP_SYS_CHROOT em seu conjunto efetivo continuará falhando mesmo com a regra no lugar.
Se o registro de auditoria não mostrar nenhuma negação do AppArmor, pare de editar o perfil. Você está diante de um problema de credenciais, e o trabalho restante está na unidade ou no processo pai, não na linguagem de política.
O gerenciador de serviços mantém seu próprio teto
O systemd define um conjunto de limites de capacidade para cada serviço, e por padrão esse conjunto é estreito. Qualquer coisa excluída ali fica indisponível para o processo, não importa o que o perfil diga, e é por isso que unidades endurecidas podem falhar em uma operação que funcionava quando o daemon era iniciado à mão. As diretivas relevantes são fáceis de ler na unidade e fáceis de passar despercebidas quando o próprio aplicativo relata a falha. Drop-ins enviados por um pacote da distribuição são a fonte usual de um teto que ninguém lembra ter configurado.
Elevar o teto e conceder a capacidade são duas mudanças, não uma. Uma entrada no conjunto de limites diz que o processo pode deter a capacidade; ela não a adiciona ao conjunto efetivo, e o NoNewPrivs pode impedir que uma elevação posterior tenha sucesso mesmo quando todo o resto se alinha. Adicione apenas o que a operação precisa, mantenha o resto do endurecimento intacto e resista ao atalho de conceder todas as capacidades para fazer uma chamada passar.
A ordem também importa aqui. Mude primeiro o teto da unidade, verifique que o CapEff agora contém o bit e só então decida se o AppArmor ainda nega. Fazer as duas mudanças de uma vez deixa você adivinhando outra vez se a falha persistir.
Depois que a negação sumir, verifique a própria jaula
Um chroot bem-sucedido move a raiz, então todo caminho que o daemon tocar depois é resolvido dentro da nova árvore. Configuração ausente, um nó de dispositivo faltando ou um diretório de certificados que não foi copiado agora aparecem como erro de arquivo, e esse erro é um problema de disposição, não de capacidade. Leia o próximo evento em vez de adicionar outra permissão ampla. A mudança também altera o significado de um caminho relativo, e é por isso que os guias de endurecimento insistem em caminhos absolutos na configuração do daemon.
Também ajuda lembrar o que o chroot não faz. Ele é uma fronteira de caminho, não um sandbox: um processo que mantém o privilégio pode escapar dele, e é por isso que a capacidade normalmente é descartada depois da chamada e por que o confinamento deve permanecer em volta do daemon. Manter juntos o perfil AppArmor, o conjunto de limites de capacidade e as restrições de sistema de arquivos é o objetivo do exercício, não uma sobra a remover quando o serviço sobe.
A regra de decisão é curta. Se o registro de auditoria nomear o AppArmor, adicione uma regra de capacidade ao perfil anexado. Se o conjunto efetivo estiver vazio, corrija o teto da unidade e a ordem de descarte de privilégios. Se o perfil não for o que você editou, pare de editar e encontre o real.