1. Visão geral
O Confidential Space oferece um ambiente seguro para colaboração entre várias partes. Este codelab demonstra como o Confidential Space pode ser usado para proteger a propriedade intelectual sensível, como modelos de machine learning.
Neste codelab, você vai usar o Confidential Space para permitir que uma empresa compartilhe com segurança o modelo de machine learning proprietário com outra empresa que queira usar o modelo. Especificamente, a empresa Primus tem um modelo de machine learning que só seria lançado para uma carga de trabalho em execução no Confidential Space, permitindo que a Primus mantenha o controle total sobre a propriedade intelectual. A empresa Secundus será o operador da carga de trabalho e vai executar a carga de trabalho de machine learning em um Confidential Space. O Secundus vai carregar esse modelo e executar uma inferência usando dados de amostra próprios.
Aqui, Primus é o autor da carga de trabalho que cria o código da carga de trabalho e um colaborador que quer proteger a propriedade intelectual do operador de carga de trabalho não confiável, Secundus. O Secundus é o operador da carga de trabalho de machine learning.

O que você vai aprender
- Como configurar um ambiente em que uma parte pode compartilhar um modelo de ML proprietário com outra sem perder o controle sobre a propriedade intelectual.
O que é necessário
- Um projeto do Google Cloud Platform
- Conhecimento básico do Google Compute Engine ( codelab), VM confidencial, contêineres e repositórios remotos
- Conhecimento básico de contas de serviço, federação de identidade da carga de trabalho e condições de atributo.
Funções envolvidas na configuração do Confidential Space
Neste codelab, a Company Primus será a proprietária do recurso e a criadora da carga de trabalho, responsável por:
- Configurar os recursos de nuvem necessários com um modelo de machine learning
- Como escrever o código da carga de trabalho
- Publicar a imagem da carga de trabalho
- Configurar a política do pool de identidades da carga de trabalho para proteger o modelo de ML contra um operador não confiável
A Secundus Company será a operadora e responsável por:
- Configurar os recursos de nuvem necessários para armazenar as imagens de amostra usadas pela carga de trabalho e os resultados
- Executar a carga de trabalho de ML no Confidential Space usando o modelo fornecido pelo Primus
Como o Confidential Space funciona
Quando você executa a carga de trabalho no Confidential Space, o processo a seguir é realizado usando os recursos configurados:
- A carga de trabalho solicita um token de acesso geral do Google para o
$PRIMUS_SERVICEACCOUNTdo pool de identidades da carga de trabalho. Ele oferece um token de serviço do verificador de atestado com declarações de carga de trabalho e ambiente. - Se as declarações de medição da carga de trabalho no token do serviço de verificação de atestado corresponderem à condição de atributo no WIP, ele vai retornar o token de acesso para
$PRIMUS_SERVICEACCOUNT.. - A carga de trabalho usa o token de acesso da conta de serviço associado a
$PRIMUS_SERVICEACCOUNTpara acessar o modelo de machine learning armazenado no bucket$PRIMUS_INPUT_STORAGE_BUCKET. - A carga de trabalho realiza uma operação nos dados de propriedade da Secundus, e ela é operada e executada pela Secundus no projeto dela.
- A carga de trabalho usa a conta de serviço
$WORKLOAD_SERVICEACCOUNTpara gravar os resultados dessa operação no bucket$SECUNDUS_RESULT_STORAGE_BUCKET.
2. Configurar recursos do Cloud
Antes de começar
- Clone este repositório usando o comando abaixo para receber os scripts necessários que são usados como parte deste codelab.
git clone https://github.com/GoogleCloudPlatform/confidential-space.git
- Mude o diretório deste codelab.
cd confidential-space/codelabs/ml_model_protection/scripts
- Verifique se você definiu as variáveis de ambiente necessárias do projeto, conforme mostrado abaixo. Para mais informações sobre como configurar um projeto do GCP, consulte este codelab. Consulte este link para saber como recuperar o ID do projeto e a diferença entre ele, o nome e o número do projeto.
export PRIMUS_PROJECT_ID=<GCP project id of Primus>
export SECUNDUS_PROJECT_ID=<GCP project id of Secundus>
- Ative o faturamento dos projetos.
- Ative a API Confidential Computing e as seguintes APIs nos dois projetos.
gcloud services enable \
cloudapis.googleapis.com \
cloudresourcemanager.googleapis.com \
cloudshell.googleapis.com \
container.googleapis.com \
containerregistry.googleapis.com \
iam.googleapis.com \
confidentialcomputing.googleapis.com
- Atribua valores às variáveis dos nomes de recursos especificados acima usando o seguinte comando. Essas variáveis permitem personalizar os nomes dos recursos conforme necessário e usar os recursos existentes, se já tiverem sido criados. (por exemplo,
export PRIMUS_INPUT_STORAGE_BUCKET='my-input-bucket')
- É possível definir as seguintes variáveis com nomes de recursos de nuvem existentes no projeto do Primus. Se a variável estiver definida, o recurso de nuvem correspondente do projeto Primus será usado. Se a variável não estiver definida, o nome do recurso de nuvem será gerado com base no nome do projeto, e um novo recurso de nuvem será criado com esse nome. Estas são as variáveis compatíveis com nomes de recursos:
| O bucket que armazena o modelo de machine learning do Primus. |
| O pool de identidade da carga de trabalho (WIP, na sigla em inglês) do Primus que valida declarações. |
| O provedor de pool de identidades de carga de trabalho do Primus, que inclui a condição de autorização para usar tokens assinados pelo serviço de verificador de atestado. |
| Conta de serviço do Primus que o |
| O repositório de artefatos em que a imagem Docker da carga de trabalho será enviada. |
- É possível definir as seguintes variáveis com nomes de recursos da nuvem no projeto Secundus. Se a variável estiver definida, o recurso de nuvem correspondente do projeto Secundus será usado. Se a variável não estiver definida, o nome do recurso de nuvem será gerado com base no nome do projeto, e um novo recurso de nuvem será criado com esse nome. Estas são as variáveis compatíveis com nomes de recursos:
| O bucket que armazena as imagens de amostra que o Secundus quer classificar usando o modelo fornecido pelo Primus. |
| O bucket que armazena os resultados da carga de trabalho. |
| O nome da imagem do contêiner da carga de trabalho. |
| A tag da imagem do contêiner da carga de trabalho. |
| A conta de serviço com permissão para acessar a VM confidencial que executa a carga de trabalho. |
- Você vai precisar de determinadas permissões para esses dois projetos. Consulte este guia sobre como conceder papéis do IAM usando o console do GCP:
- Para o
$PRIMUS_PROJECT_ID, você vai precisar de Administrador do Storage, Administrador do Artifact Registry, Administrador da conta de serviço e Administrador de pool de Identidade da carga de trabalho do IAM. - Para o
$SECUNDUS_PROJECT_ID, você vai precisar de administrador do Compute, administrador do Storage, administrador da conta de serviço, administrador de pool de Identidade da carga de trabalho do IAM e administrador de segurança (opcional). - Execute o script a seguir para definir os nomes de variáveis restantes como valores com base no ID do projeto para nomes de recursos.
source config_env.sh
Configurar os recursos da empresa Primus
Como parte desta etapa, você vai configurar os recursos de nuvem necessários para o Primus. Execute o script a seguir para configurar os recursos do Primus. Os seguintes recursos serão criados como parte da execução do script:
- Bucket do Cloud Storage (
$PRIMUS_INPUT_STORAGE_BUCKET) para armazenar o modelo de machine learning do Primus. - Pool de identidades da carga de trabalho (
$PRIMUS_WORKLOAD_IDENTITY_POOL) para validar declarações com base nas condições de atributos configuradas no provedor. - Conta de serviço (
$PRIMUS_SERVICEACCOUNT) anexada ao pool de Identidade da carga de trabalho ($PRIMUS_WORKLOAD_IDENTITY_POOL) mencionado acima com acesso do IAM para ler dados do bucket do Cloud Storage (usando o papelobjectViewer) e para conectar essa conta de serviço ao pool de Identidade da carga de trabalho (usando o papelroles/iam.workloadIdentityUser).
Como parte dessa configuração de recursos da nuvem, vamos usar um modelo do TensorFlow. Podemos salvar todo o modelo, incluindo a arquitetura, os pesos e a configuração de treinamento, em um arquivo ZIP. Para este codelab, vamos usar o modelo MobileNet V1 treinado no conjunto de dados ImageNet, encontrado aqui.
./setup_primus_company_resources.sh
O script mencionado acima vai configurar o recurso de nuvem. Agora vamos fazer o download e publicar o modelo no bucket do Cloud Storage criado pelo script.
- Faça o download do modelo pré-treinado aqui.
- Depois de fazer o download, renomeie o arquivo tar baixado para model.tar.gz.
- Publique o arquivo model.tar.gz no bucket do Cloud Storage usando o seguinte comando no diretório que contém o arquivo model.tar.gz.
gsutil cp model.tar.gz gs://${PRIMUS_INPUT_STORAGE_BUCKET}/
Configurar os recursos da empresa Secundus
Como parte desta etapa, você vai configurar os recursos de nuvem necessários para o Secundus. Execute o script a seguir para configurar os recursos do Secundus. Como parte dessas etapas, os seguintes recursos serão criados:
- Bucket do Cloud Storage (
$SECUNDUS_INPUT_STORAGE_BUCKET) para armazenar as imagens de amostra e executar inferências pelo Secundus. - Bucket do Cloud Storage (
$SECUNDUS_RESULT_STORAGE_BUCKET) para armazenar o resultado da execução da carga de trabalho de ML pelo Secundus.
Algumas imagens de exemplo estão disponíveis aqui para este codelab.
./setup_secundus_company_resources.sh
3. Criar carga de trabalho
Criar conta de serviço da carga de trabalho
Agora, você vai criar uma conta de serviço para a carga de trabalho com os papéis e as permissões necessários. Execute o seguinte script para criar uma conta de serviço de carga de trabalho no projeto Secundus. Essa conta de serviço seria usada pela VM que executa a carga de trabalho de ML.
Essa conta de serviço da carga de trabalho ($WORKLOAD_SERVICEACCOUNT) terá os seguintes papéis:
confidentialcomputing.workloadUserpara receber um token de atestadologging.logWriterpara gravar registros no Cloud Logging.objectViewerpara ler dados do bucket do Cloud Storage$SECUNDUS_INPUT_STORAGE_BUCKET.objectUserpara gravar o resultado da carga de trabalho no bucket$SECUNDUS_RESULT_STORAGE_BUCKETdo Cloud Storage.
./create_workload_service_account.sh
Criar carga de trabalho
Como parte desta etapa, você vai criar uma imagem Docker de carga de trabalho. A carga de trabalho seria criada pelo Primus. A carga de trabalho usada neste codelab é um código Python de machine learning que acessa o modelo de ML armazenado no bucket do Primus e executa inferências com as imagens de amostra armazenadas em um bucket.
O modelo de machine learning armazenado no bucket do Primus só pode ser acessado pelas cargas de trabalho que atendem às condições de atributo necessárias. Essas condições de atributo são descritas em mais detalhes na próxima seção sobre autorização da carga de trabalho.
Confira o método run_inference() da carga de trabalho que será criada e usada neste codelab. Confira o código completo da carga de trabalho aqui.
def run_inference(image_path, model):
try:
# Read and preprocess the image
image = tf.image.decode_image(tf.io.read_file(image_path), channels=3)
image = tf.image.resize(image, (128, 128))
image = tf.image.convert_image_dtype(image, tf.float32)
image = tf.expand_dims(image, axis=0)
# Get predictions from the model
predictions = model(image)
predicted_class = np.argmax(predictions)
top_k = 5
top_indices = np.argsort(predictions[0])[-top_k:][::-1]
# Convert top_indices to a TensorFlow tensor
top_indices_tensor = tf.convert_to_tensor(top_indices, dtype=tf.int32)
# Use TensorFlow tensor for indexing
top_scores = tf.gather(predictions[0], top_indices_tensor)
return {
"predicted_class": int(predicted_class),
"top_k_predictions": [
{"class_index": int(idx), "score": float(score)}
for idx, score in zip(top_indices, top_scores)
],
}
except Exception as e:
return {"error": str(e)}
Execute o script a seguir para criar uma carga de trabalho em que as seguintes etapas estão sendo realizadas:
- Crie um Artifact Registry(
$PRIMUS_ARTIFACT_REGISTRY) de propriedade do Primus. - Atualize o código da carga de trabalho com os nomes dos recursos necessários.
- Crie a carga de trabalho de ML e o Dockerfile para criar uma imagem do Docker do código da carga de trabalho. Aqui está o Dockerfile usado neste codelab.
- Crie e publique a imagem Docker no Artifact Registry (
$PRIMUS_ARTIFACT_REGISTRY) de propriedade da Primus. - Conceda permissão de leitura
$WORKLOAD_SERVICEACCOUNTpara$PRIMUS_ARTIFACT_REGISTRY. Isso é necessário para que o contêiner da carga de trabalho extraia a imagem do Docker da carga de trabalho do Artifact Registry.
./create_workload.sh
Além disso, as cargas de trabalho podem ser codificadas para garantir que a versão esperada do modelo de machine learning seja carregada. Para isso, basta verificar o hash ou a assinatura do modelo antes de usá-lo. A vantagem dessas verificações extras é que elas garantem a integridade do modelo de machine learning. Com isso, o operador de carga de trabalho também precisaria atualizar a imagem da carga de trabalho ou os parâmetros dela quando a carga de trabalho precisasse usar diferentes versões do modelo de ML.
4. Autorizar e executar a carga de trabalho
Autorizar carga de trabalho
A Primus quer autorizar cargas de trabalho a acessar o modelo de machine learning com base nos atributos dos seguintes recursos:
- O quê: código verificado
- Onde: um ambiente seguro
- Quem: um operador confiável
O Primus usa a federação de identidade da carga de trabalho para aplicar uma política de acesso com base nesses requisitos. Com a federação de identidade da carga de trabalho, é possível especificar condições de atributo. Essas condições restringem quais identidades podem ser autenticadas com o pool de identidades da carga de trabalho (WIP). É possível adicionar o serviço de verificação de atestado ao WIP como um provedor de pool de identidade da carga de trabalho para apresentar medições e aplicar a política.
O pool de identidades de carga de trabalho já foi criado como parte da etapa de configuração dos recursos da nuvem. Agora, o Primus vai criar um provedor de pool de identidades de carga de trabalho OIDC. O --attribute-condition especificado autoriza o acesso ao contêiner da carga de trabalho. Ela requer:
- O quê: o
$WORKLOAD_IMAGE_NAMEmais recente foi enviado por upload para o repositório$PRIMUS_ARTIFACT_REPOSITORY. - Onde: o ambiente de execução confiável do Confidential Space está sendo executado na imagem de VM do Confidential Space totalmente compatível.
- Quem: conta de serviço do Secundus
$WORKLOAD_SERVICE_ACCOUNT.
export WORKLOAD_IMAGE_DIGEST=$(gcloud artifacts docker images describe ${PRIMUS_PROJECT_REPOSITORY_REGION}-docker.pkg.dev/$PRIMUS_PROJECT_ID/$PRIMUS_ARTIFACT_REPOSITORY/$WORKLOAD_IMAGE_NAME:$WORKLOAD_IMAGE_TAG --format="value(image_summary.digest)" --project ${PRIMUS_PROJECT_ID})
gcloud config set project $PRIMUS_PROJECT_ID
gcloud iam workload-identity-pools providers create-oidc $PRIMUS_WIP_PROVIDER \
--location="global" \
--workload-identity-pool="$PRIMUS_WORKLOAD_IDENTITY_POOL" \
--issuer-uri="https://confidentialcomputing.googleapis.com/" \
--allowed-audiences="https://sts.googleapis.com" \
--attribute-mapping="google.subject='assertion.sub'" \
--attribute-condition="assertion.swname == 'CONFIDENTIAL_SPACE' &&
'STABLE' in assertion.submods.confidential_space.support_attributes &&
assertion.submods.container.image_digest == '${WORKLOAD_IMAGE_DIGEST}' &&
assertion.submods.container.image_reference == '${PRIMUS_PROJECT_REPOSITORY_REGION}-docker.pkg.dev/$PRIMUS_PROJECT_ID/$PRIMUS_ARTIFACT_REPOSITORY/$WORKLOAD_IMAGE_NAME:$WORKLOAD_IMAGE_TAG' &&
'$WORKLOAD_SERVICEACCOUNT@$SECUNDUS_PROJECT_ID.iam.gserviceaccount.com' in assertion.google_service_accounts"
Executar carga de trabalho
Como parte dessa etapa, vamos executar a carga de trabalho na VM do Confidential Space. Os argumentos necessários do TEE são transmitidos usando a flag de metadados. Os argumentos do contêiner de carga de trabalho são transmitidos usando a parte "tee-cmd" da flag. O resultado da execução da carga de trabalho será publicado em $SECUNDUS_RESULT_STORAGE_BUCKET.
gcloud compute instances create ${WORKLOAD_VM} \
--confidential-compute-type=SEV \
--shielded-secure-boot \
--project=${SECUNDUS_PROJECT_ID} \
--maintenance-policy=MIGRATE \
--scopes=cloud-platform --zone=${SECUNDUS_PROJECT_ZONE} \
--image-project=confidential-space-images \
--image-family=confidential-space \
--service-account=${WORKLOAD_SERVICEACCOUNT}@${SECUNDUS_PROJECT_ID}.iam.gserviceaccount.com \
--metadata ^~^tee-image-reference=${PRIMUS_PROJECT_REPOSITORY_REGION}-docker.pkg.dev/${PRIMUS_PROJECT_ID}/${PRIMUS_ARTIFACT_REPOSITORY}/${WORKLOAD_IMAGE_NAME}:${WORKLOAD_IMAGE_TAG}
Ver resultados
Depois que a carga de trabalho for concluída, o resultado dela será publicado em $SECUNDUS_RESULT_STORAGE_BUCKET.
gsutil cat gs://$SECUNDUS_RESULT_STORAGE_BUCKET/result
Confira alguns exemplos de como os resultados da inferência em imagens de amostra podem aparecer:
Image: sample_image_1.jpeg, Response: {'predicted_class': 531, 'top_k_predictions': [{'class_index': 531, 'score': 12.08437442779541}, {'class_index': 812, 'score': 10.269512176513672}, {'class_index': 557, 'score': 9.202644348144531}, {'class_index': 782, 'score': 9.08737564086914}, {'class_index': 828, 'score': 8.912498474121094}]}
Image: sample_image_2.jpeg, Response: {'predicted_class': 905, 'top_k_predictions': [{'class_index': 905, 'score': 9.53619384765625}, {'class_index': 557, 'score': 7.928380966186523}, {'class_index': 783, 'score': 7.70129919052124}, {'class_index': 531, 'score': 7.611623287200928}, {'class_index': 906, 'score': 7.021416187286377}]}
Image: sample_image_3.jpeg, Response: {'predicted_class': 905, 'top_k_predictions': [{'class_index': 905, 'score': 6.09878396987915}, {'class_index': 447, 'score': 5.992854118347168}, {'class_index': 444, 'score': 5.9582319259643555}, {'class_index': 816, 'score': 5.502010345458984}, {'class_index': 796, 'score': 5.450454235076904}]}
Para cada imagem de amostra em um bucket de armazenamento do Secundus, você vai encontrar uma entrada nos resultados. Essa entrada vai incluir duas informações importantes:
- Índice de predicted_class:é um índice numérico que representa a classe a que o modelo prevê que a imagem pertence.
- Top_k_predictions::fornece até k previsões para a imagem, classificadas da mais para a menos provável. O valor de k é definido como 5 neste codelab, mas você pode ajustá-lo no código da carga de trabalho para receber mais ou menos previsões.
Para traduzir o índice de classe em um nome de classe legível, consulte a lista de rótulos disponíveis aqui. Por exemplo, se você vir um índice de classe 2, ele vai corresponder ao rótulo "tench" na lista de rótulos.
Neste codelab, demonstramos que um modelo de propriedade da Primus é lançado apenas para a carga de trabalho em execução em um TEE. O Secundus executa a carga de trabalho de ML em um TEE, e essa carga de trabalho pode consumir o modelo de propriedade do Primus, enquanto o Primus mantém o controle total sobre ele.
Executar carga de trabalho não autorizada
O Secundus muda a imagem da carga de trabalho extraindo uma imagem diferente do próprio repositório de artefatos, que não é autorizado pelo Primus. O pool de Identidade da carga de trabalho do Primus autorizou apenas a imagem de carga de trabalho ${PRIMUS_PROJECT_REPOSITORY_REGION}-docker.pkg.dev/$PRIMUS_PROJECT_ID/$PRIMUS_ARTIFACT_REPOSITORY/$WORKLOAD_IMAGE_NAME:$WORKLOAD_IMAGE_TAG.
Executar a carga de trabalho novamente
Quando o Secundus tenta executar a carga de trabalho original com essa nova imagem, ocorre uma falha. Para ver o erro, exclua o arquivo de resultados original e a instância de VM e tente executar a carga de trabalho novamente.
Verifique se há uma nova imagem do Docker publicada no Artifact Registry do Secundus (como us-docker.pkg.dev/${SECUNDUS_PROJECT_ID}/custom-image/${WORKLOAD_IMAGE_NAME}:${WORKLOAD_IMAGE_TAG}) e se a conta de serviço da carga de trabalho ($WORKLOAD_SERVICEACCOUNT) concedeu permissão de leitura do Artifact Registry para ler essa nova imagem de carga de trabalho. Isso garante que a carga de trabalho não seja encerrada antes que a política WIP do Primus rejeite o token apresentado por ela.
Excluir o arquivo de resultados e a instância de VM
- Defina o projeto como
$SECUNDUS_PROJECT_ID.
gcloud config set project $SECUNDUS_PROJECT_ID
- Exclua o arquivo de resultados.
gsutil rm gs://$SECUNDUS_RESULT_STORAGE_BUCKET/result
- Exclua a instância de VM confidencial.
gcloud compute instances delete ${WORKLOAD_VM} --zone=${SECUNDUS_PROJECT_ZONE}
Execute a carga de trabalho não autorizada:
gcloud compute instances create ${WORKLOAD_VM} \
--confidential-compute-type=SEV \
--shielded-secure-boot \
--maintenance-policy=MIGRATE \
--scopes=cloud-platform --zone=${SECUNDUS_PROJECT_ZONE} \
--image-project=confidential-space-images \
--image-family=confidential-space \
--service-account=${WORKLOAD_SERVICEACCOUNT}@${SECUNDUS_PROJECT_ID}.iam.gserviceaccount.com \
--metadata ^~^tee-image-reference=us-docker.pkg.dev/${SECUNDUS_PROJECT_ID}/custom-image/${WORKLOAD_IMAGE_NAME}:${WORKLOAD_IMAGE_TAG}
Ver erro
Em vez dos resultados da carga de trabalho, você vê um erro (The given credential is rejected by the attribute condition).
gsutil cat gs://$SECUNDUS_RESULT_STORAGE_BUCKET/result
5. Limpeza
Aqui está o script que pode ser usado para limpar os recursos criados como parte deste codelab. Como parte dessa limpeza, os seguintes recursos serão excluídos:
- Bucket de armazenamento de entrada do Primus (
$PRIMUS_INPUT_STORAGE_BUCKET). - Conta de serviço do Primus (
$PRIMUS_SERVICEACCOUNT). - Repositório de artefatos do Primus (
$PRIMUS_ARTIFACT_REPOSITORY). - Pool de identidade da carga de trabalho do Primus (
$PRIMUS_WORKLOAD_IDENTITY_POOL). - Conta de serviço da carga de trabalho do Secundus (
$WORKLOAD_SERVICEACCOUNT). - Bucket de armazenamento de entrada do Secundus (
$SECUNDUS_INPUT_STORAGE_BUCKET). - Instâncias de computação de carga de trabalho.
- Bucket de armazenamento de resultados do Secundus (
$SECUNDUS_RESULT_STORAGE_BUCKET).
$ ./cleanup.sh
Se você já terminou de explorar, considere excluir o projeto.
- Acesse o Console do Cloud Platform.
- Selecione o projeto que você quer encerrar e clique em "Excluir" na parte de cima. Isso programa a exclusão do projeto.
Qual é a próxima etapa?
Confira alguns codelabs semelhantes: