Cloud Runで動く自分用APIを保護するため、Cloud Endpointsを手順を参考にデプロイしてみた
CloudRunで作ったAPIが保護できるだけでなく、APIの実行状況監視(実行回数、レスポンスタイム、エラー回数)もCloudEndpointsの画面でできるようになってよき
この手順は用意されたスクリプトでESPv2コンテナをビルドし、
手動でCloud Runにデプロイしなければならないので、
これをGitHubActionsで自動化した
要はスクリプトの中身でやっていることをGitHub Actionsにした
openapiの中身には触れない
gcloudのコマンドも一通り打てる環境になっていることが前提
自分のAPIサーバコンテナをCloudRunにデプロイ
保護したいAPIエンドポイントを持つサーバコンテナをCloudRunにデプロイする
払い出されたエンドポイントURLは後ほど用いる
--no-allow-unauthenticated
オプションをつけて未認証の呼び出しを許可しない設定とすること
ESPv2コンテナをCloudRunにデプロイ
手順を参考にESPv2コンテナをとりあえずデプロイ
これがゲートウェイになり、APIを保護する
払い出されたエンドポイントURLのホスト名は後ほど用いる
Cloud Endpointsをデプロイ
Cloud EndpointsをopenapiのドキュメントをデプロイするGitHubActionsを準備する
下記コマンドを打てば良い
gcloud endpoints services deploy openapi.yaml --project ${GCP_PROJECT}
openapi.yaml
openapi.yamlの中身は以下
【ENDPOINTS_HOST】にはESPv2コンテナをCloudRunにデプロイで払い出されたURLのホスト名、
【BACKEND_ADDRESS】には自分のAPIサーバコンテナをCloudRunにデプロイで払い出されたエンドポイントURLを指定
【apiの定義】にはopenapiの書式でAPIの仕様を記載する
securityDefinitionsとsecurityを下記の通り定義するとAPIキーによる保護ができるようになる
swagger: '2.0'
info:
title: [サービス名]
description: description
version: 1.0.0
host: 【ENDPOINTS_HOST】
schemes:
- https
produces:
- application/json
x-google-backend:
address: 【BACKEND_ADDRESS】
protocol: h2
security:
- api_key: []
paths:
【apiの定義】
securityDefinitions:
api_key:
type: "apiKey"
name: "key"
in: "query"
ESPv2コンテナ再ビルド/デプロイ
ESPv2コンテナに上記CloudEndpointsの設定を載せて再ビルドし、CloudRunに再デプロイする
スクリプトを確認するとESPv2にはCloudEndpointsの設定情報の入ったservice.json
が組み込まれていないといけない
この設定情報を取得するにはCloudEndpointsのConfigIDがいるので、[Cloud Endpointsをデプロイ](#Cloud Endpointsをデプロイ)でデプロイしたCloudEndpointsの最新ConfigIDをまず取得する
取得したConfigIDをもとにservice.json
を取得してESPv2コンテナに組み込んでビルド
できたコンテナをCloudRunにデプロイする
こちらは--allow-unauthenticated
オプションをつけて未認証の呼び出しを許可する
上記をやるGitHubActionsが以下
update-gateway-cloud-run:
runs-on: ubuntu-latest
needs: update-cloud-endpoints
steps:
- name: Checkout the repository
uses: actions/checkout@v1
- name: GCP Authenticate
uses: GoogleCloudPlatform/github-actions/setup-gcloud@master
with:
version: '290.0.1'
service_account_key: ${{ secrets.GCP_CI_SA_KEY }}
project_id: ${{ secrets.GCP_PROJECT }}
- name: get latest config id
run: |-
echo "::set-env name=CONFIG_ID::$(gcloud endpoints configs list \
--service=${ENDPOINTS_HOST} \
--sort-by='~CONFIG_ID' \
--limit=1 \
| tail -1 | cut -d' ' -f1)"
- name: get service.json
run: |-
curl --fail -o "service.json" -H "Authorization: Bearer $(gcloud auth print-access-token)" \
"https://servicemanagement.googleapis.com/v1/services/${ENDPOINTS_HOST}/configs/${CONFIG_ID}?view=FULL"
- name: Build a docker image
run:
docker build -t asia.gcr.io/${GCP_PROJECT}/${GATEWAY_NAME}:${GITHUB_SHA::7} -f endpoints/Dockerfile .
- name: Configure docker to use the gcloud cli
run: gcloud auth configure-docker --quiet
- name: Push the docker image
run:
docker push asia.gcr.io/${GCP_PROJECT}/${GATEWAY_NAME}:${GITHUB_SHA::7}
- name: deploy
run: |-
gcloud run deploy "${GATEWAY_NAME}" \
--quiet \
--allow-unauthenticated \
--region "${GCP_REGION}" \
--image "asia.gcr.io/${GCP_PROJECT}/${GATEWAY_NAME}:${GITHUB_SHA::7}" \
--project "${GCP_PROJECT}" \
--platform "managed" \
--service-account "${GCP_SA_EMAIL}"
Dockerfileはスクリプトで作っているものをそのままファイル化
必要権限の付与
作成されたCloudEndpointsはGCP上でサービスとして管理される(gcloud services list
ででてくる)
このサービスに対する下記権限を、ESPv2のCloudRunが動作するサービスアカウントに付与する必要がある
過不足あるかも
- roles/servicemanagement.serviceController
gcloud endpoints services add-iam-policy-binding $(SERVICE_NAME) --member=serviceAccount:$(GCP_SA_EMAIL) --role=roles/servicemanagement.serviceController
- roles/servicemanagement.configEditor
gcloud endpoints services add-iam-policy-binding $(SERVICE_NAME) --member=serviceAccount:$(GCP_SA_EMAIL) --role=roles/servicemanagement.configEditor
またESPv2CloudRunのサービスアカウントに自分のAPIサーバコンテナCloudRunの起動元権限を付与する必要がある
gcloud run services add-iam-policy-binding 自分のAPIサーバコンテナCloudRun名 \
--member "serviceAccount:$(GCP_SA_EMAIL)" \
--role "roles/run.invoker" \
--platform managed \
--project $(GCP_PROJECT) \
--region $(GCP_REGION)
APIキーの発行
APIキーはGCP上で管理される
ESPv2CloudRunが動作するGCPプロジェクトで「APIとサービス」-「認証情報」から「認証情報の作成」で作成できる
キーを制限することで、APIキーを利用できるサービスを作成したCloudEndpointsのサービスに限定したり、
APIキー利用元(ソースIPアドレス等)を制限できる
APIアクセス
発行されたAPIキーを?key=APIキー
の形で付与して、ESPv2コンテナ CloudRunのエンドポイントに対してAPIを呼び出すと成功
つけないと失敗する
$ curl -s -o /dev/null -w "%{http_code}" https://[CloudRunエンドポイント]/api/health?key=[APIキー]
200
$ curl -s -o /dev/null -w "%{http_code}" https://[CloudRunエンドポイント]/api/health
401
CloudEndpoints
APIの情報がCloudEndpointsの画面で見れるようになる
まとめ
CloudRunのエンドポイント保護はCloudEndpoints + ESPv2で Google Cloud Api Gatewayが出てきてオワコンかも
ESPv2のCloud Runを建てなくてもいいっぽいのでこっちに移行したいと考えている
詳しいコードは実際作ったやつ参照