GCSやBQにサービスアカウント権限借用を用いてアクセスする

本記事はGoogle Cloud Storage（GCS）やBig Query（BQ）を、ユーザーアカウントではなくサービスアカウントから利用する方法を解説します。

# 環境
筆者は以下の環境を利用していますが、GCのクラウド圏外から圏内にアクセスする場合は、環境問わず考え方は一緒です。

- Ubuntu (WSL)
- python
- GCS

また、厳密にはWSL上でDockerを立ち上げています。DockerイメージはDebianベースのものです。

# ニーズが発生した背景
やろうとしている事の意図を補足するセクションなので、読み飛ばしてもOKです。

GoogleのETLソリューションであるCloud Composerを用い、定期的にGCSとBQのデータを更新したいと考えました。
しかし、更新頻度が週に１回しかなく、継続的にCloud Composerを立ち上げておくのはコストに見合わないと感じました。
そこで、手元で余っているミニPCをAirflowの運用に使うことにしました。（※Cloud ComposerはAirflowをフルマネージドサービス化したものなので、Airflowで開発しておけば将来的な移植が簡単）
また、この開発は共同作業でした。

本番環境はオンプレミスなので、GCSへのアクセス認証が必要です。
これを（ユーザーアカウントではなく）サービスアカウントで行うべきと考えました。（ユーザーアカウントで認証してしまうとメンテナーが当該のユーザーに限定されてしまい、担当者が変わると認証も変えなければならないため。あるいは共有用にユーザーアカウントを作り使い回すのは、セキュリティリスクがあるため。）

そこで、オンプレミス環境（GC圏外）からGCS, BQ（GC圏内）へのアクセスを、サービスアカウントで行う方法を調査しました。

# 取りうる選択肢
サービスアカウントを用いた認証では、大まかに下記の方法があるようです。

1. サービスアカウントキーを用いた認証
2. サービスアカウントの権限借用による認証
3. Workload Identity認証

### 1. サービスアカウントキーを用いた認証
サービスアカウントキーを用いた認証はセキュリティリスクが高く、Google公式ドキュメントでも極力避けるべきとされています。サービスアカウントキー（ファイル）自体をやり取りする必要があるため、やり取りの過程でキーが流出するリスクがあります。

### 3. Workload Identity認証
Workload Identity認証は、GC以外の認証手段（IdP）が備わった外部環境…例えばAWSやAzureなどからGCにアクセスする場合は有用です。一方、Workload Identityの運用負荷がやや高いと感じます。結局はWorkload Identityで個々のログインユーザーを管理する必要があり、「共有ユーザーアカウントを使い回す」問題がクリアできないと考えました。
また、今回のような小規模環境で、SSO認証などがオンプレミス環境に備わっていない場合には、そもそも使えない選択肢です。

### 2. サービスアカウントの権限借用による認証
サービスアカウントの権限借用は、サービスアカウントキー（クレデンシャル）を明にやり取りする必要がなく、セキュリティ上の安全性が高いと考えられます。
個々のユーザー目線では、ユーザー自身がサービスアカウントに成り代わって認証を行うため、「ユーザーアカウントの使いまわし」のようなことをしなくて済みます。
さらに、クラウド側の権限管理も、個々のユーザーへの権限付与をする必要がなく、１つのサービスアカウントの権限管理を行うだけで済むメリットがあります。

上記のような理由から、サービスアカウントの権限借用を行うことにしました。

# サービスアカウントの権限借用のやり方
正確な情報はこちら：

https://cloud.google.com/docs/authentication/use-service-account-impersonation?hl=ja

大まかには、下記：

1. IAMの「サービスアカウント」からサービスアカウントを作成
1. IAMの「サービスアカウント」からサービスアカウントのプリンシパルに共同開発者のユーザーアカウントを登録
2. IAMの「IAM」からプリンシパルに「サービス アカウント トークン作成者(roles/iam.serviceAccountTokenCreator)」のロールを付与（プロジェクトオーナーであってもデフォルトでは付与されていないので注意）
3. IAMの「IAM」からサービスアカウントに必要な権限を付与（※公式ドキュメントでは記述がないが、これが必要。また、初期状態ではサービスアカウントが一覧に表示されないため、サービスアカウントのメールアドレスをクリップボードにコピーし、「アクセス権限を付与」からサービスアカウントに権限を付与していけば良い。）
5. クライアント環境にgcloudをインストール＆初期化
6. クライアント環境のCLIで下記のコマンドを実行し、サービスアカウントの権限借用を行う（SERVICE_ACCT_EMAILは作成したサービスアカウントのものに置き換える）

``` command:CLI
gcloud auth application-default login --impersonate-service-account SERVICE_ACCT_EMAIL
```

※要望があればスクショを交えながら手順を詳細化します。コメントください。

IAMの権限変更は反映に2分ほど時間がかかります。詳細は下記。

https://cloud.google.com/iam/docs/access-change-propagation?hl=ja

補足ですが、上記はクライアント環境に対して、デフォルト認証（ADC=Application Default Credentials）を設定する、ということをやっています。
gcloud SDKでは、デフォルトの認証情報を検索する機構があります。環境変数やGoogle Cloud CLIのユーザー認証情報などを順に検索し、認証情報を探します。この認証情報として、サービスアカウントを利用しています。

ADCに関して、詳しくはこちら：

https://cloud.google.com/docs/authentication/application-default-credentials?hl=ja

# 個々のユーザーが利用する方法
共同開発において、個々の開発者（GCユーザー）は、上記のサービスアカウント認証を個別に行います。
すなわち、各人の開発環境下でgcloudからサービスアカウントの権限借用を行います。
※impersonate（なりすまし）とあるように、個々のユーザーがサービスアカウントになりすます、というわけです。

# 本番環境
本番環境に於いても、基本的には個々のユーザーがサービスアカウントの権限借用を行うのと同じです。
すなわち、本番環境の構築時に、構築するユーザーが権限借用を行います。

# まとめ
Google Cloud圏外の環境から、Google Cloud内の環境（サービス）にアクセスする際に、サービスアカウントの権限借用を行う方法を概説しました。
サービスアカウントの権限借用の他にも、取りうる選択肢がありますが、小規模で行うにはこの方法がバランスの取れた方法ではないかと思います。

# よりセキュアな方法
サービスアカウントの権限借用は、サービスアカウントキーをユーザー間で直接やり取りしないとはいえ、クレデンシャル自体は個々の環境（端末）に残ります。ユーザー自身や、オンプレミス環境のセキュリティが不十分な場合、クレデンシャルファイルが流出し、不正アクセスを受けるリスクは残ります。
このリスクに対処するには、Workload Identityを使う必要があります。

Workload IdentityはIDプロバイダ（IDaaSなどのIdP）が発行するセキュアなIDが必要です。
Google Cloudでは、Cloud IdentityがIdPに相当し、Cloud IdentityからIDを払い出せます。このIDを、Workload IdentityでGC内のアカウントに対応付けしていきます。
しかし、Cloud IdentityでIDを払い出すにはドメインが必要であり、コストが掛かります。故に小規模に開発をするには不向きな面があります。（また、共同開発を手軽に行いたいならば、サービスアカウントで間に合わせた方がニーズには合っているように感じます。）
また、Google Workspaceで作ったユーザーグループに対し、IAMで権限付与する方法もありますが、こちらもWorkspaceの費用がかかりますし、このためだけに人数分のWorkspaceを契約し続けるのは大げさな気がします。

ある程度の規模があったり、ビジネスシーンでセキュリティが重視されるうような場ではWorkload Identityを使い、個人開発など手軽さにニーズがあるシーンではサービスアカウントを使う、などの使い分けが求められるものと思います。

# リファレンス

https://blog.pokutuna.com/entry/application-default-credentials