Ćwiczenie z programowania dotyczące poufnej przestrzeni

1. Omówienie

Chcesz zwiększyć bezpieczeństwo i prywatność zbiorów zadań przyspieszanych przez GPU? W tym laboratorium programistycznym poznasz możliwości Trusted Space, czyli oferty zapewniającej silną izolację operatora i obsługę akceleratora dla poufnych zbiorów zadań AI/ML.

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

Co oferuje Trusted Space:

  • Ulepszona prywatność i bezpieczeństwo: Trusted Space zapewnia bezpieczne środowisko wykonywania, w którym Twoje zasoby poufne (np. modele, cenne dane i klucze) pozostają chronione dzięki zabezpieczeniom kryptograficznym.
  • Izolacja operatora: eliminuje obawy związane z zakłóceniami ze strony operatora. W przypadku przestrzeni zaufanej nawet operatorzy zbioru zadań nie mają dostępu, co uniemożliwia im łączenie się przez SSH, uzyskiwanie dostępu do danych, instalowanie oprogramowania lub modyfikowanie kodu.
  • Obsługa akceleratorów: bezpieczna przestrzeń została zaprojektowana tak, aby sprawnie współpracować z szeroką gamą akceleratorów sprzętowych, w tym procesorów graficznych, takich jak H100, A100, T4 i L4. Dzięki temu aplikacje AI/ML o kluczowym znaczeniu dla wydajności będą działać płynnie.

Czego się nauczysz

  • Poznaj najważniejsze funkcje Trusted Space.
  • Dowiedz się, jak wdrożyć i skonfigurować środowisko zaufanego miejsca, aby zabezpieczyć cenne zasoby związane z zadaniami z zakresu AI/ML.

Czego potrzebujesz

Ochrona promptów generowania kodu zawierającego dane wrażliwe w Primus Company

W tym laboratorium programistycznym wcielimy się w rolę Primus, firmy, która priorytetowo traktuje prywatność i bezpieczeństwo danych swoich pracowników. Firma Primus chce wdrożyć model generowania kodu, aby pomóc swoim programistom w realizacji zadań związanych z tworzeniem kodu. Obawiają się jednak, że nie będą w stanie zapewnić poufności promptów przesyłanych przez pracowników, ponieważ często zawierają one fragmenty kodu, szczegóły projektów wewnętrznych lub zastrzeżone algorytmy.

Dlaczego firma Primus nie ufa Operatorowi?

Firma Primus Corp. działa na bardzo konkurencyjnym rynku. Ich kod źródłowy zawiera cenne elementy własności intelektualnej, w tym zastrzeżone algorytmy i fragmenty kodu o strategicznym znaczeniu, które dają przewagę konkurencyjną. Obawiają się, że operatorzy obciążenia mogą prowadzić szpiegostwo korporacyjne. Dodatkowo prompty dla pracowników mogą zawierać poufne części kodu, które Primus Corp. chce chronić.

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

  • Szyfrowanie promptu: zanim każdy pracownik wyśle prompt do serwera wnioskowania, szyfruje go przy użyciu klucza KMS zarządzanego przez Primus Corp. w Google Cloud. Dzięki temu tylko środowisko zaufanego pokoju, w którym dostępny jest odpowiedni klucz odszyfrowywania, może odszyfrować prompt i uzyskać dostęp do promptu w postaci zwykłego tekstu. W rzeczywistych warunkach szyfrowanie po stronie klienta może być obsługiwane przez dostępne biblioteki (np. tink). W ramach tego Codelab użyjemy tego przykładowego klienta z szyfrowaniem kopert.
  • Izolacja operatora: tylko serwer wnioskowania działający w środowisku zaufanego środowiska będzie miał dostęp do klucza używanego do szyfrowania i będzie mógł odszyfrować prompt w zaufanym środowisku. Dostęp do klucza szyfrowania byłby chroniony przez pulę Workload Identity. Ze względu na izolację gwarantowaną przez zaufaną przestrzeń nawet operator zadań nie ma dostępu do klucza użytego do szyfrowania i odszyfrowanych treści.
  • Bezpieczne wnioskowanie za pomocą akceleratorów: serwer wnioskowania zostałby uruchomiony na chronionej maszynie wirtualnej (w ramach konfiguracji zaufanego obszaru), co zapewniłoby pewność, że instancja zbioru zadań nie została naruszona przez złośliwe oprogramowanie lub rootkity na etapie rozruchu lub na poziomie jądra. Serwer ten odszyfrowuje prompt w środowisku Trusted Space, wykonuje wnioskowanie za pomocą modelu generowania kodu i zwraca wygenerowany kod pracownikowi.

2. Konfigurowanie zasobów Cloud

Zanim zaczniesz

  • Skopiuj to repozytorium, używając tego polecenia, aby pobrać wymagane skrypty używane w ramach tego Codelab.
git clone https://github.com/GoogleCloudPlatform/confidential-space.git
  • Zmień katalog dla tego Codelab.
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 CodeLab. Więcej informacji o tym, jak pobrać 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 przypadku swoich projektów.
  • Włącz interfejs Confidential Computing API i te interfejsy API w obu projektach.
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 zmiennym dla nazw zasobów określonych powyżej, używając tego polecenia. Te zmienne umożliwiają dostosowanie nazw zasobów do potrzeb oraz korzystanie z dotychczasowych zasobów, jeśli zostały już utworzone. (np.export PRIMUS_SERVICE_ACCOUNT='my-service-account')
  1. Te zmienne możesz ustawić za pomocą nazw istniejących zasobów w chmurze w projekcie Primus. Jeśli zmienna jest ustawiona, używane jest odpowiednie istniejące zasobów w chmurze z projektu Primus. Jeśli zmienna nie jest ustawiona, nazwa zasobu w chmurze zostanie wygenerowana na podstawie nazwy projektu, a nowy zasób w chmurze zostanie utworzony z tą nazwą. 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 mają być tworzone zasoby dla firmy Primus.

$PRIMUS_PROJECT_ZONE

Strefa, w której tworzone będą zasoby strefowe dla firmy Primus.

$PRIMUS_WORKLOAD_IDENTITY_POOL

Pula tożsamości zadań firmy Primus służąca 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ę weryfikatora atestu.

$PRIMUS_SERVICEACCOUNT

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

$PRIMUS_ENC_KEY

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

$PRIMUS_ENC_KEYRING

Klucz KMS, który zostanie użyty do utworzenia klucza szyfrowania $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ł już rotowany, a jego wersja została zaktualizowana.

$PRIMUS_ARTIFACT_REPOSITORY

Repozytorium artefaktów, do którego zostanie przesłany obraz Dockera zadania.

$PRIMUS_PROJECT_REPOSITORY_REGION

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

$WORKLOAD_VM

Nazwa maszyny wirtualnej obsługującej zadanie.

$WORKLOAD_IMAGE_NAME

Nazwa obrazu Dockera zbioru zadań.

$WORKLOAD_IMAGE_TAG

Tag obrazu kontenera zbioru zadań.

$WORKLOAD_SERVICEACCOUNT

Konto usługi, które ma uprawnienia dostępu do maszyny wirtualnej poufnej, na której działa obciążenie.

$CLIENT_VM

Nazwa maszyny wirtualnej klienta, na której będzie działać aplikacja kliencka serwera wnioskowania.

$CLIENT_SERVICEACCOUNT

Konto usługi używane przez $CLIENT_VM

  • W projekcie $PRIMUS_PROJECT_ID musisz mieć role administratora pamięci masowej, administratora rejestru artefaktów, administratora Cloud KMS, administratora konta usługi i administratora pul Workload Identity. W tym przewodniku znajdziesz informacje o przyznawaniu ról 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.
source config_env.sh

Konfigurowanie zasobów firmy Primus

W ramach tego kroku skonfigurujesz wymagane zasoby chmurowe dla Primus. Aby skonfigurować zasoby dla Primus, uruchom ten skrypt. W ramach wykonywania skryptu zostaną utworzone te zasoby:

  • Klucz szyfrowania ($PRIMUS_ENC_KEY) i kluczyk ($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 w ramach dostawcy.
  • Konto usługi ($PRIMUS_SERVICE_ACCOUNT) przypisane do wspomnianego zbioru tożsamości zadań ($PRIMUS_WORKLOAD_IDENTITY_POOL) ma dostęp do odszyfrowywania danych za pomocą klucza KMS (przy użyciu roli roles/cloudkms.cryptoKeyDecrypter), szyfrowania danych za pomocą klucza KMS (przy użyciu roli roles/cloudkms.cryptoKeyEncrypter), odczytywania danych z puli danych w Cloud Storage (przy użyciu roli objectViewer) oraz łączenia konta usługi z pulą tożsamości zadań (przy użyciu roli roles/iam.workloadIdentityUser).
./setup_primus_resources.sh

3. Tworzenie zadań

Tworzenie konta usługi związanej z zadaniami

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

To konto usługi zadania ($WORKLOAD_SERVICEACCOUNT) będzie mieć przypisane te role:

  • confidentialcomputing.workloadUser, aby uzyskać token atestacji
  • logging.logWriter, aby zapisywać logi w Cloud Logging.
./create_workload_service_account.sh

Tworzenie zbioru zadań

W ramach tego kroku utworzysz obraz Dockera zadania. Zadanie zostanie utworzone przez firmę Primus. Zadanie użyte w tym ćwiczeniu to kod Pythona, który korzysta z modelu CodeGemmapublicznie dostępnego zasobnika GCS (z ogrodu modeli wierzchołkowych). Workload wczyta model codegemma i uruchomi serwer wnioskowania, który będzie obsługiwać żądania generowania kodu od programistów Primus.

W odpowiedzi na żądanie wygenerowania kodu Workload otrzyma zapakowany klucz DEK wraz z zaszyfrowanym promptem. Obciążenie wykona wtedy wywołanie interfejsu KMS API w celu odszyfrowania DEK, a następnie odszyfruje prompt za pomocą tego klucza. Klucze szyfrowania (dla kluczy szyfrowania danych) byłyby chronione za pomocą puli tożsamości zadań, a dostęp byłby przyznawany zadaniom, które spełniają warunki atrybutu. Warunki atrybutów są opisane bardziej szczegółowo 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 ramach którego wykonywane są te czynności:

  • Utwórz Artifact Registry($PRIMUS_ARTIFACT_REGISTRY) należącą do Primus.
  • Zaktualizuj kod zbioru zadań, podając nazwy wymaganych zasobów.
  • Utwórz zadanie serwera wnioskowania i plik Dockerfile do utworzenia obrazu Dockera kodu zadania. Tutaj znajduje się plik Dockerfile użyty w tym ćwiczeniu.
  • Skompiluj i opublikuj obraz Dockera w repozytorium Artifact Registry ($PRIMUS_ARTIFACT_REGISTRY) należącym do Primus.
  • Przyznaj użytkownikowi $WORKLOAD_SERVICEACCOUNT uprawnienie do odczytu w przypadku zasobu $PRIMUS_ARTIFACT_REGISTRY. Jest to konieczne, aby kontener zadania pobierał obraz Dockera zadania z rejestru artefaktów.
./create_workload.sh

Poniżej znajdziesz metodę generate() zbioru zadań, która jest tworzona i używana 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 zbioru zadań

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

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

Primus używa federacji tożsamości zadań do egzekwowania zasad dostępu na podstawie tych wymagań. Federacja tożsamości zadań umożliwia określenie warunków atrybutów. Te warunki ograniczają tożsamości, które mogą uwierzytelniać się w puli tożsamości zadań. Aby przedstawić pomiary i wdrożyć zasady, możesz dodać usługę weryfikatora autentyczności do puli WIP jako dostawcę puli tożsamości zadań.

Podczas konfigurowania zasobów chmury została już utworzona pula tożsamości zadań. Teraz Primus utworzy nowego dostawcę puli tożsamości zadań OIDC. Określony element --attribute-condition autoryzuje dostęp do kontenera zbioru zadań. Wymagania:

  • Co: najnowsze $WORKLOAD_IMAGE_NAME przesłane do repozytorium $PRIMUS_ARTIFACT_REPOSITORY.
  • Gdzie: 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 jest wykonywane w zaufanym środowisku, sprawdzając, czy wartość parametru hwmodel to „GCP_SHIELDED_VM”, a parametr swname ma wartość „HARDENED_SHIELDED”. Ponadto zawiera stwierdzenia dotyczące konkretnych zadań, takie jak image_digest i image_reference, które zwiększają bezpieczeństwo i zapewniają integralność uruchomionego zadania.

Uruchamianie zadania

W ramach tego kroku uruchomimy zadanie w maszynie wirtualnej w zaufanym środowisku, do której zostanie dołączony akcelerator. Wymagane argumenty TEE są przekazywane za pomocą flagi metadanych. Argumenty dla kontenera zbioru zadań są przekazywane za pomocą części flagi „tee-cmd”. Aby wyposażyć maszynę wirtualną obciążenia w procesor graficzny Nvidia Tesla T4, użyjemy flagi --accelerator=type=nvidia-tesla-t4,count=1. Spowoduje to przyłączenie 1 GPU do maszyny wirtualnej. Musimy też uwzględnić flagę tee-install-gpu-driver=true w flagach metadanych, aby zainicjować 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 wnioskowania

Po uruchomieniu serwera wnioskowania o obciążeniu pracownicy firmy Primus mogą wysyłać do niego żądania generowania kodu.

W ramach tego ćwiczenia użyjemy tego skryptu do skonfigurowania aplikacji klienta, która będzie współpracować z serwerem wnioskowania. Aby skonfigurować maszynę wirtualną klienta, uruchom ten skrypt.

./setup_client.sh

Te czynności pokazują, jak połączyć się przez SSH z maszyną wirtualną klienta i uruchomić przykładową aplikację klienta w środowisku wirtualnym Pythona. Ta przykładowa aplikacja korzysta z szyfrowania kopert za pomocą biblioteki Fernet, ale pamiętaj, że konkretne biblioteki szyfrowania można dostosować do różnych zastosowań.

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

Aby aktywować środowisko wirtualne Pythona w maszynie wirtualnej klienta i wykonać aplikację klienta, uruchom te polecenia.

source venv/bin/activate
python3 inference_client.py

Dane wyjściowe tej przykładowej aplikacji klienta będą zawierać żądania promptów szyfrowania i tekstu zwykłego oraz odpowiadające im odpowiedzi zaszyfrowane i odszyfrowane.

5. Czyszczenie

Tutaj znajdziesz skrypt, który możesz wykorzystać do wyczyszczenia zasobów utworzonych w ramach tego ćwiczenia. 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 jej dostawcą.
  • Konto usługi zadań Primus ($WORKLOAD_SERVICEACCOUNT).
  • Maszyna wirtualna zadań ($WORKLOAD_VM) i maszyna wirtualna klienta ($CLIENT_VM).
./cleanup.sh

Gdy skończysz eksplorować, rozważ usunięcie projektu.

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