GCP - 从 API 网关访问 Google 云 运行 后端

GCP - access from API Gateway to Google Cloud Run backend

在我的 GCP 项目中,我在 docker 容器中有一个 python API 运行ning(使用连接)。我想使用 API 网关公开 API(使用 API 密钥)。

当我使用 --ingress internal 部署 docker 容器时,我在通过网关的 API 调用中得到 Access is forbidden.。所以API网关无法访问Google运行容器。 当我使用 --ingress all 时,一切都按预期工作,但是我的内部 API 可以从网络访问,这不是我想要的。

我为此创建了一个服务帐户:

gcloud iam service-accounts create $SERVICE_ACCOUNT_ID \
#  --description="the api gateway user" \
#  --display-name="api gateway user"

... 授予帐户 run.invoker 权限:

gcloud projects add-iam-policy-binding $PROJECT_ID \
  --role=roles/run.invoker --member \
  serviceAccount:$SERVICE_ACCOUNT_EMAIL

...并使用服务帐户创建 API 配置:

gcloud api-gateway api-configs create $CONFIG_ID \
  --api=$API_ID --openapi-spec=$API_DEFINITION \
  --project=$PROJECT_ID --backend-auth-service-account=$SERVICE_ACCOUNT_EMAIL

但我无法从 API 网关访问 docker API。我在这里错过了什么?如何保护我的 API,以便 API 网关可以在内部连接。

更新1: 还将角色应用到我的 运行 服务:

gcloud run services add-iam-policy-binding $SERVICE_ID \
  --region $REGION --member="serviceAccount:$SERVICE_ACCOUNT_EMAIL" \
  --role="roles/run.invoker"

更新2: John Hanley 要求的一些额外信息:

我的网关 yml 如下所示:

swagger: '2.0'

info:
  title: "title"
  description: "description"
  version: "0.1"

schemes:
- https

x-google-backend:
  address: <CLOUD_RUN_SERVICE_URL>

paths:
  /api:
    post:
      operationId: api
      consumes:
        - application/json
      produces:
        - application/json
      security:
        - api_key: []
      parameters:
        - in: body
          name: request
          description: request
          required: true
          schema:
            $ref: '#/definitions/Request'
      responses:
        200:
          description: "success"
        400:
          description: "bad data"
        503:
          description: "internal error"

definitions:
  Request:
    properties:
      parameter1:
        type: string
      parameter1:
        type: string
    required:  
      - parameter1

securityDefinitions:
  api_key:
    type: "apiKey"
    name: "key"
    in: "query"
gcloud api-gateway api-configs describe api-config --api api-api
createTime: '2021-06-12T15:02:27.382098034Z'
displayName: api-config
gatewayServiceAccount: projects/-/serviceAccounts/apigatewayuser@projectid.iam.gserviceaccount.com
name: projects/722514052893/locations/global/apis/api-api/configs/api-config
serviceConfigId: api-config-3hytlxf4gfvzj
state: ACTIVE
updateTime: '2021-06-12T15:05:09.778404414Z'
gcloud api-gateway gateways describe api-gateway --location europe-west1
apiConfig: projects/722514052893/locations/global/apis/api-api/configs/api-config
createTime: '2021-06-12T15:06:03.383002459Z'
defaultHostname: api-gateway-97x27n6l.ew.gateway.dev
displayName: api-gateway
name: projects/projectid/locations/europe-west1/gateways/api-gateway
state: ACTIVE
updateTime: '2021-06-12T15:07:37.590520122Z'
gcloud run services describe api --region europe-west1
✔ Service api in region europe-west1
 
URL:     https://api-o3rf5h4boa-ew.a.run.app
Ingress: internal
Traffic:
  100% LATEST (currently api-00010-lig)
 
Last updated on 2021-06-12T17:42:49.913232Z by myemail@gmail.com:
  Revision api-00010-lig
  Image:         gcr.io/projectid/api
  Port:          8080
  Memory:        512Mi
  CPU:           1000m
  Concurrency:   80
  Max Instances: 100
  Timeout:       300s

尝试直接在云上调试 运行:

gcloud iam service-accounts keys create $KEY_FILE --iam-account=$SERVICE_ACCOUNT_EMAIL
gcloud auth activate-service-account $SERVICE_ACCOUNT_EMAIL --key-file $KEY_FILE
BEARER=$(gcloud auth print-identity-token $SERVICE_ACCOUNT_EMAIL)


curl --header "Content-Type: application/json" \
  --header "Authorization: bearer $BEARER" \
  --request POST \
  --data '{"parameter1":"somedata"}' \
  $SERVICE_URL/api

结果还是一个Forbidden:

<!DOCTYPE html>
<html lang=en>
  <meta charset=utf-8>
  <meta name=viewport content="initial-scale=1, minimum-scale=1, width=device-width">
  <title>Error 403 (Forbidden)!!1</title>
  <style>
    *{margin:0;padding:0}html,code{font:15px/22px arial,sans-serif}html{background:#fff;color:#222;padding:15px}body{margin:7% auto 0;max-width:390px;min-height:180px;padding:30px 0 15px}* > body{background:url(//www.google.com/images/errors/robot.png) 100% 5px no-repeat;padding-right:205px}p{margin:11px 0 22px;overflow:hidden}ins{color:#777;text-decoration:none}a img{border:0}@media screen and (max-width:772px){body{background:none;margin-top:0;max-width:none;padding-right:0}}#logo{background:url(//www.google.com/images/branding/googlelogo/1x/googlelogo_color_150x54dp.png) no-repeat;margin-left:-5px}@media only screen and (min-resolution:192dpi){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat 0% 0%/100% 100%;-moz-border-image:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) 0}}@media only screen and (-webkit-min-device-pixel-ratio:2){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat;-webkit-background-size:100% 100%}}#logo{display:inline-block;height:54px;width:150px}
  </style>
  <a href=//www.google.com/><span id=logo aria-label=Google></span></a>
  <p><b>403.</b> <ins>That’s an error.</ins>
  <p>Access is forbidden.  <ins>That’s all we know.</ins>

所以问题出在服务帐户无法访问云 运行 应用程序。我不确定为什么这不起作用,因为 run.invoker 角色已添加到 运行 服务。

Ingress internal 表示“仅接受来自项目的 VPC 或 VPC SC 边界的请求”。

当您使用 API 网关时,您不在 VPC 中,它是无服务器的,它在 Google 云托管 VPC 中。因此,您的查询被禁止。

并且由于 API 网关无法插入 VPC 连接器(目前),因此无法将请求路由到您的 VPC,因此您不能使用此 ingress=internal 模式。


因此,解决方案是为所有人设置一个入口,这不是问题,因为您只授权合法帐户访问它。

为此,请检查 Cloud 运行 服务是否已在您的项目中向所有用户授予 roles/run.invoker。

  • 如果是,删除它

然后,创建一个服务帐户并授予它 roles/run.invoker on the Cloud 运行 服务。

关注这个documentation

  • 第 4 步:更新您的 OpenAPI 规范文件中的 x-google-后端,以便在您调用 Cloud 运行(这是基本服务)时添加正确的身份验证受众URL)
  • 第 5 步:使用后端服务帐户创建网关;设置您之前创建的服务帐户

最后,只有经过身份验证和授权的帐户才能访问您的云 运行 服务

所有未经授权的访问都被 Google 前端过滤并在到达您的服务之前被丢弃。因此,您的服务不会被无偿调用,因此您无需支付任何费用!

只有 API 网关(以及您在云 运行 服务上允许的潜在其他帐户)可以调用云 运行 服务。

那么,好吧,你的 URL 是 public,可以从野外互联网访问,但受到 Google 前端和 IAM 的保护。