Cloud RunにIP制限をかける

# 概要
Cloud Run に Clour Armor を使用してIP制限をかけます。
ユースケースとして、開発中のサーバーに対して社内のIPアドレスのみを許可したいなどを想定しています。

# 前提条件
今回の設定には事前にドメインを取得する必要があります。

# アーキテクチャ
Cloud Load Balancing を使用して、 Clour Run と Cloud Armor を紐付け、IP制限をかけます。

![cloudrun_ip_restriction.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336552/29c80ca3-48a3-c7d4-31c8-cdc1fdaaba4b.png)

# 手順
以下の流れで設定を行なっていきます。

1. Cloud Run のデプロイ
2. Cloud Load Balancing の設定
3. Cloud Armor の設定
4. 動作確認

# Cloud Run のデプロイ
まず始めに Cloud Run をデプロイします。
デプロイするコンテナは何でもいいですが、今回は Python + Fast API で簡単なAPIサーバを立てます。

## Dockerコンテナの作成

まずはAPIのコードを実装していきます。 `main.py` を作成します。

```python:main.py
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def read_root():
    return {"Hello": "World"}
```

続いて、Cloud Run にデプロイするための Dockerfile を作ります。

```Dockerfile:Dockerfile
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.7

COPY . /app
```

Cloud Run にデプロイする前に、一度ローカルで起動してみましょう。

```bash
docker build -t myapi .
docker run -p "80:80" myapi
```

無事にサーバーが起動すれば `http://localhost` にアクセスできるはずです。

![localhost.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336552/d8da394b-ca2f-bb86-dbb7-9d9a6cfeaa8f.png)

## 作成したコンテナをデプロイ
続いて、作成したDockerコンテナを Cloud Run にデプロイしていきます。
デプロイに当たっては Cloud Build を利用します。

始めに Cloud Build の構成ファイルを作成します。
ここで `--ingress internal-and-cloud-load-balancing` を指定し、内部または Cloud Load Balancing のみからのアクセスを許可します。これを指定しないと、外部ネットワークから Cloud Load Balancing を通さずにアクセスできてしまいます。
また、その他の認証をかけない場合は `--allow-unauthenticated` も付与します。

```yaml:cloudbuild.yaml
steps:
  # Build the container image
  - name: "gcr.io/cloud-builders/docker"
    args: ["build", "-t", "gcr.io/$_PROJECT_ID/$_SERVICE_NAME", "."]
  # Push the container image to Container Registry
  - name: "gcr.io/cloud-builders/docker"
    args: ["push", "gcr.io/$_PROJECT_ID/$_SERVICE_NAME"]
  # Deploy container image to Cloud Run
  - name: "gcr.io/google.com/cloudsdktool/cloud-sdk"
    entrypoint: gcloud
    args:
      - "run"
      - "deploy"
      - "$_SERVICE_NAME"
      - "--image"
      - "gcr.io/$_PROJECT_ID/$_SERVICE_NAME"
      - "--region"
      - "$_REGION"
      - "--ingress"
      - "internal-and-cloud-load-balancing" # LBからのアクセスのみを許可
      - "--allow-unauthenticated" # ユーザ認証はかけない場合
substitutions:
  _PROJECT_ID: <YOUR PROJECT ID>
  _SERVICE_NAME: myapi
  _REGION: asia-northeast1
images:
  - "gcr.io/$_PROJECT_ID/$_SERVICE_NAME"
```

構成ファイルができたら、 Cloud Run にデプロイします。

```bash
gcloud builds submit \
    --project=<YOUR PROJECT ID> \
    --substitutions=_PROJECT_ID=<YOUR PROJECT ID>
```

コンソール画面を開くと以下のようにデプロイされているはずです。

![cloudrun_console.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336552/9376a398-3668-8c39-2f78-4058589cc1b9.png)

更に詳細画面に進むとURLが表示されます。

![cloudrun_console_detail.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336552/7cebe7e6-132c-fce1-ebd0-d2a80f840664.png)

こちらを開こうとすると、 Cloud Load Balancing 経由ではないため、以下のように弾かれます。

![cloudrun_rejected.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336552/8bc72bcc-24fc-c37a-9346-5438a901f853.png)

# Cloud Load Balancing の設定
アクセスを行えるよう、 Cloud Load Balancing を設定しましょう。
設定項目も多く、複雑なので、コンソールベースで設定していきます。

ロードバランサの作成画面を開き、「HTTP(S) ロード バランシング」を選択します。

続いてインターネット接続または内部専用では「インターネットから VM またはサーバーレス サービスへ」、
グローバル / リージョンでは「グローバル HTTP(S) ロードバランサ」のどちらかを選択します。
[「リージョン HTTP(S) ロードバランサ」では Cloud Armor を適用することができない](https://cloud.google.com/load-balancing/docs/features?hl=ja#security)ためです。

![lb_configure.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336552/91027713-caf0-7381-d7be-82af39673046.png)

HTTP(S) ロードバランサの設定画面が開いたら、画面左上にロードバランサの名前をつけます。
ここでは 「cloudrun-lb」 とつけました。

続いてフロントエンドの構成を行います。
フロントエンドの名前をつけ (ここでは 「cloudrun-lb-frontend」 と設定)、
プロトコルにはHTTPSを選択、証明書を新規作成します。

 ![lb_frontend.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336552/3fd00c56-c0e1-9a19-5e4d-bc6b4532a228.png)

次に証明書の名前をつけ (ここでは「cloudrun-lb-cert」と設定)、
作成モードには「Google マネージドの証明書を作成する」を選択しました。
ドメインには事前に取得したドメインを設定します。

![lb_cert.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336552/9f8e32ba-8334-5eaa-a766-2054c4ee8608.png)

フロントエンドが設定できたら、バックエンドの構成を行います。
新たにバックエンドサービスを作成し、名前をつけます。(ここでは「cloudrun-lb-backend」を設定)
バックエンドタイプは「サーバレス ネットワーク エンドポイント グループ」を選び、
ネットワークエンドポイントグループは新規に作成します。
なお、Cloud CDNはデフォルトで有効になっておりますが、有料のようなので、不要であればチェックを外します。

![lb_backend.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336552/7a292319-5c7b-50a9-077b-cda81cf6e980.png)

ネットワーク エンドポイント グループでは適当な名前をつけ(ここでは「myapi-neg」)、
種類に Cloud Run を選択し、先ほどデプロイしたサービスを選択します。

![lb_neg.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336552/4ef31a3d-723d-64ea-06e2-57fcf16df4eb.png)

バックエンドの構成ができると以下のようになります。

![lb_backend2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336552/94a4bc23-0955-a78b-142c-58b4b5383cd1.png)

パスによって複数のバックエンド (Cloud Run) に振り分けたい場合はルーティング ルールを設定します。

![lb_routing.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336552/d723b7e6-3288-9167-630d-7b9dd2ac3ae0.png)

「作成」ボタンを押すとロードバランサが作成されます。
ロードバランサが作成されたら、詳細画面を開き、振られているIPアドレスを確認します。
このIPアドレスを証明書作成時に指定したドメインに設定します。

![lb_detail.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336552/37955b76-0ed0-e8cc-5702-2f3e118e5e0c.png)

以下はDNS設定画面です。(お持ちのドメインで利用しているDNSサーバーによって変わります。)

![dns_server.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336552/04d3a4b4-706b-3192-20f3-d054256c1b67.png)

DNSの設定と証明書のプロビジョニングまで30分〜1日程度の時間がかかります。
証明書のプロビジョニングが完了すると、証明書の詳細画面が以下のようになります。

![lb_cert2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336552/ef7a3a2f-918e-17d2-c737-ecd0098d4828.png)

プロビジョニングが完了したら、設定したドメインにアクセスしてみましょう。
localhostと同じ画面が開くはずです。

![api.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336552/12f1aa1d-0f20-133e-fc50-9e466af74c41.png)

この時点ではまだIP制限を行なっていないので、誰でもアクセスできる状態です。

# Cloud Armor の設定
それではいよいよ本来の目的である、 Cloud Armor によるIPアドレス制限を行なっていきます。
まずはじめにポリシーの名前を設定し(ここでは「cloudrun-policy」を設定)、
デフォルトのルールアクションは「許可しない」とします。

![armor_policy_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336552/95754a2c-bd08-dfcc-b72a-44289bc45905.png)

続いて許可したいIPアドレスを設定し、アクションは「許可」にします。
IPアドレスはカンマ区切りで複数設定できます。
優先度は低い値にします。
もし現在のIPアドレスを確認したい場合は `curl inet-ip.info` で確認できます。

![armor_policy_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336552/9cca301f-9fc2-4f0b-3b52-c17e61453146.png)

最後に先ほど Cloud Load Balancing で作成したバックエンドサービスと紐付けます。

![armor_policy_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336552/c311ee69-fed0-f43d-3c0c-96e3f4857d5c.png)

画面右の概要欄を確認し、意図通りの設定ができていることを確認したら、ポリシーを作成します。

![armor_policy_summary.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336552/0e7e277a-a879-fa0f-58e7-9525b132a395.png)

Cloud Armorのポリシー作成が完了したら、先ほどのドメインアドレスを再度開いてみましょう。
許可したIPアドレスからであれば問題なく開けるはずです。

![api.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336552/12f1aa1d-0f20-133e-fc50-9e466af74c41.png)

しかし、許可したIPアドレス以外のマシンからアクセスすると、以下のように開けなくなっているはずです。

![rejected_ip.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/336552/a3fbc111-cec2-41a6-7e4e-05992e6a0421.png)

IPアドレス制限が適用されていることが確認できたら、設定完了です。

# 終わりに
Cloud Run に対して Cloud Load Balancing と Cloud Armor を使用したIP制限を行いました。
今回は触れていないですが、他にも Cloud Run にアカウント認証をかけたい場合も [Identity-Aware Proxy](https://cloud.google.com/iap/docs/enabling-cloud-run?hl=ja#configuring_to_limit_access) と Cloud Load Balancing と組み合わせる必要があります。
Cloud Load Balancing は負荷分散だけでなく、セキュリティなどのネットワーク制御を行いたい場合も重要な役目を担うようです。

なお、[Cloud Load Balancingは時間あたりで料金が発生する](https://cloud.google.com/vpc/network-pricing?hl=ja#lb)ため、不要になったら削除してください。
今回の検証で行った設定では約100円/日かかるかと思います。

本記事がどなたかの参考になれば幸いです。

# 参考
- [Cloud RunへのアクセスにIP制限をかける](https://zenn.dev/koz/scraps/3dfccfc92a7a0b)
- [Dockerを利用したデプロイ - FastAPI](https://fastapi.tiangolo.com/ja/deployment/docker/)
- [Cloud Build を使用した Cloud Run へのデプロイ](https://cloud.google.com/build/docs/deploying-builds/deploy-cloud-run?hl=ja)
- [Cloud Run での上り（内向き）の制限](https://cloud.google.com/run/docs/securing/ingress?hl=ja)
- [Cloud Run で公開（未認証）アクセスを許可する](https://cloud.google.com/run/docs/authenticating/public?hl=ja)