Criar e personalizar cargas de trabalho

Uma carga de trabalho é criada por um autor de carga de trabalho e processa os dados confidenciais com que os colaboradores de dados querem trabalhar.

O autor de uma carga de trabalho precisa reunir os seguintes recursos para criar uma carga de trabalho:

  • Um aplicativo para processar os dados confidenciais. É possível escrever o aplicativo em qualquer linguagem, desde que você crie uma imagem do contêiner compatível com ele.

  • Uma imagem do contêiner para empacotar o aplicativo usando o Docker.

  • Um repositório no Artifact Registry para armazenar a imagem do Docker.

  • Políticas de inicialização definidas na imagem do contêiner que controlam como uma carga de trabalho pode ser executada e restringem a capacidade de um operador de carga de trabalho mal-intencionado.

Para implantar a carga de trabalho, uma VM confidencial é executada por um operador de carga de trabalho com base na imagem do Confidential Space. Isso recupera a imagem conteinerizada do Artifact Registry e a executa.

Os colaboradores de dados precisam validar os atestados de uma carga de trabalho antes que ela possa acessar os dados.

Antes de começar

Escrever uma carga de trabalho para o Confidential Space é mais do que apenas código e depuração. Você também precisa conversar com os colaboradores de dados para avaliar as necessidades deles, configurar o ambiente, empacotar o código em uma imagem do contêiner e trabalhar com um operador de carga de trabalho para garantir que tudo seja implantado corretamente.

Conversar com os colaboradores de dados

Antes de começar a escrever o aplicativo, converse com seus colaboradores de dados sobre os dados particulares que eles querem que você use. Algumas perguntas que você pode fazer:

  • Quais são os IDs das organizações envolvidas?

  • Quais são os números dos projetos envolvidos?

  • Quais são os recursos do Google Cloud que preciso acessar e quais são os IDs e nomes deles?

  • Preciso acessar recursos que não são gerenciados pelo IAM do Google Cloud?

  • Qual serviço de atestação você quer usar: Google Cloud Attestation ou Intel Trust Authority?

  • Como o aplicativo deve comparar e processar os dados particulares?

  • Em que formato a saída deve estar?

  • Onde a saída deve ser armazenada e se ela deve ser criptografada?

  • Todos os colaboradores de dados estão vendo o mesmo resultado ou as saídas são exclusivas para cada um?

Além disso, cada colaborador de dados também pode ter normas de privacidade exclusivas que você precisa cumprir. É de extrema importância que nenhum dado privado seja exposto como resultado de uma carga de trabalho.

Criar sua solução do Confidential Space

É útil configurar dois (ou mais) projetos com permissões apropriadas como um ambiente de teste, como em Criar seu primeiro ambiente do Confidential Space. Tente espelhar as configurações do projeto dos colaboradores de dados da melhor forma possível. Isso permite que você ganhe experiência com permissões entre projetos e recupere os dados necessários de recursos específicos do Google Cloud . Ele também pode ajudar você a entender os papéis de operador de carga de trabalho e colaborador de dados e as responsabilidades deles.

Durante a fase inicial de criação, é útil observar as seguintes práticas:

  • Ao trabalhar como colaborador de dados, mantenha a validação de atestado no mínimo para acelerar o desenvolvimento.

  • Ao trabalhar como operador de carga de trabalho, use a imagem de depuração do Confidential Space em vez da de produção ao implantar a carga de trabalho. Isso oferece mais maneiras de resolver problemas na carga de trabalho.

À medida que o aplicativo amadurece e o estado dele se torna mais previsível, é possível bloquear cada vez mais a solução com validação de atestado e políticas de inicialização e alternar para a imagem de produção do Confidential Space.

Depois que a carga de trabalho estiver funcionando corretamente no ambiente de teste, você poderá testar os projetos dos colaboradores de dados com recursos reais, mas dados falsos, para demonstrar aos colaboradores de dados como tudo funciona. Nesse momento, você pode começar a trabalhar com um operador de carga de trabalho independente.

Quando tudo estiver funcionando e a saída for a esperada, comece a testar os dados de produção. Depois que o teste for concluído e todas as partes aprovarem os resultados, a carga de trabalho estará pronta para ser colocada em produção.

Ter cuidado com as respostas

Ao testar seu código, pode ser tentador depurar imprimindo em STDOUT ou STDERR. Se você optar por fazer isso, tome cuidado para não expor dados privados que outras partes possam ler ao acessar os registros. Antes que o código comece a funcionar na produção, verifique se ele não está gerando nada além do estritamente necessário.

O mesmo vale para o resultado final. Forneça apenas um resultado final que não comprometa a privacidade e a sensibilidade dos dados originais.

Criar uma imagem conteinerizada com o Docker

Os aplicativos precisam ser empacotados em uma imagem conteinerizada criada pelo Docker, que é armazenada no Artifact Registry. Quando uma carga de trabalho é implantada, a imagem Docker é extraída do repositório do Artifact Registry pela imagem do Confidential Space, executada, e o aplicativo pode começar a trabalhar nos recursos do projeto apropriados.

Ao criar a imagem Docker, considere o seguinte:

Outros recursos do Linux

A carga de trabalho do Confidential Space é executada em um contêiner do Linux usando o containerd. Esse contêiner é executado usando recursos padrão do Linux.

Para adicionar recursos, use tee-added-capabilities.

Limites de disco e memória

O Confidential Space redimensiona automaticamente a partição com estado do disco de inicialização ao usar tamanhos maiores de disco de inicialização. O tamanho da partição é aproximadamente o tamanho do disco de inicialização menos 5 GB.

Como parte das proteções do sistema de arquivos de integridade do Confidential Space, o Confidential Space armazena tags de integridade de disco na memória. Isso usa aproximadamente 1% de sobrecarga de memória para cada byte de disco. Por exemplo, um disco de 100 GB requer 1 GB de memória, e um disco de 10 TB requer 100 GB de memória.

Mantenha os limites de memória da VM. A troca de memória está desativada nas VMs do Confidential Space, o que significa que o uso excessivo de memória pode causar uma falha na carga de trabalho. Verifique se a máquina selecionada é compatível com o uso da memória da carga de trabalho, além da sobrecarga de integridade do disco.

Encerramentos completos

Quando um operador solicita o encerramento de uma VM do Confidential Space, o inicializador do Confidential Space tenta encerrar a carga de trabalho de maneira adequada enviando um sinal SIGTERM. Após o recebimento, a carga de trabalho tem até 120 segundos para realizar a limpeza antes que o Confidential Space Launcher inicie o processo de encerramento. As cargas de trabalho que não implementam o processamento de sinais SIGTERM são encerradas imediatamente ao receber o sinal.

Montagens temporárias na memória

O Confidential Space permite adicionar espaços de rascunho na memória. Isso usa a memória disponível na VM do Confidential Space. Como o espaço de rascunho usa a memória da VM confidencial, ele tem as mesmas propriedades de integridade e confidencialidade da VM confidencial.

É possível usar tee-dev-shm-size para aumentar o tamanho da montagem de memória compartilhada /dev/shm para a carga de trabalho. O tamanho de /dev/shm é especificado em KB.

É possível usar tee-mount para especificar montagens de tmpfs no contêiner em execução usando configurações separadas por ponto e vírgula. type e source são sempre tmpfs. O destination é o ponto de montagem, que interage com a política de inicialização tee.launch_policy.allow_mount_destinations. Também é possível especificar o tamanho de tmpfs em bytes. O tamanho padrão é 50% da memória da VM.

Portas de entrada

Por padrão, as VMs do Confidential Space operam com uma regra de firewall para bloquear todas as portas de entrada. Ao usar a versão de imagem do Confidential Space 230600 ou superior, é possível especificar as portas de entrada que devem permanecer abertas no Dockerfile ao criar a imagem da carga de trabalho.

Para abrir portas, adicione a palavra-chave EXPOSE ao seu Dockerfile, com o número da porta que deve permanecer aberta e um protocolo opcional de tcp ou udp. Se você não especificar o protocolo para uma porta, ambos serão permitidos: TCP e UDP. Veja um exemplo de Dockerfile que expõe portas de entrada:

FROM alpine:latest
EXPOSE 80
EXPOSE 443/tcp
EXPOSE 81/udp
WORKDIR /test
COPY salary /test
ENTRYPOINT ["/test/salary"]
CMD []

Dependendo da imagem de base usada, algumas portas podem já estar expostas. O Dockerfile expõe apenas portas adicionais. Ele não pode bloquear portas que já foram abertas pela imagem de base.

Os operadores de carga de trabalho precisam garantir que as portas expostas estejam abertas no firewall da VPC antes de executar a carga de trabalho. Os números de porta podem ser fornecidos pelo autor da carga de trabalho ou extraídos das informações da imagem Docker.

As portas expostas são registradas no console e redirecionadas para o Cloud Logging ao usar a variável de metadados tee-container-log-redirect.

Políticas de lançamento

As políticas de inicialização substituem as variáveis de metadados da VM definidas pelos operadores de carga de trabalho para restringir ações mal-intencionadas. Um autor de carga de trabalho pode definir políticas com um rótulo como parte da criação da imagem do contêiner.

Por exemplo, em um Dockerfile:

LABEL "tee.launch_policy.allow_cmd_override"="true"

Em um arquivo BUILD do Bazel:

container_image(
    ...
    labels={"tee.launch_policy.allow_cmd_override":"true"}
    ...
)

As políticas de lançamento disponíveis estão na seguinte tabela:

Política Tipo Descrição

tee.launch_policy.allow_capabilities

Interage com:

Booleano (o padrão é false) Determina se o operador da carga de trabalho pode adicionar mais recursos do Linux ao contêiner da carga de trabalho.

tee.launch_policy.allow_cgroups

Interage com:

  • Operador de carga de trabalho: a variável de metadados tee-cgroup-ns.
Booleano (o padrão é false) Determina se o contêiner de carga de trabalho pode incluir uma montagem de cgroup com namespace em /sys/fs/cgroup.

tee.launch_policy.allow_cmd_override

Interage com:

Booleano (o padrão é false) Determina se o CMD especificado no Dockerfile do contêiner da carga de trabalho pode ser substituído por um operador de carga de trabalho com o valor de metadados tee-cmd.

tee.launch_policy.allow_env_override

Interage com:

String separada por vírgulas Uma string separada por vírgulas de nomes de variável de ambiente permitidas que podem ser definidas por um operador de carga de trabalho com valores de metadados tee-env-ENVIRONMENT_VARIABLE_NAME.

tee.launch_policy.allow_mount_destinations

Interage com:

  • Operador de carga de trabalho: a variável de metadados tee-mount.
String separada por dois-pontos

Uma string separada por dois-pontos de diretórios de montagem permitidos que o operador de carga de trabalho pode montar usando tee-mount.

Por exemplo: /run/tmp:/var/tmp:/tmp

tee.launch_policy.log_redirect

Interage com:

String definida

Determina como a geração de registros funciona se tee-container-log-redirect for definido como true por um operador de carga de trabalho.

Os valores válidos são os seguintes:

  • debugonly (padrão): só permite redirecionamentos de stdout e stderr ao usar uma imagem de depuração.
  • always: sempre permite redirecionamentos de stdout e stderr.
  • never: nunca permite redirecionamentos de stdout e stderr.

tee.launch_policy.monitoring_memory_allow

Interage com:

String definida

Determina como o monitoramento do uso da memória da carga de trabalho funciona se tee-monitoring-memory-enable for definido como true por um operador de carga de trabalho.

Os valores válidos são os seguintes:

  • debugonly (padrão): só permite o monitoramento do uso da memória ao usar uma imagem de depuração.
  • always: sempre permite o monitoramento do uso da memória.
  • never: nunca permite o monitoramento do uso da memória.

tee.launch_policy.nonroot_container

Interage com:

String definida

Determina se os privilégios de execução do contêiner serão restritos.

Os valores válidos são os seguintes:

  • unrestricted (padrão): o autor da carga de trabalho permite que o contêiner seja executado como raiz ou não raiz. O operador de carga de trabalho escolhe usando tee-nonroot-container.
  • nonrootonly: o autor da carga de trabalho exige que o contêiner seja executado como um usuário não raiz. O operador de carga de trabalho precisa definir tee-nonroot-container como true. Caso contrário, a VM não vai iniciar a carga de trabalho.

Várias execuções de carga de trabalho

Para executar novamente uma carga de trabalho, o operador precisa reiniciar a instância de VM. Isso inicia a carga de trabalho no ambiente mais limpo possível e criptografa o disco da instância de VM com uma nova chave temporária.

A reinicialização lida com o vetor de ataque de modificação de uma imagem de carga de trabalho no disco depois que ela é baixada e medida. No entanto, ele também adiciona sobrecargas, como tempo de inicialização e extração da imagem da carga de trabalho em cada execução da carga de trabalho. Se essas sobrecargas afetarem muito o desempenho da carga de trabalho, é possível codificar uma reinicialização para a própria carga de trabalho ao custo de aumentar seu perfil de risco.

cgroups com namespace

A carga de trabalho do Confidential Space é executada sem uma montagem de cgroup por padrão.

Para gerenciar cgroups no contêiner de carga de trabalho, use tee-cgroup-ns. Essa chave de metadados instrui a instância de VM a criar uma montagem em /sys/fs/cgroup no sistema de arquivos do contêiner.

Execução de contêiner sem privilégios de root

Como opção, é possível executar um contêiner como um usuário não raiz para reduzir o impacto de uma invasão de contêiner. Quando a execução de contêineres sem raiz está ativada, o Confidential Space configura um namespace de usuário e um namespace de rede do Linux para a carga de trabalho. Os UIDs e GIDs de contêiner 0-65535 são mapeados para IDs sem privilégios que começam em 100000 na VM confidencial. Um processo executado como raiz dentro do contêiner ou que escapa dele tem apenas acesso sem privilégios à VM. Não é necessário adicionar uma instrução USER ao Dockerfile.

A execução sem privilégios de root requer a imagem confidential-space-261001 ou posterior do Confidential Space. É possível configurá-lo como opcional ou obrigatório:

  • Autor da carga de trabalho: use o rótulo da política de inicialização tee.launch_policy.nonroot_container na imagem do contêiner. Definir essa política como nonrootonly exige que o operador da carga de trabalho execute o contêiner como um usuário não raiz.

  • Operador de carga de trabalho: transmita a variável de metadados tee-nonroot-container=true ao criar a VM. Se o autor da carga de trabalho definir nonrootonly, essa variável de metadados será obrigatória. Caso contrário, a VM não vai iniciar a carga de trabalho.

Imagens de contêiner reproduzíveis

Criar uma imagem de contêiner de maneira reproduzível pode ajudar a aumentar a confiança entre as partes. É possível criar imagens reproduzíveis com o Bazel.

Recursos não gerenciados pelo Google Cloud IAM

Para acessar recursos não gerenciados pelo Google Cloud IAM, sua carga de trabalho precisa especificar um público-alvo personalizado.

Para mais informações, consulte Acessar recursos não gerenciados pelo IAM do Google Cloud .

Imagens de contêiner assinadas

É possível assinar uma imagem de contêiner com uma chave pública, que um colaborador de dados pode usar para atestação em vez de especificar um resumo de imagem na política de WIP.

Isso significa que os colaboradores de dados não precisam atualizar as políticas de WIP sempre que uma carga de trabalho é atualizada, e a carga de trabalho pode continuar acessando recursos protegidos sem interrupção.

Use o Sigstore Cosign para assinar a imagem do contêiner. Para que o Confidential Space busque as assinaturas, os operadores de carga de trabalho precisam adicionar as informações de assinatura à variável de metadados tee-signed-image-repos antes de implantar a carga de trabalho.

Durante o ambiente de execução, as assinaturas são enviadas a um serviço de atestado para verificação. O serviço de atestado retorna um token de declarações de atestado que contém as declarações de assinatura verificadas. Veja um exemplo de declaração de assinatura:

"image_signatures": [
  {
    "key_id": "hexadecimal-sha256-fingerprint-public-key1",
    "signature": "base64-encoded-signature",
    "signature_algorithm": "RSASSA_PSS_SHA256"
  },
  {
    "key_id": "hexadecimal-sha256-fingerprint-public-key2",
    "signature": "base64-encoded-signature",
    "signature_algorithm": "RSASSA_PSS_SHA256",
  },
  {
    "key_id": "hexadecimal-sha256-fingerprint-public-key3",
    "signature": "base64-encoded-signature",
    "signature_algorithm": "RSASSA_PSS_SHA256",
  }
]

Para configurar a assinatura de imagens de contêiner, consulte o codelab de imagem de contêiner assinada.