10 configurações de securityContext do Kubernetes que você precisa conhecer
10 de março de 2021
0 minutos de leituraExecutar workloads com segurança no Kubernetes pode ser difícil. Muitas configurações afetam a segurança da API do Kubernetes, e implementá-las corretamente exige bastante conhecimento. Uma das ferramentas mais poderosas que o Kubernetes oferece nessa área são as configurações de securityContext, que podem ser usadas em todos os manifests de Pod e Container. Neste guia de referência, vamos analisar as várias configurações de securityContext, entender o que elas significam e como usá-las.

runAsNonRoot
runAsUser / runAsGroup
seLinuxOptions
seccompProfile
privileged / allowPrivilegeEscalation
capabilities
readonlyRootFilesystem
procMount
fsGroup / fsGroupChangePolicy
sysctls
Configurações de Pod vs. Container
As configurações de securityContext do Kubernetes são definidas nas APIs PodSpec e ContainerSpec, e o escopo é indicado neste documento pelas anotações [P] e/ou [C] ao lado de cada configuração. Observe que, se uma configuração estiver disponível e definida nos dois escopos, a configuração do container terá precedência.
Agora, sem uma ordem específica, vamos analisar as configurações de securityContext:
1. runAsNonRoot [P/C]
Embora um container use namespaces e cgroups para limitar seus processos, basta uma configuração incorreta nas definições de implantação para dar a esses processos acesso aos recursos do host. Se o processo for executado como root, terá o mesmo acesso a esses recursos que a conta root do host. Além disso, se outras configurações de Pod ou container forem usadas para reduzir as restrições (por exemplo, procMount ou capabilities), um UID root aumenta os riscos de qualquer exploração dessas configurações. A menos que haja um bom motivo, nunca execute um container como root.
Então, o que fazer se você tiver uma imagem para implantar que é executada como root?
Opção 1: use o usuário fornecido pela imagem base
Muitas vezes, as imagens base já incluem um usuário, mas deixam a cargo das equipes de desenvolvimento ou implantação usá-lo. Por exemplo, a imagem oficial do Node.js inclui um usuário chamado node, com UID 1000, que você pode usar para executar o processo, mas não o define explicitamente como usuário atual no Dockerfile. Será necessário configurá-lo durante a execução com a opção runAsUser ou alterar o usuário atual na imagem usando um Dockerfile derivado. A primeira opção pressupõe que o UID 1000 tenha permissão para ler os arquivos no diretório da aplicação. Em vez disso, vamos ver um exemplo que usa um Dockerfile derivado para criar nossa própria imagem.
Sem nos aprofundarmos demais na criação de imagens, vamos supor que temos uma aplicação npm já compilada. Veja um Dockerfile mínimo para criar uma imagem baseada em [**node:slim**](https://hub.docker.com/_/node) e executá-la como o usuário node fornecido.
A linha principal começa com USER, que define node como o usuário padrão em qualquer container iniciado a partir dessa imagem. Usamos o UID em vez do nome do usuário porque o Kubernetes não consegue associar o nome de usuário padrão de uma imagem ao UID antes de iniciar o container e retornará um erro se a implantação especificar runAsNotRoot: true.
Opção 2: a imagem base não fornece um usuário
E se a imagem base do node não fornecesse um usuário para usar? Para muitos processos, basta criar um usuário em um Dockerfile derivado e usá-lo. Vamos adaptar o exemplo anterior para fazer isso:
Como você pode ver, a única adição é a linha RUN, que cria um usuário — a sintaxe pode variar conforme a distribuição da imagem base —, e depois alterei as referências ao usuário e ao caminho para corresponder a ele.
OBSERVAÇÃO: isso funciona bem com node.js e npm, mas outras ferramentas podem exigir alterações de propriedade em outros elementos do sistema de arquivos. Se tiver algum problema, consulte a documentação da ferramenta.
2. runAsUser / runAsGroup [P/C]
As imagens de container podem ter um usuário e/ou grupo específico configurado para executar o processo. É possível substituir essa configuração usando as opções runAsUser e runAsGroup. Muitas vezes, elas são definidas em conjunto com montagens de volume que contêm arquivos com os mesmos IDs de propriedade.
Há riscos em usar essas configurações, pois você estará tomando decisões em tempo de execução para o container que podem não ser compatíveis com a imagem original. Por exemplo, a imagem oficial do servidor de CI jenkins/jenkins é executada com o grupo e o usuário jenkins:jenkins, e os arquivos da aplicação pertencem a esse usuário. Se configurarmos outro usuário, a inicialização falhará porque ele não existe no arquivo /etc/passwd da imagem. Mesmo que existisse, é muito provável que tivesse problemas para ler e gravar nos arquivos pertencentes a jenkins:jenkins. Um simples comando docker run permite verificar isso:
Como mencionamos acima, é uma boa prática garantir que os processos do container não sejam executados como usuário root, mas não confie nas configurações runAsUser ou runAsGroup para garantir isso. Alguém pode removê-las no futuro. Defina também runAsNonRoot como true.
3. seLinuxOptions [P/C]
O SELinux é um sistema baseado em políticas que controla o acesso a aplicações, processos e arquivos em um sistema Linux. Ele implementa no kernel Linux a estrutura Linux Security Modules. O SELinux se baseia no conceito de rótulos, que são aplicados a todos os elementos do sistema para agrupá-los. Esses rótulos são chamados de contexto de segurança — não confunda com o securityContext do Kubernetes — e consistem em user, role, type e um campo opcional level, no formato user:role:type:level.
O SELinux usa políticas para definir quais processos de determinado contexto podem acessar outros objetos rotulados no sistema. O SELinux pode ser aplicado rigorosamente, caso em que o acesso será negado, ou configurado no modo permissivo, que registra o acesso em logs. Em containers, o SELinux normalmente rotula o processo e a imagem do container de modo a restringir o processo ao acesso apenas aos arquivos dentro da imagem.
O runtime do container aplica os rótulos SELinux padrão ao criar um container. A opção seLinuxOptions em securityContext permite aplicar rótulos SELinux personalizados. Fique atento: alterar a rotulagem SELinux de um container pode permitir que o processo dentro dele escape da imagem do container e acesse o sistema de arquivos do host.
Essa funcionalidade só se aplica se o sistema operacional do host oferecer suporte ao SELinux.
4. seccompProfile [P/C]
Seccomp significa secure computing mode (modo de computação segura) e é um recurso do kernel Linux que pode restringir as chamadas que determinado processo faz do espaço do usuário para o kernel. Um perfil seccomp é uma definição JSON que normalmente contém um conjunto de chamadas de sistema (syscalls) e a ação padrão a ser tomada quando uma delas ocorrer.
O Kubernetes oferece um mecanismo para usar perfis personalizados por meio da opção seccompProfile em securityContext.
Há três valores possíveis para o campo type:
Localhost, em que a opçãolocalhostProfiledefine um caminho dentro do container para um perfil seccompUnconfined, que não aplica nenhum perfil.RuntimeDefault, que usa o padrão do runtime do container — esse é o valor padrão quando o tipo não é especificado
Você pode aplicar essas configurações em um PodSecurityContext ou em securityContext. Se ambos forem definidos, as configurações no nível do container em securityContext serão usadas. Observe que a API de configuração securityContext foi lançada no Kubernetes v1.19. Se você estiver implantando em versões anteriores, a sintaxe é diferente. Consulte o site da documentação do Kubernetes para ver detalhes e exemplos.
Como acontece com a maioria das configurações relacionadas à segurança, aplica-se aqui o princípio do menor privilégio. Dê ao container apenas os privilégios necessários, nada além disso. Comece criando um perfil que apenas registre as chamadas de sistema realizadas e, em seguida, teste a aplicação para definir o conjunto de chamadas permitidas. Saiba mais sobre esse processo nos tutoriais do Kubernetes.
5. Evite containers privilegiados e escalonamento de privilégios [C]
Conceder status privilegiado a um container é perigoso e costuma ser uma forma mais simples de obter permissões específicas que, de outra maneira, poderiam ser controladas concedendo acesso a capabilities. O runtime do container controla a implementação exata da opção privilegiada, mas, na prática, ela concede todos os privilégios ao container e remove as limitações impostas pelo controlador cgroup de dispositivos. Também pode modificar a configuração do Linux Security Module e permitir que processos dentro do container escapem dele.
Os containers isolam processos no host. Por isso, mesmo quando executados como root, há capabilities que o runtime do container não concede a eles. Com a opção privilegiada ativada, o runtime concede todas as capabilities de root do sistema. Isso é extremamente perigoso do ponto de vista da segurança, pois permite acesso completo ao sistema host subjacente.
Evite usar a opção privilegiada. Se o container precisar de capabilities adicionais, adicione apenas as necessárias usando as configurações de capabilities. A menos que seu container precise controlar configurações do kernel do sistema host — como acessar hardware específico ou reconfigurar redes — e acessar o sistema de arquivos do host, ele não precisa da opção privilegiada.
Para saber mais sobre containers privilegiados, confira o artigo de Matt: Containers Docker privilegiados: você realmente precisa deles?
6. Capabilities do kernel Linux [C]
Capabilities são permissões no nível do kernel que permitem um controle mais granular sobre as permissões de chamadas ao kernel do que simplesmente executar tudo como root. Elas incluem ações como alterar permissões de arquivos, controlar o subsistema de rede e executar funções de administração em todo o sistema. Em securityContext, o Kubernetes permite configurar a remoção ou a adição de capabilities. É possível especificar capabilities individuais ou uma lista separada por vírgulas como uma matriz de strings. Outra opção é usar a abreviação -all para adicionar ou remover todas as capabilities. Essa configuração é repassada ao runtime do container, que define o conjunto de capabilities ao criar o container. Se a seção capabilities não estiver presente em securityContext, o container receberá o conjunto padrão de capabilities fornecido pelo runtime.
A prática recomendada é remover todas as capabilities e adicionar apenas as que sua aplicação realmente precisa. Em muitos casos, as aplicações não precisam de nenhuma capability durante a operação normal. Teste isso removendo todas e investigue possíveis falhas monitorando os logs de auditoria para identificar quais capabilities foram bloqueadas.
Observe que, ao listar capabilities para remover ou adicionar em securityContext, você deve omitir o prefixo CAP_, usado pelo kernel ao nomeá-las. Para depuração, a ferramenta capsh fornece uma saída legível que mostra exatamente quais capabilities estão habilitadas no container e está disponível na maioria das distribuições. Não deixe essa ferramenta disponível em containers de produção, pois isso facilita muito a identificação das capabilities habilitadas por um invasor. Se preferir ler mapas de bits, você também pode consultar as capabilities habilitadas no arquivo /proc/1/status.
Saiba como reforçar a segurança do Kubernetes removendo as capabilities padrão de um container.
7. Execute com um sistema de arquivos somente leitura [C]
Se o seu contêiner for comprometido e tiver um sistema de arquivos com permissão de leitura e gravação, um invasor poderá alterar sua configuração, instalar software e possivelmente iniciar outros ataques. Um sistema de arquivos somente leitura ajuda a evitar esse tipo de escalonamento, limitando as ações que um invasor pode realizar. Em geral, os contêineres não precisam gravar no próprio sistema de arquivos. Se a sua aplicação tiver dados com estado, use um método de persistência externo, como um banco de dados, um volume ou outro serviço. Além disso, garanta que todos os logs sejam gravados em stdout e/ou encaminhados para um serviço de logs, onde possam ser centralizados.
8. procMount [C]
Por padrão, os runtimes de contêineres mascaram determinadas partes do sistema de arquivos /proc dentro de um contêiner para evitar possíveis problemas de segurança. No entanto, há situações em que é necessário acessar essas partes de /proc, especialmente ao usar contêineres aninhados, como costuma acontecer em processos de build dentro do cluster. Há apenas duas opções válidas para esta entrada: Default, que mantém o comportamento padrão do runtime de contêineres, ou Unmasked, que remove todas as máscaras do sistema de arquivos **/proc**.
É claro que você só deve usar esta configuração se realmente souber o que está fazendo. Se estiver usando-a para criar imagens, confira a versão mais recente da sua ferramenta de build, pois muitas já não precisam mais disso. Atualize a ferramenta e volte ao valor padrão de procMount adequado à ferramenta que você está usando.
Por fim, se você realmente precisar usar esta configuração, faça isso apenas em um contêiner aninhado; nunca exponha o sistema de arquivos /proc do host a um contêiner.
9. fsGroup / fsGroupChangePolicy [P]
A configuração fsGroup define um grupo para o qual o Kubernetes altera as permissões de todos os arquivos nos volumes quando eles são montados por um pod. Esse comportamento também é controlado por fsGroupChangePolicy, que pode ser definido como onRootMismatch ou Always. Se definido como onRootMismatch, as permissões só serão alteradas se ainda não corresponderem às permissões da raiz do contêiner.
Tenha cuidado ao usar fsGroup. Alterar o grupo proprietário de um volume inteiro pode atrasar a inicialização do pod em sistemas de arquivos grandes e/ou lentos. Isso também pode prejudicar outros processos que compartilham o mesmo volume, caso não tenham permissão de acesso ao novo GID. Por esse motivo, alguns provedores de sistemas de arquivos compartilhados, como o NFS, não implementam essa funcionalidade. Essas configurações também não afetam volumes efêmeros.
10. sysctls [P]
Sysctls são um recurso do kernel Linux que permite aos administradores modificar sua configuração. Em um sistema operacional Linux completo, eles são definidos em /etc/sysctl.conf e também podem ser modificados usando o utilitário **sysctl**.
A configuração sysctls em securityContext permite modificar sysctls específicos no contêiner. Apenas um pequeno subconjunto dos sysctls do sistema operacional pode ser modificado por contêiner, pois esses parâmetros são isolados por namespace no kernel. Alguns desse subconjunto são considerados seguros. Um conjunto muito maior é considerado inseguro, dependendo do potencial de impacto em outros pods. Em geral, os sysctls inseguros ficam desativados nos clusters e precisam ser habilitados explicitamente pelo administrador do cluster.
Como podem desestabilizar o sistema operacional subjacente, evite modificar parâmetros do kernel por meio de sysctls, a menos que você tenha requisitos muito específicos. Você também deve revisar essas alterações com o operador do cluster.
Uma observação sobre securityContext em tempo de execução
Em muitos casos, as configurações de segurança descritas aqui são combinadas com o controle de admissão baseado em políticas para garantir que as configurações necessárias estejam definidas antes do lançamento de contêineres no cluster. Ao combinar as configurações de securityContext com uma PodSecurityPolicy, você pode garantir que apenas contêineres em conformidade com a política sejam iniciados, exigindo configurações específicas de securityContext. As configurações de securityContext também podem ser adicionadas à configuração do contêiner no momento da inicialização por meio do Controle de admissão dinâmico e do uso de webhooks de mutação.
Conclusão
Há muitos aspectos a considerar ao reforçar a segurança das implantações da sua aplicação com as configurações de securityContext. Quando usadas corretamente, elas são uma ferramenta muito eficaz. Esperamos que esta lista ajude suas equipes a escolher as opções certas para suas cargas de trabalho e ambientes. A Snyk pode ajudar nessas escolhas, analisando seus arquivos YAML do Kubernetes em busca de erros comuns de configuração. Crie sua conta gratuita usando o botão abaixo.
Proteja a infraestrutura desde a origem
A Snyk automatiza a segurança e a conformidade de IaC nos fluxos de trabalho e detecta recursos com configurações divergentes ou ausentes.


