Deployment sicuro in Cloud Run

1. Panoramica

Modificherai i passaggi predefiniti per il deployment di un servizio in Cloud Run per migliorare la sicurezza e poi vedrai come accedere all'app di cui è stato eseguito il deployment in modo sicuro. L'app è un "servizio di registrazione partner" dell'applicazione Cymbal Eats, utilizzata dalle aziende che collaborano con Cymbal Eats per elaborare gli ordini di cibo.

Cosa imparerai a fare

Apportando alcune piccole modifiche ai passaggi minimi predefiniti per il deployment di un'app in Cloud Run, puoi aumentarne significativamente la sicurezza. Dovrai seguire le istruzioni relative a un'app e al deployment esistenti e modificare i passaggi di deployment per migliorare la sicurezza dell'app di cui è stato eseguito il deployment.

Quindi scoprirai come autorizzare l'accesso all'app ed effettuare richieste autorizzate.

Non si tratta di uno sguardo esaustivo alla sicurezza del deployment delle applicazioni, ma piuttosto di una panoramica delle modifiche che puoi apportare a tutti i futuri deployment delle app, per migliorarne la sicurezza con pochissimo sforzo.

2. Configurazione e requisiti

Configurazione dell'ambiente autogestito

  1. Accedi alla console Google Cloud e crea un nuovo progetto o riutilizzane uno esistente. Se non hai ancora un account Gmail o Google Workspace, devi crearne uno.

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.png

  • Il Nome progetto è il nome visualizzato dei partecipanti del progetto. Si tratta di una stringa di caratteri non utilizzata dalle API di Google. Puoi aggiornarla in qualsiasi momento.
  • L'ID progetto è univoco in tutti i progetti Google Cloud ed è immutabile (non può essere modificato dopo essere stato impostato). La console Cloud genera automaticamente una stringa univoca; di solito non ti importa cosa sia. Nella maggior parte dei codelab, dovrai fare riferimento all'ID progetto (in genere è identificato come PROJECT_ID). Se l'ID generato non ti soddisfa, puoi generarne un altro casuale. In alternativa, puoi provarne una personalizzata per verificare se è disponibile. Non può essere modificato dopo questo passaggio e rimarrà per tutta la durata del progetto.
  • Per informazione, c'è un terzo valore, un numero di progetto, utilizzato da alcune API. Scopri di più su tutti e tre questi valori nella documentazione.
  1. Successivamente, dovrai abilitare la fatturazione nella console Cloud per utilizzare risorse/API Cloud. Eseguire questo codelab non dovrebbe costare molto. Per arrestare le risorse in modo da non incorrere in fatturazione oltre questo tutorial, puoi eliminare le risorse che hai creato o eliminare l'intero progetto. I nuovi utenti di Google Cloud sono idonei al programma prova senza costi di 300$.

Attiva Cloud Shell

  1. Dalla console Cloud, fai clic su Attiva Cloud Shell 853e55310c205094.png.

55efc1aaa7a4d3ad.png

Se non hai mai avviato Cloud Shell, ti viene mostrata una schermata intermedia (below the fold) che descrive di cosa si tratta. In tal caso, fai clic su Continua (e non la vedrai più). Ecco come appare quella singola schermata:

9c92662c6a846a5c.png

Il provisioning e la connessione a Cloud Shell dovrebbero richiedere solo qualche istante.

9f0e51b578fecce5.png

Questa macchina virtuale viene caricata con tutti gli strumenti di sviluppo di cui hai bisogno. Offre una home directory permanente da 5 GB e viene eseguita in Google Cloud, migliorando notevolmente le prestazioni di rete e l'autenticazione. Gran parte, se non tutto, del lavoro in questo codelab può essere svolto semplicemente con un browser o Chromebook.

Una volta eseguita la connessione a Cloud Shell, dovresti vedere che il tuo account è già autenticato e il progetto è già impostato sul tuo ID progetto.

  1. Esegui questo comando in Cloud Shell per verificare che l'account sia autenticato:
gcloud auth list

Output comando

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

To set the active account, run:
    $ gcloud config set account `ACCOUNT`
  1. Esegui questo comando in Cloud Shell per confermare che il comando gcloud è a conoscenza del tuo progetto:
gcloud config list project

Output comando

[core]
project = <PROJECT_ID>

In caso contrario, puoi impostarlo con questo comando:

gcloud config set project <PROJECT_ID>

Output comando

Updated property [core/project].

Configurazione dell'ambiente

Per questo lab eseguirai i comandi nella riga di comando di Cloud Shell. In genere è possibile copiare i comandi e incollarli così come sono, anche se in alcuni casi sarà necessario modificare i valori segnaposto in valori corretti.

  1. Imposta una variabile di ambiente sull'ID progetto da utilizzare nei comandi successivi:
export PROJECT_ID=$(gcloud config get-value project)
export REGION=us-central1
export SERVICE_NAME=partner-registration-service
  1. Abilita l'API di servizio Cloud Run che eseguirà la tua app, l'API Firestore che fornirà l'archiviazione dei dati NoSQL, l'API Cloud Build che verrà utilizzata dal comando di deployment e l'Artifact Registry che verrà utilizzato per contenere il container dell'applicazione al momento della creazione:
gcloud services enable \
  run.googleapis.com \
  firestore.googleapis.com \
  cloudbuild.googleapis.com \
  artifactregistry.googleapis.com
  1. Inizializza il database Firestore in modalità Native. Questo comando utilizza l'API App Engine, quindi deve essere prima abilitato.

Il comando deve specificare una regione per App Engine, che non utilizzeremo, ma che dobbiamo creare per motivi storici, e una regione per il database. Utilizzeremo us-central per App Engine e nam5 per il database. nam5 è la località multiregionale degli Stati Uniti. Le località multiregionali massimizzano la disponibilità e la durabilità del database.

gcloud services enable appengine.googleapis.com

gcloud app create --region=us-central
gcloud firestore databases create --region=nam5
  1. clona il repository dell'app di esempio e vai alla directory
git clone https://github.com/GoogleCloudPlatform/cymbal-eats.git

cd cymbal-eats/partner-registration-service

3. Leggi il file README

Apri l'editor e osserva i file che compongono l'app. Visualizza README.md, che descrive i passaggi necessari per implementare questa app. Alcuni di questi passaggi possono comportare decisioni di sicurezza implicite o esplicite da prendere in considerazione. Modificherai alcune di queste scelte per migliorare la sicurezza dell'app di cui hai eseguito il deployment, come descritto qui:

Passaggio 3 - Esegui npm install

È importante conoscere la provenienza e l'integrità di qualsiasi software di terze parti utilizzato in un'app. La gestione della sicurezza della catena di fornitura del software è importante per la creazione di qualsiasi software, non solo delle app di cui è stato eseguito il deployment in Cloud Run. Questo lab era incentrato sul deployment, quindi non tratta questa area, ma potresti voler fare ricerche sull'argomento separatamente.

Passaggi 4 e 5: modifica ed esegui deploy.sh

Questi passaggi eseguono il deployment dell'app in Cloud Run lasciando le impostazioni predefinite per la maggior parte delle opzioni. Modificherai questo passaggio per rendere il deployment più sicuro in due modi chiave:

  1. Non consentire l'accesso non autenticato. Può essere pratico consentire questa operazione durante l'esplorazione, ma si tratta di un servizio web per l'utilizzo da parte di partner commerciali e deve sempre autenticare i suoi utenti.
  2. Specifica che l'applicazione deve utilizzare un account di servizio dedicato personalizzato solo con i privilegi necessari, anziché un account predefinito che probabilmente avrà più accesso ad API e risorse del necessario. Questo è noto come principio del privilegio minimo ed è un concetto fondamentale di sicurezza delle applicazioni.

Passaggi da 6 a 11 - Effettua richieste web di esempio per verificare il comportamento corretto

Poiché ora il deployment dell'applicazione richiede l'autenticazione, queste richieste devono includere una prova dell'identità del richiedente. Anziché modificare questi file, effettuerai le richieste direttamente dalla riga di comando.

4. Esegui il deployment del servizio in modo sicuro

Nello script deploy.sh sono state identificate due modifiche necessarie: il rifiuto dell'accesso non autenticato e l'utilizzo di un account di servizio dedicato con privilegi minimi.

Dovrai prima creare un nuovo account di servizio, quindi modificare lo script deploy.sh per fare riferimento a quell'account e non consentire l'accesso non autenticato, quindi eseguire il deployment del servizio eseguendo lo script modificato prima che possiamo eseguire lo script deploy.sh modificato.

Crea un account di servizio e concedigli l'accesso necessario a 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

Modifica deploy.sh

Modifica il file deploy.sh in modo da consentire l'accesso non autenticato(–no-allow-unauthenticated) e specificare il nuovo account di servizio(–service-account) per l'app di cui è stato eseguito il deployment. Correggi GOOGLE_PROJECT_ID in modo che corrisponda all'ID del tuo progetto.

Eliminerai le prime due righe e ne modificherai altre tre come mostrato di seguito.

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

Esegui il deployment del servizio

Dalla riga di comando, esegui lo script deploy.sh:

./deploy.sh

Al termine del deployment, l'ultima riga dell'output comando visualizzerà l'URL del servizio della nuova app. Salva l'URL in una variabile di ambiente:

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

Ora prova a recuperare un ordine dall'app utilizzando lo strumento curl:

curl -i -X GET $SERVICE_URL/partners

Il flag -i per il comando curl indica di includere le intestazioni della risposta nell'output. La prima riga dell'output dovrebbe essere:

HTTP/2 403

È stato eseguito il deployment dell'app con la possibilità di consentire le richieste non autenticate. Questo comando curl non contiene informazioni di autenticazione, quindi viene rifiutato da Cloud Run. L'applicazione effettiva di cui è stato eseguito il deployment non esegue o riceve dati da questa richiesta.

5. Effettua richieste autenticate

L'app di cui è stato eseguito il deployment viene richiamata effettuando richieste web, che ora devono essere autenticate affinché Cloud Run possa consentire queste app. Le richieste web vengono autenticate includendo un'intestazione Authorization del modulo:

Authorization: Bearer identity-token

Il token di identità è una stringa codificata a breve termine con firma criptata emessa da un provider di autenticazione attendibile. In questo caso, è necessario un token di identità valido e non scaduto.

Fare una richiesta come account utente

Lo strumento Google Cloud CLI può fornire un token per l'utente autenticato predefinito. Esegui questo comando per ottenere un token di identità per il tuo account e salvarlo nella variabile di ambiente ID_TOKEN:

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

Per impostazione predefinita, i token di identità emessi da Google sono validi per un'ora. Esegui questo comando curl per effettuare la richiesta rifiutata in precedenza perché non era autorizzata. Questo comando includerà l'intestazione necessaria:

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

L'output comando dovrebbe iniziare con HTTP/2 200, a indicare che la richiesta è accettabile e viene soddisfatta. Se attendi un'ora e riprovi la richiesta, l'operazione non andrà a buon fine perché il token è scaduto. Il corpo della risposta si trova alla fine dell'output, dopo una riga vuota:

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

Ancora nessun partner.

Registra i partner utilizzando i dati JSON di esempio nella directory con due comandi curl:

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

e

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

Ripeti la precedente richiesta GET per vedere ora tutti i partner registrati:

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

Dovresti vedere i dati JSON con molti più contenuti, che forniscono informazioni sui due partner registrati.

Presenta una richiesta come account non autorizzato

La richiesta autenticata nell'ultimo passaggio è andata a buon fine non solo perché è stata autenticata, ma anche perché l'utente autenticato (il tuo account) era autorizzato. In altre parole, l'account ha l'autorizzazione per richiamare l'app. Non tutti gli account autenticati dispongono dell'autorizzazione per farlo.

L'account predefinito utilizzato nella richiesta precedente è stato autorizzato perché è l'account che ha creato il progetto contenente l'app e, per impostazione predefinita, che le ha concesso l'autorizzazione a richiamare qualsiasi applicazione Cloud Run nell'account. Se necessario, questa autorizzazione può essere revocata, cosa che sarebbe auspicabile in un'applicazione di produzione. Anziché farlo ora, creerai un nuovo account di servizio senza privilegi o ruoli assegnati e lo utilizzerai per provare ad accedere all'app di cui è stato eseguito il deployment.

  1. Crea un account di servizio chiamato tester.
gcloud iam service-accounts create tester
  1. Riceverai un token di identità per questo nuovo account più o meno nello stesso modo in cui ne hai ricevuto uno per l'account predefinito in precedenza. Tuttavia, è necessario che il tuo account predefinito abbia l'autorizzazione per impersonare gli account di servizio. Concedi questa autorizzazione al tuo account.
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. Ora esegui questo comando per salvare un token di identità per questo nuovo account nella variabile di ambiente TEST_IDENTITY. Se il comando mostra un messaggio di errore, attendi un minuto o due e riprova.
export TEST_TOKEN=$( \
  gcloud auth print-identity-token \
    --impersonate-service-account \
    "tester@$PROJECT_ID.iam.gserviceaccount.com" \
)
  1. Effettua la richiesta web autenticata come prima, ma utilizzando questo token di identità:
curl -i -X GET $SERVICE_URL/partners \
  -H "Authorization: Bearer $TEST_TOKEN"

L'output comando inizierà di nuovo con HTTP/2 403 perché la richiesta, sebbene autenticata, non è autorizzata. Il nuovo account di servizio non dispone dell'autorizzazione per richiamare questa app.

Autorizzazione di un account

Un account utente o di servizio deve avere il ruolo Invoker di Cloud Run in un servizio Cloud Run per inviare richieste. Assegna all'account di servizio del tester quel ruolo con il comando:

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}

Dopo aver atteso un minuto o due per l'aggiornamento del nuovo ruolo, ripeti la richiesta autenticata. Salva un nuovo TEST_TOKEN se è trascorsa almeno un'ora dal primo salvataggio.

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

L'output comando ora inizia con HTTP/1.1 200 OK e l'ultima riga contiene la risposta JSON. Questa richiesta è stata accettata da Cloud Run ed elaborata dall'app.

6. Confronto tra programmi di autenticazione e autenticazione degli utenti

Le richieste autenticate che hai effettuato fino ad ora hanno utilizzato lo strumento a riga di comando curl. Al loro posto potrebbero essere usati altri strumenti e linguaggi di programmazione. Tuttavia, le richieste Cloud Run autenticate non possono essere effettuate utilizzando un browser web con pagine web semplici. Se un utente fa clic su un link o su un pulsante per inviare un modulo in una pagina web, il browser non aggiungerà l'intestazione Authorization richiesta da Cloud Run per le richieste autenticate.

Il meccanismo di autenticazione integrato di Cloud Run è destinato all'uso da parte di programmi, non da utenti finali.

Nota:

Cloud Run può ospitare applicazioni web rivolte agli utenti, ma questi tipi di applicazioni devono impostare Cloud Run in modo da consentire le richieste non autenticate da parte degli utenti. browser web. Se le applicazioni richiedono l'autenticazione dell'utente, devono gestirla invece di chiedere a Cloud Run di farlo. L'applicazione può farlo proprio come fanno le applicazioni web al di fuori di Cloud Run. Come farlo non rientra nell'ambito di questo codelab.

Potresti aver notato che fino a questo momento le risposte alle richieste di esempio sono state oggetti JSON, non pagine web. Il motivo è che il servizio di registrazione dei partner è destinato ai programmi e JSON è un pratico modulo da usare. Quindi, scriverai ed eseguirai un programma per consumare e utilizzare questi dati.

Richieste autenticate da un programma Python

Un programma può effettuare richieste autenticate di un'applicazione Cloud Run protetta tramite richieste web HTTP standard, ma inclusa un'intestazione Authorization. L'unica nuova sfida per questi programmi è ottenere un token di identità valido e non scaduto da inserire nell'intestazione. Questo token verrà convalidato da Cloud Run utilizzando Google Cloud Identity and Access Management (IAM), quindi deve essere emesso e firmato da un'autorità riconosciuta da IAM. Esistono librerie client in molti linguaggi che i programmi possono utilizzare per richiedere l'emissione di un token di questo tipo. La libreria client utilizzata da questo esempio è quella di Python google.auth. Esistono diverse librerie Python per effettuare richieste web in generale; in questo esempio viene usato il popolare modulo requests.

Il primo passaggio consiste nell'installare le due librerie client:

pip install google-auth
pip install requests

Il codice Python per richiedere un token di identità per l'utente predefinito è:

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

Se utilizzi una shell di comando come Cloud Shell o la shell del terminale standard sul tuo computer, l'utente predefinito è qualsiasi utente autenticato all'interno della shell. In Cloud Shell, di solito, l'utente che ha eseguito l'accesso a Google. In altri casi, si tratta di qualsiasi utente autenticato con gcloud auth login o con un altro comando gcloud. Se l'utente non ha mai eseguito l'accesso, non ci sarà alcun utente predefinito e questo codice restituirà un errore.

Per un programma che richiede un altro programma, in genere non si desidera utilizzare l'identità di una persona, ma piuttosto l'identità del programma richiedente. Ecco a cosa servono gli account di servizio. Hai eseguito il deployment del servizio Cloud Run con un account di servizio dedicato che fornisce l'identità che utilizza quando effettua le richieste API, ad esempio Cloud Firestore. Quando un programma viene eseguito su una piattaforma Google Cloud, le librerie client utilizzano automaticamente l'account di servizio assegnato all'account come identità predefinita, quindi lo stesso codice di programma funziona in entrambe le situazioni.

Il codice Python per effettuare una richiesta con un'intestazione di autorizzazione aggiunta è:

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

Il seguente programma Python completo effettuerà una richiesta autenticata al servizio Cloud Run per recuperare tutti i partner registrati e quindi stampare i relativi nomi e ID assegnati. Copia ed esegui il comando in basso per salvare questo codice nel file 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

Eseguirai questo programma con un comando shell. Dovrai prima autenticarti come utente predefinito, in modo che il programma sia in grado di utilizzare quelle credenziali. Esegui il comando gcloud auth di seguito:

gcloud auth application-default login

Segui le istruzioni per completare l'accesso. Quindi esegui il programma dalla riga di comando:

python print_partners.py

L'output sarà simile al seguente:

10102: Zippy food delivery
67292: Foodful

La richiesta del programma ha raggiunto il servizio Cloud Run perché è stata autenticata con la tua identità. Sei il proprietario di questo progetto e, di conseguenza, hai l'autorizzazione a eseguirlo per impostazione predefinita. Sarebbe più comune che questo programma venga eseguito con l'identità di un account di servizio. Quando viene eseguita sulla maggior parte dei prodotti Google Cloud, come Cloud Run o App Engine, l'identità predefinita è un account di servizio e verrà utilizzata al posto di un account personale.

7. Complimenti!

Complimenti, hai completato il codelab.

Passaggi successivi

Esplora altri codelab di Cymbal Eats:

Esegui la pulizia

Per evitare che al tuo account Google Cloud vengano addebitati costi relativi alle risorse utilizzate in questo tutorial, elimina il progetto che contiene le risorse oppure mantieni il progetto ed elimina le singole risorse.

Elimina il progetto

Il modo più semplice per eliminare la fatturazione è quello di eliminare il progetto che hai creato per il tutorial.