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:
- Permitir chamadas do sistema que sĂŁo necessĂĄrias.
- Sistemas de chamada em bloco que se sabe nĂŁo serem Ășteis.
- 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 9331Verifique 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: