Duas receitas de privilégio mínimo coladas juntas não produzem uma implantação mais restrita. Um modelo deixa o daemon começar com privilégios e descer para uma conta sem privilégios depois da inicialização; o outro faz o gestor de serviços lançá-lo sob uma identidade restrita desde a primeira instrução. Cada um funciona sozinho, e a combinação costuma produzir um serviço que não consegue fazer bem nenhuma das duas tarefas. O resto percorre a decisão em ordem.

Dois modelos, um dono da troca

O primeiro modelo pertence ao daemon. As opções OpenVPN da comunidade user e group mudam a identidade depois que o túnel e as chaves estão configurados, e persist-key mais persist-tun mantêm esses recursos utilizáveis depois.

O segundo modelo pertence ao gestor de serviços. Um ajuste do systemd User= ou Group= decide a identidade sob a qual o processo inicia, e as diretivas de capacidades decidem o que ele pode reter. Escolher entre os modelos é uma questão de empacotamento antes de ser uma questão de segurança, e o mantenedor do pacote já a respondeu. Seu trabalho é encontrar essa resposta em vez de inventar uma terceira opção.

Ambos são legítimos. O erro é tratá-los como camadas de um único projeto, porque o segundo modelo pode transformar a troca de identidade do primeiro em uma operação que o processo não tem mais permissão de executar.

Decida onde a identidade muda

Se a unidade já inicia como uma conta dedicada, adicionar uma instrução do daemon para virar outra conta exige uma transição setuid que um serviço endurecido nunca deveria fazer. A falha aparece como erro de permissão na inicialização em vez de uma mensagem de política clara. Isso é lido como uma capacidade ausente, o que empurra as pessoas para um conjunto de limites mais amplo em vez de para a questão da identidade.

Leia o que o pacote instalado realmente entrega antes de editar qualquer coisa, incluindo seu diretório drop-in. Algumas unidades omitem de propósito a queda do próprio daemon porque o gestor já cuida dela, e outras dependem dessas opções para a troca após a inicialização. Remover qualquer uma das duas sem saber qual modelo você herdou é como um serviço que funciona deixa de iniciar.

Escreva o modelo escolhido em uma linha: de propriedade do gestor ou do daemon. Toda diretiva posterior precisa ser coerente com essa frase, e uma unidade mista deve ser tratada como defeito e não como compromisso.

Capacidades são limites, não concessões

Um conjunto de limites de capacidades diz o que um processo pode reter no máximo. É um teto, e listar uma capacidade ali não concede nada por si só, por isso uma unidade com conjunto de limites generoso ainda pode falhar ao criar um túnel.

O que de fato coloca uma capacidade no conjunto ambiente de um serviço é AmbientCapabilities=. A referência de execução do systemd documenta as duas diretivas separadamente, e essa distinção explica a maior parte dos conselhos confusos sobre capacidades na internet.

Adicione uma capacidade somente depois que a operação que falha a identificar. Um conjunto de limites mais amplo esconde a causa real e deixa o serviço mais privilegiado do que o projeto pretendia. Também torna a unidade mais difícil de revisar, porque o teto não descreve mais o trabalho. Remova a capacidade extra depois que o defeito real for corrigido.

Mapeie a autoridade ao longo do ciclo de vida

Liste o que o processo precisa fazer em cada estágio: inicialização, estado estável, reautenticação e desmontagem. Criação de interface, mudanças de rota, leitura de uma chave protegida e chamada de um auxiliar não acontecem todos no mesmo momento nem sob os mesmos requisitos. Ordene a lista por estágio antes de ordená-la por capacidade.

As opções de persistência preservam recursos através da renegociação, e não concedem permissões que nunca foram detidas. Uma chave que o processo não consegue ler na inicialização continuará ilegível depois, digam o que disserem as flags de persistência. As flags preservam o que já funcionava.

Use uma conta dedicada em vez de uma compartilhada. Os desenvolvedores do OpenVPN alertam contra o hábito de usar nobody como exemplo genérico, porque outros serviços no host podem já estar rodando com essa conta e o isolamento que você pensa ter não está lá.

Recursos que mudam a carga local, como offload do canal de dados ou uma compilação que liga de forma diferente, podem deslocar quais operações são necessárias. Revise o mapa quando o pacote mudar, não só quando algo quebrar.

Escreva o plano e mantenha acesso de recuperação

Um registro de implantação deve nomear o gestor, a identidade de inicialização, a identidade estável pretendida e cada concessão junto com a operação que a exige. É esse registro que torna a próxima mudança revisável em vez de experimental.

Mantenha o acesso ao console disponível enquanto edita, porque uma unidade que falha na inicialização é difícil de reparar remotamente. Teste um reinício e um reinício completo antes de considerar a mudança concluída. Uma unidade que sobrevive a um reinício ainda pode falhar antes de a rede subir.

Sucesso é um ciclo de vida de autoridade mínima compreendido, não um processo que por acaso roda sob um nome que não é root.

Artigos relacionados