Autentikasi untuk pemanggilan

Untuk memanggil fungsi Cloud Run yang diautentikasi, prinsipal dasar harus memenuhi persyaratan berikut:

  • Memiliki izin untuk memanggil fungsi.
  • Berikan token ID saat memanggil fungsi.

Apa yang dimaksud dengan akun utama? Seperti yang dijelaskan dalam Mengamankan fungsi Cloud Run, fungsi Cloud Run mendukung dua jenis identitas berbeda, yang juga disebut akun utama:

  • Akun layanan: Ini adalah akun khusus yang berfungsi sebagai identitas non-orang, seperti fungsi atau aplikasi atau VM. Mereka memberi Anda cara untuk mengautentikasi non-orang ini.
  • Akun pengguna: Akun ini mewakili orang, baik sebagai pemegang Akun Google perorangan atau sebagai bagian dari entitas yang dikontrol Google seperti Google Grup.

Lihat Ringkasan IAM untuk mempelajari lebih lanjut konsep dasar IAM.

Untuk memanggil fungsi Cloud Run yang diautentikasi, akun utama harus memiliki izin IAM invoker:

  • run.routes.invoke. Hal ini biasanya dilakukan melalui peran Cloud Run Invoker. Izin ini harus ditetapkan di resource layanan Cloud Run.

Untuk memberikan izin ini, gunakan perintah add-invoker-policy-binding, seperti yang ditunjukkan dalam Mengautentikasi panggilan fungsi ke fungsi.

Untuk mendapatkan izin membuat, memperbarui, atau melakukan tindakan administratif lainnya pada fungsi, akun utama harus memiliki peran yang sesuai. Peran mencakup izin yang menentukan tindakan yang diizinkan untuk dilakukan oleh akun utama. Lihat Menggunakan IAM untuk Memberikan Otorisasi pada Akses untuk mengetahui informasi selengkapnya.

Memanggil fungsi dapat memiliki kompleksitas tambahan. Fungsi berbasis peristiwa hanya dapat dipanggil oleh sumber peristiwa langganannya, tetapi fungsi HTTP dapat dipanggil dengan berbagai jenis identitas, yang berasal dari berbagai tempat. Invoker mungkin adalah developer yang menguji fungsi, atau fungsi atau layanan lain yang ingin menggunakan fungsi tersebut. Secara default, identitas ini harus memberikan token ID dengan permintaan untuk mengautentikasi dirinya sendiri. Selain itu, akun yang digunakan juga harus telah diberikan izin yang sesuai.

Pelajari lebih lanjut cara membuat dan menggunakan token ID.

Contoh autentikasi

Bagian ini menunjukkan beberapa contoh autentikasi untuk pemanggilan.

Contoh 1: Mengautentikasi pengujian developer

Sebagai developer, Anda memerlukan akses untuk membuat, mengupdate, serta menghapus fungsi, dan akses ini diberikan melalui proses (IAM) normal.

Namun, sebagai developer, Anda mungkin perlu memanggil fungsi untuk tujuan pengujian. Untuk memanggil fungsi menggunakan curl atau alat serupa, perhatikan hal berikut:

  • Tetapkan peran ke akun pengguna fungsi Cloud Run Anda yang berisi izin pemanggilan.

  • Jika Anda bekerja dari komputer lokal, siapkan akses command line dengan melakukan inisialisasi Google Cloud CLI.

  • Berikan kredensial autentikasi ke permintaan Anda sebagai token ID yang dibuat Google yang disimpan di header Authorization. Misalnya, dapatkan token ID menggunakan gcloud dengan menjalankan perintah berikut:

    curl  -H "Authorization: Bearer $(gcloud auth print-identity-token)" \
      https://FUNCTION_URL

    dengan FUNCTION_URL adalah URL fungsi Anda. Ambil URL ini dari halaman fungsi Cloud Run di Konsol Google Cloud atau dengan menjalankan perintah gcloud functions describe seperti yang ditunjukkan pada langkah pertama contoh perintah deployment Google Cloud CLI.

Anda dapat menggunakan token yang dibuat oleh gcloud untuk memanggil fungsi HTTP di project mana pun selama akun Anda memiliki izin cloudfunctions.functions.invoke pada fungsi yang dipanggil. Untuk tujuan pengembangan, gunakan token ID yang dibuat gcloud. Namun, perlu diperhatikan bahwa token tersebut tidak memiliki klaim penonton, sehingga rentan terhadap serangan relay. Di lingkungan produksi, gunakan token ID yang dikeluarkan untuk akun layanan dengan audiens yang sesuai yang ditentukan. Pendekatan ini meningkatkan keamanan dengan membatasi penggunaan token hanya untuk layanan yang dimaksud.

Seperti biasa, sebaiknya alokasikan kumpulan izin minimum yang diperlukan untuk mengembangkan dan menggunakan fungsi Anda. Pastikan kebijakan IAM pada fungsi Anda dibatasi untuk jumlah pengguna dan akun layanan minimum.

Contoh 2: Mengautentikasi fungsi ke panggilan fungsi

Saat membuat layanan yang menghubungkan beberapa fungsi, sebaiknya pastikan bahwa setiap fungsi hanya dapat mengirim permintaan ke subset tertentu dari fungsi Anda yang lain. Misalnya, jika Anda memiliki fungsi login, fungsi tersebut harus dapat mengakses fungsi user profiles, tetapi mungkin tidak dapat mengakses fungsi search.

Untuk mengonfigurasi fungsi penerima agar menerima permintaan dari fungsi panggilan tertentu, Anda harus memberikan peran invoker yang sesuai ke akun layanan fungsi pemanggil pada fungsi penerima. Peran invoker adalah Cloud Run Invoker (roles/run.invoker) dan harus diberikan pada layanan yang mendasarinya:

Konsol

Gunakan Cloud Run Invoker:

  1. Buka Konsol Google Cloud:

    Buka Konsol Google Cloud

  2. Dalam daftar layanan Cloud Run, klik kotak centang di samping fungsi penerima. (Jangan mengklik fungsi itu sendiri.)

    Panel Izin akan terbuka.

  3. Klik Tambahkan akun utama.

  4. Masukkan identitas layanan panggilan. Biasanya berupa alamat email, secara default PROJECT_NUMBER-compute@developer.gserviceaccount.com.

    Perlu diperhatikan bahwa nomor project berbeda dengan project ID dan nama project. Anda dapat menemukan nomor project di halaman Dasbor Konsol Google Cloud.

  5. Pilih peran Cloud Run Invoker dari opsi Pilih peran di menu drop-down.

  6. Klik Simpan.

gcloud

Gunakan perintah gcloud functions add-invoker-policy-binding:

gcloud functions add-invoker-policy-binding RECEIVING_FUNCTION \
  --member='serviceAccount:CALLING_FUNCTION_IDENTITY'

Perintah add-invoker-policy-binding menambahkan binding kebijakan IAM peran invoker yang memungkinkan anggota (akun utama) yang ditentukan untuk memanggil fungsi yang ditentukan. Google Cloud CLI otomatis mendeteksi pembuatan fungsi dan menambahkan peran Invoker yang benar (run.invoker untuk fungsi Cloud Run).

Ganti kode berikut:

  • RECEIVING_FUNCTION: Nama fungsi penerima.
  • CALLING_FUNCTION_IDENTITY: Identitas fungsi panggilan, email akun layanan.

Karena akan memanggil fungsi penerima, fungsi pemanggil juga harus menyediakan token ID yang ditandatangani Google untuk melakukan autentikasi. Proses ini terdiri dari dua langkah:

  1. Buat token ID yang ditandatangani Google dengan kolom audience (aud) yang ditetapkan ke URL fungsi penerima.

  2. Sertakan token ID di header Authorization: Bearer ID_TOKEN dalam permintaan ke fungsi.

Sejauh ini, cara termudah dan paling andal untuk mengelola proses ini adalah dengan menggunakan library autentikasi, seperti yang ditunjukkan di bawah, untuk membuat dan menggunakan token ini.

Membuat token ID

Bagian ini menjelaskan berbagai cara untuk membuat token ID yang diperlukan akun utama untuk memanggil fungsi.

Akses yang tidak diautentikasi tanpa token ID dapat dilakukan, tetapi harus diaktifkan. Lihat Menggunakan IAM untuk Memberikan Otorisasi pada Akses untuk mengetahui informasi selengkapnya.

Membuat token secara terprogram

Setelah kode berikut menghasilkan token ID, kode akan memanggil fungsi Anda dengan token tersebut atas nama Anda. Kode ini berfungsi di lingkungan apa pun tempat library dapat memperoleh kredensial autentikasi, termasuk lingkungan yang mendukung Kredensial Default Aplikasi lokal.

Node.js

/**
 * TODO(developer): Uncomment these variables before running the sample.
 */

// Cloud Functions uses your function's url as the `targetAudience` value
// const targetAudience = 'https://project-region-projectid.cloudfunctions.net/myFunction';
// For Cloud Functions, endpoint (`url`) and `targetAudience` should be equal
// const url = targetAudience;


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

async function request() {
  console.info(`request ${url} with target audience ${targetAudience}`);
  const client = await auth.getIdTokenClient(targetAudience);

  // Alternatively, one can use `client.idTokenProvider.fetchIdToken`
  // to return the ID Token.
  const res = await client.request({url});
  console.info(res.data);
}

request().catch(err => {
  console.error(err.message);
  process.exitCode = 1;
});

Python

import urllib

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


def make_authorized_get_request(endpoint, audience):
    """
    make_authorized_get_request makes a GET request to the specified HTTP endpoint
    by authenticating with the ID token obtained from the google-auth client library
    using the specified audience value.
    """

    # Cloud Functions uses your function's URL as the `audience` value
    # audience = https://project-region-projectid.cloudfunctions.net/myFunction
    # For Cloud Functions, `endpoint` and `audience` should be equal

    req = urllib.request.Request(endpoint)

    auth_req = google.auth.transport.requests.Request()
    id_token = google.oauth2.id_token.fetch_id_token(auth_req, audience)

    req.add_header("Authorization", f"Bearer {id_token}")
    response = urllib.request.urlopen(req)

    return response.read()

Go


import (
	"context"
	"fmt"
	"io"

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

// `makeGetRequest` makes a request to the provided `targetURL`
// with an authenticated client using audience `audience`.
func makeGetRequest(w io.Writer, targetURL string, audience string) error {
	// For Cloud Functions, endpoint (`serviceUrl`) and `audience` are the same.
	// Example `audience` value (Cloud Functions): https://<PROJECT>-<REGION>-<PROJECT_ID>.cloudfunctions.net/myFunction
	// (`targetURL` and `audience` will differ for GET parameters)
	ctx := context.Background()

	// client is a http.Client that automatically adds an "Authorization" header
	// to any requests made.
	client, err := idtoken.NewClient(ctx, audience)
	if err != nil {
		return fmt.Errorf("idtoken.NewClient: %w", err)
	}

	resp, err := client.Get(targetURL)
	if err != nil {
		return fmt.Errorf("client.Get: %w", err)
	}
	defer resp.Body.Close()
	if _, err := io.Copy(w, resp.Body); err != nil {
		return fmt.Errorf("io.Copy: %w", err)
	}

	return nil
}

Java

import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpResponse;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.auth.http.HttpCredentialsAdapter;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.auth.oauth2.IdTokenCredentials;
import com.google.auth.oauth2.IdTokenProvider;
import java.io.IOException;

public class Authentication {

  // makeGetRequest makes a GET request to the specified Cloud Run or
  // Cloud Functions endpoint `serviceUrl` (must be a complete URL), by
  // authenticating with an ID token retrieved from Application Default
  // Credentials using the specified `audience`.
  //
  // For Cloud Functions, endpoint (`serviceUrl`) and `audience` are the same.
  // Example `audience` value (Cloud Functions): https://project-region-projectid.cloudfunctions.net/myFunction
  public static HttpResponse makeGetRequest(String serviceUrl, String audience) throws IOException {
    GoogleCredentials credentials = GoogleCredentials.getApplicationDefault();
    if (!(credentials instanceof IdTokenProvider)) {
      throw new IllegalArgumentException("Credentials are not an instance of IdTokenProvider.");
    }
    IdTokenCredentials tokenCredential =
        IdTokenCredentials.newBuilder()
            .setIdTokenProvider((IdTokenProvider) credentials)
            .setTargetAudience(audience)
            .build();

    GenericUrl genericUrl = new GenericUrl(serviceUrl);
    HttpCredentialsAdapter adapter = new HttpCredentialsAdapter(tokenCredential);
    HttpTransport transport = new NetHttpTransport();
    HttpRequest request = transport.createRequestFactory(adapter).buildGetRequest(genericUrl);
    return request.execute();
  }
}

Membuat token secara manual

Jika Anda memanggil fungsi dan karena alasan tertentu Anda tidak dapat menggunakan library autentikasi, ada dua cara untuk mendapatkan token ID secara manual, baik dengan menggunakanServer metadata komputasi atau dengan membuat JWT yang ditandatangani sendiri dan menukarnya dengan token ID yang ditandatangani Google.

Menggunakan server metadata

Anda dapat menggunakan server metadata Compute untuk mengambil token ID dengan audience tertentu sebagai berikut:

curl "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=AUDIENCE" \
     -H "Metadata-Flavor: Google"

Ganti AUDIENCE dengan URL fungsi yang Anda panggil. Anda dapat mengambil URL ini seperti yang dijelaskan di bagian Melakukan autentikasi pengujian developer di atas.

Menukar JWT yang ditandatangani sendiri dengan token ID yang ditandatangani Google

  1. Berikan peran Invoker (roles/cloudfunctions.invoker) ke akun layanan dari fungsi yang memanggil pada fungsi penerima.

  2. Buat akun layanan dan kunci, lalu download file dengan kunci pribadi (dalam format JSON) ke host tempat fungsi atau layanan pemanggil pembuat permintaan.

  3. Buat JWT dengan header disetel ke {"alg":"RS256","typ":"JWT"}. Payload harus menyertakan klaim target_audience yang ditetapkan ke URL fungsi penerima, serta klaim iss dan sub yang ditetapkan ke alamat email akun layanan yang digunakan di atas. Ini juga harus menyertakan klaim exp dan iat. Klaim aud harus ditetapkan ke https://www.googleapis.com/oauth2/v4/token.

  4. Gunakan kunci pribadi yang didownload di atas untuk menandatangani JWT.

  5. Dengan JWT ini, kirim permintaan POST ke https://www.googleapis.com/oauth2/v4/token. Data autentikasi harus disertakan dalam header dan isi permintaan.

    Di header:

    Authorization: Bearer $JWT - where $JWT is the JWT you just created
    Content-Type: application/x-www-form-urlencoded
    

    Di bagian isi:

    grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=$JWT
    

    Ganti $JWT dengan JWT yang baru saja Anda buat

    Tindakan ini akan menampilkan JWT lain yang menyertakan id_token yang ditandatangani oleh Google.

Kirim permintaan GET/POST Anda ke fungsi penerimaan. Sertakan token ID yang ditandatangani Google di header Authorization: Bearer ID_TOKEN_JWT dalam permintaan.

Langkah selanjutnya