Tutorial sulla protezione dei servizi Cloud Run


Questo tutorial spiega come creare un'applicazione sicura con due servizi in esecuzione su Cloud Run. Questa applicazione è un editor Markdown che include un servizio "frontend" pubblico che chiunque può utilizzare per comporre testo Markdown e un servizio "backend" privato che esegue il rendering del testo Markdown in HTML.

Diagramma che mostra il flusso di richieste dal "editor" frontend al "renderer" backend.
Il backend "Renderer" è un servizio privato. Ciò consente di garantire uno standard di trasformazione del testo in un'organizzazione senza monitorare le modifiche nelle librerie in più lingue.

Il servizio di backend è privato e utilizza la funzionalità di autenticazione da servizio a servizio basata su IAM integrata di Cloud Run, che limita chi può chiamare il servizio. Entrambi i servizi sono creati in base al principio del privilegio minimo, senza accesso al resto di Google Cloud , tranne quando necessario.

Limitazioni o non obiettivi di questo tutorial

Obiettivi

  • Crea un account di servizio dedicato con autorizzazioni minime per l'autenticazione da servizio a servizio e l'accesso al servizio al resto di Google Cloud.
  • Scrivi, crea ed esegui il deployment di due servizi su Cloud Run che interagiscono.
  • Invia richieste tra un servizio Cloud Run pubblico e uno privato.

Costi

In questo documento utilizzi i seguenti componenti fatturabili di Google Cloud:

Per generare una stima dei costi in base all'utilizzo previsto, utilizza il calcolatore prezzi.

I nuovi Google Cloud utenti potrebbero avere diritto a una prova gratuita.

Prima di iniziare

  1. Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
  2. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  3. Make sure that billing is enabled for your Google Cloud project.

  4. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  5. Make sure that billing is enabled for your Google Cloud project.

  6. Enable the Cloud Run API.

    Enable the API

  7. Installa e inizializza gcloud CLI.
  8. Installa curl per provare il servizio
  9. Ruoli obbligatori

    Per ottenere le autorizzazioni necessarie per completare il tutorial, chiedi all'amministratore di concederti i seguenti ruoli IAM nel progetto:

    Per saperne di più sulla concessione dei ruoli, consulta Gestisci l'accesso a progetti, cartelle e organizzazioni.

    Potresti anche riuscire a ottenere le autorizzazioni richieste tramite i ruoli personalizzati o altri ruoli predefiniti.

Configurazione dei valori predefiniti di gcloud

Per configurare gcloud con i valori predefiniti per il tuo servizio Cloud Run:

  1. Imposta il progetto predefinito:

    gcloud config set project PROJECT_ID

    Sostituisci PROJECT_ID con il nome del progetto che hai creato per questo tutorial.

  2. Configura gcloud per la regione scelta:

    gcloud config set run/region REGION

    Sostituisci REGION con la regione Cloud Run supportata che preferisci.

Località Cloud Run

Cloud Run è regionale, il che significa che l'infrastruttura che esegue i tuoi servizi Cloud Run si trova in una regione specifica ed è gestita da Google per essere disponibile in modo ridondante in tutte le zone all'interno di quella regione.

Il rispetto dei requisiti di latenza, disponibilità o durabilità sono fattori primari per la selezione della regione in cui vengono eseguiti i servizi Cloud Run. In genere puoi selezionare la regione più vicina ai tuoi utenti, ma devi considerare la posizione degli altri Google Cloud prodotti utilizzati dal tuo servizio Cloud Run. L'utilizzo Google Cloud combinato di prodotti in più località può influire sulla latenza e sui costi del servizio.

Cloud Run è disponibile nelle seguenti regioni:

Soggetto ai prezzi di Livello 1

Soggetto ai prezzi di Livello 2

  • africa-south1 (Johannesburg)
  • asia-east2 (Hong Kong)
  • asia-northeast3 (Seul, Corea del Sud)
  • asia-southeast1 (Singapore)
  • asia-southeast2 (Giacarta)
  • asia-south2 (Delhi, India)
  • australia-southeast1 (Sydney)
  • australia-southeast2 (Melbourne)
  • europe-central2 (Varsavia, Polonia)
  • europe-west10 (Berlino) icona foglia Bassi livelli di CO2
  • europe-west12 (Torino)
  • europe-west2 (Londra, Regno Unito) icona foglia Bassi livelli di CO2
  • europe-west3 (Francoforte, Germania) icona foglia Bassi livelli di CO2
  • europe-west6 (Zurigo, Svizzera) icona foglia Bassi livelli di CO2
  • me-central1 (Doha)
  • me-central2 (Dammam)
  • northamerica-northeast1 (Montreal) icona foglia Bassi livelli di CO2
  • northamerica-northeast2 (Toronto) icona foglia Bassi livelli di CO2
  • southamerica-east1 (San Paolo, Brasile) icona foglia Bassi livelli di CO2
  • southamerica-west1 (Santiago, Cile) icona foglia Bassi livelli di CO2
  • us-west2 (Los Angeles)
  • us-west3 (Salt Lake City)
  • us-west4 (Las Vegas)

Se hai già creato un servizio Cloud Run, puoi visualizzare la regione nella dashboard di Cloud Run nella consoleGoogle Cloud .

Recupero dell'esempio di codice

Per recuperare l'esempio di codice da utilizzare:

  1. Clona il repository dell'app di esempio in Cloud Shell o sulla tua macchina locale:

    Node.js

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

    In alternativa, puoi scaricare il campione come file ZIP ed estrarlo.

    Python

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

    In alternativa, puoi scaricare il campione come file ZIP ed estrarlo.

    Vai

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

    In alternativa, puoi scaricare il campione come file ZIP ed estrarlo.

    Java

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

    In alternativa, puoi scaricare il campione come file ZIP ed estrarlo.

    C#

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

    In alternativa, puoi scaricare il campione come file ZIP ed estrarlo.

  2. Passa alla directory che contiene il codice campione di Cloud Run:

    Node.js

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

    Python

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

    Vai

    cd golang-samples/run/markdown-preview/

    Java

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

    C#

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

Revisione del servizio di rendering Markdown privato

Dal punto di vista del frontend, esiste una semplice specifica API per il servizio Markdown:

  • Un endpoint a /
  • Si aspetta richieste POST
  • Il corpo della richiesta POST è testo Markdown

Ti consigliamo di esaminare tutto il codice per verificare la presenza di problemi di sicurezza o semplicemente per scoprirne di più esplorando la directory ./renderer/. Tieni presente che l'esercitazione non spiega il codice di trasformazione Markdown.

Spedizione del servizio di rendering Markdown privato

Per spedire il codice, esegui la build con Cloud Build, caricalo in Artifact Registry ed esegui il deployment in Cloud Run:

  1. Passa alla directory renderer:

    Node.js

    cd renderer/

    Python

    cd renderer/

    Vai

    cd renderer/

    Java

    cd renderer/

    C#

    cd Samples.Run.MarkdownPreview.Renderer/

  2. Crea un Artifact Registry:

    gcloud artifacts repositories create REPOSITORY \
        --repository-format docker \
        --location REGION

    Sostituisci:

    • REPOSITORY con un nome univoco per il repository. Per ogni posizione del repository in un progetto, i nomi dei repository devono essere univoci.
    • REGION con la regione Google Cloud da utilizzare per il repository Artifact Registry.
  3. Esegui il comando seguente per creare il container e pubblicarlo su Artifact Registry.

    Node.js

    gcloud builds submit --tag REGION-docker.pkg.dev/PROJECT_ID/REPOSITORY/renderer

    dove PROJECT_ID è l'ID progetto Google Cloud e renderer è il nome che vuoi dare al servizio.

    In caso di esito positivo, vedrai un messaggio di operazione riuscita contenente l'ID, l'ora di creazione e il nome dell'immagine. L'immagine è archiviata in Artifact Registry e, se lo desideri, può essere riutilizzata.

    Python

    gcloud builds submit --tag REGION-docker.pkg.dev/PROJECT_ID/REPOSITORY/renderer

    dove PROJECT_ID è l'ID progetto Google Cloud e renderer è il nome che vuoi dare al servizio.

    In caso di esito positivo, vedrai un messaggio di operazione riuscita contenente l'ID, l'ora di creazione e il nome dell'immagine. L'immagine è archiviata in Artifact Registry e, se lo desideri, può essere riutilizzata.

    Vai

    gcloud builds submit --tag REGION-docker.pkg.dev/PROJECT_ID/REPOSITORY/renderer

    dove PROJECT_ID è l'ID progetto Google Cloud e renderer è il nome che vuoi dare al servizio.

    In caso di esito positivo, vedrai un messaggio di operazione riuscita contenente l'ID, l'ora di creazione e il nome dell'immagine. L'immagine è archiviata in Artifact Registry e, se lo desideri, può essere riutilizzata.

    Java

    Questo esempio utilizza Jib per creare immagini Docker utilizzando strumenti Java comuni. Jib ottimizza le build dei container senza la necessità di un Dockerfile o di Docker installato. Scopri di più sulla creazione di container Java con Jib.

    1. Utilizza l'assistente per le credenziali gcloud per autorizzare Docker a eseguire il push su Artifact Registry.

      gcloud auth configure-docker

    2. Utilizza il plug-in Jib Maven per creare ed eseguire il push del container in Artifact Registry.

      mvn compile jib:build -Dimage=REGION-docker.pkg.dev/PROJECT_ID/REPOSITORY/renderer

    dove PROJECT_ID è l'ID progetto Google Cloud e renderer è il nome che vuoi dare al servizio.

    In caso di esito positivo, verrà visualizzato il messaggio BUILD SUCCESS. L'immagine è archiviata in Artifact Registry e, se lo desideri, può essere riutilizzata.

    C#

    gcloud builds submit --tag REGION-docker.pkg.dev/PROJECT_ID/REPOSITORY/renderer

    dove PROJECT_ID è l'ID progetto Google Cloud e renderer è il nome che vuoi dare al servizio.

    In caso di esito positivo, vedrai un messaggio di operazione riuscita contenente l'ID, l'ora di creazione e il nome dell'immagine. L'immagine è archiviata in Artifact Registry e, se lo desideri, può essere riutilizzata.

  4. Esegui il deployment come servizio privato con accesso limitato.

    Cloud Run fornisce funzionalità di controllo dell'accesso e di identità del servizio pronte all'uso. Il controllo dell'accesso fornisce un livello di autenticazione che impedisce agli utenti e ad altri servizi di richiamare il servizio. L'identità del servizio consente di limitare l'accesso del servizio ad altre risorse Google Cloud creando unaccount di serviziot dedicato con autorizzazioni limitate.

    1. Crea un account di servizio che funga da "identità di calcolo" del servizio di rendering. Per impostazione predefinita, non dispone di privilegi diversi dall'appartenenza al progetto.

      Riga di comando

      gcloud iam service-accounts create renderer-identity

      Terraform

      Per scoprire come applicare o rimuovere una configurazione Terraform, consulta Comandi Terraform di base.

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

      Il servizio di rendering Markdown non si integra direttamente con altri elementi in Google Cloud. Non richiede ulteriori autorizzazioni.

    2. Esegui il deployment con il account di servizio renderer-identity e nega l'accesso non autenticato.

      Riga di comando

      gcloud run deploy renderer \
      --image REGION-docker.pkg.dev/PROJECT_ID/REPOSITORY/renderer \
      --service-account renderer-identity \
      --no-allow-unauthenticated

      Cloud Run può utilizzare il nome del account di servizio in formato breve anziché l'indirizzo email completo se ilaccount di serviziot fa parte dello stesso progetto.

      Terraform

      Per scoprire come applicare o rimuovere una configurazione Terraform, consulta Comandi Terraform di base.

      resource "google_cloud_run_v2_service" "renderer" {
        name     = "renderer"
        location = "us-central1"
      
        deletion_protection = false # set to "true" in production
      
        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
        }
      }

Prova del servizio di rendering Markdown privato

I servizi privati non possono essere caricati direttamente da un browser web. Utilizza invece curl o uno strumento di interfaccia a riga di comando per richieste HTTP simile che consenta di inserire un'intestazione Authorization.

Per inviare del testo in grassetto al servizio e vedere la conversione degli asterischi Markdown in tag HTML <strong>:

  1. Recupera l'URL dall'output del deployment.

  2. Utilizza gcloud per derivare un token di identità speciale solo per lo sviluppo per l'autenticazione:

    TOKEN=$(gcloud auth print-identity-token)
  3. Crea una richiesta curl che trasmetta il testo Markdown non elaborato come parametro della stringa di query con escape URL:

    curl -H "Authorization: Bearer $TOKEN" \
       -H 'Content-Type: text/plain' \
       -d '**Hello Bold Text**' \
       SERVICE_URL

    Sostituisci SERVICE_URL con l'URL fornito dopo il deployment del servizio di rendering Markdown.

  4. La risposta deve essere uno snippet HTML:

     <strong>Hello Bold Text</strong>
    

Revisione dell'integrazione tra l'editor e i servizi di rendering

Il servizio di editor fornisce una semplice UI di inserimento del testo e uno spazio per visualizzare l'anteprima HTML. Prima di continuare, esamina il codice recuperato in precedenza aprendo la directory ./editor/.

Successivamente, esplora le sezioni di codice seguenti che integrano in modo sicuro i due servizi.

Node.js

Il modulo render.js crea richieste autenticate al servizio di rendering privato. Utilizza il server di metadati Google Cloud nell'ambiente Cloud Run per creare un token di identità e aggiungerlo alla richiesta HTTP come parte di un'intestazione Authorization.

In altri ambienti, render.js utilizza le credenziali predefinite dell'applicazione per richiedere un token dai server di Google.

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);
  }
};

Analizza il markdown da JSON e invialo al servizio Renderer per la trasformazione in 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

Il metodo new_request crea richieste autenticate ai servizi privati. Utilizza il server di metadati Google Cloud nell'ambiente Cloud Run per creare un token di identità e aggiungerlo alla richiesta HTTP come parte di un'intestazione Authorization.

In altri ambienti, new_request richiede un token ID dai server di Google autenticandosi con le credenziali predefinite dell'applicazione.

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()

Analizza il markdown da JSON e invialo al servizio Renderer per la trasformazione in 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

Vai

RenderService crea richieste autenticate ai servizi privati. Utilizza il server di metadati Google Cloud nell'ambiente Cloud Run per creare un token di identità e aggiungerlo alla richiesta HTTP come parte di un'intestazione Authorization.

In altri ambienti, RenderService richiede un token ID dai server di Google autenticandosi con le credenziali predefinite dell'applicazione.

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"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 richiesta viene inviata al servizio Renderer dopo l'aggiunta del testo in markdown da trasformare in HTML. Gli errori di risposta vengono gestiti per distinguere i problemi di comunicazione dalla funzionalità di rendering.


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 = io.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 := io.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

makeAuthenticatedRequest crea richieste autenticate ai servizi privati. Utilizza il server di metadati Google Cloud nell'ambiente Cloud Run per creare un token di identità e aggiungerlo alla richiesta HTTP come parte di un'intestazione Authorization.

In altri ambienti, makeAuthenticatedRequest richiede un token ID dai server di Google autenticandosi con le credenziali predefinite dell'applicazione.

// 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;
}

Analizza il markdown da JSON e invialo al servizio Renderer per la trasformazione in 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#

GetAuthenticatedPostResponse crea richieste autenticate ai servizi privati. Utilizza il server di metadati Google Cloud nell'ambiente Cloud Run per creare un token di identità e aggiungerlo alla richiesta HTTP come parte di un'intestazione Authorization.

In altri ambienti, GetAuthenticatedPostResponse richiede un token ID dai server di Google autenticandosi con le credenziali predefinite dell'applicazione.

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();
}

Analizza il markdown da JSON e invialo al servizio Renderer per la trasformazione in HTML.

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

Spedizione del servizio di editor pubblico

Per creare ed eseguire il deployment del codice:

  1. Passa alla directory editor:

    Node.js

    cd ../editor

    Python

    cd ../editor

    Vai

    cd ../editor

    Java

    cd ../editor

    C#

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

  2. Esegui il comando seguente per creare il container e pubblicarlo su Artifact Registry.

    Node.js

    gcloud builds submit --tag REGION-docker.pkg.dev/PROJECT_ID/REPOSITORY/editor

    dove PROJECT_ID è l'ID progetto Google Cloud e editor è il nome che vuoi dare al servizio.

    In caso di esito positivo, vedrai un messaggio di operazione riuscita contenente l'ID, l'ora di creazione e il nome dell'immagine. L'immagine è archiviata in Container Registry e, se lo desideri, può essere riutilizzata.

    Python

    gcloud builds submit --tag REGION-docker.pkg.dev/PROJECT_ID/REPOSITORY/editor

    dove PROJECT_ID è l'ID progetto Google Cloud e editor è il nome che vuoi dare al servizio.

    In caso di esito positivo, vedrai un messaggio di operazione riuscita contenente l'ID, l'ora di creazione e il nome dell'immagine. L'immagine è archiviata in Artifact Registry e, se lo desideri, può essere riutilizzata.

    Vai

    gcloud builds submit --tag REGION-docker.pkg.dev/PROJECT_ID/REPOSITORY/editor

    dove PROJECT_ID è l'ID progetto Google Cloud e editor è il nome che vuoi dare al servizio.

    In caso di esito positivo, vedrai un messaggio di operazione riuscita contenente l'ID, l'ora di creazione e il nome dell'immagine. L'immagine è archiviata in Artifact Registry e, se lo desideri, può essere riutilizzata.

    Java

    Questo esempio utilizza Jib per creare immagini Docker utilizzando strumenti Java comuni. Jib ottimizza le build dei container senza la necessità di un Dockerfile o di Docker installato. Scopri di più sulla creazione di container Java con Jib.

    mvn compile jib:build -Dimage=REGION-docker.pkg.dev/PROJECT_ID/REPOSITORY/editor

    dove PROJECT_ID è l'ID progetto Google Cloud e editor è il nome che vuoi dare al servizio.

    In caso di esito positivo, verrà visualizzato il messaggio BUILD SUCCESS. L'immagine è archiviata in Artifact Registry e, se lo desideri, può essere riutilizzata.

    C#

    gcloud builds submit --tag REGION-docker.pkg.dev/PROJECT_ID/REPOSITORY/editor

    dove PROJECT_ID è l'ID progetto Google Cloud e editor è il nome che vuoi dare al servizio.

    In caso di esito positivo, vedrai un messaggio di operazione riuscita contenente l'ID, l'ora di creazione e il nome dell'immagine. L'immagine è archiviata in Artifact Registry e, se lo desideri, può essere riutilizzata.

  3. Esegui il deployment come servizio privato con accesso speciale al servizio di rendering.

    1. Crea un account di servizio che funga da "identità di calcolo" del servizio privato. Per impostazione predefinita, non dispone di privilegi diversi dall'appartenenza al progetto.

      Riga di comando

      gcloud iam service-accounts create editor-identity

      Terraform

      Per scoprire come applicare o rimuovere una configurazione Terraform, consulta Comandi Terraform di base.

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

      Il servizio Editor non deve interagire con nient'altro in Google Cloud se non con il servizio di rendering Markdown.

    2. Concedi l'accesso all'identità di calcolo editor-identity per richiamare il servizio di rendering di Markdown. Qualsiasi servizio che lo utilizza come identità di calcolo avrà questo privilegio.

      Riga di comando

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

      Terraform

      Per scoprire come applicare o rimuovere una configurazione Terraform, consulta Comandi Terraform di base.

      resource "google_cloud_run_service_iam_member" "editor_invokes_renderer" {
        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}"
      }

      Poiché a questo servizio viene assegnato il ruolo di invoker nel contesto del servizio di rendering, il servizio di rendering è l'unico servizio Cloud Run privato che l'editor può richiamare.

    3. Esegui il deployment con il account di servizio editor-identity e consenti l'accesso pubblico non autenticato.

      Riga di comando

      gcloud run deploy editor --image REGION-docker.pkg.dev/PROJECT_ID/REPOSITORY/editor \
      --service-account editor-identity \
      --set-env-vars EDITOR_UPSTREAM_RENDER_URL=SERVICE_URL \
      --allow-unauthenticated

      Sostituisci:

      • PROJECT_ID con l'ID progetto
      • SERVICE_URL con l'URL fornito dopo il deployment del servizio di rendering Markdown.

      Terraform

      Per scoprire come applicare o rimuovere una configurazione Terraform, consulta Comandi Terraform di base.

      Esegui il deployment del servizio di editor:

      resource "google_cloud_run_v2_service" "editor" {
        name     = "editor"
        location = "us-central1"
      
        deletion_protection = false # set to "true" in production
      
        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
      
        }
      }

      Concedi l'autorizzazione allUsers per richiamare il servizio:

      data "google_iam_policy" "noauth" {
        binding {
          role = "roles/run.invoker"
          members = [
            "allUsers",
          ]
        }
      }
      
      resource "google_cloud_run_service_iam_policy" "noauth" {
        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
      }

Informazioni sul traffico HTTPS

Il rendering del markdown utilizzando questi servizi prevede tre richieste HTTP.

Diagramma che mostra il flusso di richieste dall&#39;utente all&#39;editor, dall&#39;editor per ottenere un token dal server dei metadati, dall&#39;editor per effettuare una richiesta al servizio di rendering e dal servizio di rendering per restituire il codice HTML all&#39;editor.
Il servizio di frontend con editor-identity richiama il servizio di rendering. editor-identity e renderer-identity hanno autorizzazioni limitate, quindi qualsiasi exploit di sicurezza o iniezione di codice ha accesso limitato ad altre risorse Google Cloud .

Prova

Per provare l'applicazione completa per due servizi:

  1. Nel browser, vai all'URL fornito nel passaggio di deployment precedente.

  2. Prova a modificare il testo Markdown a sinistra e fai clic sul pulsante per visualizzarne l'anteprima a destra.

    Dovrebbe avere il seguente aspetto:

    Screenshot dell&#39;interfaccia utente dell&#39;editor Markdown

Se scegli di continuare a sviluppare questi servizi, ricorda che hanno accesso Identity and Access Management (IAM) limitato al resto di Google Cloud e dovranno essere assegnati ruoli IAM aggiuntivi per accedere a molti altri servizi.

Esegui la pulizia

Se hai creato un nuovo progetto per questo tutorial, elimina il progetto. Se hai utilizzato un progetto esistente e vuoi conservarlo senza le modifiche aggiunte in questo tutorial, elimina le risorse create per il tutorial.

Elimina il progetto

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

Per eliminare il progetto:

  1. In the Google Cloud console, go to the Manage resources page.

    Go to Manage resources

  2. In the project list, select the project that you want to delete, and then click Delete.
  3. In the dialog, type the project ID, and then click Shut down to delete the project.

Eliminazione delle risorse del tutorial

  1. Elimina i servizi Cloud Run di cui hai eseguito il deployment in questo tutorial:

    gcloud

    gcloud run services delete editor
    gcloud run services delete renderer

    Puoi anche eliminare i servizi Cloud Run dalla Google Cloud console.

  2. Rimuovi le configurazioni predefinite di gcloud che hai aggiunto durante la configurazione del tutorial.

     gcloud config unset run/region
    
  3. Rimuovi la configurazione del progetto:

     gcloud config unset project
    
  4. Elimina le altre risorse Google Cloud create in questo tutorial:

Passaggi successivi