Google Cloud Storage(GCS)上の画像やJSにIP制限をかける
本記事はGCP(Google Cloud Platform) Advent Calendar 2022 2日目の記事です。
3日目: k8s × IAP @po3rin さん です。
TL;DR
- Edge Security PolicieでバックエンドバケットにもIP制限が可能に
- ただしGCSが公開状態だとそちらのURLにアクセスは出来てしまう
- 署名付きURLでプライベートなGCSでCLBからのアクセスが可能だが課題は残る
はじめに
クラウドを使っていても、社内向けシステムやテスト環境はインターネット上に公開したくないので、IP制限を掛けたいケースは良くありますよね?
GCPの場合、いくつか方法はありますがWebシステムの場合はCloud Load Balancing (CLB)とCloud Armorを組合わせてやるのが一般的だと思います。GCEに置いたシステムだろうとGKEだろうとCloud Runであろうとこの方法でIP制限を掛けることが出来ます。ただしGCSだけはIP制限を掛ける事が出来なかったので、静的コンテンツにIP制限を掛けることが難しい状態でした。やるならVMとかCloud Runを静的コンテンツサーバないしはProxyとして運用することになるのでちょっと微妙。
この辺は多くの人が苦労してるみたいで 「GCS IP制限」 とかで検索すると 「上手くいきませんでした!」 という記事がたくさん見つかります。しかし今年導入されたEdge Security Policieはバックエンド バケットにもIP制限を掛けれるという事で今回どこまで出来るのかを試してみました。
Cloud Armor - Edge Security Policieとは?
Cloud ArmorはGoogleのCLBに統合されたWeb向けのセキュリティ機能です。WAFやDDoS対策、IP制限など様々な機能を提供します。 セキュリティ ポリシーという形でどのような対応を入れていくのかを指定するのですが従来は今 「Backend security policy」 と呼ばれているものだけが存在しており、VMやCloudRunのようなバックエンド サービスに高度なセキュリティを適用する事が出来ました。新しく導入された 「Edge Security Policie」 ではCLBはもちろんCDNよりも手前で動作する事でバックエンド サービスとバックエンド バケット の両方にセキュリティを提供します。
ただし、Edgeで実行されている制限からかIP制限など基本的なセキュリティ機能のみが用途に応じて使い分ける必要があります。それぞれの機能の違いは以下の通り。
ref: https://cloud.google.com/armor/docs/security-policy-overview?hl=ja#policy-types
例えばアプリケーションなら従来通りWAFなども付く 「Backend security policy」 が良い気がしますし、今回のような静的コンテンツならIP制限くらいで十分なので 「Edge Security Policie」 で良いかと思います。
GCSで画像を公開する
まず、GCSバケットを作成し適当な画像を配置します。後で変更しますが、いったんIAMでallUsersにStorage オブジェクト閲覧者のロールを割り当てて一般公開します。これでhttps://storage.googleapis.com/{バケット名}/{オブジェクト名}
でだれでもアクセスできるようになります。
今回のようにIP制限も不要で、SPAなどのHTMLでは無く画像ファイルだけならこのURLのままでも大丈夫なケースも多いですね。
CLBとCloud ArmorでIP制限をかける
CLBの構築
CLBの作成をしていきます。フロントエンドはとりあえず設定が簡単なHTTPで進めていますが、実際に使うときにはHTTPSにしておくのが無難でしょう。今回はGCSがバックエンドなのでバックエンドバケットを作成し、CloudCDNを有効にします。Edge Security Policyは後ほど指定するので空欄で構いません。
これでしばらく待つとCLBの構築が完了します。以下のようにGCSではなくCLBのURLでアクセスする事が出来ました。
Edge Security Policieの適用
さて準備が整ったので本題のIP制限を掛けていきましょう。Cloud Armorを開いてポリシーの作成を行います。この際にポリシーのタイプで 「Edgeのセキュリティポリシー」 を選択します。合わせてTargetに先ほど作ったロードバランサの バックエンドバケット を選択します。デフォルトルールはとりあえず 「全アクセス拒否」 にしておきます。
保存してしばらく待った後に同じURLにアクセスしたら403になっていればIP制限が有効になったことが確認できます。
つづいてアクセス元が自分のIPだったら許可するようにルールを追加します。
これで自分のIPだけ許可されるようになりました。PCからはアクセス出来るけど、スマホでWiFiではなく4G/5Gでアクセスすると403エラーになるのを確認出来るかと思います。
署名付きURLでGCSをPublicではなくPrivateモードにする
Edgeのセキュリティポリシーの罠
よし! これで完璧だ!! っと思いきやこの状態では落とし穴があります。
というのもCLB経由のアクセスはIP制限がかかってるのですが、GCSを公開しているのでhttps://storage.googleapis.com/{バケット名}/{オブジェクト名}
はアクセス制限がかかってないので問題無くアクセス出来てしまいます。バケット名を推測されてしまったらアウトなのでこれは片手落ちですね。
これは中々悩みました。単純にallUserの閲覧権限を外せば良いかと最初思いましたが、それではCLBからも見えなくなり、通信エラーになります。CloudRunのように 「内部通信+CLBのみ許可」 という分かりやすいオプションがあれば良いのですが、ありません。IAMで指定しようにもCLBにはサービスアカウントが割り当てられてないし、VPC Service Controlsでも上手く制限出来ない用です。
色々調べていたらCloudCDNで鍵を発行して、その時に出来るCloudCDNのサービスアカウントをGCSのIAMに登録する事で、GCSをプライベートにしたままCloudCDNおよびCLBへのアクセスが可能になります。Edge Security Policieは前述した通りCDNに対してアクセス制限を掛ける事が出来るので、IP制限も問題無く動作します。
署名付きリクエストの鍵の構成と署名付きURLの生成
以下のドキュメントを参考にまずは署名付きリクエストの鍵を構成します。
まず、allUserのStorage オブジェクト閲覧者を削除します。これでGCSがプライベートになるのでCLBを使っても閲覧できない状態になったと思います。
つづいて、先ほど作ったCloudCDNを選択して署名鍵を追加します。生成された署名鍵の値はsign-url-key-file
とか適当なファイル名で保存しておいてください。
次にGCSに以下のCloudCDNのサービスアカウントを紐づけ、Storage オブジェクト閲覧者ロールにアサインします。
service-{PROJECT_NUM}@cloud-cdn-fill.iam.gserviceaccount.com
これで準備はOKなので署名付きURLをgcloudコマンドを使って生成します。
gcloud compute sign-url \
"http://${CLBのURL}/001.png" \
--key-name token-test \
--expires-in 30m \
--key-file sign-url-key-file
生成された署名付きURLを使うとGCSがプライベートのままでCLB経由のアクセスを確認できると思います。
まとめ
さてEdge Security Policieを使うことで念願のGCSへのIP制限を実現できました。
ただ、現時点ではGCS側のアクセスを禁止しつつCLBにIP制限をかけるには、署名付きURLにしないといけないので色々と不便...
せめてキャッシュに乗ってる間は署名無しでアクセスとか出来ればスケジューラで定期的に回すとかやりようもあるのですが、なんとも上手くいかないですね。詳しい人いたらコメント等で教えてください>< それでも、今までより大きく前身はしているので、GCPにはぜひCloudRunのようにGCSをプライベートにしつつCLBやCDNからのアクセスを簡単に許可出来るようになって欲しいです。
それではHappy Hacking!
参考
Discussion