אימות פרוגרמטי

במאמר הזה מוסבר איך לבצע אימות למשאב שמאובטח באמצעות IAP מחשבון משתמש או מחשבון שירות.

גישה פרוגרמטית מתייחסת לקריאה לאפליקציות שמאובטחות באמצעות IAP מלקוחות שאינם דפדפנים, כמו כלים של שורת פקודה, קריאות משירות לשירות ואפליקציות לנייד. בהתאם לתרחיש השימוש, יכול להיות שתרצו לבצע אימות ל-IAP באמצעות פרטי כניסה של משתמש או פרטי כניסה של שירות.

  • חשבון משתמש שייך למשתמש פרטי. אתם מאמתים חשבון משתמש כשהאפליקציה שלכם דורשת גישה למשאבים שמוגנים על ידי IAP בשם המשתמש. מידע נוסף זמין במאמר בנושא חשבונות משתמשים.

  • חשבון שירות מייצג אפליקציה ולא משתמש פרטי. מבצעים אימות של חשבון שירות כשרוצים לאפשר לאפליקציה לגשת למשאבים שמוגנים על ידי IAP. מידע נוסף זמין במאמר חשבונות שירות.

‫IAP תומך בסוגי פרטי הכניסה הבאים לגישה תוכנתית:

  • אסימון מזהה מסוג OAuth 2.0 – אסימון שהונפק על ידי Google למשתמש אנושי או לחשבון שירות, עם טענת הקהל שמוגדרת למזהה המשאב של אפליקציית ה-IAP.
  • JWT חתום בחשבון שירות – אסימון JWT בחתימה עצמית או כזה שהונפק על ידי Google לחשבון שירות.

צריך לכלול את פרטי הכניסה האלה ל-IAP בכותרת ה-HTTP‏ Authorization או Proxy-Authorization של הבקשה.

לפני שמתחילים

  1. מוודאים שיש לכם אפליקציה שאתם רוצים לגשת אליה באופן פרוגרמטי באמצעות חשבון מפתח, חשבון שירות או פרטי כניסה לאפליקציה לנייד.

  2. במאמר בנושא גישה באמצעות תוכנה מוסבר איך ליצור לקוח אחד או יותר של OAuth 2.0.

אימות של חשבון משתמש

אתם יכולים לאפשר למשתמשים לגשת לאפליקציה שלכם ממחשב או מאפליקציה לנייד כדי לאפשר לתוכנה לבצע אינטראקציה עם משאב שמוגן על ידי רכישה מתוך האפליקציה.

אימות מאפליקציה לנייד

  1. יוצרים או משתמשים במזהה לקוח קיים של OAuth 2.0 לאפליקציה לנייד. כדי להשתמש במזהה לקוח קיים של OAuth 2.0, פועלים לפי השלבים במאמר איך משתפים לקוחות OAuth. מוסיפים את מזהה הלקוח ב-OAuth לרשימת ההיתרים של גישה פרוגרמטית לאפליקציה.
  2. מקבלים אסימון מזהה למזהה הלקוח ב-OAuth 2.0 של המשאב שמאובטח באמצעות IAP.
  3. כדי לשלוח בקשה מאומתת למשאב שמוגן על ידי IAP, צריך לכלול את האסימון המזהה בכותרת Authorization: Bearer.

אימות מאפליקציה למחשב

בקטע הזה מוסבר איך לאמת חשבון משתמש משורת פקודה במחשב.

  1. כדי לאפשר למפתחים לגשת לאפליקציה משורת הפקודה, צריך ליצור מזהה לקוח OAuth 2.0 למחשב או לשתף מזהה לקוח OAuth קיים למחשב.
  2. מוסיפים את מזהה OAuth לרשימת ההיתרים של גישה באמצעות תוכנה לאפליקציה.

כניסה לאפליקציה

כל מפתח צריך להיכנס לחשבון שלו כדי לגשת לאפליקציה שמוגנת על ידי IAP. אפשר לארוז את התהליך בסקריפט, למשל באמצעות ה-CLI של gcloud. בדוגמה הבאה נעשה שימוש ב-curl כדי להיכנס וליצור אסימון שאפשר להשתמש בו כדי לגשת לאפליקציה:

  1. נכנסים לחשבון שיש לו גישה אל Google Cloud המשאב.
  2. מפעילים שרת מקומי שיכול להחזיר את הבקשות הנכנסות.

      # Example using Netcat (http://netcat.sourceforge.net/)
      nc -k -l 4444
    
  3. עוברים אל ה-URI הבא, שבו DESKTOP_CLIENT_ID הוא מזהה הלקוח של אפליקציית הדסקטופ:

      https://accounts.google.com/o/oauth2/v2/auth?client_id=DESKTOP_CLIENT_ID&response_type=code&scope=openid%20email&access_type=offline&redirect_uri=http://localhost:4444&cred_ref=true
    
  4. בפלט של השרת המקומי, מחפשים את פרמטרי הבקשה:

      GET /?code=CODE&scope=email%20openid%20https://www.googleapis.com/auth/userinfo.email&hd=google.com&prompt=consent HTTP/1.1
    
  5. מעתיקים את הערך של CODE כדי להחליף את CODE בפקודה הבאה, יחד עם ה-Client ID והסוד של אפליקציית המחשב:

      curl --verbose \
        --data client_id=DESKTOP_CLIENT_ID \
        --data client_secret=DESKTOP_CLIENT_SECRET \
        --data code=CODE \
        --data redirect_uri=http://localhost:4444 \
        --data grant_type=authorization_code \
        https://oauth2.googleapis.com/token
    

    הפקודה הזו מחזירה אובייקט JSON עם שדה id_token שאפשר להשתמש בו כדי לגשת לאפליקציה.

גישה לאפליקציה

כדי לגשת לאפליקציה, משתמשים ב-id_token:

curl --verbose --header 'Authorization: Bearer ID_TOKEN' URL

טוקן רענון

אפשר להשתמש באסימון הרענון שנוצר במהלך תהליך הכניסה כדי לקבל אסימוני מזהה חדשים. האפשרות הזו שימושית כשתוקף אסימון המזהה המקורי פג. כל אסימון מזהה תקף למשך כשעה, ובמהלך הזמן הזה אפשר לשלוח כמה בקשות לאפליקציה ספציפית.

בדוגמה הבאה נעשה שימוש ב-curl כדי להשתמש באסימון הרענון ולקבל אסימון מזהה חדש. בדוגמה הזו, REFRESH_TOKEN הוא הטוקן מתהליך הכניסה. הערכים DESKTOP_CLIENT_ID ו-DESKTOP_CLIENT_SECRET זהים לערכים שמשמשים בתהליך הכניסה:

curl --verbose \
  --data client_id=DESKTOP_CLIENT_ID \
  --data client_secret=DESKTOP_CLIENT_SECRET \
  --data refresh_token=REFRESH_TOKEN \
  --data grant_type=refresh_token \
  https://oauth2.googleapis.com/token

הפקודה הזו מחזירה אובייקט JSON עם שדה id_token חדש שבו אפשר להשתמש כדי לגשת לאפליקציה.

אימות של חשבון שירות

אתם יכולים להשתמש באסימון JWT של חשבון שירות או באסימון OpenID Connect‏ (OIDC) כדי לאמת חשבון שירות עם משאב שמוגן על ידי IAP. בטבלה הבאה מפורטים כמה מההבדלים בין אסימוני האימות השונים והתכונות שלהם.

תכונות אימות ‏JWT של חשבון שירות אסימון OpenID Connect
תמיכה בבקרת גישה מבוססת-הקשר
דרישה למזהה לקוח OAuth 2.0
היקף הטוקן כתובת ה-URL של מקור המידע שמוגן על ידי IAP מזהה לקוח OAuth 2.0

אימות באמצעות JWT של חשבון שירות

‫IAP תומך באימות JWT של חשבונות שירות עבור זהויות ב-Google, ב-Identity Platform ובאפליקציות שהוגדרו באמצעות איחוד שירותי אימות הזהות של כוח העבודה.

כדי לבצע אימות של חשבון שירות באמצעות JWT, צריך לבצע את השלבים הבאים:

  1. מקצים לחשבון השירות שקורא את התפקיד יצירת אסימונים בחשבון שירות (roles/iam.serviceAccountTokenCreator).

    התפקיד הזה מעניק לחשבונות משתמשים הרשאה ליצור פרטי כניסה לטווח קצר, כמו JWT.

  2. יוצרים JWT למשאב שמאובטח על ידי IAP.

  3. חותמים על ה-JWT באמצעות המפתח הפרטי של חשבון השירות.

יצירת ה-JWT

המטען הייעודי (payload) של ה-JWT שנוצר צריך להיות דומה לדוגמה הבאה:

{
  "iss": SERVICE_ACCOUNT_EMAIL_ADDRESS,
  "sub": SERVICE_ACCOUNT_EMAIL_ADDRESS,
  "aud": TARGET_URL,
  "iat": IAT,
  "exp": EXP,
}
  • בשדות iss ו-sub מציינים את כתובת האימייל של חשבון השירות. כתובת האימייל נמצאת בשדה client_email של קובץ ה-JSON של חשבון השירות או מסופקת כקלט. פורמט טיפוסי: service-account@PROJECT_ID.iam.gserviceaccount.com

  • בשדה aud, מציינים את כתובת ה-URL המדויקת או את כתובת ה-URL עם תבנית wildcard של נתיב (/*) של המשאב שמוגן על ידי IAP – לדוגמה, https://example.com/ או https://example.com/*. אפשר לגשת לכתובת URL ספציפית רק באמצעות JWT עם כתובת ה-URL המדויקת בשדה aud. אסימון JWT עם תו כללי לחיפוש של נתיב (/*) בשדה aud יכול לגשת לכל כתובות ה-URL שמתחילות במחרוזת aud בלי התו * בסוף.

  • בשדה iat מציינים את הזמן הנוכחי של מערכת Unix, ובשדה exp מציינים זמן שחל עד 3,600 שניות מאוחר יותר. ההגדרה הזו קובעת מתי תפוג התוקף של ה-JWT.

חתימה על ה-JWT

אפשר להשתמש באחת מהשיטות הבאות כדי לחתום על ה-JWT:

  • משתמשים ב-IAM credentials API כדי לחתום על JWT בלי לדרוש גישה ישירה למפתח פרטי.
  • משתמשים בקובץ מקומי של מפתח פרטי כניסה כדי לחתום על ה-JWT באופן מקומי.
חתימה על ה-JWT באמצעות IAM Service Account Credentials API

משתמשים ב-IAM Service Account Credentials API כדי לחתום על JWT של חשבון שירות. השיטה מאחזרת את המפתח הפרטי שמשויך לחשבון השירות ומשתמשת בו כדי לחתום על מטען ה-JWT. כך אפשר לחתום על JWT בלי גישה ישירה למפתח פרטי.

כדי לבצע אימות ב-IAP, צריך להגדיר את Application Default Credentials. מידע נוסף זמין במאמר הגדרת אימות לסביבת פיתוח מקומית.

gcloud

  1. מריצים את הפקודה הבאה כדי להכין בקשה עם מטען ייעודי (payload) של JWT:
cat > claim.json << EOM
{
  "iss": "SERVICE_ACCOUNT_EMAIL_ADDRESS",
  "sub": "SERVICE_ACCOUNT_EMAIL_ADDRESS",
  "aud": "TARGET_URL",
  "iat": $(date +%s),
  "exp": $((`date +%s` + 3600))
}
EOM
  1. משתמשים בפקודה הבאה של Google Cloud CLI כדי לחתום על מטען הייעודי (payload) ב-claim.json:
gcloud iam service-accounts sign-jwt --iam-account="SERVICE_ACCOUNT_EMAIL_ADDRESS" claim.json output.jwt

אחרי שהבקשה תאושר, output.jwt יכלול JWT חתום שבו תוכלו להשתמש כדי לגשת למשאב המאובטח באמצעות IAP.

Python

import datetime
import json

import google.auth
from google.cloud import iam_credentials_v1

def generate_jwt_payload(service_account_email: str, resource_url: str) -> str:
    """Generates JWT payload for service account.

    Creates a properly formatted JWT payload with standard claims (iss, sub,
    aud, iat, exp) needed for IAP authentication.

    Args:
        service_account_email (str): Specifies the service account that the
        JWT is created for.
        resource_url (str): Specifies the scope of the JWT, the URL that the
        JWT will be allowed to access.

    Returns:
        str: JSON string containing the JWT payload with properly formatted
        claims.
    """
    # Create current time and expiration time (1 hour later) in UTC
    iat = datetime.datetime.now(tz=datetime.timezone.utc)
    exp = iat + datetime.timedelta(seconds=3600)

    # Convert datetime objects to numeric timestamps (seconds since epoch)
    # as required by JWT standard (RFC 7519)
    payload = {
        "iss": service_account_email,
        "sub": service_account_email,
        "aud": resource_url,
        "iat": int(iat.timestamp()),
        "exp": int(exp.timestamp()),
    }

    return json.dumps(payload)

def sign_jwt(target_sa: str, resource_url: str) -> str:
    """Signs JWT payload using ADC and IAM credentials API.

    Uses Google Cloud's IAM Credentials API to sign a JWT. This requires the
    caller to have iap.webServiceVersions.accessViaIap permission on the
    target service account.

    Args:
        target_sa (str): Service Account JWT is being created for.
            iap.webServiceVersions.accessViaIap permission is required.
        resource_url (str): Audience of the JWT and scope of the JWT token.
            This is the URL of the IAP-secured application.

    Returns:
        str: A signed JWT that can be used to access IAP-secured applications.
            Use in Authorization header as: 'Bearer <signed_jwt>'
    """
    # Get default credentials from environment or application credentials
    source_credentials, project_id = google.auth.default()

    # Initialize IAM credentials client with source credentials
    iam_client = iam_credentials_v1.IAMCredentialsClient(credentials=source_credentials)

    # Generate the service account resource name.
    # Project should always be "-".
    # Replacing the wildcard character with a project ID is invalid.
    name = iam_client.service_account_path("-", target_sa)

    # Create and sign the JWT payload
    payload = generate_jwt_payload(target_sa, resource_url)

    # Sign the JWT using the IAM credentials API
    response = iam_client.sign_jwt(name=name, payload=payload)

    return response.signed_jwt

curl

  1. מריצים את הפקודה הבאה כדי להכין בקשה עם מטען ייעודי (payload) של JWT:

    cat << EOF > request.json
    {
      "payload": JWT_PAYLOAD
    }
    EOF
    
  2. חתימה על ה-JWT באמצעות IAM

    ‫Service Account Credentials API:

    curl -X POST \
      -H "Authorization: Bearer $(gcloud auth print-access-token)" \
      -H "Content-Type: application/json; charset=utf-8" \
      -d @request.json \
      "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/SERVICE_ACCOUNT_EMAIL_ADDRESS:signJwt"
    

    אחרי בקשה מוצלחת, מוחזר JWT חתום בתגובה.

  3. משתמשים ב-JWT כדי לגשת למשאב שמוגן על ידי IAP.

חתימה על ה-JWT מקובץ מקומי של מפתח פרטי כניסה

אסימוני JWT נחתמים באמצעות המפתח הפרטי של חשבון השירות.

אם יש לכם קובץ מפתח של חשבון שירות, אפשר לחתום על ה-JWT באופן מקומי.

כשחותמים על ה-JWT באופן מקומי, צריך לכלול כותרת JWT עם המטען הייעודי (payload). בשדה kid בכותרת, משתמשים במזהה המפתח הפרטי של חשבון השירות, שמופיע בשדה private_key_id בקובץ ה-JSON של פרטי הכניסה של חשבון השירות. המפתח הפרטי מהקובץ משמש לחתימה על ה-JWT.

גישה לאפליקציה

כדי לגשת לאפליקציה, צריך לכלול את ה-JWT החתום בכותרת Authorization:

curl --verbose --header 'Authorization: Bearer SIGNED_JWT' URL

אימות באמצעות אסימון OIDC של חשבון שירות

כדי לבצע אימות באמצעות אסימון OIDC של חשבון שירות:

  1. יוצרים מזהה לקוח חדש של OIDC או משתמשים במזהה לקוח קיים.

    • כדי ליצור מזהה לקוח חדש של OAuth 2.0:

      1. אם עדיין לא עשיתם זאת, ודאו שרשמתם את האפליקציה שלכם לשימוש באימות של Google.

      2. עוברים לדף של פלטפורמת האימות של Google.

        מעבר אל פלטפורמת האימות של Google

        אם לא בחרתם פרויקט, תתבקשו ליצור פרויקט.

      3. לוחצים על Create Client.

      4. בוחרים את סוג האפליקציה המתאים ומזינים את כל המידע הנוסף שנדרש.

      5. ממלאים את הפרטים הנדרשים לסוג הלקוח שנבחר. כדי ליצור את הלקוח, לוחצים על Create.

    • כדי להשתמש במזהה לקוח קיים של OAuth 2.0: פועלים לפי השלבים במאמר איך משתפים לקוחות OAuth.

  2. מוסיפים את מזהה OAuth לרשימת ההיתרים של גישה באמצעות תוכנה לאפליקציה.

  3. מוודאים שחשבון השירות שמוגדר כברירת מחדל נוסף לרשימת הגישה של הפרויקט שמאובטח באמצעות IAP.

כששולחים בקשות למשאב שמאובטח באמצעות IAP, צריך לכלול את האסימון בכותרת Authorization: Authorization: 'Bearer OIDC_TOKEN'

בדוגמאות הקוד הבאות אפשר לראות איך מקבלים אסימון OIDC.

קבלת אסימון OIDC לחשבון השירות שמוגדר כברירת מחדל

כדי לקבל אסימון OIDC בשביל חשבון השירות שמוגדר כברירת מחדל ב-Compute Engine, ב-App Engine או ב-Cloud Run, צריך להפנות לדוגמת הקוד הבאה כדי ליצור אסימון לגישה למשאב שמאובטח באמצעות IAP:

C#

כדי לבצע אימות ב-IAP, צריך להגדיר את Application Default Credentials. מידע נוסף זמין במאמר הגדרת אימות לסביבת פיתוח מקומית.


using Google.Apis.Auth.OAuth2;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;

public class IAPClient
{
    /// <summary>
    /// Makes a request to a IAP secured application by first obtaining
    /// an OIDC token.
    /// </summary>
    /// <param name="iapClientId">The client ID observed on 
    /// https://console.cloud.google.com/apis/credentials. </param>
    /// <param name="uri">HTTP URI to fetch.</param>
    /// <param name="cancellationToken">The token to propagate operation cancel notifications.</param>
    /// <returns>The HTTP response message.</returns>
    public async Task<HttpResponseMessage> InvokeRequestAsync(
        string iapClientId, string uri, CancellationToken cancellationToken = default)
    {
        // Get the OidcToken.
        // You only need to do this once in your application
        // as long as you can keep a reference to the returned OidcToken.
        OidcToken oidcToken = await GetOidcTokenAsync(iapClientId, cancellationToken);

        // Before making an HTTP request, always obtain the string token from the OIDC token,
        // the OIDC token will refresh the string token if it expires.
        string token = await oidcToken.GetAccessTokenAsync(cancellationToken);

        // Include the OIDC token in an Authorization: Bearer header to 
        // IAP-secured resource
        // Note: Normally you would use an HttpClientFactory to build the httpClient.
        // For simplicity we are building the HttpClient directly.
        using HttpClient httpClient = new HttpClient();
        httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
        return await httpClient.GetAsync(uri, cancellationToken);
    }

    /// <summary>
    /// Obtains an OIDC token for authentication an IAP request.
    /// </summary>
    /// <param name="iapClientId">The client ID observed on 
    /// https://console.cloud.google.com/apis/credentials. </param>
    /// <param name="cancellationToken">The token to propagate operation cancel notifications.</param>
    /// <returns>The HTTP response message.</returns>
    public async Task<OidcToken> GetOidcTokenAsync(string iapClientId, CancellationToken cancellationToken)
    {
        // Obtain the application default credentials.
        GoogleCredential credential = await GoogleCredential.GetApplicationDefaultAsync(cancellationToken);

        // Request an OIDC token for the Cloud IAP-secured client ID.
       return await credential.GetOidcTokenAsync(OidcTokenOptions.FromTargetAudience(iapClientId), cancellationToken);
    }
}

Go

כדי לבצע אימות ב-IAP, צריך להגדיר את Application Default Credentials. מידע נוסף זמין במאמר הגדרת אימות לסביבת פיתוח מקומית.

import (
	"context"
	"fmt"
	"io"
	"net/http"

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

// makeIAPRequest makes a request to an application protected by Identity-Aware
// Proxy with the given audience.
func makeIAPRequest(w io.Writer, request *http.Request, audience string) error {
	// request, err := http.NewRequest("GET", "http://example.com", nil)
	// audience := "IAP_CLIENT_ID.apps.googleusercontent.com"
	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)
	}

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

	return nil
}

Java

כדי לבצע אימות ב-IAP, צריך להגדיר את Application Default Credentials. מידע נוסף זמין במאמר הגדרת אימות לסביבת פיתוח מקומית.


import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpRequestInitializer;
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 com.google.common.base.Preconditions;
import java.io.IOException;
import java.util.Collections;

public class BuildIapRequest {
  private static final String IAM_SCOPE = "https://www.googleapis.com/auth/iam";

  private static final HttpTransport httpTransport = new NetHttpTransport();

  private BuildIapRequest() {}

  private static IdTokenProvider getIdTokenProvider() throws IOException {
    GoogleCredentials credentials =
        GoogleCredentials.getApplicationDefault().createScoped(Collections.singleton(IAM_SCOPE));

    Preconditions.checkNotNull(credentials, "Expected to load credentials");
    Preconditions.checkState(
        credentials instanceof IdTokenProvider,
        String.format(
            "Expected credentials that can provide id tokens, got %s instead",
            credentials.getClass().getName()));

    return (IdTokenProvider) credentials;
  }

  /**
   * Clone request and add an IAP Bearer Authorization header with ID Token.
   *
   * @param request Request to add authorization header
   * @param iapClientId OAuth 2.0 client ID for IAP protected resource
   * @return Clone of request with Bearer style authorization header with ID Token.
   * @throws IOException exception creating ID Token
   */
  public static HttpRequest buildIapRequest(HttpRequest request, String iapClientId)
      throws IOException {

    IdTokenProvider idTokenProvider = getIdTokenProvider();
    IdTokenCredentials credentials =
        IdTokenCredentials.newBuilder()
            .setIdTokenProvider(idTokenProvider)
            .setTargetAudience(iapClientId)
            .build();

    HttpRequestInitializer httpRequestInitializer = new HttpCredentialsAdapter(credentials);

    return httpTransport
        .createRequestFactory(httpRequestInitializer)
        .buildRequest(request.getRequestMethod(), request.getUrl(), request.getContent());
  }
}

Node.js

כדי לבצע אימות ב-IAP, צריך להגדיר את Application Default Credentials. מידע נוסף זמין במאמר הגדרת אימות לסביבת פיתוח מקומית.

/**
 * TODO(developer): Uncomment these variables before running the sample.
 */
// const url = 'https://some.iap.url';
// const targetAudience = 'IAP_CLIENT_ID.apps.googleusercontent.com';

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

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

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

PHP

כדי לבצע אימות ב-IAP, צריך להגדיר את Application Default Credentials. מידע נוסף זמין במאמר הגדרת אימות לסביבת פיתוח מקומית.

namespace Google\Cloud\Samples\Iap;

# Imports Auth libraries and Guzzle HTTP libraries.
use Google\Auth\ApplicationDefaultCredentials;
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;

/**
 * Make a request to an application protected by Identity-Aware Proxy.
 *
 * @param string $url The Identity-Aware Proxy-protected URL to fetch.
 * @param string $clientId The client ID used by Identity-Aware Proxy.
 */
function make_iap_request($url, $clientId)
{
    // create middleware, using the client ID as the target audience for IAP
    $middleware = ApplicationDefaultCredentials::getIdTokenMiddleware($clientId);
    $stack = HandlerStack::create();
    $stack->push($middleware);

    // create the HTTP client
    $client = new Client([
        'handler' => $stack,
        'auth' => 'google_auth'
    ]);

    // make the request
    $response = $client->get($url);
    print('Printing out response body:');
    print($response->getBody());
}

Python

כדי לבצע אימות ב-IAP, צריך להגדיר את Application Default Credentials. מידע נוסף זמין במאמר הגדרת אימות לסביבת פיתוח מקומית.

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


def make_iap_request(url, client_id, method="GET", **kwargs):
    """Makes a request to an application protected by Identity-Aware Proxy.

    Args:
      url: The Identity-Aware Proxy-protected URL to fetch.
      client_id: The client ID used by Identity-Aware Proxy.
      method: The request method to use
              ('GET', 'OPTIONS', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE')
      **kwargs: Any of the parameters defined for the request function:
                https://github.com/requests/requests/blob/master/requests/api.py
                If no timeout is provided, it is set to 90 by default.

    Returns:
      The page body, or raises an exception if the page couldn't be retrieved.
    """
    # Set the default timeout, if missing
    if "timeout" not in kwargs:
        kwargs["timeout"] = 90

    # Obtain an OpenID Connect (OIDC) token from metadata server or using service
    # account.
    open_id_connect_token = id_token.fetch_id_token(Request(), client_id)

    # Fetch the Identity-Aware Proxy-protected URL, including an
    # Authorization header containing "Bearer " followed by a
    # Google-issued OpenID Connect token for the service account.
    resp = requests.request(
        method,
        url,
        headers={"Authorization": "Bearer {}".format(open_id_connect_token)},
        **kwargs
    )
    if resp.status_code == 403:
        raise Exception(
            "Service account does not have permission to "
            "access the IAP-protected application."
        )
    elif resp.status_code != 200:
        raise Exception(
            "Bad response from application: {!r} / {!r} / {!r}".format(
                resp.status_code, resp.headers, resp.text
            )
        )
    else:
        return resp.text

Ruby

כדי לבצע אימות ב-IAP, צריך להגדיר את Application Default Credentials. מידע נוסף זמין במאמר הגדרת אימות לסביבת פיתוח מקומית.

# url = "The Identity-Aware Proxy-protected URL to fetch"
# client_id = "The client ID used by Identity-Aware Proxy"
require "googleauth"
require "faraday"

# The client ID as the target audience for IAP
id_token_creds = Google::Auth::Credentials.default target_audience: client_id

headers = {}
id_token_creds.client.apply! headers

resp = Faraday.get url, nil, headers

if resp.status == 200
  puts "X-Goog-Iap-Jwt-Assertion:"
  puts resp.body
else
  puts "Error requesting IAP"
  puts resp.status
  puts resp.headers
end

קבלת אסימון OIDC מקובץ מפתח מקומי של חשבון שירות

כדי ליצור אסימון OIDC באמצעות קובץ מפתח של חשבון שירות, צריך ליצור טענת נכונות (assertion) של JWT ולחתום עליה באמצעות קובץ המפתח, ואז להמיר את טענת הנכונות הזו לאסימון מזהה. הסקריפט הבא של Bash מדגים את התהליך הזה:

Bash

#!/usr/bin/env bash
#
# Example script that generates an OIDC token using a service account key file and uses it to access an IAP-secured resource.

set -euo pipefail

get_token() {
  # Get the bearer token in exchange for the service account credentials
  local service_account_key_file_path="${1}"
  local iap_client_id="${2}"

  # Define the scope and token endpoint
  local iam_scope="https://www.googleapis.com/auth/iam"
  local oauth_token_uri="https://www.googleapis.com/oauth2/v4/token"

  # Extract data from service account key file
  local private_key_id="$(cat "${service_account_key_file_path}" | jq -r '.private_key_id')"
  local client_email="$(cat "${service_account_key_file_path}" | jq -r '.client_email')"
  local private_key="$(cat "${service_account_key_file_path}" | jq -r '.private_key')"

  # Set token timestamps (current time and expiration 10 minutes later)
  local issued_at="$(date +%s)"
  local expires_at="$((issued_at + 600))"

  # Create JWT header and payload
  local header="{'alg':'RS256','typ':'JWT','kid':'${private_key_id}'}"
  local header_base64="$(echo "${header}" | base64 | tr -d '\n')"
  local payload="{'iss':'${client_email}','aud':'${oauth_token_uri}','exp':${expires_at},'iat':${issued_at},'sub':'${client_email}','target_audience':'${iap_client_id}'}"
  local payload_base64="$(echo "${payload}" | base64 | tr -d '\n')"

  # Create JWT signature using the private key
  local signature_base64="$(printf %s "${header_base64}.${payload_base64}" | openssl dgst -binary -sha256 -sign <(printf '%s\n' "${private_key}")  | base64 | tr -d '\n')"
  local assertion="${header_base64}.${payload_base64}.${signature_base64}"

  # Exchange the signed JWT assertion for an ID token
  local token_payload="$(curl -s \
    --data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer" \
    --data-urlencode "assertion=${assertion}" \
    https://www.googleapis.com/oauth2/v4/token)"

  # Extract just the ID token from the response
  local bearer_id_token="$(echo "${token_payload}" | jq -r '.id_token')"
  echo "${bearer_id_token}"
}

main() {
  # Check if required arguments are provided
  if [[ $# -lt 3 ]]; then
    echo "Usage: $0 <service_account_key_file.json> <iap_client_id> <url>"
    exit 1
  fi

  # Assign parameters to variables
  SERVICE_ACCOUNT_KEY="$1"
  IAP_CLIENT_ID="$2"
  URL="$3"

  # Generate the ID token
  echo "Generating token..."
  ID_TOKEN=$(get_token "${SERVICE_ACCOUNT_KEY}" "${IAP_CLIENT_ID}")

  # Access the IAP-secured resource with the token
  echo "Accessing: ${URL}"
  curl --header "Authorization: Bearer ${ID_TOKEN}" "${URL}"
}

# Run the main function with all provided arguments
main "$@"

הסקריפט הזה מבצע את השלבים הבאים:

  1. שולף את פרטי המפתח של חשבון השירות מקובץ מפתח JSON
  2. יוצר JWT עם השדות הנדרשים, כולל מזהה לקוח IAP בתור קהל היעד
  3. חתימה על ה-JWT באמצעות המפתח הפרטי של חשבון השירות
  4. מחליף את ה-JWT הזה באסימון OIDC דרך שירות ה-OAuth של Google
  5. משתמשים בטוקן שמתקבל כדי לשלוח בקשה מאומתת למשאב שמאובטח באמצעות IAP

כדי להשתמש בסקריפט הזה:

  1. שומרים אותו בקובץ, למשל: get_iap_token.sh
  2. הופכים את הקובץ לניתן להרצה: chmod +x get_iap_token.sh
  3. מריצים אותו עם שלושה פרמטרים:
  ./get_iap_token.sh service-account-key.json \
    OAUTH_CLIENT_ID \
    URL

כאשר:

  • service-account-key.json הוא קובץ המפתח של חשבון השירות שהורדתם
  • OAUTH_CLIENT_ID הוא מזהה הלקוח ב-OAuth של המשאב שמאובטח באמצעות IAP
  • URL היא כתובת ה-URL שאליה רוצים לגשת

קבלת אסימון OIDC באמצעות התחזות לחשבון שירות

בכל שאר המקרים, משתמשים ב-IAM Credentials API כדי ליצור אסימון OIDC על ידי התחזות לחשבון שירות יעד ממש לפני הגישה למשאב שמוגן על ידי IAP. התהליך הזה כולל את השלבים הבאים:

  1. נותנים לחשבון השירות שקורא (חשבון השירות שמשויך לקוד שמקבל את אסימון המזהה) את התפקיד 'יצירת אסימון זהות' ב-OpenID Connect של חשבון שירות (roles/iam.serviceAccountOpenIdTokenCreator).

    כך חשבון השירות שמבצע את הקריאה מקבל את היכולת להתחזות לחשבון השירות של היעד.

  2. משתמשים בפרטי הכניסה שסופקו על ידי חשבון השירות שמבצע את הקריאה כדי לקרוא לשיטה generateIdToken בחשבון השירות של היעד.

    מגדירים את השדה audience למזהה הלקוח.

להוראות מפורטות, תוכלו לקרוא את המאמר יצירת אסימון מזהה.

אימות מכותרת Proxy-Authorization

אם האפליקציה שלכם משתמשת בכותרת הבקשה Authorization, אתם יכולים לכלול את אסימון המזהה בכותרת Proxy-Authorization: Bearer במקום זאת. אם נמצא אסימון מזהה תקין בכותרת Proxy-Authorization, ‏ IAP מאשר את הבקשה באמצעות האסימון. אחרי אישור הבקשה, ‏ IAP מעביר את הכותרת Authorization לאפליקציה בלי לעבד את התוכן.

אם לא נמצא אסימון מזהה תקין בכותרת Proxy-Authorization,‏ IAP ממשיך לעבד את הכותרת Authorization ומסיר את הכותרת Proxy-Authorization לפני העברת הבקשה לאפליקציה.

גישה לאפליקציה

כדי לגשת לאפליקציה באמצעות הכותרת Proxy-Authorization, מריצים את הפקודה הבאה:

curl --verbose --header 'Proxy-Authorization: Bearer SIGNED_JWT' URL

המאמרים הבאים