はじめに

突然ですが、AWS やオンプレミスから Google Cloud へアクセスする際、まだサービスアカウントキー(JSON)を利用して、gcloud コマンドを実行していますか?
それらを脱して意識高い系で接続していきたいということを解説した記事となります。

「え?まだ JSON キーとかパスワード管理してるの?」

ということで、今回は「意識の高さ」を極めるために、「Amazon EC2 から Google Cloud AlloyDB への接続」 を題材に、以下の構成を構築してみました。

  1. 脱JSONキー: Workload Identity 連携で AWS Role を利用した Google Cloud へのアクセス
  2. 脱平文: AlloyDB Auth Proxy を利用したパブリック接続と、VPN 経由の Private Service Connect (PSC) 接続の両方でのアクセス
  3. 脱パスワード: IAM 認証で DB ログインアクセス

検証中に数々の「ハマりポイント」に遭遇したので、その解決策も含めて、構築のステップを共有します。

今回の構成:目指すべき「意識高い」接続

目指すのは、Amazon EC2 から AlloyDB にセキュアに接続する構成です。
基本はプライベートで行いたいですが、状況に応じて叶わない場合、パブリック接続もあることから両パターンで確認しました。

全体像は以下のようになります。

ポイント:

  • 認証: AWS の IAM Role が、Google Cloud の Workload Identity を通じてサービスアカウントになりすまします。これにより EC2 上に サービスアカウントキーである JSON ファイルが不要になり、EC2 Role にて認証することが可能になります。
  • 暗号化通信: AlloyDB への接続は プライベート、パブリック両方での接続を試す都合上、プライベート接続では VPN を利用し PSC へアクセスしておりますが、、AlloyDB へは AlloyDB Auth Proxy を利用して、接続します。
  • DBログイン: AlloyDB へのログインには IAM データベース認証 を使用します。これにより、DB接続用のパスワード管理も不要 になります。

構築ステップの全体像

「で、結局何を作ればいいの?」という全体像を簡単に要約しますと、、

AWS 側では接続元の IAM Role を用意し、Google Cloud 側ではその Role を受け入れるための Workload Identity プールやプロバイダ、そして実際に AlloyDB にアクセスするサービスアカウントを作成していきます。

では、具体的なステップを見ていきましょう。

Step 1: Google Cloud 側の受け入れ準備 (Workload Identity)

よくある図だと、結局設定という観点で何をしたらいいか、が見えづらかったので、Workload Identity を自分の理解で図解するとこんなイメージです。

まずは、「AWS の特定の Role からのアクセスなら許可するよ」という受け入れ設定を行います。

作成するもの:

  • サービスアカウント (DB接続用)
  • Workload Identity プール
  • Workload Identity プロバイダ (AWS用)

今回のサービスアカウントに設定する権限は以下となります。

  • AlloyDB クライアント:AlloyDB への AlloyDB Auth Proxy への接続で利用するため
  • AlloyDB データベースユーザ:AlloyDB への IAM 認証で利用するため
  • Service Usage ユーザ:この権限が無いと PSC 利用時に接続できません
  • サービスアカウントトークン作成者:この権限が無いと借用ができません

そのサービスアカウントにアクセスできる権限として、以下のような設定を行います。

  • 対象プリンシパル:”principalSet://iam.googleapis.com/projects/プロジェクトナンバー/locations/global/workloadIdentityPools/Workload Identity プール名/*”
  • 権限:Workload Identity ユーザ

Workload Indentity プールでは以下のような情報を設定します。

  • ID プールを作成するための名前、説明を設定します。

続いてプロバイダの設定を行います。

  • プロバイダ名:設定するプロバイダがわかりやすい名前がいいかと思います
  • AWS アカウント ID:利用したいロールが存在する AWS アカウントに設定する
  • 属性のマッピング:google.subject = assertion.arn のようなマッピングを行う
  • 属性条件:assertion.arn.contains(‘:assumed-role/Role名/’)

上記の設定まで終わると、接続済みサービスアカウントのコンソール画面から、接続時に利用する構成ファイルをダウンロードすることが可能になりますので、ダウンロードしておきます。

ハマりポイント:Role制限の検証時点の解

EC2 から接続する場合、認証情報として送られてくる Role 名の末尾には、インスタンス ID などの動的な値が付与されます。これを IAM 側で「完全一致」で許可しようとすると、リストアなどでインスタンス ID が変わるたびに設定変更が必要になります。

検証の結果、「入り口(プロバイダ)で絞り、中(IAM)は全許可する」 という構成にすることで、Role 名の許可が実現できそうでした。

  1. プロバイダ設定:属性条件ではじく
    すでに記載した通り、属性マッピングは単純 (google.subject=assertion.arn) にしておき、属性条件 (Attribute Condition) で、「この Role 以外は認めない」という設定を入れます。これにより、許可していない Role からのアクセスは弾かれます。もしアクセス元 Role が増加するようであれば、こちらのメンテナンスが必要となります。
  2. IAM 設定:特定プール内で認証したものに対する許可はワイルドカードで行う
    プロバイダで Role 制限ができているので、サービスアカウントの IAM 権限設定では、このプールからのアクセスなら ワイルドカード (/*) で許可します。これでインスタンス ID が変わっても問題ありません。


Step 2: ネットワークとDNSの準備 (PSC & Cloud DNS)

次に、VPN 経由で AlloyDB に閉域接続するためのネットワーク設定を行います。

作成するもの:

  • Cloud VPN (AWSとの接続)
  • PSC エンドポイント (AlloyDB用)
  • Cloud DNS プライベートゾーン (alloydb-psc.goog.)
  • Cloud DNS サーバーポリシー
  • Route 53 Resolver 転送ルール

Cloud VPN 、Route 53 Resolver については今回に限った特別な点はないため割愛し、PSC について説明します。

PSC を利用することで既存の VPC 内にエンドポイントを設けることができ(赤枠箇所)、プライベート通信を行うことが可能となります。

PSC で必要なことは以下のとおりです。

  • 接続ポリシー:PSC で AlloyDB 用接続ポリシーを作成します、こちらの設定で AlloyDB のエンドポイントして利用したいサブネットを指定できます。
  • Cloud DNS プライベートゾーン:PSC で利用されるプライベートゾーンを作成します。
  • Cloud DNS サーバーポリシー:プライベートゾーンへの名前解決が行えるように、内部 DNS を作成します (AWSとのVPN通信でプライベート通信するときには必要になります)

Step 3: DB と IAM 認証の準備

AlloyDB インスタンスを作成し、IAM 認証でログインできるように設定します。

作成するもの:

  • AlloyDB クラスター・インスタンス
  • DB ユーザー (IAM認証用)

AlloyDB クラスター、インスタンスについて

  • IAMデータベースユーザの作成:デフォルトでは AlloyDB のコンソールを開いたユーザの IAM 名でユーザを作成できるようになっています、今回は IAM 認証を試すものの、サービスアカウントでのアクセスのため、別途コンソールから AlloyDB のデータベースのユーザを、Wokrload Identity 経由でアクセスするサービスアカウント名の IAM 認証ユーザとして作成します。
  • プライベート IP: PSA(Private Service Access) or PSC が選択でき、今回のプライベートアクセスは PSC となるため、PSC を選択します。事前にエンドポイント、ポリシーがある場合にはスムーズに進めそうです。
  • パブリック IP:今回パブリックアクセスも試す想定であるため、パブリック IP も有効にします。
  • ネットワークセキュリティ:今回は AlloyDB Auth Proxy 利用となるため、「AlloyDB コネクタを通じて mTLS を適用」を利用します。

Step 4: AWS側の準備と接続

最後に、AWS 側の設定を行い、実際に接続してみます。

作成するもの:

  • 接続元 EC2 の準備 (EC2用 Role、Google Cloud側で許可したもの)
  • Auth Proxy 設定ファイル (xxxx.json)

接続元 EC2 の準備:

  • Role:Google Cloud 側で許可した EC2 Role
  • gcloud コマンド:インストールします
  • Step1で作成した jsonファイル:こちらは漏洩などに繋がる情報は含まれていないファイルとなります。

ハマりポイント:IMDSv2 対応

最近の EC2 はセキュリティ強化でメタデータサービス v2 (IMDSv2) が必須になっています。Workload Identity がこれに対応できるよう、Auth Proxy 用の xxxx.json に一行追加するのを忘れずに。

"credential_source": {
  // ...
  "imdsv2_session_token_url": "http://169.254.169.254/latest/api/token" ##→追記箇所
}

では、接続します

まずパブリック経由で接続します。

# AlloyDB Auth Proxy を起動
./alloydb-auth-proxy \
  projects/プロジェクトID/locations/asia-northeast1/clusters/AlloyDBクラスタ名/instances/AlloyDBインスタンス名 \
  --credentials-file ./xxxx.json \
  --public-ip
# psql で接続
psql -h 127.0.0.1 -p 5432 -U postgres

次にプライベート(PSC)経由で接続します。

# AlloyDB Auth Proxy を起動
./alloydb-auth-proxy \
  projects/プロジェクトID/locations/asia-northeast1/clusters/AlloyDBクラスタ名/instances/AlloyDBインスタンス名 \
  --credentials-file ./xxxx.json \
  --psc
# psql で接続
psql -h 127.0.0.1 -p 5432 -U postgres

次にプライベート(PSC)経由且つ IAM 認証で接続します。
最大のポイントなのですが、サービスアカウントで接続する場合、以下の通り、サービスアカウント名@プロジェクトID.iam で止める必要があります。

# AlloyDB Auth Proxy を起動
./alloydb-auth-proxy \
  projects/プロジェクトID/locations/asia-northeast1/clusters/AlloyDBクラスタ名/instances/AlloyDBインスタンス名 \
  --credentials-file ./xxxx.json \
  --auto-iam-authn \
  --psc
# psql で接続
psql -h 127.0.0.1 -p 5432 -U sa-name@project-id.iam

補足

Workload Identity の切り分けにおいてかなり重要なのが、ログを出力するように監査ログ設定を行い、必要に応じてうまくいかない時に確認しましょう。どの AWS 側の Role でこちらに接続しにきているか、を確認するために必要であり、そこが詰まるポイントになるかなと思います。設定としては以下が必要です。

  • Identity and Access Management(IAM)API の管理読み取りを有効化
  • Security Token Service API の管理読み取りを有効化

以下NGだったときの例ですが、このようにどのRoleから来ているか、がわかります。

まとめ

AWS 側からの Role と Google Cloud のサービスアカウントの関連付けということで、大規模な設定が必要と思いましたが、割とあっさり設定できてしまいました。今回、検証として行った内容となるため、もう少し権限を絞る方法などは模索してみたいと思います。また、好き放題 Google Cloud 側で Workload Identity をプロジェクト単位で作成されてしまうと管理が大変なことになりそうなので、ドキュメントにも記載がある通り、管理用のプロジェクトにて Workload Identity を実行し、どこの AWS アカウントからの接続があるか、どのような Role とサービスアカウントを利用しているか、がわかるようにしておくことも重要だと感じました。また、今回 DB のユーザ認証についても、IAM を活用した形も利用していますが、こちらも開発チームとよく連携し、実装時にどういう制限が望ましいか議論したうえで、運用方針のすり合わせを行い、適切な運用を見極めたいと思いました。

参考ドキュメント