Compilation déterministe de calcul fédéré de personnalisation sur l'appareil

Des builds déterministes sont requis pour l'attestation des charges de travail dans l'application Sur l'appareil Environnement d'exécution sécurisé (TEE) de personnalisation (ODP) accessible au public sur Google Cloud en tant qu'espace confidentiel (CS).

Les images de charge de travail doivent générer un hachage d'image déterministe qui peut être utilisé par CS pour l'attestation des charges de travail (qui utilise la norme d'attestation à distance RFC 9334 du NIST RATS (architecture RATS)).

Ce document aborde l'implémentation et la prise en charge de la déterministe dans le odp-federatedcompute un dépôt de clés. Les services ODP Aggregator et Model Updater s'exécuteront dans Espace confidentiel. Le dépôt prend en charge les compilations déterministes qui sont requis pour les cas d'utilisation en production.

Compilations déterministes

Les builds déterministes comprennent deux composants principaux:

  1. Compilation des binaires requis. Cela inclut les jar, les bibliothèques partagées, et des métadonnées.
  2. L'image de base et les dépendances de l'environnement d'exécution Base de l'environnement d'exécution image utilisée pour exécuter les binaires compilés.

À l'heure actuelle, le dépôt ODP Federated Compute prend en charge les types suivants de charges de travail:

  • Charges de travail Java et Spring
    • TaskAssignment, TaskManagement, Collecteur
  • Java + Spring avec les charges de travail JNI TensorFlow
    • ModelUpdater, agrégateur
  • Charges de travail Python
    • TaskBuilder

Dépendances

La liste suivante répertorie les dépendances sur lesquelles l'ODP s'appuie pour maintenir le déterminisme et disponibilité:

  • Bazel
  • GitHub
  • Maven
  • PyPi
  • Instantanés Debian
  • Registre DockerHub
  • Google Container Registry (GCR)

Charges de travail déterministes

Toutes les charges de travail sont compilées à l'aide de Bazel avec des chaînes d'outils propres à un langage et des images de conteneurs créées avec rules_oci L'ESPACE DE TRAVAIL fichier définit toutes les dépendances avec les versions et les hachages correspondants.

Instantanés Debian

Toutes les images de charge de travail doivent être créées dans les dockerfile basé sur un instantané Debian. Debian fournissent un instantané de dépôt stable avec des valeurs déterministes:

Charges de travail Java Spring

Bazel remotejdk_17 correspond à utilisé pour fournir un Java hermétique pour la compilation. D'autres dépendances Java sont gérées et définies dans l'espace de travail WORKSPACE fichier.

Les charges de travail Java Spring se compilent dans un fichier JAR nommé <service>_application.jar Le pot contient:

  • Fichiers de classe Java
  • META-INF/
    • Données du fichier manifeste Bazel
  • build-data.properties
    • Données de build Bazel
  • BOOT-INF/
    • Dépendances JAR empaquetées, générées par rules_spring.

Calques d'image

L'image de charge de travail Java Spring se compose de deux couches:

Configuration des images

  • Point d'entrée
    • java -jar <service>_application.jar

Charges de travail JNI TensorFlow

Les charges de travail JNI TensorFlow sont basées sur les charges de travail Java Spring. A La chaîne d'outils hermétique Clang+LLVM Bazel est fournie à l'aide de Clang+LLVM prédéfinis 16 par un sysroot fourni par l'image de l'instantané Debian pour compiler le code machine.

Les charges de travail JNI se compilent dans une bibliothèque partagée nommée libtensorflow.so, ainsi que avec <service>_application.jar.

Calques d'image

L'image de charge de travail JNI TensorFlow est composée de plusieurs couches:

  • Calque de l'image de base
  • Couches de dépendance des packages Debian. Les couches sont générées à l'aide de deb Archives téléchargées à partir de debian-snapshot et réempaquetées en tant que couches d'image
    • libc++1-16_amd64.tar
    • libc++abi1-16_amd64.tar
    • libc6_amd64.tar
    • libunwind-16_amd64.tar
    • libgcc-s1_amd64.tar
    • gcc-13-base_amd64.tar
  • Couche de charge de travail
    • binary_tar.tar
      • <service>_application.jar
      • libtensorflow-jni.so
      • libaggregation-jni.so

Configuration des images

  • Étiquettes (uniquement pour les images conçues pour s'exécuter dans TEE)
    • "tee.launch_policy.allow_env_override": "FCP_OPTS"
    • "tee.launch_policy.log_redirect": "always"
    • "tee.launch_policy.monitoring_memory_allow": "always"
  • Point d'entrée
    • java -Djava.library.path=. -jar <service>_application.jar

Charges de travail Python

Le code rules_python de Bazel est utilisé pour fournir une chaîne d'outils hermétique Python 3.10. Exigences concernant un pip verrouillé fichier est utilisé pour l'extraction déterministe des dépendances pip. L'instantané Debian permet d'extraire des distributions déterministes basées sur la plate-forme compatibilité et fournit une chaîne d'outils C++ pour compiler les distributions sources.

Les charges de travail Python seront empaquetées dans un ensemble de packages pip téléchargés, Distribution Python 3.10, code source Python de l'ODP et démarrage Python script.

  • <service>.runfiles/
    • La distribution Python est stockée sous python_x86_64-unknown-linux-gnu/.
    • Le code source est stocké sous com_google_ondevicepersonalization_federatedcompute/
    • Les packages Pip sont stockés sous pypi_<dependency_name>/
  • <service>.runfiles_manifest
    • Fichier manifeste du répertoire <service>.runfiles/
  • <service>
    • Script Python permettant d'exécuter la charge de travail Python à l'aide des fichiers d'exécution

Calques d'image

L'image de charge de travail Python se compose de quatre couches:

  • Calque de l'image de base
  • Couche interpréteur
    • interpreter_layer.jar
      • <service>/<service>.runfiles/python_x86_64-unknown-linux-gnu/**
  • Couche "Packages"
    • packages_layer.jar
      • <service>/<service>.runfiles/**/site-packages/**
  • Couche de charge de travail
    • app_tar_manifest.tar
      • Contient le code source, le script de démarrage et le fichier manifeste.
        • <service>/<service>.runfiles_manifest
        • <service>/<service>
        • <service>/<service>.runfiles/com_google_ondevicepersonalization_federatedcompute/**

Configuration des images

  • Point d'entrée
    • /<service>/<service>

Créer des images

Une fois vos charges de travail choisies, vous pouvez créer et publier images.

Prérequis

  • Bazel 6.4.0
    • Nécessite des installations Java et C++
  • Docker

Procédure

Les images doivent être créées dans le conteneur Docker créé par la dockerfile. Deux scripts sont fournis pour vous aider à créer les images déterministes finales.

  • docker_run.sh
    • docker_run.sh crée l'image Docker à partir du fichier Dockerfile, effectue l'installation dans le répertoire professionnel, installez le daemon Docker hôte et exécutez Docker avec le la commande bash fournie. Toutes les variables transmises avant la commande bash être traités comme des indicateurs d'exécution Docker.
  • build_images.sh
    • build_images.sh exécutera bazel build pour toutes les images et affichera la générés pour chaque image compilée.

Créer toutes les images

./scripts/docker/docker_run.sh "./scripts/build_images.sh"

Les hachages d'image attendus pour chaque version sont disponibles sous odp-federatedcompute GitHub versions.

Publier des images

La publication est configurée à l'aide de oci_push c'est Bazel. Pour chaque service, le dépôt cible doit être configuré tous:

  • agrégateur
  • collecteur
  • model_updater
  • task_assignment
  • task_management
  • task_scheduler
  • task_builder

Publier une seule image

./scripts/docker/docker_run.sh "bazel run //shuffler/services/<servicename_no_underscore>:<servicename_with_underscore>_image_publish"

Images créées

Toutes les images compilées devront être stockées et hébergées par le créateur, par exemple dans un GCP Artifact Registry :