Atelier de programmation Trusted Space

1. Présentation

Prêt à améliorer la sécurité et la confidentialité de vos charges de travail accélérées par GPU ? Cet atelier de programmation vous présente les fonctionnalités de Trusted Space, une offre qui offre une isolation de l'opérateur et une compatibilité avec les accélérateurs pour vos charges de travail d'IA/ML sensibles.

Protéger les données, les modèles et les clés précieux est plus important que jamais. Trusted Space offre une solution en veillant à ce que vos charges de travail s'exécutent dans un environnement sécurisé et fiable auquel même l'opérateur de la charge de travail n'a pas accès.

Voici ce que propose Trusted Space:

  • Confidentialité et sécurité renforcées:Trusted Space fournit un environnement d'exécution sécurisé dans lequel vos composants sensibles (modèles, données et clés de valeur, par exemple) restent protégés, grâce à une preuve cryptographique.
  • Isolement de l'opérateur:éliminez les inquiétudes concernant les interférences de l'opérateur. Avec l'espace sécurisé, même les opérateurs de vos charges de travail n'ont pas accès à cet espace, ce qui les empêche d'utiliser SSH, d'accéder aux données, d'installer des logiciels ou de falsifier votre code.
  • Compatibilité avec les accélérateurs:l'espace sécurisé est conçu pour fonctionner de manière transparente avec un large éventail d'accélérateurs matériels, y compris les GPU tels que les H100, A100, T4 et L4. Cela garantit le bon fonctionnement de vos applications d'IA/ML critiques pour les performances.

Points abordés

  • Découvrez les principales offres de Trusted Space.
  • Découvrez comment déployer et configurer un environnement Trusted Space pour sécuriser les éléments importants de votre charge de travail d'IA/ML.

Prérequis

Protéger les requêtes de génération de code sensibles avec Primus Company

Dans cet atelier de programmation, vous allez endosser le rôle de Primus, une entreprise qui donne la priorité à la confidentialité et à la sécurité des données de ses employés. Primus souhaite déployer un modèle de génération de code pour aider ses développeurs dans leurs tâches de codage. Cependant, il souhaite protéger la confidentialité des requêtes envoyées par ses employés, car elles contiennent souvent des extraits de code sensibles, des informations sur des projets internes ou des algorithmes propriétaires.

Pourquoi l'entreprise Primus ne fait-elle pas confiance à l'opérateur ?

Primus Corp évolue sur un marché très concurrentiel. Son code source contient une propriété intellectuelle de valeur, y compris des algorithmes propriétaires et des extraits de code sensibles qui lui donnent un avantage concurrentiel. Ils craignent la possibilité d'espionnage industriel par les opérateurs de charge de travail. De plus, les requêtes des employés peuvent inclure des parties de code confidentielles "Need To Know" que Primus Corp souhaite protéger.

Pour résoudre ce problème, Primus Corp utilisera Trusted Space pour isoler le serveur d'inférence qui exécute le modèle pour la génération de code. Le principe est le suivant :

  • Chiffrement des requêtes:avant d'envoyer une requête au serveur d'inférence, chaque employé la chiffre à l'aide d'une clé KMS gérée par Primus Corp dans Google Cloud. Cela garantit que seul l'environnement Trusted Space, où la clé de déchiffrement correspondante est disponible, peut la déchiffrer et accéder à l'invite en texte brut. Dans un scénario réel, le chiffrement côté client peut être géré par les bibliothèques disponibles (par exemple, tink). Dans cet atelier de programmation, nous allons utiliser cet exemple d'application cliente avec le chiffrement par enveloppe.
  • Isolement de l'opérateur:seul le serveur d'inférence, exécuté dans un environnement Trusted Space, aura accès à la clé utilisée pour le chiffrement et pourra déchiffrer l'invite dans un environnement sécurisé. L'accès à la clé de chiffrement serait protégé par le pool d'identités de charge de travail. En raison des garanties d'isolation de l'espace sécurisé, même l'opérateur de la charge de travail ne peut pas accéder à la clé utilisée pour le chiffrement ni au contenu déchiffré.
  • Inférence sécurisée à l'aide d'accélérateurs : le serveur d'inférence est lancé sur une VM protégée (dans le cadre de la configuration de l'espace sécurisé), ce qui garantit que l'instance de charge de travail n'a pas été compromise par des logiciels malveillants ou des rootkits au niveau du démarrage ou du noyau. Ce serveur déchiffre l'invite dans l'environnement de l'espace sécurisé, effectue l'inférence à l'aide du modèle de génération de code et renvoie le code généré à l'employé.

2. Configurer des ressources Cloud

Avant de commencer

  • Clonez ce dépôt à l'aide de la commande ci-dessous pour obtenir les scripts requis utilisés dans cet atelier de programmation.
git clone https://github.com/GoogleCloudPlatform/confidential-space.git
  • Accédez au répertoire de cet atelier de programmation.
cd confidential-space/codelabs/trusted_space_codelab/scripts
  • Assurez-vous d'avoir défini les variables d'environnement de projet requises, comme indiqué ci-dessous. Pour en savoir plus sur la configuration d'un projet GCP, consultez cet atelier de programmation. Pour savoir comment récupérer l'ID de projet et en quoi il diffère du nom et du numéro de projet, consultez cette page.
export PRIMUS_PROJECT_ID=<GCP project id of Primus>
  • Activez la facturation pour vos projets.
  • Activez l'API Confidential Computing et les API suivantes pour les deux projets.
gcloud services enable \
    cloudapis.googleapis.com \
    cloudresourcemanager.googleapis.com \
    cloudkms.googleapis.com \
    cloudshell.googleapis.com \
    container.googleapis.com \
    containerregistry.googleapis.com \
    iam.googleapis.com \
    confidentialcomputing.googleapis.com
  • Attribuez des valeurs aux variables pour les noms de ressources spécifiés ci-dessus à l'aide de la commande suivante. Ces variables vous permettent de personnaliser les noms de ressources si nécessaire et d'utiliser des ressources existantes si elles sont déjà créées. (par exemple, export PRIMUS_SERVICE_ACCOUNT='my-service-account')
  1. Vous pouvez définir les variables suivantes avec des noms de ressources cloud existants dans le projet Primus. Si la variable est définie, la ressource cloud existante correspondante du projet Primus est utilisée. Si la variable n'est pas définie, le nom de la ressource cloud est généré à partir du nom du projet, et une nouvelle ressource cloud est créée avec ce nom. Voici les variables acceptées pour les noms de ressources:

$PRIMUS_PROJECT_REGION

Région dans laquelle les ressources régionales seront créées pour l'entreprise Primus.

$PRIMUS_SERVICE_LOCATION

Emplacement où les ressources seront créées pour l'entreprise Primus.

$PRIMUS_PROJECT_ZONE

Zone dans laquelle les ressources zonales seront créées pour l'entreprise Primus.

$PRIMUS_WORKLOAD_IDENTITY_POOL

Le pool d'identités de charge de travail de l'entreprise Primus pour protéger les ressources cloud.

$PRIMUS_WIP_PROVIDER

Fournisseur de pool d'identités de charge de travail de l'entreprise Primus, qui inclut la condition d'autorisation à utiliser pour les jetons signés par le service de validation des attestations.

$PRIMUS_SERVICEACCOUNT

Compte de service de l'entreprise Primus utilisé par $PRIMUS_WORKLOAD_IDENTITY_POOL pour accéder aux ressources protégées. À cette étape, il est autorisé à afficher les données client stockées dans le bucket $PRIMUS_INPUT_STORAGE_BUCKET.

$PRIMUS_ENC_KEY

La clé KMS permet de chiffrer les requêtes fournies par les employés de l'entreprise Primus.

$PRIMUS_ENC_KEYRING

Trousseau de clés KMS qui sera utilisé pour créer la clé de chiffrement $PRIMUS_ENC_KEY pour l'entreprise Primus.

$PRIMUS_ENC_KEYVERSION

Version de la clé KMS de la clé de chiffrement $PRIMUS_ENC_KEY. La valeur par défaut est de 1. Modifiez ce paramètre si vous utilisez une clé existante qui a été remplacée par une autre dans le passé et dont la version a été mise à jour.

$PRIMUS_ARTIFACT_REPOSITORY

Dépôt d'artefacts vers lequel l'image Docker de la charge de travail sera transférée.

$PRIMUS_PROJECT_REPOSITORY_REGION

Région du dépôt d'artefacts contenant l'image Docker de la charge de travail publiée.

$WORKLOAD_VM

Nom de la VM de charge de travail.

$WORKLOAD_IMAGE_NAME

Nom de l'image Docker de la charge de travail.

$WORKLOAD_IMAGE_TAG

Tag de l'image du conteneur de charge de travail.

$WORKLOAD_SERVICEACCOUNT

Compte de service autorisé à accéder à la VM Confidential qui exécute la charge de travail.

$CLIENT_VM

Nom de la VM cliente qui exécutera l'application cliente du serveur d'inférence.

$CLIENT_SERVICEACCOUNT

Compte de service utilisé par $CLIENT_VM

  • Vous avez besoin des rôles Administrateur Storage, Administrateur Artifact Registry, Administrateur Cloud KMS, Administrateur de compte de service et Administrateur de pool d'identités de charge de travail IAM pour le projet $PRIMUS_PROJECT_ID. Pour savoir comment attribuer des rôles IAM à l'aide de la console GCP, consultez ce guide.
  • Pour $PRIMUS_PROJECT_ID, exécutez le script suivant afin de définir les noms de variable restants sur des valeurs basées sur votre ID de projet pour les noms de ressources.
source config_env.sh

Configurer les ressources de l'entreprise Primus

Au cours de cette étape, vous allez configurer les ressources cloud requises pour Primus. Exécutez le script suivant pour configurer les ressources de Primus. Les ressources suivantes sont créées lors de l'exécution du script:

  • Clé de chiffrement ($PRIMUS_ENC_KEY) et trousseau de clés ($PRIMUS_ENC_KEYRING) dans KMS pour chiffrer le fichier de données client de l'entreprise Primus.
  • Pool d'identités de charge de travail ($PRIMUS_WORKLOAD_IDENTITY_POOL) pour valider les revendications en fonction des conditions d'attributs configurées dans son fournisseur.
  • Le compte de service ($PRIMUS_SERVICE_ACCOUNT) associé au pool d'identités de charges de travail ($PRIMUS_WORKLOAD_IDENTITY_POOL) mentionné ci-dessus a accès au déchiffrement des données à l'aide de la clé KMS (à l'aide du rôle roles/cloudkms.cryptoKeyDecrypter), au chiffrement des données à l'aide de la clé KMS (à l'aide du rôle roles/cloudkms.cryptoKeyEncrypter), à la lecture des données du bucket Cloud Storage (à l'aide du rôle objectViewer) et à la connexion du compte de service au pool d'identités de charges de travail (à l'aide de roles/iam.workloadIdentityUser).
./setup_primus_resources.sh

3. Créer une charge de travail

Créer un compte de service de charge de travail

Vous allez maintenant créer un compte de service pour la charge de travail avec les rôles et les autorisations requis. Exécutez le script suivant pour créer un compte de service de charge de travail dans le projet Primus. Ce compte de service sera utilisé par la VM qui exécute le serveur d'inférence.

Ce compte de service de charge de travail ($WORKLOAD_SERVICEACCOUNT) disposera des rôles suivants:

  • confidentialcomputing.workloadUser pour obtenir un jeton d'attestation
  • logging.logWriter pour écrire des journaux dans Cloud Logging.
./create_workload_service_account.sh

Créer une charge de travail

Au cours de cette étape, vous allez créer une image Docker de charge de travail. La charge de travail serait créée par l'entreprise Primus. La charge de travail utilisée dans cet atelier de programmation est du code Python qui utilise le modèle codegemma du bucket GCS public (du jardin de modèles de vertex). La charge de travail chargera le modèle codegemma et lancera le serveur d'inférence qui traitera les requêtes de génération de code des développeurs de Primus.

Lors de la requête de génération de code, Workload reçoit le DEK encapsulé avec une invite chiffrée. La charge de travail appelle ensuite l'API KMS pour déchiffrer la DEK, puis déchiffre l'invite à l'aide de cette DEK. Les clés de chiffrement (pour les clés DEK) seraient protégées via le pool d'identités de charge de travail, et l'accès serait accordé aux charges de travail qui répondent aux conditions d'attribut. Ces conditions d'attribut sont décrites plus en détail dans la section suivante sur l'autorisation de la charge de travail. Une fois que le serveur d'inférence dispose de l'invite déchiffrée, il génère le code à l'aide d'un modèle chargé et renvoie la réponse.

Exécutez le script suivant pour créer une charge de travail dans laquelle les étapes suivantes sont effectuées:

  • Créez un Artifact Registry($PRIMUS_ARTIFACT_REGISTRY) appartenant à Primus.
  • Remplacez le code de la charge de travail par les noms des ressources requises.
  • Créez la charge de travail du serveur d'inférence et le Dockerfile pour créer une image Docker du code de la charge de travail. Voici le Dockerfile utilisé pour cet atelier de programmation.
  • Créez et publiez l'image Docker dans Artifact Registry ($PRIMUS_ARTIFACT_REGISTRY) appartenant à Primus.
  • Accordez à $WORKLOAD_SERVICEACCOUNT l'autorisation de lecture pour $PRIMUS_ARTIFACT_REGISTRY. Cela est nécessaire pour que le conteneur de la charge de travail puisse extraire l'image Docker de la charge de travail à partir d'Artifact Registry.
./create_workload.sh

Pour information, voici la méthode generate() de la charge de travail créée et utilisée dans cet atelier de programmation (vous trouverez l'intégralité du code de la charge de travail ici).

def generate():
  try:
    data = request.get_json()
    ciphertext = base64.b64decode(data["ciphertext"])
    wrapped_dek = base64.b64decode(data["wrapped_dek"])
    unwrapped_dek_response = kms_client.decrypt(
        request={"name": key_name, "ciphertext": wrapped_dek}
    )
    unwrapped_dek = unwrapped_dek_response.plaintext
    f = Fernet(unwrapped_dek)
    plaintext = f.decrypt(ciphertext)
    prompt = plaintext.decode("utf-8")
    tokens = tokenizer(prompt, return_tensors="pt")
    outputs = model.generate(**tokens, max_new_tokens=128)
    generated_code = tokenizer.decode(outputs[0])
    generated_code_bytes = generated_code.encode("utf-8")

    response = f.encrypt(generated_code_bytes)
    ciphertext_base64 = base64.b64encode(response).decode("utf-8")
    response = {"generated_code_ciphertext": ciphertext_base64}
    return jsonify(response)

  except (ValueError, TypeError, KeyError) as e:
    return jsonify({"error": str(e)}), 500

4. Autoriser et exécuter la charge de travail

Autoriser la charge de travail

Primus souhaite autoriser les charges de travail à accéder à sa clé KMS utilisée pour le chiffrement immédiat en fonction des attributs des ressources suivantes:

  • Quoi: code validé
  • : environnement sécurisé
  • Who: opérateur de confiance

Primus utilise la fédération d'identité de charge de travail pour appliquer une stratégie d'accès basée sur ces exigences. La fédération d'identité de charge de travail vous permet de spécifier des conditions d'attribut. Ces conditions limitent les identités pouvant s'authentifier avec le pool d'identités de charge de travail (WIP). Vous pouvez ajouter le service de validation des attestations au WIP en tant que fournisseur de pool d'identités de charge de travail pour présenter les mesures et appliquer la stratégie.

Le pool d'identités de charge de travail a déjà été créé lors de l'étape de configuration des ressources cloud. Primus crée maintenant un fournisseur de pools d'identités de charge de travail OIDC. L'--attribute-condition spécifié autorise l'accès au conteneur de la charge de travail. Cela nécessite :

  • Description: dernière $WORKLOAD_IMAGE_NAME importée dans le dépôt $PRIMUS_ARTIFACT_REPOSITORY.
  • : l'environnement d'exécution sécurisé Confidential Space s'exécute sur l'image de VM Confidential Space entièrement compatible.
  • Qui: compte de service $WORKLOAD_SERVICE_ACCOUNT Primus.
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 iam workload-identity-pools providers create-oidc $PRIMUS_WIP_PROVIDER \
  --location="global" \
  --project="$PRIMUS_PROJECT_ID" \
  --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 == 'HARDENED_SHIELDED' && assertion.hwmodel == 'GCP_SHIELDED_VM' && 
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@$PRIMUS_PROJECT_ID.iam.gserviceaccount.com' in assertion.google_service_accounts"

La commande ci-dessus vérifie que la charge de travail s'exécute dans un environnement d'espace sécurisé en vérifiant que hwmodel est défini sur "GCP_SHIELDED_VM" et que swname est défini sur "HARDENED_SHIELDED". De plus, il inclut des assertions spécifiques à la charge de travail, telles que image_digest et image_reference, pour renforcer la sécurité et garantir l'intégrité de la charge de travail en cours d'exécution.

Exécuter la charge de travail

Au cours de cette étape, nous allons exécuter la charge de travail dans la VM Trusted Space, à laquelle un accélérateur sera associé. Les arguments TEE requis sont transmis à l'aide de l'indicateur de métadonnées. Les arguments du conteneur de charge de travail sont transmis à l'aide de la partie "tee-cmd" de l'indicateur. Pour équiper la VM de la charge de travail d'un GPU Nvidia Tesla T4, nous utiliserons l'indicateur --accelerator=type=nvidia-tesla-t4,count=1. Un GPU est alors associé à la VM. Nous devons également inclure tee-install-gpu-driver=true dans les indicateurs de métadonnées pour déclencher l'installation du pilote de GPU approprié.

gcloud compute instances create ${WORKLOAD_VM} \
  --accelerator=type=nvidia-tesla-t4,count=1 \
  --machine-type=n1-standard-16 \
  --shielded-secure-boot \
  --image-project=conf-space-images-preview \
  --image=confidential-space-0-gpupreview-796705b \
  --zone=${PRIMUS_PROJECT_ZONE} \
  --maintenance-policy=TERMINATE \
  --boot-disk-size=40 \
  --scopes=cloud-platform \
  --service-account=${WORKLOAD_SERVICEACCOUNT}@${PRIMUS_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}~tee-install-gpu-driver=true~tee-restart-policy=Never"

Exécuter une requête d'inférence

Une fois le serveur d'inférence de charge de travail lancé, les employés de l'entreprise Primus peuvent envoyer les requêtes de génération de code au serveur d'inférence.

Dans cet atelier de programmation, nous allons utiliser le script suivant pour configurer l'application cliente qui interagira avec le serveur d'inférence. Exécutez ce script pour configurer la VM cliente.

./setup_client.sh

Les étapes suivantes montrent comment se connecter à la VM cliente via SSH et exécuter un exemple d'application cliente dans un environnement virtuel Python. Cet exemple d'application utilise le chiffrement encapsulé avec la bibliothèque Fernet, mais gardez à l'esprit que les bibliothèques de chiffrement spécifiques peuvent être adaptées à différents cas d'utilisation.

gcloud compute ssh ${CLIENT_VM} --zone=${PRIMUS_PROJECT_ZONE}

Exécutez les commandes suivantes pour activer l'environnement virtuel Python dans la VM cliente et exécuter l'application cliente.

source venv/bin/activate
python3 inference_client.py

La sortie de cet exemple d'application cliente affichera les requêtes d'invite de chiffrement et de texte brut, ainsi que les réponses chiffrées et déchiffrées correspondantes.

5. Effectuer un nettoyage

Voici le script que vous pouvez utiliser pour nettoyer les ressources que nous avons créées dans cet atelier de programmation. Dans le cadre de ce nettoyage, les ressources suivantes seront supprimées:

  • Compte de service Primus ($PRIMUS_SERVICEACCOUNT).
  • Clé de chiffrement Primus ($PRIMUS_ENC_KEY)
  • Dépôt d'artefacts de Primus ($PRIMUS_ARTIFACT_REPOSITORY).
  • Pool d'identités de charge de travail Primus ($PRIMUS_WORKLOAD_IDENTITY_POOL) avec son fournisseur.
  • Compte de service de charge de travail de Primus ($WORKLOAD_SERVICEACCOUNT).
  • VM de charge de travail ($WORKLOAD_VM) et VM cliente ($CLIENT_VM).
./cleanup.sh

Si vous avez terminé votre exploration, envisagez de supprimer votre projet.

  • Accédez à la console Cloud Platform.
  • Sélectionnez le projet que vous souhaitez arrêter, puis cliquez sur "Supprimer" en haut de la page. Le projet sera alors programmé pour être supprimé.