[GCP] ひとつのサービスアカウントで複数プロジェクトのリソースへアクセスする

# はじめに
```
Hello Kids! 
キミはもう、たっぷり GCP 使えた ?
Google Cloud Certified Professional Cloud Architect とったキミも、
まだまだのキミも、「GCP」 に挑戦だ !!
How's your mouth rolling today !
```
なんとなく、イマクニ の [ポケモン言えるかな?](https://www.youtube.com/watch?v=TQ6sQ382hRU) が頭をよぎったので書いちゃいました。

おふざけはこれでいいとして現在、PJ で GCP を使っています。
そこで、こんな要件が出てきました。

#### "ひとつのサービスアカウントで、複数プロジェクトのリソースへアクセスしたい"

要は、下図で説明すると service-account.json を持っている Host から、red-pj が抱える red-topic と yellow-pj が抱える yellow-topic のデータにアクセスをしたいということです。
![GCP.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/353538/e9c956f7-07d0-bf0e-a7bb-01d7f29e5cb7.png)

# サービスアカウントとは
そもそもサービスアカウントとは何かがよくわからない方のために説明すると、
通常、**"アカウント"**と単に言っても実際にそれを使用する対象は大きく 2 つのユーザが存在します。

1. ユーザ(人間)
2. ユーザ(アプリ)

## 1. ユーザ(人間) 
一般的なアカウントとはこの、人間のために払い出されたアカウントです。
通常、みなさんが使用しているアカウントは全てこれと言っても過言ではないでしょう。
自分で、個人情報などを入力してパスワードを設定して・・・というフローを経て取得するのがこのアカウントです。

## 2. ユーザ(アプリ)
アプリからクラウド上の特定のサービスにアクセスするために、**"アプリのために払い出されたアカウント"**、これがサービスアカウントです。
当然ですが、クラウド上で構築したリソースに対してパブリックなアクセスを許してしまうとセキュリティ的に問題があります。
そのため、そのリソースに対してアクセスする最低限の権限（ロール）を付与した特別なアカウントを作成し、アプリはそのアカウントである証拠を持ってリソースにアクセスします。


# ひとつのサービスアカウントで複数プロジェクトのリソースにアクセスするやり方
ここからが本題です。
そんなんできるん？ て感じですが結論をいうとできます。
調べる中で [こんな記事](https://stackoverflow.com/questions/35479025/cross-project-management-using-service-account) を見つけましたがいまいち分かりにくいので自分で説明します。

大まかな流れとしては、**red-pj で作成したサービスアカウントに対して yellow-pj で必要な権限を付与する**といった形になります。

> 以降の画面では、
`red-pj = gutomityan-red`、`yellow-pj = guromityan-yellow`
となっていますが適宜読み替えてください。

## red-pj 上での作業
まず、red-pj でサービスアカウントを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/353538/d84dcf73-075c-5aff-ca62-b222ed8b9107.png)

必要な権限を選択　(ここでは、red-pj　で必要な権限)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/353538/b660ac10-b7fa-8f69-f4c1-476c3181dfb7.png)

キーを作成 (JSON を選択)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/353538/a96367d5-3225-eb0b-ea41-87e3f8c151f8.png)

ダウンロードしたキーはこのような形式になっています。

```
{
  "type": "service_account",
  "project_id": "xxxxxxxxxxxx",
  "private_key_id": "xxxxxxxxxxxx",
  "private_key": "-----BEGIN PRIVATE KEY-----xxxxxxxxxxxx",
  "client_email": "xxxxxxxxxxxx",
  "client_id": "xxxxxxxxxxxx",
  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
  "token_uri": "https://oauth2.googleapis.com/token",
  "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
  "client_x509_cert_url": ""
}
```
見てわかる通り、秘密鍵が内包されています。
公開鍵は GCP で管理しているため、仮にサービスアカウントが流出してしまった場合は、その鍵を無効化することができますが、
**間違っても、service-account.json を GitHub などに push しないように気をつけてください**
「うちはプライベートリポジトリだから大丈夫♪」とか言わずに `.gitignore` に追加しましょう。
[【実録】アクセスキー流出、攻撃者のとった行動とその対策
](https://dev.classmethod.jp/cloud/aws/accesskey-leak/)

## yellow-pj 上での作業
先ほど作成したサービスアカウント: `service-account@red-pj.iam.gserviceaccount.com `をコピーしてください。
次に、IAM 上で、「追加」をクリック。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/353538/b93c21af-31e8-d091-88f5-4efa15f15775.png)

先ほどコピーしたメールアドレスをペーストし、yellow-pj 上で必要な権限を選択し、保存。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/353538/44534c49-d9a4-5b2c-c552-af0bdc8500c4.png)

これで作業は完了です。
あとはアプリ側で、環境変数 `GOOGLE_APPLICATION_CREDENTIALS` に `serivce-account.json` への絶対パスを設定しておけば認証が通ります。
[GCP ドキュメント - 認証の開始](https://cloud.google.com/docs/authentication/getting-started?hl=ja)

# 最後に
書くのがめんどくさかったので割愛しましたが、今回の Cloud Pub/Sub で実際に動きを試したい場合は、以下の記事が参考になるかと思います。
[pull を使用したメッセージの受信](https://cloud.google.com/pubsub/docs/pull?hl=ja)
2 つのプロジェクトそれぞれでトピックとサブスクリプションを定義し、Python コードを実行してみると、サービスアカウントでちゃんと認証されていることが確認できます。

また、よく "認証" と "認可" をごっちゃにしてしまいがちですが違います。
そこらへんを以前、少しだけまとめているためその違いは [こちらの記事](https://qiita.com/guromityan/items/4f8d8ed74276539e9fd1) を参考にしてみてください。