Seccomp em Kubernetes: 7 coisas que vocĂȘ precisa saber desde o inĂ­cio

Nota perev. : Apresentando a tradução de um artigo por um engenheiro de segurança de aplicativos sĂȘnior da empresa britĂąnica ASOS.com. Com ela, ele inicia uma sĂ©rie de publicaçÔes sobre como melhorar a segurança no Kubernetes atravĂ©s do uso do seccomp. Se os leitores gostarem da introdução, seguiremos o autor e continuaremos com seus futuros materiais sobre este tĂłpico.



Este artigo é a primeira de uma série de publicaçÔes sobre como criar perfis seccomp no espírito do SecDevOps sem recorrer à magia e à bruxaria. Na primeira parte, falarei sobre os conceitos båsicos e internos da implementação do seccomp no Kubernetes.

O ecossistema Kubernetes oferece uma ampla variedade de maneiras para garantir a segurança e o isolamento dos contĂȘineres. Este artigo Ă© sobre o Secure Computing Mode, tambĂ©m conhecido como seccomp . Sua essĂȘncia estĂĄ nas chamadas do sistema de filtragem disponĂ­veis para execução dos contĂȘineres.

Por que isso Ă© importante? Um contĂȘiner Ă© apenas um processo em execução em uma mĂĄquina especĂ­fica. E ele usa o kernel em pĂ© de igualdade com outros aplicativos. Se os contĂȘineres pudessem fazer chamadas do sistema, muito em breve o malware seria aproveitado para contornar o isolamento do contĂȘiner e afetar outros aplicativos: interceptar informaçÔes, alterar configuraçÔes do sistema etc.

Os perfis seccomp determinam quais chamadas do sistema devem ser permitidas ou negadas. O tempo de execução do contĂȘiner os ativa durante seu lançamento, para que o kernel possa controlar sua execução. O uso desses perfis permite limitar o vetor de ataque e reduzir os danos se algum programa dentro do contĂȘiner (ou seja, suas dependĂȘncias ou suas dependĂȘncias) começar a fazer o que nĂŁo Ă© permitido.

Compreendendo o bĂĄsico


O perfil base seccomp inclui trĂȘs elementos: defaultAction , architectures (ou archMap ) e syscalls :

 { "defaultAction": "SCMP_ACT_ERRNO", "architectures": [ "SCMP_ARCH_X86_64", "SCMP_ARCH_X86", "SCMP_ARCH_X32" ], "syscalls": [ { "names": [ "arch_prctl", "sched_yield", "futex", "write", "mmap", "exit_group", "madvise", "rt_sigprocmask", "getpid", "gettid", "tgkill", "rt_sigaction", "read", "getpgrp" ], "action": "SCMP_ACT_ALLOW" } ] } 

( medium-basic-seccomp.json )

defaultAction determina o destino padrão de qualquer chamada do sistema não especificada na seção syscalls . Para simplificar a tarefa, focamos em dois valores principais que serão usados:

  • SCMP_ACT_ERRNO - bloqueia a execução de uma chamada do sistema,
  • SCMP_ACT_ALLOW - permite.

A seção architectures lista as arquiteturas de destino. Isso Ă© importante, pois o prĂłprio filtro, aplicado no nĂ­vel do kernel, depende dos identificadores de chamadas do sistema, e nĂŁo dos nomes registrados no perfil. Antes de usar, o tempo de execução do contĂȘiner mapeia-os para identificadores. O ponto Ă© que as chamadas do sistema podem ter IDs completamente diferentes, dependendo da arquitetura do sistema. Por exemplo, a chamada do sistema recvfrom (usada para obter informaçÔes de um soquete) possui ID = 64 em sistemas x64 e ID = 517 em x86. Aqui vocĂȘ pode encontrar uma lista de todas as chamadas do sistema para arquiteturas x86-x64.

A seção syscalls lista todas as chamadas do sistema e indica o que fazer com elas. Por exemplo, vocĂȘ pode criar uma lista de permissĂ”es definindo defaultAction como SCMP_ACT_ERRNO e atribuir chamadas Ă  seção SCMP_ACT_ALLOW a SCMP_ACT_ALLOW . Assim, vocĂȘ sĂł permite chamadas registradas na seção syscalls e proĂ­be todas as outras. Para a lista negra, vocĂȘ deve alterar os valores e açÔes defaultAction para o oposto.

Agora, algumas palavras devem ser ditas sobre as nuances que nĂŁo sĂŁo tĂŁo Ăłbvias. Observe que as recomendaçÔes abaixo vĂȘm do fato de vocĂȘ estar implantando uma linha de aplicativos de negĂłcios no Kubernetes e Ă© importante que eles trabalhem com o mĂ­nimo de privilĂ©gios.

1. AllowPrivilegeEscalation = false


HĂĄ um parĂąmetro AllowPrivilegeEscalation no securityContext contĂȘiner. Se estiver definido como false , os contĂȘineres começarĂŁo com o bit no_new_priv definido como ( on ). O significado desse parĂąmetro Ă© Ăłbvio no nome: ele nĂŁo permite que o contĂȘiner inicie novos processos com privilĂ©gios maiores do que aqueles que possui.

Um efeito colateral desse parĂąmetro definido como true (o valor padrĂŁo) Ă© que o tempo de execução do contĂȘiner aplica o perfil seccomp no inĂ­cio do processo de inicialização. Portanto, todas as chamadas do sistema necessĂĄrias para iniciar os processos internos do tempo de execução (por exemplo, configurando identificadores de usuĂĄrio / grupo, eliminando alguns recursos) devem ser permitidas no perfil.

O contĂȘiner que executa o echo hi banal echo hi precisarĂĄ das seguintes permissĂ”es:

 { "defaultAction": "SCMP_ACT_ERRNO", "architectures": [ "SCMP_ARCH_X86_64", "SCMP_ARCH_X86", "SCMP_ARCH_X32" ], "syscalls": [ { "names": [ "arch_prctl", "brk", "capget", "capset", "chdir", "close", "execve", "exit_group", "fstat", "fstatfs", "futex", "getdents64", "getppid", "lstat", "mprotect", "nanosleep", "newfstatat", "openat", "prctl", "read", "rt_sigaction", "statfs", "setgid", "setgroups", "setuid", "stat", "uname", "write" ], "action": "SCMP_ACT_ALLOW" } ] } 

( hi-pod-seccomp.json )

... em vez destes:

 { "defaultAction": "SCMP_ACT_ERRNO", "architectures": [ "SCMP_ARCH_X86_64", "SCMP_ARCH_X86", "SCMP_ARCH_X32" ], "syscalls": [ { "names": [ "arch_prctl", "brk", "close", "execve", "exit_group", "futex", "mprotect", "nanosleep", "stat", "write" ], "action": "SCMP_ACT_ALLOW" } ] } 

( hi-container-seccomp.json )

Mas, novamente, por que isso Ă© um problema? Pessoalmente, eu evitaria colocar na lista branca as seguintes chamadas do sistema (se elas nĂŁo forem realmente necessĂĄrias): capset , set_tid_address , setgid , setgroups e setuid . No entanto, a dificuldade real Ă© que, ao permitir processos sobre os quais vocĂȘ nĂŁo tem absolutamente nenhum controle, vocĂȘ vincula perfis Ă  implementação do tempo de execução do contĂȘiner. Em outras palavras, um dia vocĂȘ pode encontrar o fato de que, apĂłs atualizar o ambiente de tempo de execução do contĂȘiner (por vocĂȘ ou, mais provavelmente, pelo provedor de serviços em nuvem), os contĂȘineres pararĂŁo repentinamente.

Dica 1 : execute contĂȘineres com AllowPrivilegeEscaltion=false . Isso reduzirĂĄ o tamanho dos perfis seccomp e os tornarĂĄ menos sensĂ­veis Ă s alteraçÔes no tempo de execução do contĂȘiner.

2. Configurando perfis seccomp no nĂ­vel do contĂȘiner


O perfil seccomp pode ser definido no nĂ­vel do pod:

 annotations: seccomp.security.alpha.kubernetes.io/pod: "localhost/profile.json" 

... ou no nĂ­vel do contĂȘiner:

 annotations: container.security.alpha.kubernetes.io/<container-name>: "localhost/profile.json" 

Observe que a sintaxe acima serĂĄ alterada quando o Kubernetes seccomp se tornar GA (este evento Ă© esperado na prĂłxima versĂŁo do Kubernetes - 1.18 - aprox. Transl.).

Poucas pessoas sabem que o Kubernetes sempre teve um bug que fazia com que os perfis seccomp fossem aplicados ao contĂȘiner de pausa . O tempo de execução compensa parcialmente essa desvantagem, mas esse contĂȘiner nĂŁo desaparece dos pods, pois Ă© usado para configurar sua infraestrutura.

O problema Ă© que esse contĂȘiner sempre começa com AllowPrivilegeEscalation=true , levando aos problemas AllowPrivilegeEscalation=true no parĂĄgrafo 1, e isso nĂŁo pode ser alterado.

Ao aplicar perfis seccomp no nĂ­vel do contĂȘiner, vocĂȘ evita essa interceptação e pode criar um perfil que serĂĄ "aguçado" para um contĂȘiner especĂ­fico. Isso terĂĄ que ser feito atĂ© que os desenvolvedores consertem o bug e a nova versĂŁo (talvez 1,18?) Fique disponĂ­vel para todos.

Dica 2 : defina perfis seccomp no nĂ­vel do contĂȘiner.

Em um sentido pråtico, essa regra geralmente serve como uma resposta universal à pergunta: "Por que meu perfil seccomp funciona com o docker run , mas não funciona após a implantação em um cluster Kubernetes?"

3. Use o tempo de execução / padrĂŁo como Ășltimo recurso


O Kubernetes possui duas opçÔes para perfis internos: runtime/default e docker/default . Ambos sĂŁo implementados pelo tempo de execução do contĂȘiner, nĂŁo pelo Kubernetes. Portanto, eles podem diferir dependendo do tempo de execução usado e de sua versĂŁo.

Em outras palavras, como resultado da alteração do tempo de execução, o contĂȘiner pode acessar outro conjunto de chamadas do sistema que podem ser usadas ou nĂŁo. A maioria dos tempos de execução usa uma implementação do Docker . Se vocĂȘ deseja usar esse perfil, verifique se ele combina com vocĂȘ.

O docker/default perfil docker/default estĂĄ obsoleto desde o Kubernetes 1.11, portanto, evite usĂĄ-lo.

Na minha opinião, o perfil de runtime/default é perfeito para a finalidade para a qual foi criado: proteger os usuårios dos riscos associados à execução do docker run em suas måquinas. No entanto, se falarmos sobre aplicativos de negócios executados em clusters do Kubernetes, ousaria afirmar que esse perfil é muito aberto e que os desenvolvedores devem se concentrar na criação de perfis para seus aplicativos (ou tipos de aplicativos).

Dica # 3 : Crie perfis seccomp para aplicativos especĂ­ficos. Se isso nĂŁo for possĂ­vel, lide com perfis para tipos de aplicativos, por exemplo, crie um perfil avançado que inclua todas as APIs de aplicativos da web Golang. Somente como Ășltimo recurso, use o tempo de execução / padrĂŁo.

Nas prĂłximas publicaçÔes, mostrarei como criar perfis secccomp no espĂ­rito do SecDevOps, automatizar e testĂĄ-los em pipelines. Em outras palavras, vocĂȘ nĂŁo terĂĄ desculpas para nĂŁo mudar para perfis para aplicativos especĂ­ficos.

4. Não confinado NÃO é uma opção


Desde a primeira auditoria de segurança do Kubernetes, o seccomp foi desativado por padrĂŁo. Isso significa que, se vocĂȘ nĂŁo especificar um PodSecurityPolicy que o habilitarĂĄ no cluster, todos os pods para os quais o perfil seccomp nĂŁo estĂĄ definido funcionarĂŁo no modo seccomp=unconfined .

Trabalhar nesse modo significa que uma camada inteira de isolamento é perdida, o que fornece proteção de cluster. Essa abordagem não é recomendada pelos profissionais de segurança.

Dica # 4 : Nenhum contĂȘiner em um cluster deve funcionar no modo seccomp=unconfined , especialmente em ambientes de produção.

5. "Modo de auditoria"


Este ponto nĂŁo Ă© exclusivo do Kubernetes, mas ainda se enquadra na categoria de "o que vocĂȘ deve saber antes de começar".

Aconteceu que a criação de perfis seccomp sempre foi um negĂłcio complicado e foi amplamente baseado em tentativa e erro. O fato Ă© que os usuĂĄrios simplesmente nĂŁo tĂȘm a oportunidade de testĂĄ-los em ambientes de produção sem arriscar "abandonar" o aplicativo.

ApĂłs o advento do kernel Linux 4.14, tornou-se possĂ­vel executar partes do perfil no modo de auditoria, registrando informaçÔes sobre todas as chamadas do sistema no syslog, mas nĂŁo as bloqueando. VocĂȘ pode ativar este modo usando o parĂąmetro SCMT_ACT_LOG :

SCMP_ACT_LOG : o seccomp não afetarå a operação de um encadeamento que faz uma chamada do sistema se não se enquadrar em nenhuma regra do filtro, mas as informaçÔes sobre a chamada do sistema serão registradas.

Aqui estå um exemplo de estratégia para usar esse recurso:

  1. Permitir chamadas do sistema que sĂŁo necessĂĄrias.
  2. Sistemas de chamada em bloco que se sabe nĂŁo serem Ășteis.
  3. Registre informaçÔes sobre todas as outras chamadas no log.

Um exemplo simplificado Ă© o seguinte:

 { "defaultAction": "SCMP_ACT_LOG", "architectures": [ "SCMP_ARCH_X86_64", "SCMP_ARCH_X86", "SCMP_ARCH_X32" ], "syscalls": [ { "names": [ "arch_prctl", "sched_yield", "futex", "write", "mmap", "exit_group", "madvise", "rt_sigprocmask", "getpid", "gettid", "tgkill", "rt_sigaction", "read", "getpgrp" ], "action": "SCMP_ACT_ALLOW" }, { "names": [ "add_key", "keyctl", "ptrace" ], "action": "SCMP_ACT_ERRNO" } ] } 

( medium-mixed-seccomp.json )

Mas lembre-se de que vocĂȘ precisa bloquear todas as chamadas que sĂŁo conhecidas por nĂŁo serem utilizadas e que podem prejudicar o cluster. Uma boa base para a listagem Ă© a documentação oficial do Docker . Ele explica em detalhes quais chamadas do sistema estĂŁo bloqueadas no perfil padrĂŁo e por quĂȘ.

No entanto, hĂĄ uma captura. Embora o SCMT_ACT_LOG suportado pelo kernel Linux desde o final de 2017, ele entrou recentemente apenas no ecossistema Kubernetes. Portanto, para usar esse mĂ©todo, vocĂȘ precisarĂĄ do kernel Linux 4.14 e da versĂŁo runC nĂŁo inferior Ă  v1.0.0-rc9 .

Dica 5 : vocĂȘ pode criar um perfil de modo de auditoria para teste em produção combinando listas em preto e branco e registrar todas as exceçÔes.

6. Use listas brancas


Criar listas brancas requer esforços adicionais, pois vocĂȘ precisa identificar todas as chamadas que o aplicativo possa precisar, mas essa abordagem melhora significativamente a segurança:

É altamente recomendĂĄvel que vocĂȘ use a abordagem da lista de permissĂ”es, pois Ă© mais simples e mais confiĂĄvel. A lista negra precisarĂĄ ser atualizada sempre que uma chamada do sistema potencialmente perigosa (ou sinalizador / opção perigosa, se estiverem na lista negra) for adicionada. AlĂ©m disso, vocĂȘ pode alterar frequentemente a apresentação de um parĂąmetro sem alterar sua essĂȘncia e, assim, contornar as limitaçÔes da lista negra.

Para aplicativos Go, desenvolvi uma ferramenta especial que acompanha o aplicativo e coleta todas as chamadas feitas em tempo de execução. Por exemplo, para o seguinte aplicativo:

 package main import "fmt" func main() { fmt.Println("test") } 

... execute gosystract assim:

 go install https://github.com/pjbgf/gosystract gosystract --template='{{- range . }}{{printf "\"%s\",\n" .Name}}{{- end}}' application-path 

... e obtenha o seguinte resultado:

 "sched_yield", "futex", "write", "mmap", "exit_group", "madvise", "rt_sigprocmask", "getpid", "gettid", "tgkill", "rt_sigaction", "read", "getpgrp", "arch_prctl", 

Até agora, este é apenas um exemplo - detalhes sobre as ferramentas serão mais detalhados.

Dica # 6 : permita apenas chamadas que vocĂȘ realmente precisa e bloqueie todos os outros.

7. Estabeleça as bases (ou prepare-se para comportamentos inesperados)


O kernel monitorarĂĄ a conformidade com o perfil, nĂŁo importa o que vocĂȘ registrou nele. Mesmo que isso nĂŁo seja exatamente o que eu queria. Por exemplo, se vocĂȘ bloquear o acesso a chamadas como exit ou exit_group , o contĂȘiner nĂŁo poderĂĄ concluir o trabalho corretamente e mesmo um comando simples como echo hi suspenderĂĄ por um perĂ­odo indeterminado. Como resultado, vocĂȘ obterĂĄ alta utilização da CPU no cluster:



Nesses casos, o utilitĂĄrio strace pode ser Ăștil - mostrarĂĄ qual Ă© o problema:


sudo strace -c -p 9331

Verifique se os perfis contĂȘm todas as chamadas do sistema que o aplicativo precisa enquanto estĂĄ em execução.

Dica 7 : Preste atenção às pequenas coisas e verifique se todas as chamadas necessårias do sistema estão incluídas na lista branca.

Com isso, a primeira parte de uma série de artigos sobre o uso do seccomp no Kubernetes no espírito do SecDevOps chega ao fim. Nas partes a seguir, falaremos sobre por que isso é importante e como automatizar o processo.

PS do tradutor


Leia também em nosso blog:

Source: https://habr.com/ru/post/pt481114/


All Articles