securityContext do Kubernetes: recursos do Linux no Kubernetes
26 de janeiro de 2021
0 minutos de leituraNos primórdios dos sistemas operacionais Unix, o modelo de permissões era relativamente simples. Ou você era um usuário comum, ou era root, o superusuário com permissão para fazer tudo.
Embora fosse possível conceder permissões elevadas a usuários comuns em arquivos ou diretórios, quase todas as funções no nível do kernel eram restritas ao usuário root. Se o seu aplicativo precisasse de uma única chamada no nível do kernel para funcionar, teria que receber privilégios SUID — o que, na prática, concederia privilégios de root ao processo se ele fosse iniciado por um usuário comum.
Esse modelo funcionava bem quando máquinas físicas eram usadas por grupos relativamente pequenos de pessoas, mas essa granularidade grosseira já não atendia às necessidades do mundo moderno. Para oferecer mais flexibilidade e segurança, os desenvolvedores do kernel Linux criaram uma solução muito mais granular: agruparam chamadas no nível do kernel em recursos e permitiram atribuí-los a processos individuais. Assim, se um aplicativo precisasse de uma única chamada do kernel, bastaria atribuir a ele o recurso correspondente, limitando a exposição do sistema a riscos de segurança.
Gerenciamento de privilégios em contêineres do Kubernetes
Como isso funciona nos contêineres?
Na prática, um contêiner é apenas um processo em execução no sistema, isolado por meio de cgroups e namespaces do kernel. Isso significa que os recursos podem ser atribuídos a ele da mesma forma que a qualquer outro processo. O runtime de contêineres faz isso ao criar o contêiner.
Normalmente, o runtime de contêineres atribui um conjunto padrão de recursos ao contêiner (você pode ver aqui o conjunto padrão fornecido pelo Docker) e também oferece um mecanismo para adicionar ou remover recursos. Se você executar um contêiner com a flag --privileged, concederá a ele todos os recursos. Se o contêiner for executado como root, isso poderá causar um problema grave de segurança — um assunto que explorei em uma publicação anterior.
Configuração de recursos para um contêiner no securityContext do Kubernetes
Ao usar o runtime de contêineres no Kubernetes, o plano de controle do Kubernetes usa esses controles para definir quais recursos serão atribuídos ao contêiner quando ele for iniciado. As configurações de recursos são disponibilizadas ao usuário por meio de várias opções na seção securityContext do YAML do contêiner. A configuração fica assim:
Neste caso, removeríamos todos os recursos e adicionaríamos o recurso CAP_NET_ADMIN. Se a seção de recursos em securityContext estiver vazia, teremos o conjunto padrão definido pelo runtime de contêineres. Em geral, esse conjunto é bastante amplo e pode incluir muito mais recursos do que o aplicativo precisa. Vamos iniciar um pod no Kubernetes e ver quais recursos ele recebe.
Primeiro, vamos criar um YAML para implantar um Pod. Nesta configuração, usamos uma imagem do Docker Hub mantida pelo projeto original, baseada no Alpine e com a ferramenta capsh, que nos permite ver quais recursos nosso contêiner tem.
Observe que não definimos uma seção securityContext para esse contêiner, então todas as configurações serão os padrões do sistema. Lembre-se: se você não especificar um usuário no Kubernetes, o contêiner será executado como o usuário padrão definido no Dockerfile usado para criá-lo — que, no caso de muitos contêineres, é root.
Quando o pod estiver em execução, podemos abrir um shell dentro dele e executar capsh para conferir os recursos:
Remoção de recursos no securityContext do Kubernetes
Como podemos ver, por padrão, estamos executando como root e temos muitos recursos. Esse é o conjunto definido por padrão pelo runtime de contêineres. Agora, vamos tentar remover um desses recursos nas configurações de securityContext: vamos remover a capacidade de criar nós do sistema de arquivos, que corresponde a CAP_MKNOD:
Comparando com o primeiro pod, podemos ver que este pod agora não tem mais o recurso cap_mknod. Além de remover recursos individuais, também podemos remover todos eles pelo securityContext:
Sem nenhum recurso, algumas funções do sistema falharão quando tentarmos executá-las. Por exemplo, vamos tentar instalar o bash usando apk:
Mesmo que nosso contêiner esteja sendo executado como root, a instalação do pacote bash falha porque precisa definir permissões no sistema de arquivos, e removemos esse recurso do contêiner. Se quisermos impedir que alguém instale software no contêiner, gerenciar essas permissões por meio de recursos é uma das opções.
Embora o capsh ofereça uma forma bem organizada de ver quais recursos nosso contêiner tem, essa não é a única maneira de descobrir quais estão disponíveis. Também podemos acessar essas informações diretamente pelo sistema de arquivos proc, sem instalar nenhum software adicional:
No arquivo /proc/1/status, os recursos são exibidos como um bitmap. Como nenhum recurso está habilitado neste contêiner, todos os valores são zero. Se consultarmos o mesmo arquivo no contêiner original, com todos os recursos habilitados:
Não é tão fácil de ler quanto a saída do capsh, mas cada bit exibido representa um recurso específico, conforme definido no cabeçalho correspondente do kernel.
Saiba mais sobre como melhorar a segurança do Kubernetes removendo os recursos padrão de um contêiner.
Adição de recursos no securityContext do Kubernetes
Além de remover recursos, também podemos adicioná-los novamente. Vamos retomar o exemplo anterior e adicionar um único recurso às configurações de securityContext: o recurso CAP_MKNOD:
Neste exemplo, podemos ver que agora temos apenas esse recurso habilitado.
Princípio do menor privilégio no securityContext do Kubernetes
Neste artigo, vimos como as capacidades funcionam nos contêineres, como são configuradas no securityContext do Kubernetes e como são controladas pelo runtime de contêineres.
Seguindo o princípio do menor privilégio, a melhor prática de segurança é fornecer apenas os recursos de que o contêiner realmente precisa. Explorei esse assunto em uma publicação anterior. Pode ser surpreendente saber que a maioria dos processos não precisa de nenhum recurso do kernel, pois, mesmo quando precisam de permissões elevadas, isso pode ser controlado principalmente com permissões no nível dos arquivos.
Comece removendo todos os recursos no securityContext e, em seguida, adicione apenas os necessários. Para depurar falhas, consulte a saída de ferramentas como o SELinux e identifique quais recursos podem estar causando o problema. Também é importante lembrar que os contêineres do Kubernetes podem ser executados como root, a menos que você especifique outro usuário.
Como aplicar as configurações de recursos do securityContext no Kubernetes
Para garantir que as configurações do securityContext, como os recursos e a execução como usuário não root, estejam definidas, podemos usar controladores de admissão no cluster do Kubernetes. Assim, os contêineres não serão iniciados sem as configurações de segurança adequadas. O Kubernetes inclui o controlador PodSecurityPolicy, que permite aplicar configurações do securityContext.
No entanto, observe que esse recurso será descontinuado na versão 1.21, em favor de projetos mantidos externamente, como o Open Policy Agent.
Também precisamos incorporar ao nosso processo de desenvolvimento a visibilidade e a correção desse tipo de configuração de segurança. O Snyk pode analisar seus arquivos YAML do Kubernetes, detectar configurações inseguras de recursos no securityContext e outros problemas de configuração, além de oferecer recomendações de correção diretamente nos fluxos de trabalho dos desenvolvedores. Esse recurso está disponível pela Snyk CLI e também pode ser integrado diretamente a sistemas de gerenciamento de código-fonte e de integração contínua:

Quer experimentar esse recurso? Crie uma conta gratuita hoje mesmo!
Comece a participar de desafios de Capture the Flag
Aprenda a resolver desafios de Capture the Flag assistindo sob demanda ao nosso workshop virtual introdutório.
