Workload Identity連携でAWSからGoogle Cloudにセキュアにアクセスする
はじめに
GoogleCloudのWorkload Identityでの連携をAWSのLambdaを使用し、S3のファイルをGoogle CloudのCloud Storageにアップロードする方法を解説します。本記事では、Workload Identityを利用して、IAMロールとGoogle Cloudのサービスアカウントを紐付けることで、安全にアクセスできる環境を構築します。
lambdaではPythonを使用します。また、Terraformを使用します。
Workload Identityとは
Workload Identityとは、AWSや他のクラウドプロバイダーのIAMロールとGoogle Cloudのサービスアカウントを紐付けることで、シークレットキーを使わずにGoogle Cloudのリソースにアクセスできる仕組みです。
Workload Identity Poolとは
Workload Identity Poolは、外部のクラウドプロバイダーやIDプロバイダーからの認証情報を受け入れるためのリソースです。
Workload Identity Providerとは
Workload Identity Providerは、特定のクラウドプロバイダーのIDをWorkload Identity Poolに関連付けるためのリソースです。
処理の流れ
- LambdaがS3のファイルを取得する。
- Lambdaに紐づくIAMロールがWorkload IdentityによってGoogle Cloudのサービスアカウントの権限を借用する。
- LambdaがCloud Storageにファイルをアップロードする。
Workload Identityでは、AWSのIAMロールがGoogle Cloudのサービスアカウントの権限を借用することで、Google Cloudのサービスにアクセスできるようになります。
Terraformで構築していく
IAMロールとS3バケットの作成
LambdaにアタッチするIAMロールとS3バケットを作成します。
Lambdaの構築にはGoogle Cloudの設定が必要となるため、後で行います。
# IAM Role の作成
resource "aws_iam_role" "lambda_role" {
name = "lambda_execution_role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "lambda.amazonaws.com"
}
}]
})
}
# IAMポリシー(S3読み取り権限)
resource "aws_iam_policy" "s3_read_policy" {
name = "s3_read_policy"
description = "Allow Lambda to read from S3"
policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Action = ["s3:GetObject", "s3:ListBucket"]
Effect = "Allow"
Resource = [
aws_s3_bucket.lambda_bucket.arn,
"${aws_s3_bucket.lambda_bucket.arn}/*"
]
}]
})
}
# IAMポリシーをIAMロールにアタッチ
resource "aws_iam_role_policy_attachment" "lambda_s3_read" {
policy_arn = aws_iam_policy.s3_read_policy.arn
role = aws_iam_role.lambda_role.name
}
# Lambda 用のS3バケット作成
resource "aws_s3_bucket" "lambda_bucket" {
bucket = "test-bucket"
}
サービスアカウントとCloud Storageの作成
Google Cloud上でサービスアカウントとCloud Storageを作成し、サービスアカウントにCloud Storageへのアクセス権を付与します。
resource "google_service_account" "main" {
account_id = "gc-test-sa"
display_name = "gc-test-sa"
}
resource "google_storage_bucket" "main" {
name = "gc-test-storage"
location = "ASIA-NORTHEAST1"
force_destroy = true
public_access_prevention = "enforced"
storage_class = "REGIONAL"
}
resource "google_storage_bucket_iam_member" "main_viewer" {
bucket = google_storage_bucket.receiver.name
role = "roles/storage.objectViewer"
member = "serviceAccount:${google_service_account.main.email}"
}
resource "google_storage_bucket_iam_member" "main_creator" {
bucket = google_storage_bucket.receiver.name
role = "roles/storage.objectCreator"
member = "serviceAccount:${google_service_account.main.email}"
}
Workload Identityの作成
Workload IdentityプールとWorkload Identityプロバイダを作成します。
resource "google_iam_workload_identity_pool" "aws_pool" {
project = local.project_id
workload_identity_pool_id = "test-aws-pool"
display_name = "test-aws-pool"
description = "aws pool for test"
}
resource "google_iam_workload_identity_pool_provider" "aws_provider" {
workload_identity_pool_id = google_iam_workload_identity_pool.aws_pool.workload_identity_pool_id
workload_identity_pool_provider_id = "test-aws-provider"
display_name = "test-aws-provider"
description = "test-aws provider"
aws {
account_id = local.aws_id #AWSアカウントのID
}
}
Workload IdentityでサービスアカウントとIAMロールの紐付け
Workload IdentityでサービスアカウントとIAMロールの紐付けをします。
principalSetの設定を書くときには、以下のような形である必要があります。
principalSet://iam.googleapis.com/${WorkloadIdentity Poolの名前}/attribute.aws_role/arn:aws:sts::${AWSのアカウントID}:assumed-role/${IAMロール名}
以下の部分はIAMロールのARNではないことに注意してください。
arn:aws:sts::${AWSのアカウントID}:assumed-role/${IAMロール名}
resource "google_service_account_iam_binding" "aws" {
service_account_id = google_service_account.main.id
role = "roles/iam.workloadIdentityUser"
members = [ "principalSet://iam.googleapis.com/${google_iam_workload_identity_pool.aws_pool.name}/attribute.aws_role/arn:aws:sts::${local.aws_id}:assumed-role/${local.aws_role_name}"
]
}
構成ファイルのダウンロード
以下の記事を参考に、Google Cloudのコンソール、またはgcloudのコマンドからダウンロードしてください。
※ワークスペース間をまたぐと、構成ファイルのダウンロードはGoogle Cloudのコンソールではできなくて、gcloudのコマンドを使用してダウンロードする必要がある。
Lambdaの作成
フォルダー構成は以下のようになります。
root
┣━ lambda
┃ ┗━ src
┃ ┣━ test.py
┃ ┣━ GoogleCloudの構成ファイル.json
┃ ┗━ lambda.zip
┣━ main.tf
┣━ variables.tf
今回はPythonで実装しています。
lambda/src/test.py
がソースコードとなり、archive_file
リソースが自動でZIP化してくれます。
環境変数 GOOGLE_APPLICATION_CREDENTIALS
に Google Cloud の構成ファイルのパスを設定するのを忘れないでください。
# `lambda_function.py` をZIP化
data "archive_file" "lambda_zip" {
type = "zip"
source_dir = "lambda/src" # ローカルの `lambda/src` をZIP化
output_path = "lambda/src/lambda.zip"
}
# Lambda関数の作成
resource "aws_lambda_function" "my_lambda" {
function_name = "MyPythonLambda"
role = aws_iam_role.lambda_role.arn
runtime = "python3.9"
handler = "test.lambda_handler"
filename = data.archive_file.lambda_zip.output_path
source_code_hash = data.archive_file.lambda_zip.output_base64sha256
environment {
variables = {
GOOGLE_APPLICATION_CREDENTIALS = "構成ファイルのパス/構成ファイル名"
}
}
}
test.py
import boto3
from google.cloud import storage
def get_from_s3(s3_bucket_name, s3_object_name):
# S3クライアントの作成
s3 = boto3.client('s3')
s3_object_path = f"{s3_bucket_name}/{s3_object_name}"
tmp_file_path = f"/tmp/{s3_object_name}"
# ファイルを Lambda の一時領域にダウンロード
s3.download_file(s3_bucket_name, s3_object_name, tmp_file_path)
print(f"{s3_object_path} was downloaded to {tmp_file_path}.")
return tmp_file_path
def upload_to_gcs(tmp_file_path, gcs_bucket_name):
# Cloud Storage クライアントの作成
gcs = storage.Client()
file_name = tmp_file_path.split('/')[-1]
gcs_object_path = f"my_gcs_path/{file_name}"
bucket = gcs.bucket(gcs_bucket_name)
blob = bucket.blob(gcs_object_path)
# オブジェクトを Cloud Storage バケットにアップロード
blob.upload_from_filename(tmp_file_path)
print(f"{tmp_file_path} was uploaded to {gcs_bucket_name}/{gcs_object_path}.")
return None
def lambda_handler(event, context):
# event から各種情報を取得
s3_bucket_name = event['s3_bucket_name']
s3_object_name = event['s3_object_name']
gcs_bucket_name = event['gcs_bucket_name']
# オブジェクトを S3 から取得
tmp_file_path = get_from_s3(
s3_bucket_name=s3_bucket_name,
s3_object_name=s3_object_name
)
# オブジェクトを Cloud Storage にアップロード
upload_to_gcs(
tmp_file_path=tmp_file_path,
gcs_bucket_name=gcs_bucket_name
)
return {'statusCode': 200}
注意点
- Workload Identity を利用して AWS IAM ロールと Google Cloud のサービスアカウントを紐付ける際、プリンシパルセットの設定が必要です。その際、AWS の IAM ロールの ARN はそのまま使用できないため、適切な形式に変換する必要があります。
- Lambda の環境変数に
GOOGLE_APPLICATION_CREDENTIALS
を設定する必要があります。-
GOOGLE_APPLICATION_CREDENTIALS
は Google Cloud のデフォルトの認証 (ADC) で必要になります。 - 参考: Google Cloud Application Default Credentials
-
- ワークスペースをまたぐ場合、構成ファイルのダウンロードは Google Cloud コンソールでは行えません。
gcloud
コマンドを使用してダウンロードする必要があります。
感想
Google Cloudが初めてということもあり、概念の理解にかなり苦労しました。
今まではサービスアカウントでキーを発行する方法でやっていたのですが、今回のWorkload Idenitytによる認証でよりセキュアにすることができました。
不明点や間違い、感想ありましたらコメントいただけると幸いです。
参考記事
Discussion