ホワイトヘルスケア - テックブログ

ホワイトヘルスケアは、日本のヘルスケア領域における社会課題に正面から向き合い、現実世界を生きる人々の不安や痛みといった見過ごされるべきでない問題に対して本質的な解決に取り組むことで、持続的な医療システム・社会の実現を目指します。

AWS IAM RoleとGoogleサービスアカウントのWIP連携によるキーレス認証

こんにちは、開発チームの菅野です。

弊社ではアプリケーションにAWS、データ基盤にGoogle Gloud BigQueryを利用していて、 両者でデータをやり取りする場合に認証が必要になります。

その際、AWS IAM Role とサービスアカウントの間でWorkload Identity Poolによる連携を行い、 恒常的なキーを利用することなくセキュアに認証を行っていきたいので、その取り組みを紹介していきます。

前提

  • ECS上で稼働するRailsアプリケーション
  • RDSとBigQueryの間でデータのやりとりをしたい
  • Terraformによる構成で管理

キーレス認証の重要性

まず長期的なキーを作成することなく認証を行いたいという前提があります。 もし恒常的なキーをアプリケーションに設定した場合、このキーが長期的な侵害リスクを抱えてしまうことになります。

タイトルの通り、IAM UserではなくIAM Roleを利用しているのもそのためです。 ベストプラクティスに従い、RailsからはアタッチされたECSタスクロールが sts:AssumeRole することによってAWSの各種リソースにアクセスが可能になります。

docs.aws.amazon.com

同様に、AWSGoogle Cloudの連携についてもキーレスでセキュアなアクセスを志向したいので、 Workload Identity Pool (WIP)連携を使う方向で取り組んでいきます。

cloud.google.com

実装

直接関係のないリソースについては割愛しますが、 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の設定

AWSGoogle 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の内部的な実装のギャップも相まって技術的にまだ解決できていない課題となってます。

github.com

こちらはもしかしたら ecs-local-container-endpoints ではなく IAM Roles Anywhare を利用した方がスムーズかもしれません。

aws.amazon.com