Um daemon VPN precisa de operações de rede privilegiadas mesmo que as aplicações comuns abram sockets de saída sem qualquer privilégio. É nesta lacuna que a maioria dos erros de capacidades começa: alguém vê um erro de permissão, concede todo o conjunto e o serviço funciona por motivos que ninguém consegue explicar depois. O percurso abaixo começa na operação que falhou, associa-a a uma única autoridade e depois verifica o contexto em que o processo realmente é executado. Executar como root sem restrições é uma resposta válida para algumas implementações, mas deve ser uma decisão e não um valor predefinido.

Comece pela operação que falhou

Leia o erro antes de ler um tutorial. «Operação não permitida» numa alteração de interface, a recusa de abrir um nó de dispositivo e uma falha de bind numa porta baixa são três problemas diferentes com três concessões diferentes. Se o processo arranca e só falha na primeira alteração de rota, isso também indica que a criação da interface já teve sucesso, o que estreita bastante a resposta.

Reproduza a falha com o serviço parado e depois inicie-o em primeiro plano com o mesmo utilizador, se a implementação o permitir. Um erro que consegue ver é melhor do que uma linha de registo que tem de adivinhar, e torna evidente a diferença entre um problema de capacidades e uma recusa do SELinux ou do AppArmor.

Associe cada operação a uma autoridade

A tabela é um mapa inicial, não um substituto da documentação da implementação:

OperationUsual authorityWatch for
Create or configure tun/tapCAP_NET_ADMIN in the relevant namespaceCreating the device and opening it are separate steps
Add routes, rules, firewall entriesCAP_NET_ADMINSame capability, different namespace than you think
Open /dev/net/tunDevice permissions, not a capabilityGroup membership or a udev rule may be enough
Raw socketsCAP_NET_RAWNeeded by some user-space tunnel implementations
Bind a port below 1024CAP_NET_BIND_SERVICEIrrelevant for most modern tunnel ports

As duas linhas dessa tabela são as que as pessoas confundem. Abrir /dev/net/tun é uma questão de permissões de ficheiro; pedir ao kernel para configurar o que abriu é uma questão de capacidades. Conceder CAP_NET_ADMIN não corrige um nó de dispositivo que o seu utilizador de serviço não consegue ler, e corrigir o nó não lhe dá autoridade sobre as rotas. Verifique as duas coisas antes de alterar qualquer uma.

O WireGuard do kernel e os programas de túnel em espaço de utilizador também não têm os mesmos requisitos. A via do módulo do kernel configura uma interface existente através de netlink, enquanto uma implementação em espaço de utilizador também pode precisar do dispositivo e, para alguns transportes, de sockets em bruto.

Leia o contexto: serviço, contentor ou shell

Uma capacidade no espaço de nomes de rede do anfitrião pode afetar muito mais do que a mesma operação dentro de uma pilha deliberadamente isolada. Um ambiente de execução de contentores começa normalmente com um conjunto de limites reduzido; adicionar NET_ADMIN a um contentor que partilha a rede do anfitrião é uma concessão muito maior do que adicioná-lo a um com espaço de nomes de rede próprio e sem montagens do anfitrião.

Para unidades systemd, leia o que é realmente aplicado e não o que o ficheiro da unidade parece dizer: systemctl show -p CapabilityBoundingSet -p AmbientCapabilities reporta os valores efetivos, que podem ter sido definidos por um drop-in de que se esqueceu. Peça o valor efetivo, nunca o pretendido. As capacidades ambient são importantes porque um serviço que não é root perde capacidades através de exec sem elas.

Tome como base a configuração suportada do pacote ou da imagem. Não acrescente permissões de sockets em bruto nem modo privilegiado só porque outro tutorial as listou para outra ferramenta; esse raciocínio não transfere nada e custa-lhe o isolamento que pagou. Em caso de dúvida, não conceda nada e leia as notas da implementação para a operação exata de que precisa.

Observe o que acontece depois de os privilégios caírem

Muitos daemons arrancam com privilégios, configuram a interface e depois passam para um utilizador sem privilégios e mantêm o túnel. A partir desse ponto o processo já não consegue adicionar uma rota, reescrever uma regra de firewall ou abrir um novo dispositivo privilegiado, o que está bem até uma reconexão precisar exatamente disso. Leituras de ficheiros e execução de hooks posteriores podem falhar pelo mesmo motivo.

Teste todo o ciclo de vida em vez do primeiro segundo: o arranque, uma renovação ou reconexão e a limpeza ao parar. Um primeiro sucesso na criação da interface não estabelece que todas as operações privilegiadas posteriores continuem a funcionar, e a falha costuma aparecer horas depois em produção.

As implementações que mantêm o túnel aberto durante uma mudança de utilizador costumam combiná-lo com opções para persistir o túnel e as chaves. Do lado do systemd, a pergunta equivalente é se o conjunto de capacidades da unidade sobrevive à mudança de utilizador que configurou.

Registe a concessão e o motivo

Anote a concessão exata, a operação que ela permite e o erro que corrigiu. Uma linha nas notas de implementação — «CAP_NET_ADMIN no espaço de nomes de rede da unidade, necessário para alterações de rota após uma reconexão» — basta para impedir a pessoa seguinte de voltar a alargar a concessão.

Depois releia o conjunto efetivo após cada atualização. Os fornecedores acrescentam funcionalidades, as imagens mudam os valores predefinidos e uma capacidade que no ano passado era desnecessária pode tornar-se portante sem que ninguém note.

Artigos relacionados