Tutorial mengamankan layanan Cloud Run


Tutorial ini membahas cara membuat aplikasi dua layanan aman yang berjalan di Cloud Run. Aplikasi ini adalah editor Markdown mencakup layanan "frontend" publik yang dapat digunakan siapa pun untuk menulis teks markdown, dan layanan "backend" pribadi yang merender teks Markdown ke HTML.

Diagram yang menunjukkan alur permintaan dari 'editor' frontend ke 'renderer' backend.
Backend "Renderer" adalah layanan pribadi. Hal ini memungkinkan jaminan standar transformasi teks di seluruh organisasi tanpa melacak perubahan di seluruh library dalam berbagai bahasa.

Layanan backend bersifat pribadi menggunakan bawaan Cloud Run, fitur autentikasi layanan-ke-layanan berbasis IAM Cloud Run, yang membatasi siapa yang dapat memanggil layanan. Kedua layanan dibuat dengan prinsip hak istimewa terendah, tanpa akses ke Google Cloud lainnya kecuali jika diperlukan.

Batasan atau bukan sasaran dari tutorial ini

Tujuan

  • Buat akun layanan khusus dengan izin minimal untuk autentikasi layanan ke layanan dan akses layanan keseluruh Google Cloud.
  • Menulis, membangun, dan men-deploy dua layanan ke Cloud Run yang berinteraksi.
  • Buat permintaan antara layanan Cloud Run publik dan pribadi.

Biaya

Dalam dokumen ini, Anda menggunakan komponen Google Cloud yang dapat ditagih berikut:

Untuk membuat perkiraan biaya berdasarkan proyeksi penggunaan Anda, gunakan kalkulator harga. Pengguna baru Google Cloud mungkin memenuhi syarat untuk mendapatkan uji coba gratis.

Sebelum memulai

  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. Di konsol Google Cloud, pada halaman pemilih project, pilih atau buat project Google Cloud.

    Buka pemilih project

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

  4. Di konsol Google Cloud, pada halaman pemilih project, pilih atau buat project Google Cloud.

    Buka pemilih project

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

  6. Aktifkan API Cloud Run.

    Mengaktifkan API

  7. Instal dan lakukan inisialisasi gcloud CLI.
  8. Instal curl untuk mencoba layanan

Peran yang diperlukan

Untuk mendapatkan izin yang Anda perlukan untuk menyelesaikan tutorial, minta administrator Anda untuk memberi Anda peran IAM berikut di project Anda:

Untuk mengetahui informasi selengkapnya tentang cara memberikan peran, lihat Mengelola akses ke project, folder, dan organisasi.

Anda mungkin juga bisa mendapatkan izin yang diperlukan melalui peran khusus atau peran bawaan lainnya.

Menyiapkan default gcloud

Untuk mengonfigurasi gcloud dengan setelan default untuk layanan Cloud Run Anda:

  1. Setel project default Anda:

    gcloud config set project PROJECT_ID

    Ganti PROJECT_ID dengan nama project yang Anda buat untuk tutorial ini.

  2. Konfigurasi gcloud untuk region yang Anda pilih:

    gcloud config set run/region REGION

    Ganti REGION dengan region Cloud Run pilihan Anda yang didukung.

Lokasi Cloud Run

Cloud Run bersifat regional, berarti infrastruktur yang menjalankan layanan Cloud Run Anda terletak di region tertentu dan dikelola oleh Google agar tersedia secara redundan di semua zona dalam region tersebut.

Memenuhi persyaratan latensi, ketersediaan, atau ketahanan adalah faktor utama untuk memilih region tempat layanan Cloud Run dijalankan. Pada umumnya, Anda dapat memilih region yang paling dekat dengan pengguna Anda, tetapi Anda harus mempertimbangkan lokasi dari produk Google Cloud lain yang digunakan oleh layanan Cloud Run Anda. Menggunakan produk Google Cloud secara bersamaan di beberapa lokasi dapat memengaruhi latensi serta biaya layanan Anda.

Cloud Run tersedia di region berikut:

Tergantung harga Tingkat 1

  • asia-east1 (Taiwan)
  • asia-northeast1 (Tokyo)
  • asia-northeast2 (Osaka)
  • asia-south1 (Mumbai, India)
  • europe-north1 (Finlandia) ikon daun CO2 Rendah
  • europe-southwest1 (Madrid) ikon daun CO2 Rendah
  • europe-west1 (Belgia) ikon daun CO2 Rendah
  • europe-west4 (Belanda) ikon daun CO2 Rendah
  • europe-west8 (Milan)
  • europe-west9 (Paris) ikon daun CO2 Rendah
  • me-west1 (Tel Aviv)
  • us-central1 (Iowa) ikon daun CO2 Rendah
  • us-east1 (South Carolina)
  • us-east4 (North Virginia)
  • us-east5 (Columbus)
  • us-south1 (Dallas) ikon daun CO2 Rendah
  • us-west1 (Oregon) ikon daun CO2 Rendah

Tergantung harga Tingkat 2

  • africa-south1 (Johannesburg)
  • asia-east2 (Hong Kong)
  • asia-northeast3 (Seoul, Korea Selatan)
  • asia-southeast1 (Singapura)
  • asia-southeast2 (Jakarta)
  • asia-south2 (Delhi, India)
  • australia-southeast1 (Sydney)
  • australia-southeast2 (Melbourne)
  • europe-central2 (Warsawa, Polandia)
  • europe-west10 (Berlin) ikon daun CO2 Rendah
  • europe-west12 (Turin)
  • europe-west2 (London, Inggris Raya) ikon daun CO2 Rendah
  • europe-west3 (Frankfurt, Jerman) ikon daun CO2 Rendah
  • europe-west6 (Zurich, Swiss) ikon daun CO2 Rendah
  • me-central1 (Doha)
  • me-central2 (Dammam)
  • northamerica-northeast1 (Montreal) ikon daun CO2 Rendah
  • northamerica-northeast2 (Toronto) ikon daun CO2 Rendah
  • southamerica-east1 (Sao Paulo, Brasil) ikon daun CO2 Rendah
  • southamerica-west1 (Santiago, Cile) ikon daun CO2 Rendah
  • us-west2 (Los Angeles)
  • us-west3 (Salt Lake City)
  • us-west4 (Las Vegas)

Jika sudah membuat layanan Cloud Run, Anda dapat melihat region di dasbor Cloud Run di Konsol Google Cloud.

Mengambil contoh kode

Untuk mengambil contoh kode agar dapat digunakan:

  1. Clone repositori aplikasi contoh tersebut ke Cloud Shel atau mesin lokal Anda:

    Node.js

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

    Atau, Anda dapat mendownload contoh dalam file ZIP dan mengekstraknya.

    Python

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

    Atau, Anda dapat mendownload contoh dalam file ZIP dan mengekstraknya.

    Go

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

    Atau, Anda dapat mendownload contoh dalam file ZIP dan mengekstraknya.

    Java

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

    Atau, Anda dapat mendownload contoh sebagai file ZIP dan mengekstraknya.

    C#

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

    Atau, Anda dapat mendownload contoh sebagai file ZIP dan mengekstraknya.

  2. Ubah ke direktori yang memuat kode contoh 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/

Meninjau layanan rendering Markdown pribadi

Dari perspektif frontend, ada spesifikasi API sederhana untuk layanan Markdown:

  • Satu endpoint di /
  • Mengharapkan permintaan POST
  • Isi permintaan POST adalah teks Markdown

Anda mungkin ingin meninjau semua kode untuk menemukan masalah keamanan atau hanya mempelajarinya lebih lanjut dengan menjelajahi ./renderer/direktori Perhatikan bahwa tutorial tidak menjelaskan kode transformasi Markdown.

Mengirimkan layanan rendering Markdown pribadi

Untuk mengirimkan kode Anda, build dengan Cloud Build, upload ke Artifact Registry, dan deploy ke Cloud Run:

  1. Ubah ke direktori renderer:

    Node.js

    cd renderer/

    Python

    cd renderer/

    Go

    cd renderer/

    Java

    cd renderer/

    C#

    cd Samples.Run.MarkdownPreview.Renderer/

  2. Buat Artifact Registry:

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

    Ganti:

    • REPOSITORY dengan nama unik untuk repositori. Untuk setiap lokasi repositori dalam project, nama repositori harus unik.
    • REGION dengan region Google Cloud yang akan digunakan untuk repositori Artifact Registry.
  3. Jalankan perintah berikut untuk mem-build container dan memublikasikannya di Artifact Registry.

    Node.js

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

    Dengan PROJECT_ID sebagai project ID Google Cloud Anda, dan renderer merupakan nama yang ingin Anda berikan ke layanan.

    Setelah berhasil, Anda akan melihat pesan SUCCESS yang berisi ID, waktu pembuatan, dan nama image. Image tersebut disimpan di Artifact Registry dan dapat digunakan kembali jika diinginkan.

    Python

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

    Dengan PROJECT_ID sebagai project ID Google Cloud Anda, dan renderer merupakan nama yang ingin Anda berikan ke layanan.

    Setelah berhasil, Anda akan melihat pesan SUCCESS yang berisi ID, waktu pembuatan, dan nama image. Image tersebut disimpan di Artifact Registry dan dapat digunakan kembali jika diinginkan.

    Go

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

    Dengan PROJECT_ID sebagai project ID Google Cloud Anda, dan renderer merupakan nama yang ingin Anda berikan ke layanan.

    Setelah berhasil, Anda akan melihat pesan SUCCESS yang berisi ID, waktu pembuatan, dan nama image. Image tersebut disimpan di Artifact Registry dan dapat digunakan kembali jika diinginkan.

    Java

    Contoh ini menggunakan Jib untuk mem-build image Docker menggunakan alat Java umum. Jib mengoptimalkan build container tanpa memerlukan Dockerfile atau menginstal Docker. Pelajari lebih lanjut cara mem-build container Java dengan Jib.

    1. Gunakan helper kredensial gcloud untuk memberi otorisasi Docker agar dikirim ke Artifact Registry Anda.

      gcloud auth configure-docker

    2. Gunakan Plugin Maven Jib untuk mem-build dan mengirim container ke Artifact Registry.

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

    Dengan PROJECT_ID sebagai project ID Google Cloud Anda, dan renderer merupakan nama yang ingin Anda berikan ke layanan.

    Setelah berhasil, Anda akan melihat pesan BUILD SUCCESS. Image tersebut disimpan di Artifact Registry dan dapat digunakan kembali jika diinginkan.

    C#

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

    Dengan PROJECT_ID sebagai project ID Google Cloud Anda dan renderer merupakan nama yang ingin Anda berikan pada layanan Anda.

    Setelah berhasil, Anda akan melihat pesan SUCCESS yang berisi ID, waktu pembuatan, dan nama image. Image tersebut disimpan di Artifact Registry dan dapat digunakan kembali jika diinginkan.

  4. Deploy sebagai layanan pribadi dengan akses terbatas.

    Cloud Run menyediakan fitur kontrol akses dan layanan identitas siap pakai. Kontrol akses menyediakan lapisan autentikasi yang membatasi pengguna dan layanan lainnya agar tidak memanggil layanan. Identitas layanan memungkinkan pembatasan layanan Anda agar tidak mengakses resource Google Cloud lainnya dengan membuat akun layanan khusus dengan izin terbatas.

    1. Buat akun layanan untuk digunakan sebagai "identitas compute" dari layanan render. Secara default, opsi ini tidak memiliki hak istimewa selain keanggotaan project.

      Command line

      gcloud iam service-accounts create renderer-identity

      Terraform

      Untuk mempelajari cara menerapkan atau menghapus konfigurasi Terraform, lihat Perintah dasar Terraform.

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

      Markdown layanan rendering tidak terintegrasi langsung dengan hal lainnya di Google Cloud. Aplikasi ini tidak memerlukan izin lebih lanjut.

    2. Deploy dengan renderer-identity akun layanan dan tolak akses yang tidak diautentikasi.

      Command line

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

      Cloud Run dapat menggunakan nama akun layanan dalam format pendek bukan alamat email lengkap jika akun layanan merupakan bagian dari project yang sama.

      Terraform

      Untuk mempelajari cara menerapkan atau menghapus konfigurasi Terraform, lihat Perintah dasar Terraform.

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

Mencoba layanan rendering Markdown pribadi

Layanan pribadi tidak dapat dimuat langsung oleh browser web. Sebagai gantinya, gunakan curl atau alat CLI permintaan HTTP yang memungkinkan injeksi Authorization header.

Untuk mengirim beberapa teks tebal ke layanan dan melihatnya mengubah markdown asterisks menjadi tag <strong> HTML:

  1. Mendapatkan URL dari output deployment.

  2. Gunakan gcloud untuk mendapatkan token identitas khusus pengembangan hanya untuk autentikasi:

    TOKEN=$(gcloud auth print-identity-token)
  3. Buat permintaan curl yang meneruskan teks Markdown mentah sebagai parameter string kueri yang akan menjadi URL:

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

    Ganti SERVICE_URL dengan URL yang diberikan setelah men-deploy layanan rendering Markdown.

  4. Responsnya harus berupa cuplikan HTML:

     <strong>Hello Bold Text</strong>
    

Meninjau integrasi antara layanan editor dan rendering

Layanan editor menyediakan UI entri teks yang sederhana dan ruang untuk melihat pratinjau HTML. Sebelum melanjutkan, tinjau kode yang diambil sebelumnya dengan membuka direktori ./editor/.

Selanjutnya, pelajari beberapa bagian kode berikut yang mengintegrasikan kedua layanan tersebut dengan aman.

Node.js

Modul render.js membuat permintaan terautentikasi ke layanan renderer pribadi. Ini menggunakan layanan etaata Google Cloud pada lingkungan Cloud Run untuk membuat token identitas dan menambahkannya pada permintaan HTTP sebagai bagian dari Authorization header.

Di lingkungan lain, render.js menggunakan Kredensial Default Aplikasi untuk meminta token dari server 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);
  }
};

Mengurai markdown dari JSON dan mengirimkannya ke layanan Renderer untuk diubah menjadi 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

Metode new_request membuat permintaan yang diautentikasi ke layanan pribadi. ID ini menggunakan server metadata Google Cloud di lingkungan Cloud Run untuk membuat token identitas dan menambahkannya ke permintaan HTTP sebagai bagian dari Authorization header.

Di lingkungan lain, new_request permintaan token identitas dari server google menggunakan autentikasi dengan Kredensial Default Aplikasi.

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

Mengurai markdown dari JSON dan mengirimkannya ke layanan Renderer untuk diubah menjadi 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

RenderService membuat permintaan yang diautentikasi ke layanan pribadi. ID ini menggunakan server metadata Google Cloud di lingkungan Cloud Run untuk membuat token identitas dan menambahkannya ke permintaan HTTP sebagai bagian dari Authorization header.

Di lingkungan lain, RenderService permintaan token identitas dari server google menggunakan autentikasi dengan Kredensial Default Aplikasi.

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
}

Permintaan dikirim ke Layanan Renderer setelah menambahkan teks markdown untuk ditransformasikan ke dalam HTML. Kesalahan respons ditangani untuk membedakan masalah komunikasi dari fungsi 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 membuat permintaan yang diautentikasi ke layanan pribadi. Ini menggunakan layanan metadata Google Cloud pada lingkungan Cloud Run untuk membuat token identitas dan menambahkannya pada permintaan HTTP sebagai bagian dari Authorization header.

Di lingkungan lain, makeAuthenticatedRequest permintaan token identitas dari server google menggunakan autentikasi dengan Kredensial Default Aplikasi.

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

Mengurai markdown dari JSON dan mengirimkannya ke layanan Renderer untuk diubah menjadi 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 membuat permintaan yang diautentikasi ke layanan pribadi. Ini menggunakan layanan metadata Google Cloud pada lingkungan Cloud Run untuk membuat token identitas dan menambahkannya pada permintaan HTTP sebagai bagian dari Authorization header.

Di lingkungan lain, GetAuthenticatedPostResponse permintaan token identitas dari server google menggunakan autentikasi dengan Kredensial Default Aplikasi.

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

Mengurai markdown dari JSON dan mengirimkannya ke layanan Renderer untuk diubah menjadi HTML.

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

Mengirim layanan editor publik

Untuk mem-build dan men-deploy kode Anda:

  1. Ubah ke direktori editor:

    Node.js

    cd ../editor

    Python

    cd ../editor

    Go

    cd ../editor

    Java

    cd ../editor

    C#

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

  2. Jalankan perintah berikut untuk mem-build container dan memublikasikannya di Artifact Registry.

    Node.js

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

    Dengan PROJECT_ID sebagai project ID Google Cloud Anda, dan editor merupakan nama yang ingin Anda berikan ke layanan.

    Setelah berhasil, Anda akan melihat pesan SUCCESS yang berisi ID, waktu pembuatan, dan nama image. Image tersebut disimpan di Container Registry dan dapat digunakan kembali bila diinginkan.

    Python

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

    Dengan PROJECT_ID sebagai project ID Google Cloud Anda, dan editor merupakan nama yang ingin Anda berikan ke layanan.

    Setelah berhasil, Anda akan melihat pesan SUCCESS yang berisi ID, waktu pembuatan, dan nama image. Image tersebut disimpan di Artifact Registry dan dapat digunakan kembali jika diinginkan.

    Go

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

    Dengan PROJECT_ID sebagai project ID Google Cloud Anda, dan editor merupakan nama yang ingin Anda berikan ke layanan.

    Setelah berhasil, Anda akan melihat pesan SUCCESS yang berisi ID, waktu pembuatan, dan nama image. Image tersebut disimpan di Artifact Registry dan dapat digunakan kembali jika diinginkan.

    Java

    Contoh ini menggunakan Jib untuk mem-build image Docker menggunakan alat Java umum. Jib mengoptimalkan build container tanpa memerlukan Dockerfile atau menginstal Docker. Pelajari lebih lanjut cara mem-build container Java dengan Jib.

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

    Dengan PROJECT_ID sebagai project ID Google Cloud Anda dan editor merupakan nama yang ingin Anda berikan pada layanan Anda.

    Setelah berhasil, Anda akan melihat pesan BUILD SUCCESS. Image tersebut disimpan di Artifact Registry dan dapat digunakan kembali jika diinginkan.

    C#

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

    Dengan PROJECT_ID sebagai project ID Google Cloud Anda dan editor merupakan nama yang ingin Anda berikan pada layanan Anda.

    Setelah berhasil, Anda akan melihat pesan SUCCESS yang berisi ID, waktu pembuatan, dan nama image. Image tersebut disimpan di Artifact Registry dan dapat digunakan kembali jika diinginkan.

  3. Deploy sebagai layanan pribadi dengan akses khusus ke layanan rendering.

    1. Buat akun layanan untuk digunakan sebagai "identitas komputasi" dari layanan pribadi. Secara default, opsi ini tidak memiliki hak istimewa selain keanggotaan project.

      Command line

      gcloud iam service-accounts create editor-identity

      Terraform

      Untuk mempelajari cara menerapkan atau menghapus konfigurasi Terraform, lihat Perintah dasar Terraform.

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

      Layanan Editor tidak perlu berinteraksi dengan hal lain di Google Cloud selain layanan rendering Markdown.

    2. Memberikan akses ke identitas komputasi editor-identity untuk memanggil layanan rendering Markdown. Setiap layanan yang menggunakan identitas ini sebagai identitas komputasi akan memiliki hak istimewa ini.

      Command line

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

      Terraform

      Untuk mempelajari cara menerapkan atau menghapus konfigurasi Terraform, lihat Perintah dasar Terraform.

      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}"
      }

      Karena ini diberi perab invoker dalam konteks layanan render, layanan render adalah satu-satunya layanan Cloud Run pribadi yang dapat dipanggil editor.

    3. Deploy dengan akun layanan editor-identity dan izinkan akses publik yang tidak diautentikasi.

      Command line

      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

      Ganti:

      • PROJECT_ID dengan project ID Anda
      • SERVICE_URL dengan URL yang diberikan setelah men-deploy layanan rendering Markdown.

      Terraform

      Untuk mempelajari cara menerapkan atau menghapus konfigurasi Terraform, lihat Perintah dasar Terraform.

      Deploy layanan 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
      
        }
      }

      Memberikan allUsers izin untuk memanggil layanan:

      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
      }

Memahami traffic HTTPS

Ada tiga permintaan HTTP yang terlibat dalam rendering markdown menggunakan layanna ini.

Diagram yang menunjukkan alur permintaan dari pengguna ke editor, editor untuk mendapatkan token dari server Metadata, editor untuk membuat permintaan ke layanan render, layanan render untuk menampilkan HTML ke editor.
Layanan frontend dengan editor-identity memanggil layanan render. Dan editor-identity keduanya renderer-identity memiliki izin terbatas sehingga eksploitasi atau injeksi keamanan pun memiliki batas akses ke resource Google Cloud lainnya.

Melakukan Percobaan

Untuk mencoba aplikasi dua layanan yang selesai:

  1. Buka browser Anda ke URL yang disediakan pada langkah deployment di atas.

  2. Cobalah edit teks Markdown di sebelah kiri lalu klik tombol untuk melihat pratinjaunya di sebelah kanan.

    Kodenya akan terlihat seperti berikut:

    Screenshot Antarmuka Pengguna Markdown Editor

Jika Anda memilih untuk terus mengembangkan layanan ini, perlu diingat bahwa layanan tersebut telah membatasi akses Identity and Access Management (IAM) ke bagian lain Google Cloud dan perlu diberi peran IAM tambahan untuk mengakses banyak layanan lainnya.

Pembersihan

Jika Anda membuat project baru untuk tutorial ini, hapus project tersebut. Jika Anda menggunakan project yang ada dan ingin mempertahankannya tanpa ada perubahan yang ditambahkan dalam tutorial ini, hapus resource yang dibuat untuk tutorial.

Menghapus project

Cara termudah untuk menghilangkan penagihan adalah dengan menghapus project yang Anda buat untuk tutorial.

Untuk menghapus project:

  1. Di konsol Google Cloud, buka halaman Manage resource.

    Buka Manage resource

  2. Pada daftar project, pilih project yang ingin Anda hapus, lalu klik Delete.
  3. Pada dialog, ketik project ID, lalu klik Shut down untuk menghapus project.

Menghapus resource tutorial

  1. Hapus layanan Cloud Run yang telah Anda deploy dalam tutorial ini:

    gcloud

    gcloud run services delete editor
    gcloud run services delete renderer

    Anda juga dapat menghapus layanan Cloud Run dari Google Cloud Console.

  2. Hapus konfigurasi default gcloud yang Anda tambahkan selama penyiapan tutorial.

     gcloud config unset run/region
    
  3. Hapus konfigurasi project:

     gcloud config unset project
    
  4. Hapus resource Google Cloud lain yang dibuat dalam tutorial ini:

Langkah berikutnya