こんにちは、開発チームの菅野です。
弊社ではアプリケーションにAWS、データ基盤にGoogle Gloud BigQueryを利用していて、 両者でデータをやり取りする場合に認証が必要になります。
その際、AWS IAM Role とサービスアカウントの間でWorkload Identity Poolによる連携を行い、 恒常的なキーを利用することなくセキュアに認証を行っていきたいので、その取り組みを紹介していきます。
前提
- ECS上で稼働するRailsアプリケーション
- RDSとBigQueryの間でデータのやりとりをしたい
- Terraformによる構成で管理
キーレス認証の重要性
まず長期的なキーを作成することなく認証を行いたいという前提があります。 もし恒常的なキーをアプリケーションに設定した場合、このキーが長期的な侵害リスクを抱えてしまうことになります。
タイトルの通り、IAM UserではなくIAM Roleを利用しているのもそのためです。
ベストプラクティスに従い、RailsからはアタッチされたECSタスクロールが sts:AssumeRole
することによってAWSの各種リソースにアクセスが可能になります。
同様に、AWSとGoogle Cloudの連携についてもキーレスでセキュアなアクセスを志向したいので、 Workload Identity Pool (WIP)連携を使う方向で取り組んでいきます。
実装
直接関係のないリソースについては割愛しますが、 Terraformの構成については以下のようになります。
AWS側の設定
まずはAWS ECSタスクロールに必要な権限を付与します:
resource "aws_iam_role" "ecs_task_role" { name = "${var.name_snake}_${var.env}_fargate_ecs_task_role" assume_role_policy = data.aws_iam_policy_document.ecs_task_role.json } data "aws_iam_policy_document" "ecs_task_role" { statement { actions = ["sts:AssumeRole"] principals { type = "Service" identifiers = [ "ecs-tasks.amazonaws.com", # その他必要なサービス ] } } } resource "aws_iam_role_policy" "role_inline" { # 必要な権限を付与するポリシー }
Google Cloud側の設定
Google Cloudのサービスアカウントを作成し、必要な権限を付与します:
resource "google_service_account" "app_user" { account_id = "app-user" display_name = "app-user" } resource "google_project_iam_member" "bigquery_job_user" { project = var.project_id role = "roles/bigquery.jobUser" member = "serviceAccount:${google_service_account.app_user.email}" } # 他の必要な権限
Workload Identity Poolの設定
AWSとGoogle Cloudで連携するためのWorkload Identity Poolを設定します: ここでは大元になるAWSとのWIPと、それを利用するProviderの構成が必要になります。
これにより、AWSが信頼できるアクセス元とみなされ、 GoogleのサービスアカウントとECSタスクロールとの接続が許可されます。
resource "google_iam_workload_identity_pool" "aws_pool" { workload_identity_pool_id = "aws-pool" } resource "google_iam_workload_identity_pool_provider" "aws_app_provider" { workload_identity_pool_id = google_iam_workload_identity_pool.aws_pool.workload_identity_pool_id workload_identity_pool_provider_id = "aws-app-provider" aws { account_id = var.aws_account_id } } resource "google_service_account_iam_binding" "workload_identity_binding" { service_account_id = google_service_account.app_user.name role = "roles/iam.workloadIdentityUser" members = [ "principalSet://iam.googleapis.com/projects/${var.project_number}/locations/global/workloadIdentityPools/${google_iam_workload_identity_pool.aws_pool.workload_identity_pool_id}/attribute.aws_role/arn:aws:sts::${var.aws_account_id}:assumed-role/${aws_iam_role.ecs_task_role.name}/*" ] }
アプリケーションへの設定
次にアプリケーションへの設定手順です。 通常のサービスアカウントのキーとは違い、認証に関する構成情報のJSONをアプリケーションに設定していきます。 (認証情報ではなく、あくまで認証に関する構成情報なのでこれはgit管理に乗せることが可能です)
構成情報はコンソール上からダウンロードするか、以下のようにgcloudで作成します。
gcloud iam workload-identity-pools create-cred-config \ "projects/[PROJECT_NUMBER]/locations/global/workloadIdentityPools/[WIP_ID]/providers/[PROVIDER_ID]" \ --service-account="[SERVICE_ACCOUNT_EMAIL]" \ --aws --output-file="sample.json"
あとは通常のサービスアカウントキーのjsonファイルと同様に、このjsonをアプリケーションの認証設定に適用すれば準備は完了です。
これでAWS ECS上で稼働するRailsアプリケーションから、ECSタスクロールを通じてAWSの各種リソースに、 またECSタスクロールからサービスアカウントを通じてGoogle Cloudの各種リソースに対しても恒常的なキーを介さずにアクセスができるようになりました。
余談
このようにクラウド上でのWIP連携によるAWS - Google間のアクセスを行ってきましたが、 実はローカル環境での疎通がうまくいっていません。
ローカル環境ではもともと awslabs/amazon-ecs-local-container-endpoints を利用して タスクロールの検証やクラウドリソースとの疎通を行っていましたが、WIP連携の認証構成情報とうまく噛み合わず、 また両クラウドのRuby SDKの内部的な実装のギャップも相まって技術的にまだ解決できていない課題となってます。
こちらはもしかしたら ecs-local-container-endpoints ではなく IAM Roles Anywhare を利用した方がスムーズかもしれません。