Tutoriel sur la sécurisation des services Cloud Run


Ce tutoriel vous explique comment créer deux applications de service sécurisées exécutées sur Cloud Run. Cette application est un éditeur Markdown qui inclut un service d'interface public permettant à n'importe qui de rédiger du texte Markdown et un service de backend privé qui convertit le texte Markdown au format HTML.

Schéma illustrant le flux de requêtes entre l'éditeur d'interface et le moteur de rendu backend.
Le moteur de rendu backend est un service privé. Cela permet de garantir une transformation du texte standardisée au sein d'une organisation sans suivi des modifications dans différentes bibliothèques de plusieurs langages.

Le service de backend est privé et utilise la fonctionnalité d'authentification de service à service basée sur IAM intégrée à Cloud Run, qui limite l'accès au service. Les deux services sont conçus selon le principe du moindre privilège, sans accès au reste de Google Cloud, sauf si nécessaire.

Limites ou non-objectifs de ce tutoriel

Objectifs

  • Créer un compte de service dédié avec des autorisations minimales pour l'authentification de service à service et l'accès du service au reste de Google Cloud
  • Écrire, créer et déployer deux services interagissant sur Cloud Run
  • Effectuer des requêtes entre un service Cloud Run public et un service privé

Coûts

Dans ce document, vous utilisez les composants facturables suivants de Google Cloud :

Obtenez une estimation des coûts en fonction de votre utilisation prévue à l'aide du simulateur de coût. Les nouveaux utilisateurs de Google Cloud peuvent bénéficier d'un essai gratuit.

Avant de commencer

  1. Connectez-vous à votre compte Google Cloud. Si vous débutez sur Google Cloud, créez un compte pour évaluer les performances de nos produits en conditions réelles. Les nouveaux clients bénéficient également de 300 $ de crédits gratuits pour exécuter, tester et déployer des charges de travail.
  2. Dans Google Cloud Console, sur la page de sélection du projet, sélectionnez ou créez un projet Google Cloud.

    Accéder au sélecteur de projet

  3. Vérifiez que la facturation est activée pour votre projet Google Cloud.

  4. Dans Google Cloud Console, sur la page de sélection du projet, sélectionnez ou créez un projet Google Cloud.

    Accéder au sélecteur de projet

  5. Vérifiez que la facturation est activée pour votre projet Google Cloud.

  6. Activez l'API Cloud Run

    Activer l'API

  7. Installez et initialisez gcloud CLI.
  8. Installez curl pour tester le service

Rôles requis

Pour obtenir les autorisations nécessaires pour suivre le tutoriel, demandez à votre administrateur de vous accorder les rôles IAM suivants sur votre projet :

Pour en savoir plus sur l'attribution de rôles, consultez la section Gérer les accès.

Vous pouvez également obtenir les autorisations requises via des rôles personnalisés ou d'autres rôles prédéfinis.

Configurer les paramètres par défaut de gcloud

Pour configurer gcloud avec les valeurs par défaut pour votre service Cloud Run, procédez comme suit :

  1. Définissez le projet par défaut :

    gcloud config set project PROJECT_ID

    Remplacez PROJECT_ID par le nom du projet que vous avez créé pour ce tutoriel.

  2. Configurez gcloud pour la région choisie :

    gcloud config set run/region REGION

    Remplacez REGION par la région Cloud Run compatible de votre choix.

Emplacements Cloud Run

Cloud Run est régional, ce qui signifie que l'infrastructure qui exécute vos services Cloud Run est située dans une région spécifique et gérée par Google pour être disponible de manière redondante dans toutes les zones de cette région.

Lors de la sélection de la région dans laquelle exécuter vos services Cloud Run, vous devez tout d'abord considérer vos exigences en matière de latence, de disponibilité et de durabilité. Vous pouvez généralement sélectionner la région la plus proche de vos utilisateurs, mais vous devez tenir compte de l'emplacement des autres produits Google Cloud utilisés par votre service Cloud Run. L'utilisation conjointe de produits Google Cloud dans plusieurs emplacements peut avoir une incidence sur la latence et le coût de votre service.

Cloud Run est disponible dans les régions suivantes :

Soumis aux tarifs de niveau 1

Soumis aux tarifs de niveau 2

  • africa-south1 (Johannesburg)
  • asia-east2 (Hong Kong)
  • asia-northeast3 (Séoul, Corée du Sud)
  • asia-southeast1 (Singapour)
  • asia-southeast2 (Jakarta)
  • asia-south1 (Mumbai, Inde)
  • asia-south2 (Delhi, Inde)
  • australia-southeast1 (Sydney)
  • australia-southeast2 (Melbourne)
  • europe-central2 (Varsovie, Pologne)
  • europe-west10 (Berlin)
  • europe-west12 (Turin)
  • europe-west2 (Londres, Royaume-Uni) icône feuille Faibles émissions de CO2
  • europe-west3 (Francfort, Allemagne) icône feuille Faibles émissions de CO2
  • europe-west6 (Zurich, Suisse) icône feuille Faibles émissions de CO2
  • me-central1 (Doha)
  • me-central2 (Dammam)
  • northamerica-northeast1 (Montréal) icône feuille Faibles émissions de CO2
  • northamerica-northeast2 (Toronto) icône feuille Faibles émissions de CO2
  • southamerica-east1 (São Paulo, Brésil) icône feuille Faibles émissions de CO2
  • southamerica-west1 (Santiago, Chili) icône feuille Faibles émissions de CO2
  • us-west2 (Los Angeles)
  • us-west3 (Salt Lake City)
  • us-west4 (Las Vegas)

Si vous avez déjà créé un service Cloud Run, vous pouvez afficher la région dans le tableau de bord Cloud Run de la console Google Cloud.

Récupérer l'exemple de code

Pour récupérer l’exemple de code à utiliser, procédez comme suit :

  1. Clonez le dépôt de l'exemple d'application sur votre Cloud Shell ou votre machine locale :

    Node.js

    git clone https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git

    Vous pouvez également télécharger l'exemple en tant que fichier ZIP et l'extraire.

    Python

    git clone https://github.com/GoogleCloudPlatform/python-docs-samples.git

    Vous pouvez également télécharger l'exemple en tant que fichier ZIP et l'extraire.

    Go

    git clone https://github.com/GoogleCloudPlatform/golang-samples.git

    Vous pouvez également télécharger l'exemple en tant que fichier ZIP et l'extraire.

    Java

    git clone https://github.com/GoogleCloudPlatform/java-docs-samples.git

    Vous pouvez également télécharger l'exemple en tant que fichier ZIP et l'extraire.

    C#

    git clone https://github.com/GoogleCloudPlatform/dotnet-docs-samples.git

    Vous pouvez également télécharger l'exemple en tant que fichier ZIP et l'extraire.

  2. Accédez au répertoire contenant l'exemple de code Cloud Run :

    Node.js

    cd nodejs-docs-samples/run/markdown-preview/

    Python

    cd python-docs-samples/run/markdown-preview/

    Go

    cd golang-samples/run/markdown-preview/

    Java

    cd java-docs-samples/run/markdown-preview/

    C#

    cd dotnet-docs-samples/run/markdown-preview/

Examiner le service de rendu privé Markdown

Du point de vue de l'interface, il existe une spécification d'API simple pour le service Markdown :

  • Un point de terminaison à /
  • Des requêtes POST sont attendues
  • Le corps de la requête POST est sous forme de texte Markdown

Vous pouvez examiner l'ensemble du code pour des raisons de sécurité ou simplement pour en savoir plus en consultant le répertoire ./renderer/. Notez que le tutoriel n'explique pas le code de transformation Markdown.

Transmettre le code à l'aide du service de rendu privé Markdown

Pour transmettre votre code, créez le service avec Cloud Build, importez-le dans Container Registry, puis déployez-le sur Cloud Run :

  1. Accédez au répertoire renderer :

    Node.js

    cd renderer/

    Python

    cd renderer/

    Go

    cd renderer/

    Java

    cd renderer/

    C#

    cd Samples.Run.MarkdownPreview.Renderer/

  2. Exécutez la commande suivante pour créer votre conteneur et publier sur Container Registry.

    Node.js

    gcloud builds submit --tag gcr.io/PROJECT_ID/renderer

    PROJECT_ID correspond à votre ID de projet Google Cloud et renderer au nom que vous souhaitez attribuer à votre service.

    En cas de réussite, un message SUCCESS apparaît contenant l'ID, l'heure de création et le nom de l'image. Celle-ci est stockée dans Container Registry et peut être réutilisée au besoin.

    Python

    gcloud builds submit --tag gcr.io/PROJECT_ID/renderer

    PROJECT_ID correspond à votre ID de projet Google Cloud et renderer au nom que vous souhaitez attribuer à votre service.

    En cas de réussite, un message SUCCESS apparaît contenant l'ID, l'heure de création et le nom de l'image. Celle-ci est stockée dans Container Registry et peut être réutilisée au besoin.

    Go

    gcloud builds submit --tag gcr.io/PROJECT_ID/renderer

    PROJECT_ID correspond à votre ID de projet Google Cloud et renderer au nom que vous souhaitez attribuer à votre service.

    En cas de réussite, un message SUCCESS apparaît contenant l'ID, l'heure de création et le nom de l'image. Celle-ci est stockée dans Container Registry et peut être réutilisée au besoin.

    Java

    Cet exemple utilise Jib pour créer des images Docker à l'aide d'outils Java courants. Jib optimise les builds de conteneurs sans requérir de fichier Dockerfile ni d'installation Docker. Découvrez comment créer des conteneurs Java avec Jib.

    1. Utilisez l'assistant d'identification gcloud pour autoriser Docker à transférer du contenu vers votre registre de conteneurs.

      gcloud auth configure-docker

    2. Utilisez le plug-in Maven Jib pour créer et transférer le conteneur dans Container Registry.

      mvn compile jib:build -Dimage=gcr.io/PROJECT_ID/renderer

    PROJECT_ID correspond à votre ID de projet Google Cloud et renderer au nom que vous souhaitez attribuer à votre service.

    En cas de réussite, un message BUILD SUCCESS apparaît. L'image est stockée dans Container Registry et peut être réutilisée si vous le souhaitez.

    C#

    gcloud builds submit --tag gcr.io/PROJECT_ID/renderer

    PROJECT_ID correspond à votre ID de projet Google Cloud et renderer au nom que vous souhaitez attribuer à votre service.

    En cas de réussite, un message SUCCESS apparaît contenant l'ID, l'heure de création et le nom de l'image. Celle-ci est stockée dans Container Registry et peut être réutilisée au besoin.

  3. Déployez le service en tant que service privé avec accès restreint.

    Cloud Run fournit des fonctionnalités prêtes à l'emploi pour le contrôle des accès et l'identité du service. Le contrôle des accès fournit une couche d'authentification qui empêche les utilisateurs et les autres services d'appeler le service. L'identité du service permet d'empêcher votre service d'accéder à d'autres ressources Google Cloud en créant un compte de service dédié avec des autorisations limitées.

    1. Créez un compte de service à utiliser comme "identité de calcul" du service de rendu. Par défaut, il ne dispose d'aucun autre droit que l'abonnement au projet.

      Ligne de commande

      gcloud iam service-accounts create renderer-identity

      Terraform

      Pour savoir comment appliquer ou supprimer une configuration Terraform, consultez la page Commandes Terraform de base.

      resource "google_service_account" "renderer" {
        provider     = google-beta
        account_id   = "renderer-identity"
        display_name = "Service identity of the Renderer (Backend) service."
      }

      Le service de rendu Markdown ne s'intègre pas directement à d'autres éléments de Google Cloud. Aucune autorisation supplémentaire n'est requise.

    2. Déployez le service avec le compte de service renderer-identity et refusez tout accès non authentifié.

      Ligne de commande

      gcloud run deploy renderer \
      --image gcr.io/PROJECT_ID/renderer \
      --service-account renderer-identity \
      --no-allow-unauthenticated

      Cloud Run peut utiliser le nom de compte de service court au lieu de l'adresse e-mail complète si le compte de service fait partie du même projet.

      Terraform

      Pour savoir comment appliquer ou supprimer une configuration Terraform, consultez la page Commandes Terraform de base.

      resource "google_cloud_run_v2_service" "renderer" {
        provider = google-beta
        name     = "renderer"
        location = "us-central1"
        template {
          containers {
            # Replace with the URL of your Secure Services > Renderer image.
            #   gcr.io/<PROJECT_ID>/renderer
            image = "us-docker.pkg.dev/cloudrun/container/hello"
          }
          service_account = google_service_account.renderer.email
        }
      }

Essayer le service de rendu privé Markdown

Les services privés ne peuvent pas être chargés directement par un navigateur Web. Utilisez plutôt curl ou un outil d'interface de requête HTTP similaire qui permet d'injecter un en-tête Authorization.

Pour envoyer du texte en gras au service et faire convertir les astérisques Markdown en balises HTML <strong>, procédez comme suit :

  1. Obtenez l'URL à partir du résultat de déploiement.

  2. Utilisez gcloud pour dériver un jeton d'identité spécial exclusivement dédié au développement pour l'authentification :

    TOKEN=$(gcloud auth print-identity-token)
  3. Créez une requête curl qui transmet le texte brut Markdown en tant que paramètre de chaîne de requête avec une URL contenant une séquence d'échappement :

    curl -H "Authorization: Bearer $TOKEN" \
       -H 'Content-Type: text/plain' \
       -d '**Hello Bold Text**' \
       SERVICE_URL
  4. La réponse doit être un extrait de code HTML :

     <strong>Hello Bold Text</strong>
    

Examiner l'intégration entre le service d'édition et le service de rendu

Le service d'édition propose une interface utilisateur de saisie de texte simple et un espace permettant d'afficher l'aperçu HTML. Avant de continuer, examinez le code récupéré précédemment en ouvrant le répertoire ./editor/.

Ensuite, explorez les quelques sections de code suivantes qui intègrent de manière sécurisée les deux services.

Node.js

Le module render.js crée des requêtes authentifiées à destination du service de rendu privé. Il utilise le serveur de métadonnées Google Cloud de l'environnement Cloud Run pour créer un jeton d'identité et l'ajouter à la requête HTTP dans un en-tête Authorization.

Dans d'autres environnements, le module render.js demande un jeton aux serveurs de Google à l'aide des identifiants par défaut de l'application.

const {GoogleAuth} = require('google-auth-library');
const got = require('got');
const auth = new GoogleAuth();

let client, serviceUrl;

// renderRequest creates a new HTTP request with IAM ID Token credential.
// This token is automatically handled by private Cloud Run (fully managed) and Cloud Functions.
const renderRequest = async markdown => {
  if (!process.env.EDITOR_UPSTREAM_RENDER_URL)
    throw Error('EDITOR_UPSTREAM_RENDER_URL needs to be set.');
  serviceUrl = process.env.EDITOR_UPSTREAM_RENDER_URL;

  // Build the request to the Renderer receiving service.
  const serviceRequestOptions = {
    method: 'POST',
    headers: {
      'Content-Type': 'text/plain',
    },
    body: markdown,
    timeout: 3000,
  };

  try {
    // Create a Google Auth client with the Renderer service url as the target audience.
    if (!client) client = await auth.getIdTokenClient(serviceUrl);
    // Fetch the client request headers and add them to the service request headers.
    // The client request headers include an ID token that authenticates the request.
    const clientHeaders = await client.getRequestHeaders();
    serviceRequestOptions.headers['Authorization'] =
      clientHeaders['Authorization'];
  } catch (err) {
    throw Error('could not create an identity token: ' + err.message);
  }

  try {
    // serviceResponse converts the Markdown plaintext to HTML.
    const serviceResponse = await got(serviceUrl, serviceRequestOptions);
    return serviceResponse.body;
  } catch (err) {
    throw Error('request to rendering service failed: ' + err.message);
  }
};

Analysez le texte Markdown JSON et envoyez-le au service de rendu afin qu'il soit converti au format HTML.

app.post('/render', async (req, res) => {
  try {
    const markdown = req.body.data;
    const response = await renderRequest(markdown);
    res.status(200).send(response);
  } catch (err) {
    console.error('Error rendering markdown:', err);
    res.status(500).send(err);
  }
});

Python

La méthode new_request crée des requêtes authentifiées à destination de services privés. Elle utilise le serveur de métadonnées Google Cloud de l'environnement Cloud Run pour créer un jeton d'identité et l'ajouter à la requête HTTP dans le cadre d'un en-tête Authorization.

Dans d'autres environnements, le service new_request demande un jeton d'identité aux serveurs de Google en s'authentifiant avec les identifiants par défaut de l'application.

import os
import urllib

import google.auth.transport.requests
import google.oauth2.id_token

def new_request(data):
    """Creates a new HTTP request with IAM ID Token credential.

    This token is automatically handled by private Cloud Run and Cloud Functions.

    Args:
        data: data for the authenticated request

    Returns:
        The response from the HTTP request
    """
    url = os.environ.get("EDITOR_UPSTREAM_RENDER_URL")
    if not url:
        raise Exception("EDITOR_UPSTREAM_RENDER_URL missing")

    req = urllib.request.Request(url, data=data.encode())
    auth_req = google.auth.transport.requests.Request()
    target_audience = url

    id_token = google.oauth2.id_token.fetch_id_token(auth_req, target_audience)
    req.add_header("Authorization", f"Bearer {id_token}")

    response = urllib.request.urlopen(req)
    return response.read()

Analysez le texte Markdown JSON et envoyez-le au service de rendu afin qu'il soit converti au format HTML.

@app.route("/render", methods=["POST"])
def render_handler():
    """Parse the markdown from JSON and send it to the Renderer service to be
    transformed into HTML.
    """
    body = request.get_json(silent=True)
    if not body:
        return "Error rendering markdown: Invalid JSON", 400

    data = body["data"]
    try:
        parsed_markdown = render.new_request(data)
        return parsed_markdown, 200
    except Exception as err:
        return f"Error rendering markdown: {err}", 500

Go

Le service RenderService crée des requêtes authentifiées à destination de services privés. Il utilise le serveur de métadonnées Google Cloud de l'environnement Cloud Run pour créer un jeton d'identité et l'ajouter à la requête HTTP dans le cadre d'un en-tête Authorization.

Dans d'autres environnements, le service RenderService demande un jeton d'identité aux serveurs de Google en s'authentifiant avec les identifiants par défaut de l'application.

import (
	"bytes"
	"context"
	"fmt"
	"io/ioutil"
	"net/http"
	"time"

	"golang.org/x/oauth2"
	"google.golang.org/api/idtoken"
)

// RenderService represents our upstream render service.
type RenderService struct {
	// URL is the render service address.
	URL string
	// tokenSource provides an identity token for requests to the Render Service.
	tokenSource oauth2.TokenSource
}

// NewRequest creates a new HTTP request to the Render service.
// If authentication is enabled, an Identity Token is created and added.
func (s *RenderService) NewRequest(method string) (*http.Request, error) {
	req, err := http.NewRequest(method, s.URL, nil)
	if err != nil {
		return nil, fmt.Errorf("http.NewRequest: %w", err)
	}

	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	// Create a TokenSource if none exists.
	if s.tokenSource == nil {
		s.tokenSource, err = idtoken.NewTokenSource(ctx, s.URL)
		if err != nil {
			return nil, fmt.Errorf("idtoken.NewTokenSource: %w", err)
		}
	}

	// Retrieve an identity token. Will reuse tokens until refresh needed.
	token, err := s.tokenSource.Token()
	if err != nil {
		return nil, fmt.Errorf("TokenSource.Token: %w", err)
	}
	token.SetAuthHeader(req)

	return req, nil
}

La requête est envoyée au service de rendu après l'ajout du texte Markdown à convertir au format HTML. Les erreurs de réponse sont traitées de manière à différencier les problèmes de communication de la fonctionnalité de rendu.


var renderClient = &http.Client{Timeout: 30 * time.Second}

// Render converts the Markdown plaintext to HTML.
func (s *RenderService) Render(in []byte) ([]byte, error) {
	req, err := s.NewRequest(http.MethodPost)
	if err != nil {
		return nil, fmt.Errorf("RenderService.NewRequest: %w", err)
	}
	req.Body = ioutil.NopCloser(bytes.NewReader(in))
	defer req.Body.Close()

	resp, err := renderClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("http.Client.Do: %w", err)
	}

	out, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("ioutil.ReadAll: %w", err)
	}

	if resp.StatusCode != http.StatusOK {
		return out, fmt.Errorf("http.Client.Do: %s (%d): request not OK", http.StatusText(resp.StatusCode), resp.StatusCode)
	}

	return out, nil
}

Java

La requête makeAuthenticatedRequest crée des requêtes authentifiées à destination de services privés. Elle utilise le serveur de métadonnées Google Cloud de l'environnement Cloud Run pour créer un jeton d'identité et l'ajouter à la requête HTTP dans le cadre d'un en-tête Authorization.

Dans d'autres environnements, la requête makeAuthenticatedRequest demande un jeton d'identité aux serveurs de Google en s'authentifiant avec les identifiants par défaut de l'application.

// makeAuthenticatedRequest creates a new HTTP request authenticated by a JSON Web Tokens (JWT)
// retrievd from Application Default Credentials.
public String makeAuthenticatedRequest(String url, String markdown) {
  String html = "";
  try {
    // Retrieve Application Default Credentials
    GoogleCredentials credentials = GoogleCredentials.getApplicationDefault();
    IdTokenCredentials tokenCredentials =
        IdTokenCredentials.newBuilder()
            .setIdTokenProvider((IdTokenProvider) credentials)
            .setTargetAudience(url)
            .build();

    // Create an ID token
    String token = tokenCredentials.refreshAccessToken().getTokenValue();
    // Instantiate HTTP request
    MediaType contentType = MediaType.get("text/plain; charset=utf-8");
    okhttp3.RequestBody body = okhttp3.RequestBody.create(markdown, contentType);
    Request request =
        new Request.Builder()
            .url(url)
            .addHeader("Authorization", "Bearer " + token)
            .post(body)
            .build();

    Response response = ok.newCall(request).execute();
    html = response.body().string();
  } catch (IOException e) {
    logger.error("Unable to get rendered data", e);
  }
  return html;
}

Analysez le texte Markdown JSON et envoyez-le au service de rendu afin qu'il soit converti au format HTML.

// '/render' expects a JSON body payload with a 'data' property holding plain text
// for rendering.
@PostMapping(value = "/render", consumes = "application/json")
public String render(@RequestBody Data data) {
  String markdown = data.getData();

  String url = System.getenv("EDITOR_UPSTREAM_RENDER_URL");
  if (url == null) {
    String msg =
        "No configuration for upstream render service: "
            + "add EDITOR_UPSTREAM_RENDER_URL environment variable";
    logger.error(msg);
    throw new IllegalStateException(msg);
  }

  String html = makeAuthenticatedRequest(url, markdown);
  return html;
}

C#

La requête GetAuthenticatedPostResponse crée des requêtes authentifiées à destination de services privés. Elle utilise le serveur de métadonnées Google Cloud de l'environnement Cloud Run pour créer un jeton d'identité et l'ajouter à la requête HTTP dans le cadre d'un en-tête Authorization.

Dans d'autres environnements, la requête GetAuthenticatedPostResponse demande un jeton d'identité aux serveurs de Google en s'authentifiant avec les identifiants par défaut de l'application.

private async Task<string> GetAuthenticatedPostResponse(string url, string postBody)
{
    // Get the OIDC access token from the service account via Application Default Credentials
    GoogleCredential credential = await GoogleCredential.GetApplicationDefaultAsync();
    OidcToken token = await credential.GetOidcTokenAsync(OidcTokenOptions.FromTargetAudience(url));
    string accessToken = await token.GetAccessTokenAsync();

    // Create request to the upstream service with the generated OAuth access token in the Authorization header
    var upstreamRequest = new HttpRequestMessage(HttpMethod.Post, url);
    upstreamRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
    upstreamRequest.Content = new StringContent(postBody);

    var upstreamResponse = await _httpClient.SendAsync(upstreamRequest);
    upstreamResponse.EnsureSuccessStatusCode();

    return await upstreamResponse.Content.ReadAsStringAsync();
}

Analysez le texte Markdown JSON et envoyez-le au service de rendu afin qu'il soit converti au format HTML.

public async Task<IActionResult> Index([FromBody] RenderModel model)
{
    var markdown = model.Data ?? string.Empty;
    var renderedHtml = await GetAuthenticatedPostResponse(_editorUpstreamRenderUrl, markdown);
    return Content(renderedHtml);
}

Transmettre le code à l'aide du service d'édition public

Pour créer et déployer votre code, procédez comme suit :

  1. Accédez au répertoire editor :

    Node.js

    cd ../editor

    Python

    cd ../editor

    Go

    cd ../editor

    Java

    cd ../editor

    C#

    cd ../Samples.Run.MarkdownPreview.Editor/

  2. Exécutez la commande suivante pour créer votre conteneur et publier sur Container Registry.

    Node.js

    gcloud builds submit --tag gcr.io/PROJECT_ID/editor

    PROJECT_ID correspond à votre ID de projet Google Cloud et editor au nom que vous souhaitez attribuer à votre service.

    En cas de réussite, un message SUCCESS apparaît contenant l'ID, l'heure de création et le nom de l'image. Celle-ci est stockée dans Container Registry et peut être réutilisée au besoin.

    Python

    gcloud builds submit --tag gcr.io/PROJECT_ID/editor

    PROJECT_ID correspond à votre ID de projet Google Cloud et editor au nom que vous souhaitez attribuer à votre service.

    En cas de réussite, un message SUCCESS apparaît contenant l'ID, l'heure de création et le nom de l'image. Celle-ci est stockée dans Container Registry et peut être réutilisée au besoin.

    Go

    gcloud builds submit --tag gcr.io/PROJECT_ID/editor

    PROJECT_ID correspond à votre ID de projet Google Cloud et editor au nom que vous souhaitez attribuer à votre service.

    En cas de réussite, un message SUCCESS apparaît contenant l'ID, l'heure de création et le nom de l'image. Celle-ci est stockée dans Container Registry et peut être réutilisée au besoin.

    Java

    Cet exemple utilise Jib pour créer des images Docker à l'aide d'outils Java courants. Jib optimise les builds de conteneurs sans requérir de fichier Dockerfile ni d'installation Docker. Découvrez comment créer des conteneurs Java avec Jib.

    mvn compile jib:build -Dimage=gcr.io/PROJECT_ID/editor

    PROJECT_ID correspond à votre ID de projet Google Cloud et editor au nom que vous souhaitez attribuer à votre service.

    En cas de réussite, un message BUILD SUCCESS apparaît. L'image est stockée dans Container Registry et peut être réutilisée si vous le souhaitez.

    C#

    gcloud builds submit --tag gcr.io/PROJECT_ID/editor

    PROJECT_ID correspond à votre ID de projet Google Cloud et editor au nom que vous souhaitez attribuer à votre service.

    En cas de réussite, un message SUCCESS apparaît contenant l'ID, l'heure de création et le nom de l'image. Celle-ci est stockée dans Container Registry et peut être réutilisée au besoin.

  3. Déployez le service en tant que service privé avec un accès spécial au service de rendu.

    1. Créez un compte de service à utiliser comme "identité de calcul" du service de rendu. Par défaut, il ne dispose d'aucun autre droit que l'abonnement au projet.

      Ligne de commande

      gcloud iam service-accounts create editor-identity

      Terraform

      Pour savoir comment appliquer ou supprimer une configuration Terraform, consultez la page Commandes Terraform de base.

      resource "google_service_account" "editor" {
        provider     = google-beta
        account_id   = "editor-identity"
        display_name = "Service identity of the Editor (Frontend) service."
      }

      Le service d'édition n'a pas besoin d'interagir avec des éléments Google Cloud autres que le service de rendu Markdown.

    2. Accordez à l'identité de calcul editor-identity un accès lui permettant d'appeler le service de rendu Markdown. Tout service qui l'utilise comme identité de calcul disposera de ce privilège.

      Ligne de commande

      gcloud run services add-iam-policy-binding renderer \
      --member serviceAccount:editor-identity@PROJECT_ID.iam.gserviceaccount.com \
      --role roles/run.invoker

      Terraform

      Pour savoir comment appliquer ou supprimer une configuration Terraform, consultez la page Commandes Terraform de base.

      resource "google_cloud_run_service_iam_member" "editor_invokes_renderer" {
        provider = google-beta
        location = google_cloud_run_v2_service.renderer.location
        service  = google_cloud_run_v2_service.renderer.name
        role     = "roles/run.invoker"
        member   = "serviceAccount:${google_service_account.editor.email}"
      }

      Étant donné que le rôle de demandeur est attribué dans le contexte du service de rendu, ce service est le seul service Cloud Run privé que l'éditeur peut invoquer.

    3. Déployez le service avec le compte de service editor-identity et autorisez l'accès public non authentifié.

      Ligne de commande

      gcloud run deploy editor --image gcr.io/PROJECT_ID/editor \
      --service-account editor-identity \
      --set-env-vars EDITOR_UPSTREAM_RENDER_URL=RENDERER_SERVICE_URL \
      --allow-unauthenticated

      Remplacez les éléments suivants :

      • PROJECT_ID par votre ID de projet
      • RENDERER_SERVICE_URL par l'URL fournie après le déploiement du service de rendu Markdown

      Terraform

      Pour savoir comment appliquer ou supprimer une configuration Terraform, consultez la page Commandes Terraform de base.

      Déployez le service d'édition :

      resource "google_cloud_run_v2_service" "editor" {
        provider = google-beta
        name     = "editor"
        location = "us-central1"
        template {
          containers {
            # Replace with the URL of your Secure Services > Editor image.
            #   gcr.io/<PROJECT_ID>/editor
            image = "us-docker.pkg.dev/cloudrun/container/hello"
            env {
              name  = "EDITOR_UPSTREAM_RENDER_URL"
              value = google_cloud_run_v2_service.renderer.uri
            }
          }
          service_account = google_service_account.editor.email
      
        }
      }

      Accordez à allUsers l'autorisation d'appeler le service :

      data "google_iam_policy" "noauth" {
        provider = google-beta
        binding {
          role = "roles/run.invoker"
          members = [
            "allUsers",
          ]
        }
      }
      
      resource "google_cloud_run_service_iam_policy" "noauth" {
        provider = google-beta
        location = google_cloud_run_v2_service.editor.location
        project  = google_cloud_run_v2_service.editor.project
        service  = google_cloud_run_v2_service.editor.name
      
        policy_data = data.google_iam_policy.noauth.policy_data
      }

Comprendre le trafic HTTPS

Le rendu du contenu Markdown à l'aide de ces services implique trois requêtes HTTP.

Schéma illustrant le flux des requêtes de l'utilisateur vers l'éditeur : l'éditeur récupère un jeton auprès du serveur de métadonnées et envoie une requête au service de rendu, puis le service de rendu renvoie le code HTML à l'éditeur.
Le service d'interface ayant l'identité editor-identity appelle le service de rendu. editor-identity et renderer-identity disposent d'autorisations limitées. Par conséquent, l'accès aux autres ressources Google Cloud est limité en cas d'exploitation de failles de sécurité ou d'injection de code.

Essayez-le !

Pour essayer les deux applications de service complètes, procédez comme suit :

  1. Accédez dans votre navigateur à l'URL fournie par l'étape de déploiement ci-dessus.

  2. Essayez de modifier le texte Markdown sur la gauche, puis cliquez sur le bouton pour en afficher un aperçu sur la droite.

    Elle devrait se présenter comme ceci :

    Capture d'écran de l'interface utilisateur de l'éditeur Markdown

Si vous décidez de poursuivre le développement de ces services, n'oubliez pas qu'ils ont un accès IAM restreint au reste de Google Cloud. Des rôles IAM supplémentaires devront donc leur être attribués afin de pouvoir accéder à de nombreux autres services.

Nettoyer

Si vous avez créé un projet pour ce tutoriel, supprimez-le. Si vous avez utilisé un projet existant et que vous souhaitez le conserver sans les modifications du présent tutoriel, supprimez les ressources créées pour ce tutoriel.

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.

Pour supprimer le projet :

  1. Dans la console Google Cloud, accédez à la page Gérer les ressources.

    Accéder à la page Gérer les ressources

  2. Dans la liste des projets, sélectionnez le projet que vous souhaitez supprimer, puis cliquez sur Supprimer.
  3. Dans la boîte de dialogue, saisissez l'ID du projet, puis cliquez sur Arrêter pour supprimer le projet.

Supprimer les ressources du tutoriel

  1. Supprimez les services Cloud Run que vous avez déployés dans ce tutoriel :

    Ligne de commande

    gcloud run services delete editor
    gcloud run services delete renderer

    Vous pouvez également supprimer des services Cloud Run à partir de Google Cloud Console.

  2. Supprimez les configurations gcloud par défaut que vous avez ajoutées lors de la configuration du tutoriel.

     gcloud config unset run/region
    
  3. Supprimez la configuration du projet :

     gcloud config unset project
    
  4. Supprimez les autres ressources Google Cloud créées dans ce tutoriel :

Étape suivante

  • Sécurisez davantage votre projet en parcourant la checklist Utiliser IAM en toute sécurité.
  • Approfondissez cet exemple d'application pour suivre l'utilisation de Markdown avec les métriques personnalisées de Cloud Monitoring.
  • Consultez le tutoriel Pub/Sub pour découvrir une approche des microservices asynchrones et sécurisés.