未認証を許可してインターネットに公開した Cloud Run、外部アクセス来ない説
こんにちは、クラウドエースの吉崎です。
フルマネージドのコンテナ実行基盤である Cloud Run は、サービス作成時に URL が自動で生成され、簡単にインターネットに公開できることをご存知でしょうか。
簡単に公開できる反面、インターネットに公開するので、初めてデプロイしたときは「あ、やばいインターネットから大量のアクセスが来てしまう」と思いました。
しかし、それは本当でしょうか?
本稿では、Cloud Run をインターネットに公開して「本当にインターネットから大量のアクセスが来てしまうのか」を検証します。
前置き
用語
- サービス
Cloud Run のメインリソース。
サービスの中にリビジョン(≒ バージョン)というリソースがあり、リビジョンごとにコンテナインスタンスが生成される。
Ingress や認証の設定はリビジョンにではなく、サービスに対して行う。 - Ingress
インターネット →Cloud Run へのトラフィック。
画像にあるように 3 つのオプションから選択します。
インターネットからの直接の通信を想定しているのであれば、「すべてのトラフィックを許可する」にします。
- 認証
画像にあるように 2 つのオプションから選択します。
公開する場合は「未認証の呼び出しを許可」にします。
準備
以下の Cloud Run サービスを 100 個デプロイします。
同時実行 | 最大インスタンス数 | 最小インスタンス数 | CPU 上限 | メモリ上限 | Ingress | 認証 |
---|---|---|---|---|---|---|
100 | 1 | 0 | 1 | 128MiB | すべてのトラフィックを許可する | 未認証の呼び出しを許可 |
デプロイするアプリケーションは、 公式ドキュメントに記載されている Go の Hello World サービス です。
検証方法
用意した 100 個の Cloud Run サービスをインターネットに一週間公開したままにします。
一週間後、Cloud Monitoring にてリクエスト数を観測し、リクエストが来たかどうかを確認します。
画像の通り、大量の Cloud Run サービスを作成しました。
予想
インターネットからのアクセスは一つも発生しないと思います。
同僚 6 人に以下の問いを投げました。
未認証を許可する Cloud Run サービスを 100 個、一週間公開すると、外部アクセスは 1 つでも来ると思いますか?
結果、4 人が来そう、3 人が来なさそう、と答えました。面白いですね。
結果
インターネットからのアクセスは一度も発生しませんでした。
10/14 0:00~10/21:00におけるCloud Run のリクエスト数
※リクエストがなかったため、データポイントが存在していないことが表示されています。
考察
なぜインターネットからのアクセスは一度も発生しなかったのでしょうか。
2 つの要因が考えられます。
URL が容易に予測できないから
実は、Cloud Run の URL には生成ルール[1]があります。
Cloud Run サービスの URL は、https://[TAG---]SERVICE_IDENTIFIER.run.app の形式です。ここで、TAG はリクエストしているリビジョンのトラフィック タグを表し、SERVICE_IDENTIFIER は Cloud Run サービスの安定した一意の識別子です。SERVICE_IDENTIFIER は固定された形式ではないため、解析しないでください。SERVICE_IDENTIFIER 生成のロジックは変更される可能性があります。
今回はタグを使っていませんので、https://[TAG---]SERVICE_IDENTIFIER.run.app
の形式になります。
koi1
というサービスの URL は https://koi1-hqoygfnuxq-an.a.run.app
でした。
また、koi100
というサービスの URL は https://koi100-hqoygfnuxq-an.a.run.app
でした。
実は結果検証中に気づきましたが、SERVICE_IDENTIFIER
のサービス名以降の部分は全 100 サービスで共通でした。
https://[ルールに則った文字列].run.app
とはいえ、[ルールに則った文字列]はサービス名を除くと 16 文字あり、規則性はあるにしろ、ランダムに文字列を生成して一致することを期待するのは儚いです。
攻撃してもそれほど旨みがないから
一言で言ってしまうと、Google Cloud のマネージドのコンテナ実行基盤サービス Cloud Run を利用するリテラシがある開発者によってデプロイされたコンテナアプリケーションから攻撃の糸口を探すのはクラッカーにとってコスパが悪いだろうから、です。
Web サイトや API を狙った攻撃の対象が偶然 Cloud Run のサービスだったということはあるかもしれませんが、あえて Cloud Run の URL をスキャンして攻撃の糸口を探すというのは考えにくい、という考えです。
(標的型攻撃やサービス拒否攻撃であれば、まずは攻撃の対象を絞ることから始まるでしょう)
事後談
Cloud Run のセキュリティについて
今回は課金額を抑えられる設定をした状態でインターネットに公開しましたが、デフォルトの設定のまま公開し、EDoS攻撃を受けるといわゆるクラウド破産と言われる状態になりかねません。
Ingress と認証には以下のオプションがありますので、用途に応じてセキュリティを意識した設定をしてください。
- Ingress
- すべてのトラフィックを許可する
今回の設定。すべて許可する。 - 内部トラフィックと Cloud Load Balancing からのトラフィックを許可する
VPC内部のトラフィックとロードバランサ経由のトラフィックのみ許可 - 内部トラフィックのみを許可する
VPC内部のトラフィックのみ許可。マイクロサービスでサービス間通信に。
- すべてのトラフィックを許可する
- 認証
- 未認証の呼び出しを許可
今回の設定。認証不要。 - 認証が必要
Cloud IAM による認証が必要。マイクロサービスでサービス間通信に。
- 未認証の呼び出しを許可
また、組織のポリシーを使うことで Cloud Run サービスの上り・下りの通信の制限も可能です。
組織全体で誤った外部公開を防ぎたいときは、こちらが有効です。
課金について
今回の検証による課金は発生していません。
なぜなら、リクエストが一度も発生していないからです。
Cloud Run は、リクエストを処理する時間に応じて課金が発生しますが、今回はリクエストがありませんでした。
Cloud Run のコンテナイメージを格納するために Artifact Registry を利用していますが、こちらも 0.5 GB 未満のため無料です。
詳しくは料金ページをご覧ください。
ちなみに、Cloud Run にも無料枠がありますのでご覧ください。
100 個のサービスのデプロイ方法について
以下のシェルスクリプトを使ってまとめてデプロイしました。
デプロイ時は「リージョンどこ?」や「未認証を許可していいの?」と対話的に聞かれてしまうことを避けるため、デフォルトリージョンの設定と --quiet
オプションを使っています。
#!/bin/sh
# Artifact Registry API の有効化
gcloud services enable artifactregistry.googleapis.com
# Run のデフォルトリージョン設定
gcloud config set run/region asia-northeast1
# Cloud Runサービス(koi[n])を100デプロイする
for i in $(seq 1 100); do
gcloud run deploy "koi$i" \
--image "asia-northeast1-docker.pkg.dev/$PROJECT_ID/cloud-run-source-deploy/go" \
--concurrency=1 \
--cpu=1 \
--memory=128Mi \
--max-instances=1 \
--min-instances=0 \
--allow-unauthenticated \
--quiet
done
おわりに
簡単にインターネットに公開出来てしまうので、Cloud Run でアプリケーションをデプロイする際は注意しましょう。
-
URL 生成のロジックは変更される可能性があるため、予測可能な URL とは言い切れません。 ↩︎
Discussion