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 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: