Bezpieczne wdrażanie w Cloud Run

1. Omówienie

Zmodyfikujesz domyślne kroki wdrażania usługi w Cloud Run, aby zwiększyć bezpieczeństwo, a także dowiesz się, jak bezpiecznie uzyskiwać dostęp do wdrożonej aplikacji. Aplikacja jest „usługą rejestracji partnerów”. aplikacji Cymbal Eats, która jest używana przez firmy współpracujące z Cymbal Eats przy przetwarzaniu zamówień na jedzenie.

Czego się nauczysz

Wprowadź kilka niewielkich zmian w minimalnych domyślnych krokach wdrażania aplikacji w Cloud Run, by znacznie zwiększyć jej bezpieczeństwo. Wykorzystasz istniejącą aplikację i instrukcje wdrażania, a także zmienisz kroki wdrażania, aby zwiększyć bezpieczeństwo wdrożonej aplikacji.

Zobaczysz, jak autoryzować dostęp do aplikacji i wysyłać autoryzowane żądania.

Nie jest to wyczerpujące omówienie zabezpieczeń wdrażania aplikacji, a jedynie zmiany, które możesz wprowadzić we wszystkich przyszłych wdrożeniach aplikacji, które zwiększą ich bezpieczeństwo przy minimalnym nakładzie pracy.

2. Konfiguracja i wymagania

Samodzielne konfigurowanie środowiska

  1. Zaloguj się w konsoli Google Cloud i utwórz nowy projekt lub wykorzystaj już istniejący. Jeśli nie masz jeszcze konta Gmail ani Google Workspace, musisz je utworzyć.

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.png

  • Nazwa projektu jest wyświetlaną nazwą uczestników tego projektu. To ciąg znaków, który nie jest używany przez interfejsy API Google. W każdej chwili możesz ją zmienić.
  • Identyfikator projektu jest unikalny we wszystkich projektach Google Cloud i nie można go zmienić (po jego ustawieniu nie można go zmienić). Cloud Console automatycznie wygeneruje unikalny ciąg znaków. zwykle nieważne, co ona jest. W większości ćwiczeń z programowania konieczne jest odwołanie się do identyfikatora projektu (zwykle nazywa się on PROJECT_ID). Jeśli nie podoba Ci się wygenerowany identyfikator, możesz wygenerować kolejny losowy. Możesz też spróbować własnych sił i sprawdzić, czy jest dostępna. Potem nie będzie można go zmienić. Pozostanie ono przez czas trwania projektu.
  • Dostępna jest trzecia wartość, numer projektu, z którego korzystają niektóre interfejsy API. Więcej informacji o wszystkich 3 wartościach znajdziesz w dokumentacji.
  1. Następnie musisz włączyć płatności w Cloud Console, aby korzystać z zasobów Cloud/interfejsów API. Ukończenie tego ćwiczenia z programowania nie powinno kosztować zbyt wiele. Aby wyłączyć zasoby, aby nie naliczać opłat po zakończeniu tego samouczka, możesz usunąć utworzone zasoby lub cały projekt. Nowi użytkownicy Google Cloud mogą skorzystać z programu bezpłatnego okresu próbnego o wartości 300 USD.

Aktywowanie Cloud Shell

  1. W konsoli Cloud kliknij Aktywuj Cloud Shell 853e55310c205094.png.

55efc1aaa7a4d3ad.png

Jeśli dopiero zaczynasz korzystać z Cloud Shell, wyświetli się ekran pośredni (w części strony widocznej po przewinięciu) z opisem tej funkcji. W takim przypadku kliknij Dalej (nie zobaczysz go więcej). Tak wygląda ten jednorazowy ekran:

9c92662c6a846a5c.png

Uzyskanie dostępu do Cloud Shell i połączenie się z nim powinno zająć tylko kilka chwil.

9f0e51b578fecce5.png

Ta maszyna wirtualna ma wszystkie potrzebne narzędzia dla programistów. Zawiera stały katalog domowy o pojemności 5 GB i działa w Google Cloud, co znacznie zwiększa wydajność sieci i uwierzytelnianie. Większość czynności z tego ćwiczenia z programowania można wykonać w przeglądarce lub na Chromebooku.

Po nawiązaniu połączenia z Cloud Shell powinno pojawić się informacja, że użytkownik jest już uwierzytelniony i że projekt jest już ustawiony na identyfikator Twojego projektu.

  1. Uruchom to polecenie w Cloud Shell, aby potwierdzić, że jesteś uwierzytelniony:
gcloud auth list

Dane wyjściowe polecenia

 Credentialed Accounts
ACTIVE  ACCOUNT
*       <my_account>@<my_domain.com>

To set the active account, run:
    $ gcloud config set account `ACCOUNT`
  1. Uruchom to polecenie w Cloud Shell, aby sprawdzić, czy polecenie gcloud zna Twój projekt:
gcloud config list project

Dane wyjściowe polecenia

[core]
project = <PROJECT_ID>

Jeśli tak nie jest, możesz go ustawić za pomocą tego polecenia:

gcloud config set project <PROJECT_ID>

Dane wyjściowe polecenia

Updated property [core/project].

Konfiguracja środowiska

W tym module będziesz uruchamiać polecenia w wierszu poleceń Cloud Shell. Zwykle możesz skopiować polecenia i wkleić je w niezmienionej formie, ale w niektórych przypadkach trzeba będzie zmienić wartości obiektów zastępczych na prawidłowe.

  1. Ustaw zmienną środowiskową na identyfikator projektu, aby móc go używać w późniejszych poleceniach:
export PROJECT_ID=$(gcloud config get-value project)
export REGION=us-central1
export SERVICE_NAME=partner-registration-service
  1. Włącz interfejs API usługi Cloud Run, który będzie obsługiwać Twoją aplikację, interfejs Firestore API zapewniający miejsce na dane NoSQL, interfejs Cloud Build API, który będzie używany przez polecenie wdrożenia, oraz Artifact Registry, które będą używane do przechowywania kontenera aplikacji podczas kompilacji:
gcloud services enable \
  run.googleapis.com \
  firestore.googleapis.com \
  cloudbuild.googleapis.com \
  artifactregistry.googleapis.com
  1. Zainicjuj bazę danych Firestore w trybie natywnym. To polecenie używa interfejsu App Engine API, więc najpierw musisz je włączyć.

Polecenie musi określać region dla App Engine, którego nie będziemy używać, ale musimy utworzyć ze względów historycznych, oraz region dla bazy danych. Dla bazy danych będziemy używać us-central dla App Engine i nam5. nam5 to lokalizacja obejmująca wiele regionów w Stanach Zjednoczonych. Lokalizacje w wielu regionach maksymalizują dostępność i trwałość bazy danych.

gcloud services enable appengine.googleapis.com

gcloud app create --region=us-central
gcloud firestore databases create --region=nam5
  1. Skopiuj repozytorium przykładowej aplikacji i przejdź do katalogu
git clone https://github.com/GoogleCloudPlatform/cymbal-eats.git

cd cymbal-eats/partner-registration-service

3. Przejrzyj plik README

Otwórz edytor i sprawdź pliki zawierające aplikację. Otwórz plik README.md zawierający czynności niezbędne do wdrożenia tej aplikacji. Niektóre z tych kroków mogą wiązać się z dorozumianymi lub wyraźnymi decyzjami dotyczącymi bezpieczeństwa, które należy wziąć pod uwagę. Zmienisz kilka z tych opcji, aby zwiększyć bezpieczeństwo wdrożonej aplikacji, zgodnie z opisem poniżej:

Krok 3. Uruchom npm install

Warto znać pochodzenie i integralność oprogramowania innych firm wykorzystywanego w aplikacji. Zarządzanie bezpieczeństwem łańcucha dostaw oprogramowania jest istotne przy tworzeniu dowolnego oprogramowania, a nie tylko w przypadku aplikacji wdrażanych w Cloud Run. Ten moduł koncentruje się na wdrożeniu, więc nie dotyczy tego zagadnienia, ale warto zapoznać się z tym tematem oddzielnie.

Kroki 4 i 5. Zmodyfikuj i uruchom polecenie deploy.sh

Te kroki wdrażają aplikację w Cloud Run, pozostawiając większość opcji w wartościach domyślnych. Zmodyfikujesz ten krok, aby zwiększyć bezpieczeństwo wdrożenia na 2 sposoby:

  1. Nie zezwalaj na nieuwierzytelniony dostęp. Warto pozwolić na wypróbowanie tego rozwiązania podczas eksploracji, ale jest to usługa internetowa przeznaczona dla partnerów komercyjnych i powinna zawsze być uwierzytelniana.
  2. Określ, że aplikacja musi używać dedykowanego konta usługi dostosowanego tylko z wymaganymi uprawnieniami, a nie konta domyślnego, które prawdopodobnie będzie miało większy dostęp do interfejsów API i zasobów, niż jest to konieczne. Jest to zasada jak najmniejszych uprawnień i stanowi podstawową koncepcję bezpieczeństwa aplikacji.

Kroki 6–11. Twórz przykładowe żądania internetowe, aby zweryfikować prawidłowe działanie

Wdrażanie aplikacji wymaga teraz uwierzytelniania, więc żądania te muszą teraz zawierać potwierdzenie tożsamości zgłaszającego. Zamiast modyfikować te pliki, możesz wysyłać żądania bezpośrednio z wiersza poleceń.

4. Bezpieczne wdrażanie usługi

W skrypcie deploy.sh zidentyfikowano 2 wymagane zmiany: brak zezwolenia na nieuwierzytelniony dostęp oraz użycie dedykowanego konta usługi z minimalnymi uprawnieniami.

Najpierw utwórz nowe konto usługi, zmodyfikuj skrypt deploy.sh tak, aby się do niego odwoływał i zablokował nieuwierzytelniony dostęp. Następnie wdróż usługę, uruchamiając zmodyfikowany skrypt. Dopiero wtedy będziemy mogli uruchomić zmodyfikowany skrypt deploy.sh.

Utwórz konto usługi i przyznaj mu potrzebny dostęp do Firestore/Datastore

gcloud iam service-accounts create partner-sa

gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:partner-sa@${PROJECT_ID}.iam.gserviceaccount.com" \
  --role=roles/datastore.user

Edytuj: deploy.sh

Zmodyfikuj plik deploy.sh tak, aby nie zezwalać na nieuwierzytelniony dostęp(–no-allow-unauthenticated) oraz określić nowe konto usługi (konto usługi) dla wdrożonej aplikacji. Zmień wartość GOOGLE_PROJECT_ID na identyfikator Twojego projektu.

Spowoduje to usunięcie 2 pierwszych wierszy i zmianę trzech pozostałych, tak jak pokazano poniżej.

gcloud run deploy $SERVICE_NAME \
  --source . \
  --platform managed \
  --region ${REGION} \
  --no-allow-unauthenticated \
  --project=$PROJECT_ID \
  --service-account=partner-sa@${PROJECT_ID}.iam.gserviceaccount.com

Wdrażanie usługi

W wierszu poleceń uruchom skrypt deploy.sh:

./deploy.sh

Po zakończeniu wdrażania w ostatnim wierszu wyniku polecenia wyświetli się adres URL usługi nowej aplikacji. Zapisz adres URL w zmiennej środowiskowej:

export SERVICE_URL=<URL from last line of command output>

Teraz spróbuj pobrać zamówienie z aplikacji za pomocą narzędzia curl:

curl -i -X GET $SERVICE_URL/partners

Flaga -i polecenia curl informuje, że należy uwzględnić nagłówki odpowiedzi w danych wyjściowych. Pierwszy wiersz danych wyjściowych powinien wyglądać tak:

HTTP/2 403

Aplikacja została wdrożona z opcją blokowania nieuwierzytelnionych żądań. To polecenie curl nie zawiera danych uwierzytelniających, więc jest odrzucane przez Cloud Run. Rzeczywista wdrożona aplikacja nie uruchamia nawet żadnych danych z tego żądania ani nie otrzymuje z niego żadnych danych.

5. Tworzenie uwierzytelnionych żądań

Wdrożona aplikacja jest wywoływana przez wysyłanie żądań internetowych, które teraz muszą być uwierzytelniane, aby umożliwić Cloud Run. Żądania sieciowe są uwierzytelniane przez umieszczenie w formularzu nagłówka Authorization:

Authorization: Bearer identity-token

Token tożsamości to krótki, podpisany kryptograficznie zakodowany ciąg znaków wystawiony przez zaufanego dostawcę uwierzytelniania. W takim przypadku wymagany jest ważny token tożsamości wydany przez Google.

Prześlij prośbę jako swoje konto użytkownika

Narzędzie Google Cloud CLI może udostępnić token dla domyślnego uwierzytelnionego użytkownika. Uruchom to polecenie, aby uzyskać token tożsamości dla swojego konta i zapisać go w zmiennej środowiskowej ID_TOKEN:

export ID_TOKEN=$(gcloud auth print-identity-token)

Domyślnie tokeny tożsamości wydane przez Google są ważne przez godzinę. Uruchom następujące polecenie curl, aby wysłać żądanie, które zostało wcześniej odrzucone, ponieważ nie było autoryzowane. To polecenie będzie zawierać niezbędny nagłówek:

curl -i -X GET $SERVICE_URL/partners \
  -H "Authorization: Bearer $ID_TOKEN"

Dane wyjściowe polecenia powinny zaczynać się od HTTP/2 200, co oznacza, że żądanie jest akceptowalne i uznawane. Jeśli odczekasz godzinę i spróbujesz ponownie, to żądanie zakończy się niepowodzeniem, ponieważ token utracił ważność. Treść odpowiedzi znajduje się na końcu danych wyjściowych, po pustym wierszu:

{"status":"success","data":[]}

Nie masz jeszcze żadnych partnerów.

Zarejestruj partnerów przy użyciu przykładowych danych JSON w katalogu za pomocą 2 poleceń curl:

curl -X POST \
  -H "Authorization: Bearer $ID_TOKEN" \
  -H "Content-Type: application/json" \
  -d "@example-partner.json" \
  $SERVICE_URL/partner

i

curl -X POST \
  -H "Authorization: Bearer $ID_TOKEN" \
  -H "Content-Type: application/json" \
  -d "@example-partner2.json" \
  $SERVICE_URL/partner

Powtórz wcześniejsze żądanie GET, by wyświetlić teraz wszystkich zarejestrowanych partnerów:

curl -i -X GET $SERVICE_URL/partners \
  -H "Authorization: Bearer $ID_TOKEN"

Powinny pojawić się znacznie więcej danych JSON z informacjami o 2 zarejestrowanych partnerach.

Prześlij prośbę jako nieautoryzowane konto

Uwierzytelnione żądanie wysłane w ostatnim kroku zostało zrealizowane nie tylko dlatego, że zostało uwierzytelnione, ale również dlatego, że uwierzytelniono użytkownika (Twoje konto). Oznacza to, że konto miało uprawnienia do wywoływania aplikacji. Nie wszystkie uwierzytelnione konta umożliwiają taką czynność.

Konto domyślne użyte w poprzednim żądaniu zostało autoryzowane, ponieważ to konto utworzyło projekt zawierający aplikację i domyślnie przyznało jej uprawnienia do wywoływania aplikacji Cloud Run na koncie. W razie potrzeby można je anulować – jest to bardzo przydatne w aplikacji produkcyjnej. Zamiast tego utworzysz nowe konto usługi bez przypisanych do niego uprawnień ani ról i użyjesz go, aby spróbować uzyskać dostęp do wdrożonej aplikacji.

  1. Utwórz konto usługi o nazwie tester.
gcloud iam service-accounts create tester
  1. Otrzymasz token tożsamości dla tego nowego konta w taki sam sposób jak token tożsamości na koncie domyślnym. Wymaga to jednak, aby konto domyślne miało uprawnienia do odgrywania ról kont usługi. Przyznaj swojemu kontu to uprawnienie.
export USER_EMAIL=$(gcloud config list account --format "value(core.account)")

gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="user:$USER_EMAIL" \
  --role=roles/iam.serviceAccountTokenCreator
  1. Teraz uruchom poniższe polecenie, aby zapisać token tożsamości dla tego nowego konta w zmiennej środowiskowej TEST_IDENTITY. Jeśli polecenie wyświetli komunikat o błędzie, odczekaj minutę lub dwie i spróbuj ponownie.
export TEST_TOKEN=$( \
  gcloud auth print-identity-token \
    --impersonate-service-account \
    "tester@$PROJECT_ID.iam.gserviceaccount.com" \
)
  1. Utwórz uwierzytelnione żądanie internetowe tak jak poprzednio, ale przy użyciu tego tokena tożsamości:
curl -i -X GET $SERVICE_URL/partners \
  -H "Authorization: Bearer $TEST_TOKEN"

Dane wyjściowe polecenia będą ponownie rozpoczynać się od HTTP/2 403, ponieważ żądanie, choć jest uwierzytelnione, nie jest autoryzowane. Nowe konto usługi nie ma uprawnień do wywoływania tej aplikacji.

Autoryzowanie konta

Użytkownik lub konto usługi musi mieć przypisaną w usłudze Cloud Run rolę wywołującego Cloud Run, aby można było wysyłać do niej żądania. Przypisz do konta usługi testera tę rolę za pomocą polecenia:

export REGION=us-central1
gcloud run services add-iam-policy-binding ${SERVICE_NAME} \
  --member="serviceAccount:tester@$PROJECT_ID.iam.gserviceaccount.com" \
  --role=roles/run.invoker \
  --region=${REGION}

Po odczekaniu minut lub dwóch na zaktualizowanie nowej roli powtórz uwierzytelnione żądanie. Zapisz nowy test TEST_TOKEN, jeśli od jego pierwszego zapisu co najmniej godzina minęła.

curl -i -X GET $SERVICE_URL/partners \
  -H "Authorization: Bearer $TEST_TOKEN"

Wynik polecenia zaczyna się teraz od HTTP/1.1 200 OK, a ostatni wiersz zawiera odpowiedź JSON. To żądanie zostało zaakceptowane przez Cloud Run i przetworzone przez aplikację.

6. Uwierzytelnianie programów a uwierzytelnianie użytkowników

Utworzone do tej pory uwierzytelnione żądania używały narzędzia wiersza poleceń curl. Istnieją inne narzędzia i języki programowania, których można użyć zamiast nich. Jednak uwierzytelnionych żądań Cloud Run nie można wysyłać za pomocą przeglądarki ze zwykłymi stronami internetowymi. Jeśli użytkownik kliknie link lub przycisk, aby przesłać formularz na stronie internetowej, przeglądarka nie doda nagłówka Authorization wymaganego przez Cloud Run dla żądań uwierzytelnionych.

Wbudowany mechanizm uwierzytelniania Cloud Run jest przeznaczony do użytku przez programy, a nie użytkowników.

Uwaga:

Cloud Run może hostować aplikacje internetowe dla użytkowników, ale tego typu aplikacje muszą zezwalać na żądania nieuwierzytelnione przeglądarek. Jeśli aplikacje wymagają uwierzytelniania użytkownika, muszą obsługiwać to narzędzie, a nie Cloud Run. Możesz to robić w taki sam sposób jak aplikacje internetowe spoza Cloud Run. Nie obejmuje to tego ćwiczenia.

Jak widzisz, do tej pory odpowiedzi na przykładowe żądania były obiektami JSON, a nie stronami internetowymi. Wynika to z faktu, że ta usługa rejestracji partnera jest przeznaczona dla programów, a format JSON jest dla nich wygodny w użyciu. Następnie napisz i uruchomisz program do przetwarzania i wykorzystywania tych danych.

Uwierzytelnione żądania z programu w Pythonie

Program może wysyłać uwierzytelnione żądania do zabezpieczonej aplikacji Cloud Run za pomocą standardowych żądań sieciowych HTTP, ale zawierających nagłówek Authorization. Jedynym nowym wyzwaniem dla tych programów jest uzyskanie ważnego, ważnego tokena tożsamości do umieszczenia w nagłówku. Ten token zostanie zweryfikowany przez Cloud Run za pomocą usługi Google Cloud Identity and Access Management (IAM), więc token musi zostać wystawiony i podpisany przez urząd rozpoznawany przez uprawnienia. Istnieją biblioteki klienckie w wielu językach, których programy mogą używać do żądania wystawienia takiego tokena. Biblioteka klienta, której użyjemy w tym przykładzie, to Python google.auth. Istnieje kilka bibliotek Pythona do ogólnych żądań sieciowych; w tym przykładzie wykorzystano popularny moduł requests.

Pierwszym krokiem jest zainstalowanie 2 bibliotek klienta:

pip install google-auth
pip install requests

Kod Pythona do żądania tokena tożsamości dla użytkownika domyślnego to:

credentials, _ = google.auth.default()
credentials.refresh(google.auth.transport.requests.Request())
identity_token = credentials.id_token

Jeśli na swoim komputerze używasz powłoki poleceń, takiej jak Cloud Shell lub standardowej powłoki terminala, domyślnym użytkownikiem jest ten, który uwierzytelnił się w tej powłoce. W Cloud Shell jest to zwykle użytkownik zalogowany w Google. W innych przypadkach jest to konto, którego użytkownik uwierzytelnił za pomocą gcloud auth login lub innego polecenia gcloud. Jeśli użytkownik nigdy się nie logował, nie będzie użytkownika domyślnego, a kod zadziała nie tak.

W przypadku programu wysyłającego żądania do innego programu zwykle nie chcesz używać tożsamości osoby, tylko tożsamości programu wysyłającego prośbę. Właśnie do tego służą konta usługi. Wdrożyłeś usługę Cloud Run za pomocą dedykowanego konta usługi, które zapewnia tożsamość używaną przy wysyłaniu żądań do interfejsu API, takich jak Cloud Firestore. Gdy program działa na platformie Google Cloud, biblioteki klienta automatycznie używają przypisanego do niego konta usługi jako domyślnej tożsamości, więc ten sam kod programu działa w obu sytuacjach.

Kod w Pythonie umożliwiający wysłanie żądania z dodanym nagłówkiem autoryzacji:

auth_header = {"Authorization": "Bearer " + identity_token}
response = requests.get(url, headers=auth_header)

Ten kompletny program w Pythonie wyśle uwierzytelnione żądanie do usługi Cloud Run, aby pobrać wszystkich zarejestrowanych partnerów, a następnie wydrukować ich nazwy i przypisane identyfikatory. Skopiuj i uruchom poniższe polecenie, aby zapisać ten kod w pliku print_partners.py.

cat > ./print_partners.py << EOF
def print_partners():
    import google.auth
    import google.auth.transport.requests
    import requests

    credentials, _ = google.auth.default()
    credentials.refresh(google.auth.transport.requests.Request())
    identity_token = credentials.id_token

    auth_header = {"Authorization": "Bearer " + identity_token}
    response = requests.get("${SERVICE_URL}/partners", headers=auth_header)

    parsed_response = response.json()
    partners = parsed_response["data"]

    for partner in partners:
        print(f"{partner['partnerId']}: {partner['name']}")


print_partners()
EOF

Uruchomisz ten program poleceniem powłoki. Musisz najpierw uwierzytelnić się jako użytkownik domyślny, aby program mógł korzystać z tych danych. Uruchom to polecenie gcloud auth:

gcloud auth application-default login

Postępuj zgodnie z instrukcjami, aby dokończyć logowanie. Następnie uruchom program z poziomu wiersza poleceń:

python print_partners.py

Dane wyjściowe będą wyglądały mniej więcej tak:

10102: Zippy food delivery
67292: Foodful

Żądanie programu dotarło do usługi Cloud Run, ponieważ zostało uwierzytelnione za pomocą Twojej tożsamości, a Ty jesteś właścicielem tego projektu i dlatego masz uprawnienia do jego domyślnie uruchamiania. Bardziej typowe byłoby, aby ten program działał w ramach tożsamości konta usługi. W przypadku większości usług Google Cloud, takich jak Cloud Run czy App Engine, tożsamością domyślną jest konto usługi, które jest używane zamiast konta osobistego.

7. Gratulacje!

Gratulacje. Udało Ci się ukończyć ćwiczenia z programowania.

Co dalej:

Poznaj inne ćwiczenia z programowania Cymbal Eats:

Czyszczenie danych

Aby uniknąć obciążenia konta Google Cloud opłatami za zasoby zużyte w tym samouczku, możesz usunąć projekt zawierający te zasoby lub zachować projekt i usunąć poszczególne zasoby.

Usuwam projekt

Najprostszym sposobem na uniknięcie płatności jest usunięcie projektu utworzonego na potrzeby samouczka.