この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
こんにちは、みかみです。
GCP & BigQuery 勉強中です。
実際に業務で使用するとなると、セキュリティの考慮は必須です。
普段業務で使っている AWS 環境では、セキュリティグループで IP 制限をかけ、許可されていない IP からのアクセスはできないようにしています。
GCP でも IP 制限かけられるの? どうやって? どの範囲で?
ということで。
やりたいこと
- GCP ではどうやって IP 制限するのか学びたい
- EC2 から BigQuery に IP 制限付きでアクセスしたい
GCP の IP 制限
AWS 同様 GCP でも VPC(VPC Service Controls)があり、Access Context Manager でアクセスレベルを設定できるそうです。
Access Context Manager では、IP アドレスやリージョン、デバイス単位でアクセスを制限できるとのこと。
こちらの記事が、非常に分かりやすかったです。
やってみた
GCP アカウントの権限設定
操作するアカウントに、以下2つの権限を付与してもらいました。
- Access Context Manager 管理者(roles/accesscontextmanager.policyAdmin)
- Resource Manager 組織閲覧者(roles/resourcemanager.organizationViewer)
VPC Service Controls も Access Context Manager も、プロジェクトより上位の「組織」に紐づくため、この2つの権限がないと、プロジェクトオーナーであってもアクセス制御を設定できません。
サービス境界の作成
ナビゲーションメニューの「セキュリティ」から「VPC Service Controls」をクリックし、サービス境界を作成します。
「新しい境界」リンクをクリックして、サービス境界を作成します。
「境界名」に任意の名前を入力し、「保護するプロジェクト」に自分のプロジェクトを追加。「保護するサービス」に「BigQuery API」と「Google Cloud Storage API」を追加して、「保存」をクリック。
無事サービス境界が作成できました。
ちなみに、Resource Manager 組織閲覧者(resourcemanager.organizationViewer) 権限がないと、「保存」ボタンクリックで下記エラーが表示されます。。(初め何のエラーか分かりませんでした。。。
サービス境界で保護設定した自分のプロジェクトを選択して、コンソールから BigQuery と GCS を参照しようとすると・・・
コンソールからのアクセスは、結局のところサービス境界外の local PC からのアクセスになるので、設定通り、参照できなくなりました。
同じプロジェクト内にあらかじめ作成しておいた GCE から BigQuery にアクセスしてみると・・・
mikami.yuki@test-cm-da-mikami:~$ bq ls
datasetId
-----------
test_s3
mikami.yuki@test-cm-da-mikami:~$ bq query --use_legacy_sql=false \
> 'SELECT
> name
> FROM
> `cm-da-mikami-yuki-258308`.test_s3.pref
> WHERE
> code = 1'
Waiting on bqjob_r5f3f0be91ace4063_0000016f12dca461_1 ... (0s) Current status: DONE
+------+
| name |
+------+
| 北海道 |
+------+
この GCE は サービス境界内のため、ちゃんとデータが参照できました。「保護するサービス」に GCE を設定しなくても、同じプロジェクト内ならサービス境界内に入るようです。
さらに、サービス境界外の EC2 からアクセスしようとすると・・・
[ec2-user@ip-172-31-31-170 ~]$ bq ls
BigQuery error in ls operation: VPC Service Controls: Request is prohibited by
organization's policy. vpcServiceControlsUniqueIdentifier: b9f2dd46d888e3ef.
ちゃんとアクセス拒否されています。
アクセスレベルを作成してサービス境界にアタッチ
続いてサービス境界に IP 制限を付与し、先ほどアクセスできなかった EC2 からのアクセスを許可してみます。
組織を選択した状態でナビゲーションメニュー「セキュリティ」の「Access Context Manager」画面で「新規」リンクをクリック。
「アクセスレベルのタイトル」に任意の名前を入力し、「条件」欄「属性を追加」から「IP サブネットワーク」を選択。許可する EC2 インスタンスのパブリック IP を入力したら「保存」をクリック。
アクセスレベルが作成できました。
作成したアクセスレベルを、サービス境界にアタッチします。
「VPC Service Controls」コンソールで初めに作成したサービス境界を選択し、編集画面でアクセスレベルを追加して「保存」。
先ほどアクセスエラーになった EC2 から、再度アクセスしてみます。
[ec2-user@ip-172-31-31-170 ~]$ bq ls
datasetId
-----------
test_s3
[ec2-user@ip-172-31-31-170 ~]$ bq ls test_s3
tableId Type Labels Time Partitioning Clustered Fields
--------- ------- -------- ------------------- ------------------
pref TABLE
[ec2-user@ip-172-31-31-170 ~]$ bq query --use_legacy_sql=false \
> 'SELECT
> name
> FROM
> `cm-da-mikami-yuki-258308`.test_s3.pref
> WHERE
> code = 1'
Waiting on bqjob_r4385587201489758_0000016f1336af95_1 ... (0s) Current status: DONE
+------+
| name |
+------+
| 北海道 |
+------+
無事、BigQueryが参照できるようになりました!
なお、初めは一部 bq コマンドでアクセスエラーが出たのですが、しばらく経ってからもう一度実行したら、無事データ参照できました。(設定後、少し待つ必要があるのかな?
アクセス許可していない IPからのアクセス(ローカルPCからコンソールでアクセス)は、拒否されたままです。
続いて EC2 で Python を実行して、GCS に格納済みのデータをロードしてみます。
ロードするデータはこちらからいただいてきた、アメリカの赤ちゃんの名前ファイルを.csv に変更したものです。
test_load.py
from google.cloud import bigquery
# データセット作成
client = bigquery.Client()
dataset_id = "{}.test_dataset".format(client.project)
dataset = bigquery.Dataset(dataset_id)
dataset.location = "asia-northeast1"
dataset = client.create_dataset(dataset)
print("Created dataset {}.{}".format(client.project, dataset.dataset_id))
# テーブル作成
table_id = "{}.{}.names_1880".format(client.project, dataset.dataset_id)
schema = [
bigquery.SchemaField("name", "STRING", mode="REQUIRED"),
bigquery.SchemaField("gender", "STRING", mode="REQUIRED"),
bigquery.SchemaField("count", "INTEGER", mode="REQUIRED"),
]
table = bigquery.Table(table_id, schema=schema)
table = client.create_table(table)
print(
"Created table {}.{}.{}".format(table.project, table.dataset_id, table.table_id)
)
# データロード
dataset_id = dataset_id.split(".")[-1]
table_id = table_id.split(".")[-1]
dataset_ref = client.dataset(dataset_id)
job_config = bigquery.LoadJobConfig()
job_config.schema = [
bigquery.SchemaField("name", "STRING", mode="REQUIRED"),
bigquery.SchemaField("gender", "STRING", mode="REQUIRED"),
bigquery.SchemaField("count", "INTEGER", mode="REQUIRED"),
]
# The source format defaults to CSV, so the line below is optional.
job_config.source_format = bigquery.SourceFormat.CSV
uri = "gs://test-cm-mikami/yob1880.csv"
load_job = client.load_table_from_uri(
uri, dataset_ref.table(table_id), job_config=job_config
) # API request
print("Starting job {}".format(load_job.job_id))
load_job.result() # Waits for table load to complete.
print("Job finished.")
destination_table = client.get_table(dataset_ref.table(table_id))
print("Loaded {} rows.".format(destination_table.num_rows))
実行してみると・・・
(test_bq) [ec2-user@ip-172-31-31-170 ~]$ python test_load.py
Created dataset cm-da-mikami-yuki-258308.test_dataset
Created table cm-da-mikami-yuki-258308.test_dataset.names_1880
Starting job d9d8fd70-e0ff-4fbd-a1fd-0534dc45950d
Job finished.
Loaded 2000 rows.
ロード完了。
確認してみます。
[ec2-user@ip-172-31-31-170 ~]$ bq ls
datasetId
--------------
test_dataset
test_s3
[ec2-user@ip-172-31-31-170 ~]$ bq ls test_dataset
tableId Type Labels Time Partitioning Clustered Fields
------------ ------- -------- ------------------- ------------------
names_1880 TABLE
[ec2-user@ip-172-31-31-170 ~]$ bq query --use_legacy_sql=false \
> 'SELECT
> name,
> gender
> FROM
> `cm-da-mikami-yuki-258308`.test_dataset.names_1880
> WHERE
> count = (SELECT max(count) FROM `cm-da-mikami-yuki-258308`.test_dataset.names_1880)'
Waiting on bqjob_r6573da5ad95dd040_0000016f15098a30_1 ... (0s) Current status: DONE
+------+--------+
| name | gender |
+------+--------+
| John | M |
+------+--------+
無事データがロードできています。
BigQuery へのアクセスに IP 制限をつけることができました。
まとめ(所感)
GCP では、以下の 2step で IP 制限が設定できます。
※組織やプロジェクト、制限するサービスは構築済みで、アカウントへの権限付与は実施済みの前提です。
- アクセスレベルの作成(Access Context Manager)
- サービス境界の作成(VPC Service Controls)
コンソールからプルダウン選択などで操作していくだけで完了なので、思っていたより簡単に設定できました。
また、サービス境界を設定すれば、外からのアクセスだけではなく、サービス境界外のリソースへのデータ出力もできなくなります。
特に個人情報など大事なデータを格納しているリソースには、サービス境界を設定しておけば、コンソールからのアクセスもできなくなるため、流出のリスクがかなり軽減されますね。