JSON Web 令牌身份验证

Vector Search 支持使用自签名 JSON Web 令牌 (JWT) 进行身份验证的索引端点。 为了控制对索引端点的访问,它配置为仅接受由明确授权的 Google 服务账号颁发的已签名 JWT。 这意味着只有使用这些指定账号的客户端才能与端点进行交互。

本页面概述了使用 JSON Web 令牌 (JWT) 身份验证设置索引端点并对其运行查询所需的步骤。

限制

  • 只有具备 VPC 对等互连Private Service Connect (PSC) 的专用端点才支持 JWT 身份验证。
  • 只有使用 gRPC 调用的数据平面 RPC API(例如 MatchService)才支持 JWT 身份验证。本页面中的 RPC 示例使用开源 grpc_cli 工具将 gRPC 请求发送到所部署的索引服务器。
  • 用于创建、部署和管理索引的 Admin API 使用预定义 IAM 角色进行保护。

创建索引并使用 JWT 进行查询

请按照以下步骤创建索引端点,并使用自签名 JWT 查询该端点。

创建索引

按照创建索引中的说明创建 Vector Search 索引。

创建专用端点

按照以下某个文档页面中的说明创建专用端点:

创建服务账号

创建服务账号并向其授予 Service Account Token Creator IAM 角色。

  1. 启用 IAM Service Account Credentials API 并创建服务账号:

    gcloud services enable iamcredentials.googleapis.com --project="PROJECT_ID"
    gcloud iam service-accounts create SERVICE_ACCOUNT_ID --project="PROJECT_ID"
    

    替换以下值:

    • PROJECT_ID:要在其中创建服务账号的项目。
    • SERVICE_ACCOUNT_ID:服务账号的 ID。

    详细了解如何创建服务账号

  2. 使用以下命令之一向您的服务账号授予 iam.serviceAccountTokenCreator IAM 角色:

    • 以下命令授权您使用来自关联了服务账号的 Compute Engine 虚拟机的服务账号创建 JWT:

      gcloud iam service-accounts add-iam-policy-binding \
         "SERVICE_ACCOUNT_ID@PROJECT_ID.iam.gserviceaccount.com" \
         --role "roles/iam.serviceAccountTokenCreator" \
         --member "serviceAccount:SERVICE_ACCOUNT_ID@PROJECT_ID.iam.gserviceaccount.com" \
         --project "PROJECT_ID"
      

      替换以下值:

      • SERVICE_ACCOUNT_ID:服务账号的 ID。
      • PROJECT_ID:要在其中创建服务账号的项目。
    • 以下命令授权您使用来自您自己的 Google 账号(在您的工作站上)的服务账号创建 JWT:

      gcloud iam service-accounts add-iam-policy-binding \
         "SERVICE_ACCOUNT_ID@PROJECT_ID.iam.gserviceaccount.com" \
         --role "roles/iam.serviceAccountTokenCreator" \
         --member "user:EMAIL_ADDRESS" \
         --project PROJECT_ID
      

      替换以下值:

      • SERVICE_ACCOUNT_ID:服务账号的 ID。
      • PROJECT_ID:要在其中创建服务账号的项目。
      • EMAIL_ADDRESS:您的电子邮件地址。

使用 JWT 身份验证配置将索引部署到端点

  1. 将索引部署到专用端点,如以下示例所示:

    gcloud ai index-endpoints deploy-index INDEX_ENDPOINT_ID \
       --index=INDEX_ID \
       --deployed-index-id=DEPLOYED_INDEX_ID \
       --display-name=DEPLOYED_INDEX_NAME \
       --audiences=AUDIENCES \
       --allowed-issuers="SERVICE_ACCOUNT_ID@PROJECT_ID.iam.gserviceaccount.com" \
       --project=PROJECT_ID \
       --region=LOCATION
    

    替换以下值:

    • INDEX_ENDPOINT_ID:索引端点的 ID。
    • INDEX_ID:索引的 ID。
    • DEPLOYED_INDEX_ID:用户指定的字符串,用于唯一标识已部署的索引。必须以字母开头,并且只包含字母、数字或下划线。如需了解格式准则,请参阅 DeployedIndex.id
    • DEPLOYED_INDEX_NAME:已部署索引的显示名称。
    • AUDIENCES:用于标识服务、工作负载或应用的预期目标对象的描述性字符串,例如 "123456-my-app"
    • SERVICE_ACCOUNT_ID:服务账号的 ID。
    • PROJECT_ID:您的 Google Cloud 项目 ID
    • LOCATION:您在其中使用 Vertex AI 的区域。

使用自签名 JWT 查询索引

概括来讲,所需步骤如下:

  1. 创建 JWT 载荷。
  2. 使用先前创建的服务账号对令牌签名。
  3. 使用 gRPC 调用查询索引(在授权标头中传递令牌)。

Python

创建和签署 JWT 载荷

此示例使用 Python IAM API 凭据库的 sign_jwt 方法获取已签名令牌。如需详细了解如何安装和使用此库,请参阅 IAM API 客户端库文档

   from google.cloud import iam_credentials_v1
   from datetime import datetime, timezone
   import json

   def sign_jwt(issuer: str, audience: str):
      client = iam_credentials_v1.IAMCredentialsClient()
      payload = {
            'aud': audience,
            'sub': audience,
            'iss': issuer,
            'iat': int(datetime.now(timezone.utc).timestamp()),
            'exp': int(datetime.now(timezone.utc).timestamp()) + 600,
      }
      response = client.sign_jwt(name="projects/-/serviceAccounts/" + issuer,
                                 payload=json.dumps(payload))
      return response.signed_jwt

   sign_jwt("SERVICE_ACCOUNT_ID@PROJECT_ID.iam.gserviceaccount.com",
            "AUDIENCES")

命令行

创建 JWT 载荷

Vector Search 身份验证接受使用预先授权的服务账号签名的 JWT 以用于预定义的目标对象。服务账号和目标对象必须在索引部署到专用端点时,由调用者指定。 使用这些设置部署索引后,发送到该端点的所有 gRPC API 请求都需要包含一个授权标头,其中包含由颁发者(服务账号)签名并针对所提供的目标对象的 JWT。已签名的 JWT 在 gRPC 请求的 authorization 标头中以不记名令牌形式进行传递。除了由服务账号签名之外,JWT 还必须包含以下声明:

  • iss(允许的颁发者)声明应该是服务账号电子邮件地址,例如:

    "iss": "SERVICE_ACCOUNT_ID@PROJECT_ID.iam.gserviceaccount.com"
    
  • aud(目标对象)和 sub(主题)声明应设置为相同的值。这是用于标识服务、工作负载或应用的预期目标对象的描述性字符串,例如:

    "aud": "123456-my-app",
    "sub": "123456-my-app"
    

    此值必须与在索引部署时传递的 --audiences 参数匹配。

  • iat(颁发时间)声明应设置为颁发令牌的时间。exp(到期时间)声明应设置为随后的较短时间(大约一小时)。这些值以 Unix 纪元时间表示,例如:

    "iat": 1698966927, // unix time since epoch eg via date +%s
    "exp": 1698967527 // iat + a few mins (eg 600 seconds)
    

以下示例显示单个 JWT 载荷中的这些声明:

{
   "iss": "SERVICE_ACCOUNT_ID@PROJECT_ID.iam.gserviceaccount.com",
   "aud": "123456-my-app",
   "sub": "123456-my-app",
   "iat": 1698956084,
   "exp": 1698960084
}

JWT 载荷使用 iss 声明中指定的服务账号进行签名。

创建 JWT

  1. 确保您(调用者)可以对服务账号使用 roles/iam.serviceAccountTokenCreator 角色。

  2. 创建名为 jwt_in.json 且包含原始 JWT 的 JSON 文件:

    SA="serviceAccount:SERVICE_ACCOUNT_ID@PROJECT_ID.iam.gserviceaccount.com"
    cat << EOF > jwt_in.json
    {
      "aud": "AUDIENCES",
      "sub": "AUDIENCES",
      "iss": "${SA}",
      "iat": $(date +%s),
      "exp": $(expr $(date +%s) + 600)
    }
    EOF
    

    替换以下值:

    • SERVICE_ACCOUNT_ID:服务账号的 ID。
    • PROJECT_ID:您的 Google Cloud 项目 ID
    • AUDIENCES:用于标识服务、工作负载或应用的预期目标对象的描述性字符串,例如 "123456-my-app"

对 JWT 签名 (REST API)

  1. 使用 jq 工具,通过将 JWT 编码为字符串来创建 curl 请求载荷:

    cat jwt_in.json | jq -Rsa >request.json
    
  2. 通过将请求载荷传递给 signJwt REST API 方法来对令牌签名。

    SA="serviceAccount:SERVICE_ACCOUNT_ID@PROJECT_ID.iam.gserviceaccount.com"
    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/$SA:signJwt"
    

    替换以下值:

    • SERVICE_ACCOUNT_ID:服务账号的 ID。
    • PROJECT_ID:您的 Google Cloud 项目 ID

    将返回的 signedJwt 值存储到名为 signedJwt 的环境变量中。

对 JWT 签名 (gcloud CLI)

或者,您可以通过将 jwt_in.json 文件直接传递给 gcloud CLI sign-jwt 方法来对 JWT 签名。

gcloud iam service-accounts sign-jwt jwt_in.json jwt_out \
   --iam-account=SERVICE_ACCOUNT_ID@PROJECT_ID.iam.gserviceaccount.com

替换以下值:

  • SERVICE_ACCOUNT_ID:服务账号的 ID。
  • PROJECT_ID:您的 Google Cloud 项目 ID

已签名的 JWT 会在 jwt_out 输出文件中返回。将其存储到名为 signedJwt 的环境变量中。

将已签名的 JWT 发送到索引端点

Python

如需了解如何安装或更新 Vertex AI SDK for Python,请参阅安装 Vertex AI SDK for Python。 如需了解详情,请参阅 Python API 参考文档

def vector_search_match_jwt(
    project: str,
    location: str,
    index_endpoint_name: str,
    deployed_index_id: str,
    queries: List[List[float]],
    num_neighbors: int,
    signed_jwt: str,
) -> List[List[aiplatform.matching_engine.matching_engine_index_endpoint.MatchNeighbor]]:
    """Query the vector search index.

    Args:
        project (str): Required. Project ID
        location (str): Required. The region name
        index_endpoint_name (str): Required. Index endpoint to run the query
        against. The endpoint must be a private endpoint.
        deployed_index_id (str): Required. The ID of the DeployedIndex to run
        the queries against.
        queries (List[List[float]]): Required. A list of queries. Each query is
        a list of floats, representing a single embedding.
        num_neighbors (int): Required. The number of neighbors to return.
        signed_jwt (str): Required. The signed JWT token for the private
        endpoint. The endpoint must be configured to accept tokens from JWT's
        issuer and encoded audience.

    Returns:
        List[List[aiplatform.matching_engine.matching_engine_index_endpoint.MatchNeighbor]] - A list of nearest neighbors for each query.
    """
    # Initialize the Vertex AI client
    aiplatform.init(project=project, location=location)

    # Create the index endpoint instance from an existing endpoint.
    my_index_endpoint = aiplatform.MatchingEngineIndexEndpoint(
        index_endpoint_name=index_endpoint_name
    )

    # Query the index endpoint for matches.
    resp = my_index_endpoint.match(
        deployed_index_id=deployed_index_id,
        queries=queries,
        num_neighbors=num_neighbors,
        signed_jwt=signed_jwt,
    )
    return resp

命令行

从同一 VPC 网络中的 Compute Engine 虚拟机调用 MatchService gRPC 端点(在 authorization 标头中传递 signedJwt 令牌),如以下示例所示:

./grpc_cli call ${TARGET_IP}:10000 google.cloud.aiplatform.container.v1.MatchService.Match \
   '{deployed_index_id: "${DEPLOYED_INDEX_ID}", float_val: [-0.1,..]}' \
   --metadata "authorization: Bearer $signedJwt"

如需运行此命令,您需要设置以下环境变量:

  • TARGET_IP 是所部署的索引服务器的 IP 地址。如需了解如何检索此值,请参阅查询索引以获取最近邻
  • DEPLOYED_INDEX_ID:用户指定的字符串,用于唯一标识已部署的索引。必须以字母开头,并且只包含字母、数字或下划线。如需了解格式准则,请参阅 DeployedIndex.id

signedJwt 是包含已签名 JWT 的环境变量。

问题排查

下表列出了一些常见 gRPC 错误消息。

gRPC 错误消息 原因
找不到用于索引“INDEX_ID”的授权标头 gRPC 元数据不包含授权标头
JWT 格式无效 令牌格式不正确,无法正确解析
JWT 身份验证失败 令牌已过期或者并非由正确的服务账号签名
JWT 颁发者应处于允许的颁发者列表中 令牌 iss 不在 auth_config 允许的颁发者中
针对索引“INDEX_ID”的权限检查失败 令牌 audsub 声明不在 auth_config 目标对象中

后续步骤