1. Présentation
Vous allez modifier les étapes par défaut pour déployer un service sur Cloud Run afin d'améliorer la sécurité, puis découvrir comment accéder à l'application déployée de manière sécurisée. L'application est un "service d'enregistrement des partenaires" de l'application Cymbal Eats, qui est utilisée par les entreprises travaillant avec Cymbal Eats pour traiter les commandes de nourriture.
Objectifs de l'atelier
En apportant quelques petites modifications aux étapes minimales par défaut pour déployer une application sur Cloud Run, vous pouvez renforcer considérablement sa sécurité. Vous allez prendre une application et des instructions de déploiement existantes, puis modifier les étapes de déploiement pour améliorer la sécurité de l'application déployée.
Vous verrez ensuite comment autoriser l'accès à l'application et effectuer des requêtes autorisées.
Il ne s'agit pas d'un examen exhaustif de la sécurité du déploiement d'applications, mais plutôt d'un aperçu des modifications que vous pouvez apporter à tous vos futurs déploiements d'applications pour améliorer leur sécurité avec très peu d'efforts.
2. Préparation
Configuration de l'environnement d'auto-formation
- Connectez-vous à la console Google Cloud, puis créez un projet ou réutilisez un projet existant. (Si vous ne possédez pas encore de compte Gmail ou Google Workspace, vous devez en créer un.)



- Le nom du projet est le nom à afficher pour les participants au projet. Il s'agit d'une chaîne de caractères non utilisée par les API Google. Vous pouvez le modifier à tout moment.
- L'ID du projet est unique parmi tous les projets Google Cloud et non modifiable une fois défini. La console Cloud génère automatiquement une chaîne unique (en général, vous n'y accordez d'importance particulière). Dans la plupart des ateliers de programmation, vous devrez indiquer l'ID du projet (généralement identifié par
PROJECT_ID). Si l'ID généré ne vous convient pas, vous pouvez en générer un autre de manière aléatoire. Vous pouvez également en spécifier un et voir s'il est disponible. Après cette étape, l'ID n'est plus modifiable et restera donc le même pour toute la durée du projet. - Pour information, il existe une troisième valeur (le numéro de projet) que certaines API utilisent. Pour en savoir plus sur ces trois valeurs, consultez la documentation.
- Vous devez ensuite activer la facturation dans la console Cloud pour utiliser les ressources/API Cloud. L'exécution de cet atelier de programmation est très peu coûteuse, voire sans frais. Pour désactiver les ressources et éviter ainsi que des frais ne vous soient facturés après ce tutoriel, vous pouvez supprimer le projet ou les ressources que vous avez créées. Les nouveaux utilisateurs de Google Cloud peuvent participer au programme d'essai sans frais pour bénéficier d'un crédit de 300$.
Activer Cloud Shell
- Dans Cloud Console, cliquez sur Activer Cloud Shell
.

Si vous n'avez jamais démarré Cloud Shell auparavant, un écran intermédiaire s'affiche en dessous de la ligne de flottaison, décrivant de quoi il s'agit. Si tel est le cas, cliquez sur Continuer. Cet écran ne s'affiche qu'une seule fois. Voici à quoi il ressemble :

Le provisionnement et la connexion à Cloud Shell ne devraient pas prendre plus de quelques minutes.

Cette machine virtuelle contient tous les outils de développement dont vous avez besoin. Elle comprend un répertoire d'accueil persistant de 5 Go et s'exécute sur Google Cloud, ce qui améliore nettement les performances du réseau et l'authentification. Vous pouvez réaliser une grande partie, voire la totalité, des activités de cet atelier dans un simple navigateur ou sur votre Chromebook.
Une fois connecté à Cloud Shell, vous êtes en principe authentifié et le projet est défini avec votre ID de projet.
- Exécutez la commande suivante dans Cloud Shell pour vérifier que vous êtes authentifié :
gcloud auth list
Résultat de la commande
Credentialed Accounts
ACTIVE ACCOUNT
* <my_account>@<my_domain.com>
To set the active account, run:
$ gcloud config set account `ACCOUNT`
- Exécutez la commande suivante dans Cloud Shell pour vérifier que la commande gcloud connaît votre projet :
gcloud config list project
Résultat de la commande
[core] project = <PROJECT_ID>
Si vous obtenez un résultat différent, exécutez cette commande :
gcloud config set project <PROJECT_ID>
Résultat de la commande
Updated property [core/project].
Configuration de l'environnement
Dans cet atelier, vous exécuterez des commandes dans la ligne de commande Cloud Shell. Vous pouvez généralement copier les commandes et les coller telles quelles, mais dans certains cas, vous devrez remplacer les valeurs de l'espace réservé par les valeurs correctes.
- Définissez une variable d'environnement sur l'ID du projet pour l'utiliser dans les commandes ultérieures :
export PROJECT_ID=$(gcloud config get-value project)
export REGION=us-central1
export SERVICE_NAME=partner-registration-service
- Activez l'API du service Cloud Run qui exécutera votre application, l'API Firestore qui fournira le stockage de données NoSQL, l'API Cloud Build qui sera utilisée par la commande de déploiement et Artifact Registry qui sera utilisé pour stocker le conteneur d'application une fois créé :
gcloud services enable \
run.googleapis.com \
firestore.googleapis.com \
cloudbuild.googleapis.com \
artifactregistry.googleapis.com
- Initialisez la base de données Firestore en mode natif. Cette commande utilise l'API App Engine. Vous devez donc l'activer au préalable.
La commande doit spécifier une région pour App Engine, que nous n'utiliserons pas, mais que nous devons créer pour des raisons historiques, ainsi qu'une région pour la base de données. Nous utiliserons us-central pour App Engine et nam5 pour la base de données. nam5 est l'emplacement multirégional des États-Unis. Les emplacements multirégionaux optimisent la disponibilité et la durabilité de la base de données.
gcloud services enable appengine.googleapis.com
gcloud app create --region=us-central
gcloud firestore databases create --region=nam5
- Cloner le dépôt de l'exemple d'application et accéder au répertoire
git clone https://github.com/GoogleCloudPlatform/cymbal-eats.git
cd cymbal-eats/partner-registration-service
3. Consulter le fichier README
Ouvrez l'éditeur et examinez les fichiers qui composent l'application. Consultez README.md, qui décrit les étapes nécessaires au déploiement de cette application. Certaines de ces étapes peuvent impliquer des décisions de sécurité implicites ou explicites à prendre en compte. Vous allez modifier certains de ces choix pour améliorer la sécurité de votre application déployée, comme décrit ici :
Étape 3 : Exécuter npm install
Il est important de connaître la provenance et l'intégrité de tout logiciel tiers utilisé dans une application. La gestion de la sécurité de la chaîne d'approvisionnement logicielle est pertinente pour la création de tout logiciel, et pas seulement des applications déployées sur Cloud Run. Cet atelier étant axé sur le déploiement, il n'aborde pas ce domaine. Toutefois, vous pouvez effectuer des recherches sur le sujet séparément.
Étapes 4 et 5 : Modifier et exécuter deploy.sh
Ces étapes déploient l'application sur Cloud Run en laissant la plupart des options sur leur valeur par défaut. Vous allez modifier cette étape pour sécuriser davantage le déploiement de deux manières principales :
- N'autorisez pas l'accès non authentifié. Il peut être pratique d'autoriser cela pour tester des choses lors de l'exploration, mais il s'agit d'un service Web destiné aux partenaires commerciaux, qui doit toujours authentifier ses utilisateurs.
- Spécifiez que l'application doit utiliser un compte de service dédié, doté uniquement des privilèges nécessaires, au lieu d'un compte par défaut qui aura probablement plus d'accès aux API et aux ressources que nécessaire. C'est ce qu'on appelle le principe du moindre privilège, un concept fondamental de la sécurité des applications.
Étapes 6 à 11 : Effectuez des requêtes Web exemples pour vérifier que le comportement est correct.
Étant donné que le déploiement d'applications nécessite désormais une authentification, ces demandes doivent désormais inclure une preuve de l'identité du demandeur. Au lieu de modifier ces fichiers, vous allez envoyer des requêtes directement à partir de la ligne de commande.
4. Déployer le service de manière sécurisée
Deux modifications ont été identifiées comme nécessaires dans le script deploy.sh : ne pas autoriser l'accès non authentifié et utiliser un compte de service dédié avec des droits minimaux.
Vous allez d'abord créer un compte de service, puis modifier le script deploy.sh pour qu'il fasse référence à ce compte de service et interdise l'accès non authentifié.Vous pourrez ensuite déployer le service en exécutant le script modifié avant d'exécuter le script deploy.sh modifié.
Créez un compte de service et accordez-lui l'accès nécessaire à 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
Modifier deploy.sh
Modifiez le fichier deploy.sh pour interdire l'accès non authentifié(–no-allow-unauthenticated) et pour spécifier le nouveau compte de service(–service-account) pour l'application déployée. Corrigez GOOGLE_PROJECT_ID pour qu'il corresponde à l'ID de votre propre projet.
Vous allez supprimer les deux premières lignes et modifier trois autres lignes, comme indiqué ci-dessous.
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
Déployer le service
Depuis la ligne de commande, exécutez le script deploy.sh :
./deploy.sh
Une fois le déploiement terminé, la dernière ligne du résultat de la commande affiche l'URL du service de la nouvelle application. Enregistrez l'URL dans une variable d'environnement :
export SERVICE_URL=<URL from last line of command output>
Essayez maintenant de récupérer une commande depuis l'application à l'aide de l'outil curl :
curl -i -X GET $SERVICE_URL/partners
L'indicateur -i de la commande curl lui indique d'inclure les en-têtes de réponse dans le résultat. La première ligne du résultat doit être la suivante :
HTTP/2 403
L'application a été déployée avec l'option Interdire les requêtes non authentifiées. Cette commande curl ne contient aucune information d'authentification. Elle est donc refusée par Cloud Run. L'application déployée ne s'exécute même pas et ne reçoit aucune donnée de cette requête.
5. Effectuer des requêtes authentifiées
L'application déployée est appelée en effectuant des requêtes Web, qui doivent désormais être authentifiées pour que Cloud Run les autorise. Les requêtes Web sont authentifiées en incluant un en-tête Authorization au format suivant :
Authorization: Bearer identity-token
Le jeton d'identité est une chaîne encodée, signée de manière cryptographique et à court terme, émise par un fournisseur d'authentification de confiance. Dans ce cas, un jeton d'identité valide et non expiré émis par Google est requis.
Envoyer une demande avec votre compte utilisateur
L'outil Google Cloud CLI peut fournir un jeton pour l'utilisateur authentifié par défaut. Exécutez cette commande pour obtenir un jeton d'identité pour votre propre compte et l'enregistrer dans la variable d'environnement ID_TOKEN :
export ID_TOKEN=$(gcloud auth print-identity-token)
Par défaut, les jetons d'identité émis par Google sont valides pendant une heure. Exécutez la commande curl suivante pour envoyer la requête qui a été refusée précédemment, car elle n'était pas autorisée. Cette commande inclura l'en-tête nécessaire :
curl -i -X GET $SERVICE_URL/partners \
-H "Authorization: Bearer $ID_TOKEN"
Le résultat de la commande doit commencer par HTTP/2 200, ce qui indique que la requête est acceptable et qu'elle est traitée. (Si vous attendez une heure et que vous réessayez d'envoyer cette requête, elle échouera, car le jeton aura expiré.) Le corps de la réponse se trouve à la fin du résultat, après une ligne vide :
{"status":"success","data":[]}
Aucun partenaire pour le moment.
Enregistrez les partenaires à l'aide des exemples de données JSON dans le répertoire avec deux commandes curl :
curl -X POST \
-H "Authorization: Bearer $ID_TOKEN" \
-H "Content-Type: application/json" \
-d "@example-partner.json" \
$SERVICE_URL/partner
et
curl -X POST \
-H "Authorization: Bearer $ID_TOKEN" \
-H "Content-Type: application/json" \
-d "@example-partner2.json" \
$SERVICE_URL/partner
Répétez la requête GET précédente pour afficher tous les partenaires enregistrés :
curl -i -X GET $SERVICE_URL/partners \
-H "Authorization: Bearer $ID_TOKEN"
Vous devriez voir des données JSON avec beaucoup plus de contenu, qui fournissent des informations sur les deux partenaires enregistrés.
Envoyer une demande en tant que compte non autorisé
La requête authentifiée effectuée lors de la dernière étape a abouti non seulement parce qu'elle était authentifiée, mais aussi parce que l'utilisateur authentifié (votre compte) était autorisé. Autrement dit, le compte était autorisé à appeler l'application. Tous les comptes authentifiés ne sont pas autorisés à le faire.
Le compte par défaut utilisé dans la requête précédente était autorisé, car il s'agit du compte qui a créé le projet contenant l'application. Par défaut, il était donc autorisé à appeler toutes les applications Cloud Run du compte. Cette autorisation peut être révoquée si nécessaire, ce qui est souhaitable dans une application de production. Au lieu de le faire maintenant, vous allez créer un compte de service sans privilège ni rôle, et l'utiliser pour essayer d'accéder à l'application déployée.
- Créez un compte de service appelé
tester.
gcloud iam service-accounts create tester
- Vous obtiendrez un jeton d'identité pour ce nouveau compte de la même manière que vous en avez obtenu un pour votre compte par défaut précédemment. Toutefois, votre compte par défaut doit être autorisé à emprunter l'identité de comptes de service. Accordez cette autorisation à votre compte.
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
- Exécutez maintenant la commande suivante pour enregistrer un jeton d'identité pour ce nouveau compte dans la variable d'environnement TEST_IDENTITY. Si la commande affiche un message d'erreur, patientez une ou deux minutes, puis réessayez.
export TEST_TOKEN=$( \
gcloud auth print-identity-token \
--impersonate-service-account \
"tester@$PROJECT_ID.iam.gserviceaccount.com" \
)
- Effectuez la requête Web authentifiée comme avant, mais en utilisant ce jeton d'identité :
curl -i -X GET $SERVICE_URL/partners \
-H "Authorization: Bearer $TEST_TOKEN"
Le résultat de la commande commencera à nouveau par HTTP/2 403, car la requête, bien qu'authentifiée, n'est pas autorisée. Le nouveau compte de service n'est pas autorisé à appeler cette application.
Autoriser un compte
Un utilisateur ou un compte de service doit disposer du rôle Demandeur Cloud Run sur un service Cloud Run pour pouvoir lui envoyer des requêtes. Attribuez ce rôle au compte de service du testeur à l'aide de la commande suivante :
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}
Attendez une ou deux minutes que le nouveau rôle soit mis à jour, puis répétez la requête authentifiée. Enregistrez un nouveau TEST_TOKEN si une heure ou plus s'est écoulée depuis son premier enregistrement.
curl -i -X GET $SERVICE_URL/partners \
-H "Authorization: Bearer $TEST_TOKEN"
Le résultat de la commande commence désormais par HTTP/1.1 200 OK et la dernière ligne contient la réponse JSON. Cette requête a été acceptée par Cloud Run et traitée par l'application.
6. Authentifier les programmes par rapport aux utilisateurs
Les requêtes authentifiées que vous avez effectuées jusqu'à présent ont utilisé l'outil de ligne de commande curl. Vous auriez également pu utiliser d'autres outils et langages de programmation. Toutefois, les requêtes Cloud Run authentifiées ne peuvent pas être effectuées à l'aide d'un navigateur Web avec des pages Web simples. Si un utilisateur clique sur un lien ou sur un bouton pour envoyer un formulaire sur une page Web, le navigateur n'ajoutera pas l'en-tête Authorization requis par Cloud Run pour les requêtes authentifiées.
Le mécanisme d'authentification intégré de Cloud Run est destiné à être utilisé par des programmes, et non par des utilisateurs finaux.
Remarque :
Cloud Run peut héberger des applications Web destinées aux utilisateurs, mais ces types d'applications doivent configurer Cloud Run pour autoriser les requêtes non authentifiées provenant des navigateurs Web des utilisateurs. Si les applications nécessitent une authentification de l'utilisateur, l'application doit la gérer au lieu de demander à Cloud Run de le faire. L'application peut le faire de la même manière que les applications Web en dehors de Cloud Run. La façon dont cela est fait dépasse le cadre de cet atelier de programmation.
Vous avez peut-être remarqué que les réponses aux exemples de requêtes jusqu'à présent étaient des objets JSON, et non des pages Web. En effet, ce service d'enregistrement des partenaires est destiné aux programmes, et le format JSON est pratique pour eux. Vous allez ensuite écrire et exécuter un programme pour consommer et utiliser ces données.
Requêtes authentifiées à partir d'un programme Python
Un programme peut effectuer des requêtes authentifiées d'une application Cloud Run sécurisée via des requêtes Web HTTP standards, mais en incluant un en-tête Authorization. Le seul nouveau défi pour ces programmes est d'obtenir un jeton d'identité valide et non expiré à placer dans cet en-tête. Ce jeton sera validé par Cloud Run à l'aide de Google Cloud Identity and Access Management (IAM). Il doit donc être émis et signé par une autorité reconnue par IAM. Des bibliothèques clientes sont disponibles dans de nombreux langages que les programmes peuvent utiliser pour demander l'émission d'un tel jeton. La bibliothèque cliente utilisée dans cet exemple est la bibliothèque Python google.auth. Il existe plusieurs bibliothèques Python permettant d'effectuer des requêtes Web en général. Cet exemple utilise le module requests, qui est très répandu.
La première étape consiste à installer les deux bibliothèques clientes :
pip install google-auth
pip install requests
Le code Python permettant de demander un jeton d'identité pour l'utilisateur par défaut est le suivant :
credentials, _ = google.auth.default()
credentials.refresh(google.auth.transport.requests.Request())
identity_token = credentials.id_token
Si vous utilisez un shell de commande tel que Cloud Shell ou le shell de terminal standard sur votre propre ordinateur, l'utilisateur par défaut est celui qui s'est authentifié dans ce shell. Dans Cloud Shell, il s'agit généralement de l'utilisateur connecté à Google. Dans d'autres cas, il s'agit de l'utilisateur authentifié avec la commande gcloud auth login ou une autre commande gcloud. Si l'utilisateur ne s'est jamais connecté, il n'y aura pas d'utilisateur par défaut et ce code échouera.
Pour un programme qui envoie des requêtes à un autre programme, il est généralement préférable d'utiliser l'identité du programme demandeur plutôt que celle d'une personne. C'est à cela que servent les comptes de service. Vous avez déployé le service Cloud Run avec un compte de service dédié qui fournit l'identité qu'il utilise pour effectuer des requêtes API, par exemple vers Cloud Firestore. Lorsqu'un programme s'exécute sur une plate-forme Google Cloud, les bibliothèques clientes utilisent automatiquement le compte de service qui lui est attribué comme identité par défaut. Le même code de programme fonctionne donc dans les deux cas.
Voici le code Python permettant d'effectuer une requête avec un en-tête d'autorisation ajouté :
auth_header = {"Authorization": "Bearer " + identity_token}
response = requests.get(url, headers=auth_header)
Le programme Python complet suivant envoie une requête authentifiée au service Cloud Run pour récupérer tous les partenaires enregistrés, puis affiche leurs noms et leurs ID attribués. Copiez et exécutez la commande ci-dessous pour enregistrer ce code dans le fichier 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
Vous exécuterez ce programme avec une commande shell. Vous devrez d'abord vous authentifier en tant qu'utilisateur par défaut pour que le programme puisse utiliser ces identifiants. Exécutez la commande gcloud auth ci-dessous :
gcloud auth application-default login
Suivez les instructions pour vous connecter. Exécutez ensuite le programme à partir de la ligne de commande :
python print_partners.py
Le résultat devrait être semblable à ce qui suit :
10102: Zippy food delivery
67292: Foodful
La requête du programme a atteint le service Cloud Run, car elle a été authentifiée avec votre identité. Vous êtes le propriétaire de ce projet et êtes donc autorisé à l'exécuter par défaut. Il est plus courant que ce programme s'exécute sous l'identité d'un compte de service. Lorsqu'il est exécuté sur la plupart des produits Google Cloud, tels que Cloud Run ou App Engine, l'identité par défaut est un compte de service qui est utilisé à la place d'un compte personnel.
7. Félicitations !
Félicitations, vous avez terminé cet atelier de programmation.
Étapes suivantes :
Découvrez d'autres ateliers de programmation Cymbal Eats :
- Déclencher des workflows Cloud avec Eventarc
- Déclencher le traitement des événements à partir de Cloud Storage
- Se connecter à une instance CloudSQL privée depuis Cloud Run
- Se connecter à des bases de données entièrement gérées depuis Cloud Run
- Sécuriser une application serverless avec Identity-Aware Proxy (IAP)
- Déclencher des jobs Cloud Run avec Cloud Scheduler
- Sécuriser le trafic d'entrée Cloud Run
- Se connecter à une instance AlloyDB privée depuis GKE Autopilot
Effectuer un nettoyage
Pour éviter que les ressources utilisées lors de ce tutoriel soient facturées sur votre compte Google Cloud, supprimez le projet contenant les ressources, ou conservez le projet et supprimez chaque ressource individuellement.
Supprimer le projet
Le moyen le plus simple d'empêcher la facturation est de supprimer le projet que vous avez créé pour ce tutoriel.