【GCP】特定のGCSバケットに特定のユーザーしかアクセスできないようにする
やりたいこと
GCSバケットを何も設定しないで作成すると、プロジェクトのOwner、Editor、ViewerはGCSバケットのオブジェクトを参照することができてします。
さらに、Organization 横断ユーザ(例: セキュリティ監査チームやGSuites管理者)などもアクセスできてしまいます。
たとえば、機密情報を管理するバケットを作る場合など、許可した特定のユーザーのみを、特定のバケットにアクセスを許可する方法を調べました。
結論
- プロジェクトレベルのIAMで
storage.objects.get
の権限を付与しない- プロジェクトレベルのIAMとはここで設定する権限のこと
- https://console.cloud.google.com/iam-admin/iam
- GCSバケットのアクセス制御は
Uniform
モードで作成する - バケットの詳細ページでパーミッションを設定する
- 特定のユーザーだけにアクセス権限を与えるには、デフォルト作成されるOwner、Editor、Viewerに与えられる権限を削除する必要があります
- https://console.cloud.google.com/storage/browser/あなたのGCSバケット名;tab=permissions
解説
GCSのオブジェクトを参照するのに必要な権限は"storage.objects.get"
GCPコンソールからGCSバケット内にあるオブジェクトまでたどり着くためには、resourcemanager.projects.get
storage.buckets.list
storage.objects.list
など他にも必要な権限はありますが、実際にオブジェクトの中身をみるために必要な権限はresourcemanager.projects.get
だけです。
この権限を持ってるユーザーやサービスアカウントはオブジェクトをダウンロードできることになります。
プロジェクトレベルのIAMで"storage.objects.get"を付与しない
GCPのIAMの付与権限は、上位の権限を継承する仕組みなっています。リソースレベル
です。
あるユーザーが、リソースレベル
よりも上位の階層(プロジェクトレベル、組織レベルなど)で storage.objects.get
の権限を持っている場合、GCSのバケットやオブジェクトのPermission設定をどんなにがんばってもオブジェクトへ参照できるようになってしまいます。
プロジェクトOwner、Editor、Viewerは"storage.objects.get"の権限を持っていない
プロジェクトOwner、Editor、ViewerはGCSバケットのオブジェクトを参照することができるので、storage.objects.get
の権限を持っていそうなのですが、実はもっていません。
では、なぜGCSバケットの中身をみることができるのか?
それは、GCSバケットのデフォルトのPermissionがプロジェクトOwner、Editor、Viewerに対して、リソースレベル
でStorage Legacy Bucket Owner
とStorage Legacy Object Owner
のロールを付与しているからです。
実は"storage.objects.get"を持ってるロールに注意!
GCPには初めから定義されたロールがたくさんあります。Owner、Editor、Viewerなどもたくさん定義されたロールのうちのひとつです。
Owner、Editor、Viewerは storage.objects.get
の権限を持っていないのですが、他のロールでstorage.objects.get
の権限を持っているロールが50近くあります。
たとえば、 Firebase Viewerなどです。GCPコンソールのロールのページで、storage.objects.get
で検索すると、storage.objects.get
の権限をもったロールの一覧が見れるので、気になる方はぜひみてみてください。
Uniform
なの?
なんでアクセス制御はGCSのアクセス制御にはUniform
とFine-grained
の2種類があります。
Fine-grained
を使うと、GCSバケットとオブジェクトに対するアクセス権限を ACL
で制御することになります。
しかし、Googleはすでに Fine-grained
を非推奨 としていますし、ACLを使うとアクセス権限のコントロールがさらに複雑になります。
ACLについて書くと長くなってしまうので、この記事ではACLについて説明は割愛します。
Fine-grained
のGCSバケットをUniform
に変更することができますし、Uniform
してから90日間はFine-grained
に戻すことができます
Fine-grained
はいつ使えばいいの?
Fine-grained
を使う必要がある時は、以下の時だけです
- バケット単位ではなく、オブジェクト単位でアクセス権限をコントロールしたい
それ以外の時はUniform
にした方がよいですし、もし上に該当する場合は、もしかすると、設計そのものを見直して、バケット単位のアクセス権限で十分なようにした方がよいかもしれません。
ACLを使ってオブジェクト単位でアクセス権限のコントロールについては、この記事では割愛しますが、以下の記事が参考になると思います。
GCSバケットへのアクセス権限をもったユーザーの一覧って見れるの?
みれます!
GCPコンソールで特定のバケットのページへアクセスし、Permissionタブを開きます。
ここに表示されるユーザーやサービスアカウントなどが、今みているGCSバケットの権限をもつ全てです。
Inheritance
のところになにか値の入ってるものは、リソースレベル
よりも上位の階層で権限を持っていてるものになります。それらの権限はこのページからは変更することができませんので、上位の階層で権限を修正する必要があります。
このページで、GCSバケットにアクセスしてほしくないユーザー・サービスアカウントなどがある場合はまずいので、即座に対応しましょう。
たとえば、こちらの画像が僕が作ったGCS BucketのPermissionなのですが、以下の5種類のユーザーがこのGCSバケットへの権限を持っていることになります。持っている権限の詳細はRoleタブにかいてあります。
- このプロジェクトのOwner
- このプロジェクトのEditor
- このプロジェクトのViewer
- service-centerのサービスアカウント
- これは組織レベルの権限です
- GCPのセキュリティーセンターを有効した時に勝手に作成されました
- cloudrunのサービスアカウント
- これはプロジェクトレベルの権限です
- CloudRunを有効にした時に勝手に作成されました
セキュリティーセンターに関しては、セキュリティーをチェックしてもらいたいので仕方ないですが、CloudRunで勝手に作られたサービスアカウントは権限の調整をした方がよいかもしれませんね
実際にGCSバケットを作ってみる〜GCPコンソール編〜
バケットの作成
GCPコンソールへアクセスし、「CREATE BUCKET」からUniform
なバケットを作成してください。他の設定はなんでも大丈夫です。
Permissionタブを開く
ロールを追加したり削除したりする
自分の設定したようにロールを追加したり削除したりしましょう
ここで一つ注意なので、自分が操作するための権限を間違って消さないようにしてください。
消してしまうと、Permissionをいじることはできなくなり、GCSバケットを消すこともできなくなります。
もし消してしまった場合は、プロジェクトレベルで自分自身にStorage Admin
などのロールを付与してから改めてGCSバケットのPermissionをいじりましょう。(いじった後は自分につけたロールを消すのをお忘れなく)
実際にGCSバケットを作ってみる〜Terraform編〜
ここで紹介するコードはこちらになります
バケットの作成
resource "google_storage_bucket" "private_bucket_project_owner_and_one_user_can_access" {
name = "private_bucket_project_owner_and_one_user_can_access"
location = var.gcp_regions["tokyo"]
# アクセス制御をUniformモードにする
uniform_bucket_level_access = true
# このオプションをtrueにしておくと、バケット内にデータが残っていても削除してくれる
# 本番運用時はtrueにしない方がおそらくいいが、これはサンプルのためtrueにしておく
force_destroy = true
}
IAMポリシーの定義
今回は、プロジェクトのオーナーにroles/storage.admin
を、指定したユーザーにroles/storage.objectViewer
のロールをリソースレベルで付与してみます
data "google_iam_policy" "private_bucket_project_owner_and_one_user_can_access" {
binding {
# 定義済みのロール。保有する権限の詳細は以下
# https://cloud.google.com/storage/docs/access-control/iam-roles#standard-roles
role = "roles/storage.admin"
# ロールを付与する対象のユーザーやサービスアカウント
members = [
"projectOwner:${var.gcp_project_id}",
]
}
binding {
# 定義済みのロール。保有する権限の詳細は以下
# https://cloud.google.com/storage/docs/access-control/iam-roles#standard-roles
role = "roles/storage.objectViewer"
# ロールを付与する対象のユーザーやサービスアカウント
members = [
"user:${var.allowed_user_mail}",
]
}
}
IAMポリシーをバケットに適応する
resource "google_storage_bucket_iam_policy" "private_bucket_project_owner_and_one_user_can_access" {
bucket = google_storage_bucket.private_bucket_project_owner_and_one_user_can_access.name
policy_data = data.google_iam_policy.private_bucket_project_owner_and_one_user_can_access.policy_data
}
これでOKデス!
最後に
- GCSバケットの権限はめちゃ大事です!
- たとえば、terraform使ってパスワードなど管理する場合、tfstateファイルにパスワードがそのまま乗ることになります。アクセスできるユーザーは絶対に制御しましょう
- ついうっかり、プロジェクトレベルのIAMで
storage.objects.get
の権限を付与しちゃってるかもはあるあるな気がします!- GCSバケットのPermissionsでちゃんとアクセス可能なユーザーを確認しましょう
- GCSバケット以外でも、たとえばBigQueryのデータセットなどリソースレベルでアクセス権限を設定すべきものをついうっかりプロジェクトレベルでなんてこともあるあるかもしれません
- 今回は紹介してませんが、GCSバケットのアクセスログをとるのも非常に重要です!別記事で紹介しようかなと思います!
- 公式ドキュメントを読んで僕が理解したことをまとめましたが、間違っているかもしれません。間違っていたらご指摘いただけると大変嬉しいです!
Discussion