Ćwiczenie z programowania dotyczące poufnej przestrzeni

1. Przegląd

Chcesz zwiększyć bezpieczeństwo i prywatność zbiorów zadań przyspieszanych przez GPU? Ten przewodnik pomoże Ci poznać możliwości Trusted Space, czyli usługi zapewniającej silną izolację operatora i obsługę akceleratorów w przypadku wrażliwych zbiorów zadań AI/ML.

Ochrona cennych danych, modeli i kluczy jest ważniejsza niż kiedykolwiek. Trusted Space zapewnia rozwiązanie, które gwarantuje, że Twoje zadania działają w bezpiecznym, zaufanym środowisku, do którego nawet operator zadania nie ma dostępu.

Oto co oferuje zaufana przestrzeń:

  • Większa prywatność i bezpieczeństwo: zaufana przestrzeń zapewnia zaufane środowisko wykonawcze, w którym Twoje zasoby wrażliwe (np. modele, cenne dane i klucze) pozostają chronione dzięki dowodom kryptograficznym.
  • Izolacja operatora: eliminuje obawy dotyczące zakłóceń ze strony operatora. W przypadku zaufanej przestrzeni nawet operatorzy zbiorów zadań nie mają dostępu, co uniemożliwia im łączenie się przez SSH, uzyskiwanie dostępu do danych, instalowanie oprogramowania ani manipulowanie kodem.
  • Obsługa akceleratorów: Trusted Space został zaprojektowany tak, aby bezproblemowo współpracować z szeroką gamą akceleratorów sprzętowych, w tym z procesorami graficznymi, takimi jak H100, A100, T4 i L4. Dzięki temu aplikacje AI/ML o kluczowym znaczeniu dla wydajności będą działać bez zakłóceń.

Czego się nauczysz

  • Poznaj kluczowe oferty Trusted Space.
  • Dowiedz się, jak wdrożyć i skonfigurować środowisko zaufanej przestrzeni, aby zabezpieczyć cenne zasoby zbioru zadań AI/ML.

Czego potrzebujesz

Ochrona promptów do generowania kodu wrażliwego za pomocą Primus Company

W tym laboratorium kodowym wcielimy się w rolę firmy Primus, która priorytetowo traktuje prywatność i bezpieczeństwo danych swoich pracowników. Firma Primus chce wdrożyć model generowania kodu, aby pomagać programistom w ich zadaniach związanych z kodowaniem. Obawiają się jednak o poufność promptów przesyłanych przez pracowników, ponieważ często zawierają one fragmenty kodu z informacjami poufnymi, szczegóły projektów wewnętrznych lub algorytmy zastrzeżone.

Dlaczego firma Primus nie ufa operatorowi?

Firma Primus Corp działa na bardzo konkurencyjnym rynku. Ich baza kodu zawiera cenną własność intelektualną, w tym zastrzeżone algorytmy i wrażliwe fragmenty kodu, które zapewniają im przewagę konkurencyjną. Obawiają się szpiegostwa korporacyjnego ze strony operatorów zbiorów zadań. Dodatkowo prompty pracowników mogą zawierać poufne fragmenty kodu, które firma Primus Corp chce chronić.

Aby rozwiązać ten problem, firma Primus Corp wykorzysta zaufaną przestrzeń do odizolowania serwera wnioskowania, na którym działa model generowania kodu. Jak to działa:

  • Szyfrowanie promptów: przed wysłaniem prompta na serwer wnioskowania każdy pracownik zaszyfruje go za pomocą klucza KMS zarządzanego przez Primus Corp w Google Cloud. Dzięki temu tylko środowisko zaufanej przestrzeni, w którym dostępny jest odpowiedni klucz odszyfrowywania, może odszyfrować prompta i uzyskać do niego dostęp w formie zwykłego tekstu. W rzeczywistości szyfrowanie po stronie klienta może być obsługiwane przez dostępne biblioteki (np. tink). W ramach tego laboratorium użyjemy tej przykładowej aplikacji klienta z szyfrowaniem kopertowym.
  • Izolacja operatora: tylko serwer wnioskowania działający w środowisku zaufanej przestrzeni będzie mieć dostęp do klucza używanego do szyfrowania i będzie mógł odszyfrować prompt w zaufanym środowisku. Dostęp do klucza szyfrowania będzie chroniony przez pulę tożsamości zadań. Ze względu na gwarancje izolacji Poufnej przestrzeni nawet operator zadania nie ma dostępu do klucza używanego do szyfrowania ani do odszyfrowanych treści.
  • Bezpieczne wnioskowanie z użyciem akceleratorów: serwer wnioskowania zostanie uruchomiony na chronionej maszynie wirtualnej (w ramach konfiguracji zaufanej przestrzeni), co zapewni, że instancja obciążenia nie została naruszona przez złośliwe oprogramowanie ani rootkity działające na etapie rozruchu lub na poziomie jądra systemu operacyjnego. Ten serwer odszyfrowuje prompt w środowisku Trusted Space, przeprowadza wnioskowanie przy użyciu modelu generowania kodu i zwraca wygenerowany kod pracownikowi.

2. Konfigurowanie zasobów w chmurze

Zanim zaczniesz

  • Sklonuj to repozytorium za pomocą poniższego polecenia, aby uzyskać wymagane skrypty, które są używane w tym laboratorium.
git clone https://github.com/GoogleCloudPlatform/confidential-space.git
  • Zmień katalog tego laboratorium.
cd confidential-space/codelabs/trusted_space_codelab/scripts
  • Sprawdź, czy masz ustawione wymagane zmienne środowiskowe projektu, jak pokazano poniżej. Więcej informacji o konfigurowaniu projektu GCP znajdziesz w tym samouczku. Więcej informacji o tym, jak uzyskać identyfikator projektu i czym różni się on od nazwy i numeru projektu, znajdziesz tutaj.
export PRIMUS_PROJECT_ID=<GCP project id of Primus>
  • Włącz płatności w swoich projektach.
  • Włącz w obu projektach interfejs Confidential Computing API i te interfejsy API:
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
  • Przypisz wartości do zmiennych dla nazw zasobów określonych powyżej za pomocą tego polecenia. Te zmienne umożliwiają dostosowywanie nazw zasobów do potrzeb, a także używanie istniejących zasobów, jeśli zostały już utworzone. (np.export PRIMUS_SERVICE_ACCOUNT='my-service-account')
  1. W projekcie Primus możesz ustawić te zmienne, używając nazw istniejących zasobów w chmurze. Jeśli zmienna jest ustawiona, zostanie użyty odpowiedni istniejący zasób w chmurze z projektu Primus. Jeśli zmienna nie jest ustawiona, nazwa zasobu w chmurze zostanie wygenerowana na podstawie nazwy projektu i zostanie utworzony nowy zasób w chmurze o tej nazwie. Oto obsługiwane zmienne nazw zasobów:

$PRIMUS_PROJECT_REGION

Region, w którym zostaną utworzone zasoby regionalne dla firmy Primus.

$PRIMUS_SERVICE_LOCATION

Lokalizacja, w której zostaną utworzone zasoby dla firmy Primus.

$PRIMUS_PROJECT_ZONE

Strefa, w której zostaną utworzone zasoby strefowe dla firmy Primus.

$PRIMUS_WORKLOAD_IDENTITY_POOL

Pula tożsamości zadań firmy Primus do ochrony zasobów w chmurze.

$PRIMUS_WIP_PROVIDER

Dostawca puli tożsamości zadań firmy Primus, który zawiera warunek autoryzacji do użycia w przypadku tokenów podpisanych przez usługę weryfikacji atestu.

$PRIMUS_SERVICEACCOUNT

Konto usługi firmy Primus, którego $PRIMUS_WORKLOAD_IDENTITY_POOL używa do uzyskiwania dostępu do chronionych zasobów. Na tym etapie ma uprawnienia do wyświetlania danych klientów przechowywanych w zasobniku $PRIMUS_INPUT_STORAGE_BUCKET.

$PRIMUS_ENC_KEY

Klucz KMS służy do szyfrowania promptów dostarczanych przez pracowników firmy Primus.

$PRIMUS_ENC_KEYRING

Pęk kluczy KMS, który będzie używany do tworzenia klucza szyfrującego $PRIMUS_ENC_KEY dla firmy Primus.

$PRIMUS_ENC_KEYVERSION

Wersja klucza KMS klucza szyfrowania $PRIMUS_ENC_KEY. Wartością domyślną jest 1. Zaktualizuj tę wartość, jeśli używasz istniejącego klucza, który został w przeszłości poddany rotacji, a jego wersja została zaktualizowana.

$PRIMUS_ARTIFACT_REPOSITORY

Repozytorium artefaktów, do którego zostanie przesłany obraz Dockera obciążenia.

$PRIMUS_PROJECT_REPOSITORY_REGION

Region repozytorium artefaktów, w którym będzie opublikowany obraz Dockera zbioru zadań.

$WORKLOAD_VM

Nazwa maszyny wirtualnej zadania.

$WORKLOAD_IMAGE_NAME

Nazwa obrazu Dockera zadania.

$WORKLOAD_IMAGE_TAG

Tag obrazu kontenera zbioru zadań.

$WORKLOAD_SERVICEACCOUNT

Konto usługi, które ma uprawnienia dostępu do maszyny wirtualnej poufnego środowiska, na której działa zadanie.

$CLIENT_VM

Nazwa maszyny wirtualnej klienta, na której będzie uruchamiana aplikacja kliencka serwera wnioskowania.

$CLIENT_SERVICEACCOUNT

Konto usługi używane przez $CLIENT_VM

  • W projekcie $PRIMUS_PROJECT_ID musisz mieć role Administrator pamięci masowej, Administrator Artifact Registry, Administrator Cloud KMS, Administrator konta usługi i Uprawnienia – administrator pul Workload Identity. W tym przewodniku znajdziesz informacje o tym, jak przyznawać role uprawnień za pomocą konsoli GCP.
  • W przypadku $PRIMUS_PROJECT_ID uruchom ten skrypt, aby ustawić pozostałe nazwy zmiennych na wartości oparte na identyfikatorze projektu dla nazw zasobów.
source config_env.sh

Konfigurowanie zasobów firmy Primus

W ramach tego kroku skonfigurujesz wymagane zasoby w chmurze dla Primusa. Aby skonfigurować zasoby dla Primusa, uruchom ten skrypt. W ramach wykonania skryptu zostaną utworzone te zasoby:

  • Klucz szyfrowania ($PRIMUS_ENC_KEY) i pęk kluczy ($PRIMUS_ENC_KEYRING) w KMS do szyfrowania pliku danych klienta firmy Primus.
  • Pula tożsamości zadań ($PRIMUS_WORKLOAD_IDENTITY_POOL) do weryfikowania roszczeń na podstawie warunków atrybutów skonfigurowanych u jej dostawcy.
  • Konto usługi ($PRIMUS_SERVICE_ACCOUNT) dołączone do wspomnianej puli tożsamości zadań ($PRIMUS_WORKLOAD_IDENTITY_POOL) ma dostęp do odszyfrowywania danych za pomocą klucza KMS (za pomocą roli roles/cloudkms.cryptoKeyDecrypter), szyfrowania danych za pomocą klucza KMS (za pomocą roli roles/cloudkms.cryptoKeyEncrypter), odczytywania danych z zasobnika Cloud Storage (za pomocą roli objectViewer) i łączenia konta usługi z pulą tożsamości zadań (za pomocą roli roles/iam.workloadIdentityUser).
./setup_primus_resources.sh

3. Tworzenie zadania

Tworzenie konta usługi obciążenia

Teraz utworzysz konto usługi dla zadania z wymaganymi rolami i uprawnieniami. Uruchom ten skrypt, aby utworzyć konto usługi obciążenia w projekcie Primus. To konto usługi będzie używane przez maszynę wirtualną, na której działa serwer wnioskowania.

To konto usługi obciążenia ($WORKLOAD_SERVICEACCOUNT) będzie miało przypisane te role:

  • confidentialcomputing.workloadUser, aby uzyskać token atestu.
  • logging.logWriter – zapisywanie logów w Cloud Logging.
./create_workload_service_account.sh

Tworzenie zadania

W ramach tego kroku utworzysz obraz Dockera z obciążeniem. Zadanie zostanie utworzone przez firmę Primus. Obciążenie użyte w tym ćwiczeniu z programowania to kod w Pythonie, który korzysta z modelu codegemmapublicznie dostępnego zasobnika GCS (bazy modeli Vertex). Obciążenie wczyta model codegemma i uruchomi serwer wnioskowania, który będzie obsługiwać żądania generowania kodu od deweloperów Primus.

W żądaniu wygenerowania kodu zadanie otrzyma opakowany klucz DEK wraz z zaszyfrowanym promptem. Obciążenie wywoła interfejs KMS API, aby odszyfrować klucz DEK, a następnie odszyfruje prompt przy użyciu tego klucza. Klucze szyfrowania (w przypadku klucza DEK) będą chronione za pomocą puli tożsamości zadań, a dostęp do nich będzie przyznawany zadaniom spełniającym warunki atrybutów. Warunki atrybutów są szczegółowo opisane w następnej sekcji dotyczącej autoryzacji obciążenia. Gdy serwer wnioskowania otrzyma odszyfrowany prompt, wygeneruje kod za pomocą załadowanego modelu i zwróci odpowiedź.

Uruchom ten skrypt, aby utworzyć zadanie, w którym wykonywane są te czynności:

  • Utwórz Artifact Registry($PRIMUS_ARTIFACT_REGISTRY) należący do Primus.
  • Zaktualizuj kod zbioru zadań, podając nazwy wymaganych zasobów.
  • Utwórz zadanie serwera wnioskowania i plik Dockerfile do utworzenia obrazu Dockera z kodem zadania. Tutaj znajdziesz plik Dockerfile użyty w tych ćwiczeniach z programowania.
  • Utwórz obraz Dockera i opublikuj go w Artifact Registry ($PRIMUS_ARTIFACT_REGISTRY) należącym do firmy Primus.
  • Przyznaj usłudze $WORKLOAD_SERVICEACCOUNT uprawnienia do odczytu $PRIMUS_ARTIFACT_REGISTRY. Jest to potrzebne, aby kontener zbioru zadań mógł pobrać obraz Dockera zbioru zadań z Artifact Registry.
./create_workload.sh

Dla Twojej wygody podajemy metodę generate() zbioru zadań, który jest tworzony i używany w tym ćwiczeniu z programowania (cały kod zbioru zadań znajdziesz tutaj).

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. Autoryzowanie i uruchamianie zbioru zadań

Autoryzowanie zadania

Firma Primus chce autoryzować zbiory zadań do uzyskiwania dostępu do klucza KMS używanego do szyfrowania promptów na podstawie atrybutów tych zasobów:

  • Co: kod, który został zweryfikowany.
  • Gdzie: bezpieczne środowisko.
  • Kto: zaufany operator

Primus używa federacji tożsamości zadań, aby egzekwować zasady dostępu oparte na tych wymaganiach. Federacja tożsamości zadań umożliwia określanie warunków atrybutów. Te warunki ograniczają tożsamości, które mogą uwierzytelniać się w puli tożsamości zadań. Możesz dodać usługę weryfikacji zaświadczeń do puli tożsamości zadań jako dostawcę puli tożsamości zadań, aby przedstawiać pomiary i egzekwować zasady.

Pula tożsamości zadań została już utworzona wcześniej w ramach kroku konfiguracji zasobów w chmurze. Primus utworzy teraz nowego dostawcę puli tożsamości zadań OIDC. Określony --attribute-condition autoryzuje dostęp do kontenera zadania. Wymagania:

  • Co: najnowsza wersja $WORKLOAD_IMAGE_NAME została przesłana do repozytorium $PRIMUS_ARTIFACT_REPOSITORY.
  • Miejsce: zaufane środowisko wykonawcze Poufnej przestrzeni działa na w pełni obsługiwanym obrazie maszyny wirtualnej Poufnej przestrzeni.
  • Kto: konto usługi Primus $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 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"

Powyższe polecenie sprawdza, czy zadanie działa w zaufanym środowisku, weryfikując, czy wartość hwmodel to „GCP_SHIELDED_VM”, a wartość swname to „HARDENED_SHIELDED”. Zawiera też asercje dotyczące konkretnych zadań, takie jak image_digestimage_reference, które zwiększają bezpieczeństwo i zapewniają integralność uruchomionego zadania.

Uruchamianie zadania

W ramach tego kroku uruchomimy zadanie na maszynie wirtualnej w zaufanej przestrzeni, do której będzie dołączony akcelerator. Wymagane argumenty TEE są przekazywane za pomocą flagi metadanych. Argumenty kontenera zadania są przekazywane za pomocą części „tee-cmd” flagi. Aby wyposażyć maszynę wirtualną z obciążeniem w procesor graficzny Nvidia Tesla T4, użyjemy flagi --accelerator=type=nvidia-tesla-t4,count=1. Spowoduje to podłączenie jednego GPU do maszyny wirtualnej. W flagach metadanych musimy też umieścić symbol tee-install-gpu-driver=true, aby uruchomić instalację odpowiedniego sterownika GPU.

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"

Uruchamianie zapytania o wnioskowanie

Po pomyślnym uruchomieniu serwera wnioskowania dotyczącego obciążenia pracownicy firmy Primus mogą wysyłać do niego żądania generowania kodu.

W ramach tego ćwiczenia z programowania użyjemy poniższego skryptu do skonfigurowania aplikacji klienckiej, która będzie wchodzić w interakcje z serwerem wnioskowania. Uruchom ten skrypt, aby skonfigurować kliencką maszynę wirtualną.

./setup_client.sh

Poniższe kroki pokazują, jak połączyć się przez SSH z maszyną wirtualną klienta i uruchomić przykładową aplikację kliencką w wirtualnym środowisku Pythona. Ta przykładowa aplikacja wykorzystuje szyfrowanie kopertowe z biblioteką Fernet, ale pamiętaj, że konkretne biblioteki szyfrowania można dostosować do różnych przypadków użycia.

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

Uruchom te polecenia, aby aktywować środowisko wirtualne Pythona na maszynie wirtualnej klienta i wykonać aplikację klienta.

source venv/bin/activate
python3 inference_client.py

Dane wyjściowe tej przykładowej aplikacji klienckiej będą zawierać żądania szyfrowania i tekstu jawnego oraz odpowiadające im zaszyfrowane i odszyfrowane odpowiedzi.

5. Czyszczenie

Tutaj znajdziesz skrypt, który umożliwia usunięcie zasobów utworzonych w ramach tego ćwiczenia z programowania. W ramach tego czyszczenia zostaną usunięte te zasoby:

  • Konto usługi Primus ($PRIMUS_SERVICEACCOUNT).
  • Klucz szyfrowania Primus ($PRIMUS_ENC_KEY).
  • Repozytorium artefaktów Primus ($PRIMUS_ARTIFACT_REPOSITORY).
  • Pula tożsamości zadań Primus ($PRIMUS_WORKLOAD_IDENTITY_POOL) z dostawcą.
  • Konto usługi Primus ($WORKLOAD_SERVICEACCOUNT).
  • Maszyna wirtualna obciążenia ($WORKLOAD_VM) i maszyna wirtualna klienta ($CLIENT_VM).
./cleanup.sh

Jeśli skończysz eksplorowanie, rozważ usunięcie projektu.

  • Otwórz konsolę Cloud Platform.
  • Wybierz projekt, który chcesz zamknąć, a następnie kliknij „Usuń” u góry. Spowoduje to zaplanowanie usunięcia projektu.