Como executar cargas de trabalho não confiáveis com o GKE Sandbox

Nesta página, você verá como usar o GKE Sandbox para proteger o kernel do host nos nós quando contêineres no pod executam códigos desconhecidos ou não confiáveis. Por exemplo, clusters multilocatários, como provedores de Software as a Service (SaaS), geralmente executam código desconhecido enviado por usuários.

O GKE Sandbox usa o gVisor, um projeto de código aberto. Neste tópico, o gVisor é abordado mais amplamente, mas é possível saber mais detalhes na documentação oficial do gVisor (em inglês).

Visão geral

O GKE Sandbox fornece uma camada extra de segurança para evitar que um código não confiável afete o kernel do host nos nós do cluster. Antes de discutir como o GKE Sandbox funciona, é bom entender a natureza dos riscos potenciais que ele ajuda a mitigar.

Um ambiente de execução do contêiner, como docker ou containerd, fornece algum grau de isolamento entre os processos do contêiner e o kernel em execução no nó. No entanto, o ambiente de execução do contêiner é frequentemente executado como um usuário privilegiado no nó e tem acesso à maioria das chamadas do sistema no kernel do host.

Ameaças em potencial

Clusters de multilocação e clusters que têm contêineres que executam cargas de trabalho não confiáveis estão mais expostos a vulnerabilidades de segurança do que outros clusters. Os exemplos incluem provedores de SaaS, provedores de hospedagem na Web ou outras organizações que permitem que os usuários deles façam upload e executem códigos. Com uma falha no ambiente de execução do contêiner ou no kernel do host, é possível que um processo executado em um contêiner "escape" do contêiner e afete o kernel do nó, possivelmente derrubando o nó.

Também existe a possibilidade de um locatário mal-intencionado ter acesso e extrair dados de outro locatário na memória ou no disco, explorando esse tipo de falha.

Por fim, uma carga de trabalho não confiável pode acessar outros metadados de cluster ou serviços do Google Cloud Platform.

Como o GKE Sandbox mitiga essas ameaças

O gVisor é uma reimplementação do espaço do usuário da API do kernel do Linux que não precisa de privilégios elevados. Em conjunto com um ambiente de execução do contêiner, como containerd, o kernel do espaço do usuário reimplementa a maioria das chamadas do sistema e as atende em nome do kernel do host. O acesso direto ao kernel do host é limitado. Consulte o Guia de arquitetura do gVisor (em inglês) para informações detalhadas sobre como isso funciona. Do ponto de vista do contêiner, o gVisor é quase transparente e não requer alterações no aplicativo em contêiner.

Quando você ativa o GKE Sandbox em um pool de nós, um sandbox é criado para cada pod executado em um nó nesse pool. Além disso, os nós que executam pods no sandbox são impedidos de acessar outros serviços do GCP ou metadados de cluster.

Cada sandbox usa seu próprio kernel do espaço do usuário. Com isso em mente, é possível tomar decisões sobre como agrupar seus contêineres nos pods, com base no nível de isolamento necessário e nas características dos aplicativos.

O GKE Sandbox é ideal para os seguintes tipos de aplicativos. Consulte a parte de limitações para mais informações. Elas ajudarão você a decidir quais aplicativos serão colocados no sandbox.

  • Aplicativos não confiáveis ou de terceiros que usam ambientes de execução como Rust, Java, Python, PHP, Node.js ou Golang.
  • Proxies, caches ou front-ends do servidor Web.
  • Aplicativos que processam mídia ou dados externos com CPUs.
  • Cargas de trabalho de machine learning que usam CPUs.
  • Aplicativos que consomem muita memória ou CPU.

Outras recomendações de segurança

Ao usar o GKE Sandbox, aconselhamos que você também siga estas recomendações:

  • A menos que seus nós usem apenas uma única vCPU, recomendamos que você desative o Hyper-Threading para mitigar as vulnerabilidades da amostragem de dados de microarquitetura (MDS, na sigla em inglês) anunciadas pela Intel. Para mais informações, consulte o boletim de segurança.

  • É altamente recomendado especificar os limites de recursos (em inglês) em todos os contêineres em execução em um sandbox. Isso evita o risco de um aplicativo defeituoso ou malicioso privar o nó de recursos e afetar negativamente outros aplicativos ou processos do sistema em execução no nó.

Limitações

O GKE Sandbox funciona bem com muitos aplicativos, mas não todos. Você verá nesta seção mais informações sobre as limitações atuais do GKE Sandbox.

Configuração do pool de nós

  • Não é possível ativar o GKE Sandbox no pool de nós padrão.
  • Ao usar o GKE Sandbox, seu cluster precisa ter pelo menos dois pools de nós. É sempre necessário ter pelo menos um pool de nós em que o GKE Sandbox esteja desativado. Esse pool precisa conter pelo menos um nó, mesmo que todas as suas cargas de trabalho estejam no sandbox.

Acesso aos metadados do cluster

  • Os nós que executam os Pods no sandbox são impedidos de acessar os metadados do cluster no nível do sistema operacional no nó.
  • É possível executar pods regulares em um nó com o GKE Sandbox ativado. No entanto, por padrão, esses pods regulares não podem acessar os serviços do GCP nem os metadados do cluster. Use a Identidade da carga de trabalho para conceder aos pods acesso aos serviços do GCP.

Recursos

Por padrão, o contêiner é impedido de abrir soquetes brutos para reduzir o potencial de ataques mal-intencionados. Algumas ferramentas relacionadas à rede, como ping e tcpdump, criam soquetes brutos como parte da funcionalidade principal delas. Para ativar soquetes brutos, adicione explicitamente o recurso NET_RAW ao contexto de segurança do contêiner:

spec:
  containers:
  - name: my-container
    securityContext:
      capabilities:
        add: ["NET_RAW"]

Recursos incompatíveis

No momento, não é possível usar o GKE Sandbox com os seguintes recursos do Kubernetes:

  • Aceleradores como GPUs ou TPUs.
  • Istio.
  • Monitoramento de estatísticas no nível do pod ou contêiner.
  • Armazenamento do Hostpath (em inglês).
  • Os limites de CPU e memória são aplicados somente para pods garantidos e com burst e somente quando esses limites são especificados para todos os contêineres em execução no pod.
  • Pods usando PodSecurityPolicies (em inglês) que especificam namespaces de host, como hostNetwork, hostPID, hostIPC.
  • Pods usando as configurações do PodSecurityPolicy, como modo privilegiado (em inglês).
  • VolumeDevices (em inglês).
  • Portforward (em inglês).
  • Módulos de segurança do kernel do Linux, como Seccomp, Apparmor ou Selinux Sysctl, NoNewPrivileges, MountPropagation bidirecional, FSGroup ou ProcMount (páginas em inglês).

Características da carga de trabalho

Impor uma camada extra de indireção para acessar o kernel do nó tem vantagens e desvantagens de desempenho. O GKE Sandbox oferece benefícios mais evidentes em clusters de multilocatários grandes em que o isolamento é importante. Pense nas seguintes diretrizes ao testar suas cargas de trabalho com o GKE Sandbox.

Chamadas do sistema

Cargas de trabalho que geram um grande volume de chamadas de sistema com baixa sobrecarga, como um grande número de operações pequenas de E/S, podem exigir mais recursos do sistema durante a execução em um sandbox. Por isso, talvez seja necessário usar nós mais poderosos ou adicionar mais nós ao cluster.

Acesso direto ao hardware ou à virtualização

Se a carga de trabalho precisar de qualquer um dos itens a seguir, o GKE Sandbox talvez não seja adequado porque impede o acesso direto ao kernel do host no nó:

  • Acesso direto ao hardware do nó
  • Recursos de virtualização no nível do kernel
  • Contêineres privilegiados

Como ativar o GKE Sandbox

É possível ativar o GKE Sandbox em um cluster novo ou atual.

Antes de começar

Execute as etapas a seguir para se preparar para a tarefa:

  • Verifique se você ativou a API do Google Kubernetes Engine.
  • Ativar a API do Google Kubernetes Engine
  • Verifique se o SDK do Cloud está instalado.
  • Defina o ID do projeto padrão:
    gcloud config set project [PROJECT_ID]
  • Se você estiver trabalhando com clusters zonais, defina a zona padrão do Compute:
    gcloud config set compute/zone [COMPUTE_ZONE]
  • Se você estiver trabalhando com clusters regionais, defina a região padrão do Compute:
    gcloud config set compute/region [COMPUTE_REGION]
  • Atualize gcloud para a versão mais recente:
    gcloud components update
  • O GKE Sandbox exige o GKE v1.12.7-gke.17 ou superior, ou v1.13.5-gke.15 ou superior, para os nós e mestre do cluster.
  • Certifique-se de que o comando gcloud seja da versão 243.0.0 ou superior.

Em um novo cluster

Para ativar o GKE Sandbox, configure um pool de nós. O pool de nós padrão (o primeiro pool de nós do cluster, criado quando o cluster é criado) não pode usar o GKE Sandbox. Para ativar o GKE Sandbox durante a criação do cluster, é necessário adicionar um segundo pool de nós ao criar o cluster.

Console

Para visualizar os clusters, acesse o menu do Google Kubernetes Engine no Console do GCP.

  1. Acesse o menu do Google Kubernetes Engine no Console do GCP.

    Acessar o menu do Google Kubernetes Engine

  2. Clique em Criar cluster.

  3. Escolha o Cluster padrão ou um modelo apropriado para a carga de trabalho.

  4. Opcional, mas recomendado : ative o Stackdriver Logging e o Stackdriver Monitoring para que as mensagens do gVisor sejam registradas.

  5. Clique em Adicionar pool de nós.

  6. Configure o pool de nós de acordo com seus requisitos. Clique em Mais opções do pool de nós para o pool de nós. Defina estas configurações:

    • Para a versão do nó, selecione v1.12.6-gke.8 ou superior.
    • Para a imagem do nó, selecione Container-Optimized OS com Containerd (cos_containerd) (Beta).
    • Clique em Ativar sandbox com o gVisor (Beta).
    • Se os nós no pool usarem mais de uma única vCPU, clique em Adicionar rótulo. Defina a chave como cloud.google.com/gke-smt-disabled e o valor como true. Depois, siga as instruções para desativar o Hyper-Threading no boletim de segurança.

    Defina outras configurações do pool de nós conforme necessário.

  7. Salve as configurações do pool de nós e continue configurando o cluster.

gcloud

O GKE Sandbox não pode ser ativado para o pool de nós padrão e não é possível criar outros pools de nós ao mesmo tempo que você cria um novo cluster usando o comando gcloud. Em vez disso, crie seu cluster normalmente. É opcional, mas recomendável, ativar o Stackdriver Logging e o Stackdriver Monitoring adicionando a sinalização --enable-stackdriver-kubernetes. As mensagens do gVisor são registradas.

Em seguida, use o comando gcloud beta container node-pools create e defina a sinalização --sandbox como type=gvisor. Substitua os valores entre colchetes pelos seus e lembre-se de especificar uma versão de nó de v1.12.6-gke.8 ou superior.

gcloud beta container node-pools create [NODE_POOL_NAME] \
  --cluster=[CLUSTER_NAME] \
  --node-version=[NODE_VERSION] \
  --image-type=cos_containerd \
  --sandbox type=gvisor \
  --enable-autoupgrade

A RuntimeClass do gvisor é instanciada durante a criação do nó, antes de qualquer carga de trabalho ser programada no nó. É possível verificar a existência de RuntimeClass do gvisor usando o seguinte comando:

kubectl get runtimeclasses
NAME     AGE
gvisor   19s

Em um cluster atual

É possível ativar o GKE Sandbox em um cluster atual adicionando um novo pool de nós e ativando o recurso para esse pool ou modificando um pool de nós não padrão atual.

Console

  1. Acesse o menu do Google Kubernetes Engine no Console do GCP.

    Acessar o menu do Google Kubernetes Engine

  2. Clique no botão de edição do cluster, que tem a forma de um lápis.

  3. Se necessário, adicione outro pool de nós clicando em Adicionar pool de nós. Para editar um pool de nós atual, clique no botão "Editar" do pool de nós. Não ative o sandbox com o gVisor (Beta) no pool de nós padrão.

  4. Ative o sandbox com o gVisor (Beta) e clique em Concluído.

  5. Se necessário, faça outras alterações de configuração no cluster e clique em Salvar.

gcloud

Para criar um novo pool de nós com o GKE Sandbox ativado, use um comando como este:

gcloud beta container node-pools create [NODE_POOL_NAME] \
  --cluster=[CLUSTER_NAME] \
  --image-type=cos_containerd \
  --sandbox type=gvisor \
  --enable-autoupgrade

Para ativar o GKE Sandbox em um pool de nós atual, use um comando como este. Não ative --sandbox type=gvisor no pool de nós padrão.

 gcloud beta container node-pools update [NODE_POOL_NAME] \
  --sandbox type=gvisor

A RuntimeClass do gvisor é instanciada durante a criação do nó, antes de qualquer carga de trabalho ser programada no nó. É possível verificar a existência de RuntimeClass do gvisor usando o seguinte comando:

kubectl get runtimeclasses
NAME     AGE
gvisor   19s

Opcional: ativar o Stackdriver Logging e o Stackdriver Monitoring

É opcional, mas recomendável, ativar o Stackdriver Logging e o Stackdriver Monitoring no cluster para que as mensagens do gVisor sejam registradas. É necessário usar o Console do Google Cloud Platform para ativar esses recursos em um cluster atual.

  1. Acesse o menu do Google Kubernetes Engine no Console do GCP.

    Acessar o menu do Google Kubernetes Engine

  2. Clique no botão de edição do cluster, que tem a forma de um lápis.

  3. Ative o Stackdriver Logging e o Stackdriver Monitoring.

  4. Se necessário, faça outras alterações de configuração no cluster e clique em Salvar.

Como trabalhar com o GKE Sandbox

Como executar um aplicativo em um sandbox

Para forçar uma implantação a ser executada em um nó com o GKE Sandbox ativado, defina spec.template.spec.runtimeClassName para gvisor, conforme mostrado por este manifesto para uma implantação:

# httpd.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpd
  labels:
    app: httpd
spec:
  replicas: 1
  selector:
    matchLabels:
      app: httpd
  template:
    metadata:
      labels:
        app: httpd
    spec:
      runtimeClassName: gvisor
      containers:
      - name: httpd
        image: httpd

Para criar a implantação, use o comando kubectl create:

kubectl create -f httpd.yaml

O pod é implantado em um nó em um pool de nós com o GKE Sandbox ativado. Para verificar isso, use o comando kubectl get pods para encontrar o nó em que o pod está implantado:

kubectl get pods

NAME                    READY   STATUS    RESTARTS   AGE
httpd-db5899bc9-dk7lk   1/1     Running   0          24s

Encontre o nome do pod na saída e execute o seguinte comando para verificar seu valor para RuntimeClass:

kubectl get pods [NAME-OF-POD] -o jsonpath='{.spec.runtimeClassName}'

gvisor

Como alternativa, é possível listar a RuntimeClass de cada pod e procurar aqueles em que ela está definido como gvisor:

kubectl get pods -o jsonpath=$'{range .items[*]}{.metadata.name}: {.spec.runtimeClassName}\n{end}'

[NAME-OF-POD]: gvisor

Esse método para verificar se o pod está sendo executado em um sandbox é confiável, porque não depende de dados no próprio sandbox. Qualquer item do sandbox que seja reportado não é confiável, porque pode estar com erros ou ser malicioso.

Como executar um pod normal junto com pods no sandbox

Depois de ativar o GKE Sandbox em um pool de nós, é possível executar aplicativos confiáveis nesses nós sem usar um sandbox, com tolerâncias e taints de nó. Esses pods são chamados de “pods normais” para diferenciá-los dos pods em sandbox.

Os pods normais, assim como os no sandbox, são impedidos de acessar outros serviços do GCP ou metadados de cluster. Essa prevenção faz parte da configuração do nó. Se os pods normais ou os no sandbox exigirem acesso aos serviços do GCP, use a Identidade da carga de trabalho.

O GKE Sandbox adiciona o seguinte rótulo e taint aos nós que podem executar pods no sandbox:

labels:
  sandbox.gke.io: gvisor
taints:
- effect: NoSchedule
  key: sandbox.gke.io
  value: gvisor

Além das configurações de afinidade e tolerância do nó no manifesto do pod, o GKE Sandbox aplica a seguinte afinidade e tolerância a todos os pods com RuntimeClass definida no gvisor:

affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
      - matchExpressions:
        - key: sandbox.gke.io/runtime
          operator: In
          values:
          - gvisor
tolerations:
  - effect: NoSchedule
    key: sandbox.gke.io/runtime
    operator: Equal
    value: gvisor

Para programar um pod normal em um nó com o GKE Sandbox ativado, aplique manualmente a afinidade e a tolerância do nó acima no manifesto do pod.

  • Se seu pod puder ser executado em nós com o GKE Sandbox ativado, adicione a tolerância.
  • Se seu pod precisar ser executado em nós com o GKE Sandbox ativado, adicione a afinidade e a tolerância do nó.

Por exemplo, o manifesto a seguir modifica o manifesto usado em Como executar um aplicativo em um sandbox para que ele seja executado como um pod normal em um nó com pods do sandbox removendo a runtimeClass e adicionando a tolerância e o taint acima.

# httpd-no-sandbox.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpd-no-sandbox
  labels:
    app: httpd
spec:
  replicas: 1
  selector:
    matchLabels:
      app: httpd
  template:
    metadata:
      labels:
        app: httpd
    spec:
      containers:
      - name: httpd
        image: httpd
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: sandbox.gke.io/runtime
                operator: In
                values:
                - gvisor
      tolerations:
        - effect: NoSchedule
          key: sandbox.gke.io/runtime
          operator: Equal
          value: gvisor

Primeiro, verifique se a implantação não está sendo executada em um sandbox:

kubectl get pods -o jsonpath=$'{range .items[*]}{.metadata.name}: {.spec.runtimeClassName}\n{end}'

httpd-db5899bc9-dk7lk: gvisor
httpd-no-sandbox-5bf87996c6-cfmmd:

A implantação httpd criada anteriormente está sendo executada em um sandbox, porque sua runtimeClass é gvisor. A implantação httpd-no-sandbox não tem valor para runtimeClass. Por isso, ela não está sendo executada em um sandbox.

Em seguida, verifique se a implantação fora do sandbox está em execução em um nó com o GKE Sandbox executando o seguinte comando:

kubectl get pod -o jsonpath=$'{range .items[*]}{.metadata.name}: {.spec.nodeName}\n{end}'

O nome do pool de nós é incorporado ao valor de nodeName. Verifique se o pod está sendo executado em um nó em um pool de nós com o GKE Sandbox ativado.

Como verificar a proteção de metadados

Para validar a afirmação de que os metadados estão protegidos dos nós que podem executar pods no sandbox, é possível fazer um teste:

  1. Crie uma implantação em sandbox a partir do seguinte manifesto, usando kubectl apply -f. Ele usa a imagem fedora, que inclui o comando curl. O pod executa o comando /bin/sleep para garantir que a implantação seja executada por 10.000 segundos.

    # sandbox-metadata-test.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: fedora
      labels:
        app: fedora
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: fedora
      template:
        metadata:
          labels:
            app: fedora
        spec:
          runtimeClassName: gvisor
          containers:
          - name: fedora
            image: fedora
            command: ["/bin/sleep","10000"]
    
  2. Consiga o nome do pod usando kubectl get pods e use kubectl exec para se conectar ao pod de forma interativa.

    kubectl exec -it [POD-NAME] /bin/sh
    

    Você está conectado a um contêiner em execução no pod, em uma sessão /bin/sh.

  3. Na sessão interativa, tente acessar um URL que retorne metadados de cluster:

    curl -s "http://metadata.google.internal/computeMetadata/v1/instance/attributes/kube-env" -H "Metadata-Flavor: Google"
    

    O comando trava e, por fim, expira, porque os pacotes são descartados silenciosamente.

  4. Pressione Ctrl+C para encerrar o comando curl e digite exit para se desconectar do pod.

  5. Remova a linha RuntimeClass do manifesto YAML e reimplante o pod usando kubectl apply -f [FILENAME]. O pod no sandbox é encerrado e recriado em um nó sem o GKE Sandbox.

  6. Consiga o novo nome do pod, conecte-se a ele usando kubectl exec e execute o comando curl novamente. Dessa vez, os resultados são retornados. Este exemplo de resultado está truncado.

    ALLOCATE_NODE_CIDRS: "true"
    API_SERVER_TEST_LOG_LEVEL: --v=3
    AUTOSCALER_ENV_VARS: kube_reserved=cpu=60m,memory=960Mi,ephemeral-storage=41Gi;...
    ...
    

    Digite exit para se desconectar do pod.

  7. Remova a implantação:

    kubectl delete deployment fedora
    

Como desativar o GKE Sandbox

No momento, não é possível atualizar um pool de nós para desativar o GKE Sandbox. Para desativar o GKE Sandbox em um pool de nós atual, é possível realizar uma das seguintes ações:

  • Exclua os pods que estavam previamente em sandbox. Caso contrário, após a desativação do GKE Sandbox, esses pods serão executados como pods normais se nenhum nó disponível estiver ativado no GKE Sandbox. Em seguida, exclua o pool de nós em que o GKE Sandbox estava ativado. Ou
  • Redimensione o pool de nós para zero nós . Ou
  • Recrie os pods sem especificar um valor para RuntimeClassName.

A seguir

Esta página foi útil? Conte sua opinião sobre:

Enviar comentários sobre…

Documentação do Kubernetes Engine