はじめに
本記事はQualiArts Advent Calendar 2020 14日目の記事です。
今回は、Google Cloud Platform (GCP)のオブジェクトストレージであるGoogle Cloud Storage (GCS)に対して、同じくGCPのサービスであるVPC Servive Controls (VPC-SC)とAccess Context Manager (ACM)でIP制限をかける構成の紹介と、その構成をTerraformで管理する方法についてまとめます。
なお、Access Context Managerの一般的に広まっている略語を見つけられなかったため、本記事では勝手にACMとしています。(AWS Certificate Managerと被ってしまいますがご了承ください。)
前提知識
VPC Servive Controls
https://cloud.google.com/vpc-service-controls/docs/overview
Access Context Manager
https://cloud.google.com/access-context-manager/docs/overview
組織リソース
https://cloud.google.com/resource-manager/docs/cloud-platform-resource-hierarchy#organizations
いずれも公式ドキュメントに詳しくまとめられています。
また、本記事で出てくるVPC-SC、ACM関連の用語は下記に簡単にまとめられているので軽く目を通しておくと良いかもしれません。
https://cloud.google.com/vpc-service-controls/docs/overview#terminology
構成
VPC-SCでプロジェクトのGCSを含むサービス境界を作成します。
また、そのサービス境界に特定のIPアドレスのみを許可するアクセスレベル(ACMで作成)を設定し、1部のクライアントはアクセスできる状態にします。
VPC-SCの制約でバケット毎に制限をかけるかけることはできないので、プロジェクトの全てのバケットがサービス境界に含まれ、アクセスが制限されます。
実践
GCPプロジェクトの準備
VPC-SCとACMを使用するためにはプロジェクトとは別に、そのプロジェクトが所属する組織リソースが必要となります。組織リソースの作成方法は本記事では解説しないので公式ドキュメントなどを参考にお願いします。
Terraform用のサービスアカウントの準備
自分のローカル環境からterraformコマンドを実行する場合、通常であればgcloud auth application-default login
などで生成したエンドユーザーのcredentialファイルを認証に使用するかと思います。しかし、ACMのAPIではエンドユーザーのcredentialによる認証がサポートされていないため、ACM関連のresourceを使おうとすると下記のようなエラーが発生します。
$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.
google_access_context_manager_access_policy.access-policy: Refreshing state... [id=${your AccessPolicy id}]
Error: Error when reading or editing AccessContextManagerAccessPolicy "${your AccessPolicy id}": googleapi: Error 403: Your application has authenticated using end user credentials from the Google Cloud SDK or Google Cloud Shell which are not supported by the accesscontextmanager.googleapis.com. We recommend configuring the billing/quota_project setting in gcloud or using a service account through the auth/impersonate_service_account setting. For more information about service accounts and how to use them in your application, see https://cloud.google.com/docs/authentication/.
Details:
[
{
"@type": "type.googleapis.com/google.rpc.ErrorInfo",
"domain": "googleapis.com",
"metadata": {
"consumer": "projects/${your project number}",
"service": "accesscontextmanager.googleapis.com"
},
"reason": "SERVICE_DISABLED"
}
]
そこで、必要な権限を持ったサービスアカウントを作成し、そのサービスアカウントのcredentialファイルで認証するようにします。
必要な権限は以下の1つです。
- Access Context Manager 管理者 (組織レベル)
ここで注意しなければならないのは、Access Context Manager 管理者の権限を組織リソースのIAMで割り当てる必要があることです。また、組織リソースではサービスアカウントの管理ができないため、いずれかのプロジェクトで作成したサービスアカウントを組織リソースのIAMに追加して権限を割り当てることになります。特に理由がなければ制限をかける予定のプロジェクトでサービスアカウントを管理するのが丸いかと思います。
サービスアカウントを作成して権限を付与したら、キーをjson形式でDLしてterraformで使用するように環境変数を設定します。
$ export GOOGLE_CLOUD_KEYFILE_JSON=${your_credential_file_path}
ちなみに、GCP Consoleで操作する場合は加えて下記の権限を自分のIAMに付与しておく必要があります。
- Access Context Manager 管理者 (組織レベル)
- Resource Manager 組織閲覧者
アクセスレベルの作成
まずはACMのアクセスレベルを作成します。
アクセスレベルにはアクセスを許可する条件を設定します。
ソースコードは下記の通りです。
provider "google" {
project = "${your project id}"
region = "${your project default region}"
version = "~> 3.43"
}
resource "google_access_context_manager_access_policy" "access_policy" {
parent = "organizations/${your organization id}"
title = "default policy"
}
resource "google_access_context_manager_access_level" "access_level" {
parent = "accessPolicies/${google_access_context_manager_access_policy.access_policy.name}"
name = "accessPolicies/${google_access_context_manager_access_policy.access_policy.name}/accessLevels/${your access level name}"
title = "${your title}"
basic {
conditions {
ip_subnetworks = [
"${your ip range}"
]
}
}
}
google_access_context_manager_access_levelがアクセスレベルを管理するresourceです。
アクセスレベルのnameには他のアクセスレベルのnameと重複しないような文字列を設定する必要があります。
また、特殊文字( _ や - )も使用できないので注意が必要です。
google_access_context_manager_access_levelのbasicブロックにアクセスを許可する条件を記述します。
今回は特定のIPアドレスからのアクセスを許可したいのでip_subnetworksを条件として使います。
${your ip range}の箇所に許可したいIPアドレス範囲を指定します。
terraform apply
を実行するとconsole上でアクセスレベルが作成されているのを確認できると思います。
サービス境界の作成
次にVPC-SCのサービス境界を作成してGCSにアクセス制限をかけます。
なお、GCSにアクセス制限をかける関係上、tfstateの保管先に同じプロジェクトのGCSバケットを指定してしまうとterraformコマンド実行時にアクセス制限に引っかかる場合があるので注意が必要です。
resource "google_access_context_manager_service_perimeter" "service_perimeter" {
parent = "accessPolicies/${google_access_context_manager_access_policy.access_policy.name}"
name = "accessPolicies/${google_access_context_manager_access_policy.access_policy.name}/servicePerimeters/${your service perimeter name}"
title = "${your title}"
status {
resources = [
"projects/${your project number}"
]
restricted_services = [
"storage.googleapis.com"
]
access_levels = [
google_access_context_manager_access_level.access_level.name,
]
}
}
statusブロックのaccess_levelsで先ほど許可するIP範囲を設定したアクセスレベルを指定します。
複数のアクセスレベルを指定した場合は、いずれかのアクセスレベルを満たしていればアクセスを許可されます。
また、restricted_servicesにも複数のサービスを指定することが可能です。
GCS以外にも様々なGCPサービスに対してアクセス制限をかけることができます。
terraform apply
を実行するとconsole上でサービス境界が作成されているのを確認できると思います。
アクセス確認
では、実際にアクセスを制限できているか確認してみましょう。
GCSにバケットを作成し、適当なファイルを一般公開状態で配置してそのファイルを閲覧できるか試してみます。
バケットの作成、ファイルの配置は許可したIPアドレスで行うように注意してください。
許可したIPアドレス以外ではconsole上でもアクセス拒否されてしまいます。
許可したIPアドレスでアクセスすると以下の通りです。
次に、IPアドレスを許可されてないものに切り替えアクセスします。
拒否されていることが確認できました。
なお、ドキュメントには記載がありませんが、VPC-SCの設定が反映されるまでに最大15分の遅延が発生する場合がある1そうなので、applyしたのにアクセスが制限されてない方は少し待ってみると良いと思います。
おわりに
VPC-SCとACMを駆使することでGCSにIP制限をかけることができました。
サービスアカウントの権限周りなど、いくつかハマりポイントはあるものの、シンプルな設定で気軽にアクセス制限をかけられるサービスだと思います。
GCSバケット毎に境界を設定できるともっと使い勝手がよくなるのですが...
明日はtmanaさんの記事です!
-
GCPのサポートに問合せを行いました。 ↩