環境変数を使わないで、Workload Identity連携でAWSからGCPを操作

# はじめに

GCPを、AWSからセキュアに操作する**Workload Identity**という機能について、以前に何度か記事にしました。

https://qiita.com/a_b_/items/f30b85be4f44ed1f620c

https://qiita.com/a_b_/items/102dfa252761eceab528

https://qiita.com/a_b_/items/ce5c3f1eb3c716f5565e

その際、
1. 認証情報が記述された構成ファイルを保存して
1. 環境変数`GOOGLE_APPLICATION_CREDENTIALS`にファイルパスを記載する

という方法で行っていました。

今回は、"ファイル保存&環境変数"という方法を使わず、コードの中で実現する方法になります。

# 概要

以下のページにサンプルがありました。

https://google-auth.readthedocs.io/en/master/user-guide.html#using-external-identities

```python
json_config_info = json.loads("<ファイルの中身の文字列>")

# jsonの認証設定を読み込む
credentials = aws.Credentials.from_info(json_config_info)

# スコープを設定
## スコープに設定する内容は以下より
## https://developers.google.com/identity/protocols/oauth2/scopes
scoped_credentials = credentials.with_scopes([''])

# プロジェクト、認証を指定してクライアント作成
client = <任意のGCPサービス>.Client(
    project="<対象のGCPプロジェクトID>",
    credentials=scoped_credentials
)
```
またこの方法は、EC2などに限定されます。メタデータURLが異なるためとのことですので、他の環境で実装する方はご注意ください。

https://zenn.dev/ohsawa0515/articles/gcp-workload-identity-federation#%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AE%E5%AE%9F%E8%A1%8C-1



# 参考

https://zenn.dev/ohsawa0515/articles/gcp-workload-identity-federation

https://developers.google.com/identity/protocols/oauth2/scopes?hl=ja#storage


# やったこと

## AWSの準備

- Cloud9を使いました
- アカウントIDをGCP側で設定しますので、要メモ

## GCPの準備

IAM Service Account Credentals APIを有効化しておきます。無効のままでも後で有効化するようURLに誘導されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553680/3fbb250d-4cd1-693a-7a53-db0e500c262d.png)


以降はCloudShellからやっていきます。

### 各種変数定義
許可する権限は、最も強い`roles/owner`にしています。適宜変更ください。
変数セット直後に、変数の中身をechoしています。
```bash
# 先のAWSアカウントIDをセット
AWS_ACCOUND_ID="123456789012" && echo $AWS_ACCOUND_ID

# 以下、適宜変更していく
POOL="hogehoge-pool" && echo $POOL
PROVIDER="hogehoge-aws-provider" && echo $PROVIDER
SERVICE_ACCOUNT="access-from-hogehoge-aws" && echo $SERVICE_ACCOUNT
DESCRIPTION="Access from hogehoge env on AWS with workload identity federation" && echo $DESCRIPTION
BIND_ROLE="roles/owner" && echo $BIND_ROLE

# 以下二つは`gcloud projects list`で確認する
PROJECT_NUMBER="123456789012" && echo $PROJECT_NUMBER
PROJECT_ID="hogehpge-pj" && echo $PROJECT_ID
```

### サービスアカウントの作成
削除用のコマンドも併記しておきます。
```bash
gcloud iam service-accounts create $SERVICE_ACCOUNT --description="${DESCRIPTION}"
# 削除時は以下
# gcloud iam service-accounts delete "${SERVICE_ACCOUNT}@${PROJECT_ID}.iam.gserviceaccount.com"
```

### Workload Identity Poolの作成
```bash
gcloud iam workload-identity-pools create $POOL --location="global"
# 削除時は以下
# gcloud iam workload-identity-pools delete $POOL --location="global"
```

### Workload Identity Providerの作成
```bash
gcloud iam workload-identity-pools providers create-aws $PROVIDER \
  --location="global" \
  --workload-identity-pool=$POOL \
  --account-id=$AWS_ACCOUND_ID

# 削除
# gcloud iam workload-identity-pools providers delete $PROVIDER \
#  --location="global" \
#  --workload-identity-pool=$POOL
```

### サービスアカウントに権限付与
```bash
# 権限の付与
gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:${SERVICE_ACCOUNT}@${PROJECT_ID}.iam.gserviceaccount.com" \
  --role=$BIND_ROLE

# 確認
gcloud projects get-iam-policy $PROJECT_ID

# 権限の削除
# gcloud projects remove-iam-policy-binding $PROJECT_ID \
#  --member="serviceAccount:${SERVICE_ACCOUNT}@${PROJECT_ID}.iam.gserviceaccount.com" \
#  --role=$BIND_ROLE
```

### Workload Identity Poolとサービスアカウントの連携
AWS側に使用を許可する範囲を任意にしています。必要に応じて制限してください。
```bash
# 一旦全権限で付与してみる
gcloud iam service-accounts add-iam-policy-binding "${SERVICE_ACCOUNT}@${PROJECT_ID}.iam.gserviceaccount.com" \
  --role="roles/iam.workloadIdentityUser" \
  --member="principalSet://iam.googleapis.com/projects/${PROJECT_NUMBER}/locations/global/workloadIdentityPools/${POOL}/*"

# 確認
gcloud iam service-accounts get-iam-policy "${SERVICE_ACCOUNT}@${PROJECT_ID}.iam.gserviceaccount.com"

# 削除
# gcloud iam service-accounts remove-iam-policy-binding "${SERVICE_ACCOUNT}@${PROJECT_ID}.iam.gserviceaccount.com" \
#  --role="roles/iam.workloadIdentityUser" \
#  --member="principalSet://iam.googleapis.com/projects/${PROJECT_NUMBER}/locations/global/workloadIdentityPools/${POOL}/*"

```

### 認証構成ファイルの取得
```bash
gcloud iam workload-identity-pools create-cred-config \
  "projects/${PROJECT_NUMBER}/locations/global/workloadIdentityPools/${POOL}/providers/${PROVIDER}" \
  --service-account="${SERVICE_ACCOUNT}@${PROJECT_ID}.iam.gserviceaccount.com" \
  --aws \
  --output-file="config-aws-provider.json"
```


## Pythonで操作(cloud9から)

環境は依然と同様に、`virtualenv`で仮想化環境で行いました。

```bash
mkdir aws-to-gcp
cd aws-to-gcp

# virtualenv上で環境構築
virtualenv a2genv
source a2genv/bin/activate

(a2genv) $ pip install google-cloud-storage
```

:::note warn
以前はCloud9のtemporary credentialを無効にして、IAMロールをアタッチしていましたが、今回やったときは特に必要なく実行できました。
このあたりのところは以前の状態もハッキリしないので、試す際はご留意ください。
:::

Pythonスクリプトは以下です。今回はGCSのバケット一覧を取ってくる例です。
```python:connect_gcp.py
from google.cloud import storage
import json
from google.auth import aws

json_config_info = json.loads("""
< 先ほどGCPで作成したファイルの中身をここにコピペ >
""")

credentials = aws.Credentials.from_info(json_config_info)
scoped_credentials = credentials.with_scopes(
    ['https://www.googleapis.com/auth/devstorage.full_control'])

storage_client = storage.Client(project="<対象のGCPプロジェクトID>",credentials=scoped_credentials)
print(list(storage_client.list_buckets()))
```


# おわりに
今回はWorkload Identity連携の、別方法を記事にしました。
認証情報をコードに直に書いていますが、実際に使う場合はParameter Storeで保存してそこから取得する、という方法がセキュアかと思います。

どなたかのお役に立てれば幸いです。