服務之間的驗證

您可以使用 gRPC 服務的服務帳戶在服務之間進行驗證。本頁面以完整範例逐步示範如何進行服務與服務之間的驗證,包括如何在 gRPC 服務中設定可擴充服務 Proxy (ESP),以利支援通過驗證的要求,以及如何從 gRPC 用戶端呼叫服務。

呼叫服務必須要有服務帳戶,且必須在呼叫中傳送驗證憑證,才能對 Cloud Endpoints API 發出通過驗證的呼叫。呼叫端必須使用 Google ID 憑證或自訂的 JSON Web Token (JWT),且這個憑證只能由呼叫端的服務帳戶簽署。ESP 會檢查 JWT 中的 iss 憑證附加資訊是否與服務設定中的 issuer 設定一致。ESP 不會檢查該服務帳戶已經獲授予的 Identity and Access Management 權限。

在我們的範例中,您將會設定並使用最簡單的服務對服務驗證形式,其中用戶端會使用其 Google Cloud 服務帳戶產生用於驗證的 JWT。其他驗證方法的方式也大同小異,但用戶端取得有效驗證憑證的程序會因其採用的驗證方法而異。

事前準備

本指南沿用教學課程中所用的 Bookstore 範例。

  1. 複製含有託管的 gRPC 範例程式碼的 Git 存放區:

    git clone https://github.com/GoogleCloudPlatform/python-docs-samples.git
    
  2. 變更您的工作目錄:

    cd python-docs-samples/endpoints/bookstore-grpc/
    
  3. 如果您還沒有專案,請按照教學課程中的操作說明進行設定。

您將在此範例中使用 Google Kubernetes Engine 部署,但 Compute Engine 的驗證設定是一樣的。

此範例引用兩個 Google Cloud Platform 專案:

  • 服務「生產端專案」,也就是擁有 gRPC 專用的 Cloud Endpoints 服務的專案。
  • 服務「消費端專案」,也就是擁有 gRPC 用戶端的專案。

建立消費端服務帳戶和金鑰

如要建立「消費端專案」的服務帳戶和金鑰:

  1. 在 Google Cloud 控制台中,前往「API 和服務」。

    API 和服務

    確認您位於「消費端專案」中
  2. 在「Credentials」(憑證) 頁面中,自「Create credentials」(建立憑證) 下拉式清單選取 [Service account key] (服務帳戶金鑰)。
  3. 在「Create service account key」(建立服務帳戶金鑰) 頁面中,若出現您想使用的現有服務帳戶,請選取該帳戶。否則,請在「Service account」(服務帳戶) 下拉式清單中,選取 [New service account] (新增服務帳戶) 並輸入帳戶名稱。

    系統隨即會建立一個對應的服務帳戶 ID。記下該 ID,後續步驟中會用到這個 ID。例如:

    service-account-name@YOUR_PROJECT_ID.iam.gserviceaccount.com
    
  4. 按一下 [角色] 下拉式清單,選取下列角色:

    • [服務帳戶] > [服務帳戶使用者]
    • [服務帳戶] > [服務帳戶憑證建立者]
  5. 請務必選取 JSON 金鑰類型。

  6. 按一下 [建立]。您的服務帳戶 JSON 金鑰檔案隨即會下載到本機電腦中。請記下位置並妥善存放,因為您之後會需要使用此金鑰產生憑證。

設定服務驗證

在本節中,請使用「生產端專案」執行所有步驟。

在 gRPC API 設定中設定驗證

ESP 驗證是在 gRPC API 設定 YAML 檔案中的 authentication 區段所設定,服務範例的驗證是在 api_config_auth.yaml 中設定。

authentication:
  providers:
  - id: google_service_account
    # Replace SERVICE-ACCOUNT-ID with your service account's email address.
    issuer: SERVICE-ACCOUNT-ID
    jwks_uri: https://www.googleapis.com/robot/v1/metadata/x509/SERVICE-ACCOUNT-ID
  rules:
  # This auth rule will apply to all methods.
  - selector: "*"
    requirements:
      - provider_id: google_service_account

providers 區段會指定您要使用的驗證供應商。在此範例中,您將使用的驗證供應商是 Google 服務帳戶。rules 區段指定了您要求此供應商必須具備哪些憑證才能存取服務的所有方法。

在您自己從存放區複製而來的檔案副本中:

  • MY_PROJECT_ID 變更為您的「生產端專案」ID。
  • authentication 區段 (在 issuerjwks_uri 值之中) 的 SERVICE-ACCOUNT-ID 變更為你在先前章節中記下的消費端服務帳戶 ID。此操作可告訴 ESP,您要將服務存取權授予提供這個特定服務帳戶有效憑證的使用者。
  • 您也可以視需要在 providers 元素下方新增 jwt_locations。您可以使用這個值來定義自訂 JWT 位置。預設的 JWT 位置為 Authorization 中繼資料 (前置字串為「Bearer」) 和 X-Goog-Iap-Jwt-Assertion 中繼資料。

儲存檔案以供下一步使用。

部署設定和服務

以下步驟與開始在 GKE 使用 gRPC 一文中的步驟相同:

  1. 將服務設定部署至 Endpoints:即使您已在教學課程中執行過此步驟,現在仍需再進行一次,因為這是不同的設定。記下傳回的服務名稱:

    gcloud endpoints services deploy api_descriptor.pb api_config_auth.yaml --project PRODUCER_PROJECT
    
  2. 若尚未建立容器叢集向叢集驗證 kubectl,請執行此步驟。

  3. 將範例 API 和 ESP 部署至叢集。若您使用不同的生產端和消費端專案,請先確認您已在 gcloud 指令列工具中設定了適當的專案:

    gcloud config set project PRODUCER_PROJECT
    

從 gRPC 用戶端呼叫已驗證方法

最後,您可以針對用戶端使用服務帳戶金鑰以產生 JWT 憑證,接著再使用該憑證呼叫已驗證的 Bookstore 方法。 請先安裝適當的 Python 必要元件,以產生憑證並執行範例用戶端。確認目前的位置是複製的用戶端 python-docs-samples/endpoints/bookstore-grpc 資料夾中,然後輸入以下內容:

virtualenv bookstore-env
source bookstore-env/bin/activate
pip install -r requirements.txt

產生 JWT 憑證

在這個範例中,Bookstore 使用的是服務對服務驗證,其中的呼叫服務完全由服務帳戶驗證。因此,建立能隨要求一併傳送的適當憑證非常簡單。提醒您,您也可以要求使用更嚴謹的服務對服務驗證,由 Google 透過 Google ID 憑證進一步驗證產生的憑證。

在此範例中,我們提供的 Python 指令碼可透過虛擬使用者 ID 和電子郵件,使用稍早下載的 JSON 金鑰檔案產生憑證。

#!/usr/bin/env python

# Copyright 2016 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Example of generating a JWT signed from a service account file."""

import argparse
import json
import time

import google.auth.crypt
import google.auth.jwt

"""Max lifetime of the token (one hour, in seconds)."""
MAX_TOKEN_LIFETIME_SECS = 3600


def generate_jwt(service_account_file, issuer, audiences):
    """Generates a signed JSON Web Token using a Google API Service Account."""
    with open(service_account_file) as fh:
        service_account_info = json.load(fh)

    signer = google.auth.crypt.RSASigner.from_string(
        service_account_info["private_key"], service_account_info["private_key_id"]
    )

    now = int(time.time())

    payload = {
        "iat": now,
        "exp": now + MAX_TOKEN_LIFETIME_SECS,
        # aud must match 'audience' in the security configuration in your
        # swagger spec. It can be any string.
        "aud": audiences,
        # iss must match 'issuer' in the security configuration in your
        # swagger spec. It can be any string.
        "iss": issuer,
        # sub and email are mapped to the user id and email respectively.
        "sub": issuer,
        "email": "user@example.com",
    }

    signed_jwt = google.auth.jwt.encode(signer, payload)
    return signed_jwt


if __name__ == "__main__":
    parser = argparse.ArgumentParser(
        description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter
    )
    parser.add_argument("--file", help="The path to your service account json file.")
    parser.add_argument("--issuer", default="", help="issuer")
    parser.add_argument("--audiences", default="", help="audiences")

    args = parser.parse_args()

    signed_jwt = generate_jwt(args.file, args.issuer, args.audiences)
    print(signed_jwt.decode("utf-8"))

使用指令碼產生憑證:

  • 產生 JWT 憑證並將其指派給 $JWT_TOKEN 變數:

    JWT_TOKEN=$(python jwt_token_gen.py \
        --file=[SERVICE_ACCOUNT_FILE] \
        --audiences=[SERVICE_NAME] \
        --issuer=[SERVICE-ACCOUNT-ID])
    

    其中:

    • [SERVICE_ACCOUNT_FILE] 是已下載的消費端服務帳戶 JSON 金鑰檔案。
    • [SERVICE_NAME] 是 Bookstore 服務的名稱。當您將更新後的服務設定部署至 Endpoints 時,系統會傳回此名稱。
    • [SERVICE-ACCOUNT-ID] 是您產生服務帳戶時系統提供的完整消費端服務帳戶 ID。

發出已驗證的 gRPC 呼叫

最後一個步驟使用的是 bookstore_client.py,也就是教學課程使用的同一個用戶端。為了發出已驗證的呼叫,用戶端會在方法呼叫中以中繼資料的形式傳遞 JWT。

def run(
    host, port, api_key, auth_token, timeout, use_tls, servername_override, ca_path

執行範例:

  1. 使用 kubectl get services 取得已部署 Bookstore 的外部 IP 位址:

    #kubectl get services
    NAME                 CLUSTER-IP      EXTERNAL-IP      PORT(S)           AGE
    echo                 10.11.246.240   104.196.186.92   80/TCP            10d
    endpoints            10.11.243.168   104.196.210.50   80/TCP,8090/TCP   10d
    esp-grpc-bookstore   10.11.254.34    104.196.60.37    80/TCP            1d
    kubernetes           10.11.240.1     <none>           443/TCP           10d
    

    在這種情況下,這是外部 IP 為 104.196.60.37esp-grpc-bookstore 服務。

  2. 將 IP 位址指派給 EXTERNAL_IP 變數

    EXTERNAL_IP=104.196.60.37
    
  3. 列出 Bookstore 服務的所有書架:

    python bookstore_client.py --port=80 --host=$EXTERNAL_IP --auth_token=$JWT_TOKEN
    

    服務會傳回目前 Bookstore 中的所有書架。您可以不提供憑證或在產生 JWT 時指定錯誤的服務帳戶 ID,藉此進行檢查。指令應該會失敗。

後續步驟