GCP と OAuth2
はじめに
GCP のサービスにプログラムからアクセスするためには必ず認証・認可が必要ですが、以下のような様々なコマンドや概念が出てくるので少しとっつきにくい印象があります。
gcloud auth login
gcloud auth application-default login
- Service Account
- Application Default Credentials
これらの概念は認証・認可のベースとなっている OAuth2 の文脈で眺めてみると全体像が理解しやすくなるので、本記事でまとめてみたいと思います。
GCP での認証・認可
GCP の認証・認可は一部(*)を除いて全て OAuth2 ベースでやり取りされています。(* API Key)
OAuth2 は三者間の手続きです。
Client が Resource Owner の代わりに Resource Server にアクセスするための手続きが OAuth2 です。
ここで、登場人物を整理します。
Resource Server:
GCE, GKE, GCS などの GCP の種々のサービスであり、Client がアクセスする対象になります。
Resource Owner
Client に対して Resource Server にアクセスできる認可を与える人物です。ここでは Google アカウントが該当します。
Client
Resource Owner の認可を受けて GCP の API を叩くクライアントです。gcloud などの Cloud SDK もクライアントですし、GAE 上のアプリケーションが別のサービスを呼び出す時は GAE そのものがクライアントになります。
この三者構造をきちんと把握しておくことで、この後の話が理解しやすくなると思います。
認可タイプと認可対象
次に、誰がどのクライアントをどのように認可するのか詳しく見てみましょう。
認可タイプ
まず、Resource Owner がどのように Client を認可するか説明します。
OAuth2 ではユースケースに応じて認可する手段をいくつか用意しています。それが 「Grant Type」と呼ばれるものです。
GCP では二通りの Grant Type を使い分けています。
- Authorization Code
- JWT Bearer
一つ目の Authorization Code は RFC6749 に定義されている方法であり、エンドユーザがブラウザを介して認可し、最終的にサーバ経由でアクセストークンを取得する方法です。GCP では User Account を使って認可します。
ブラウザを使うためエンドユーザが介入する必要がありますが、ユーザのアカウントさえあれば認可を取れるのでローカルでの開発時に向いています。
二つ目の JWT Bearer は RFC7523 に定義されており、エンドユーザの介入なしに、あらかじめ GCP 側と握っておいたシークレット情報を提示することでアクセストークンを取得する方法です。GCP では Service Account を使って認可します。
エンドユーザの存在が必要ないので、実際のプロダクション環境で使われることが多いでしょう。
認可対象
次に認可する対象となる Client にどのようなものが存在するか説明します。GCP の API を叩きたいクライアントは数多く存在しますが、大きく分けて以下の二つに分類することができます。
- Cloud SDK
- Server Application (Service Account)
Cloud SDK とは gcloud, gsutil, bq といったツールを総称したものです。
開発者が gcloud コマンドを叩く時、gcloud は OAuth2 クライアントになっているのです。
Server Application の方は大雑把に分類してしまいましたが、Cloud SDK 以外のアプリケーションと考えてください。
自前で開発している GCP の API を叩くアプリケーションもそうですし、GCE で動いているアプリケーションや Cloud SQL Proxy などもそうです。
ちなみに GCP に限らず Google の API を叩くためには事前にクライアントの発行が必要になりますが、Server Application 用のクライアントの発行はどこで行っているのでしょうか?
実は認可で使う Service Account が OAuth2 クライアントそのものになっています。(これは Service Account の鍵ファイルに client_id が記載されていることからもわかると思います。)
Service Account は IAM で権限を付与したりといったアカウントの側面も持っていますが、GCP の認可を取得する OAuth2 クライアントとしての側面も持っているのでした。
蛇足ですが、OAuth2 では一つのアプリケーションにつき、一つのクライアントを払い出すのが権限管理上望ましいです。そのため、Service Account も使用するアプリケーション毎に個別に払い出すのが望ましいと考えられます。
具体的な認可手段
ここまでは概念的な話をしてきましたが、具体的な認可手段、つまりアクセストークンの取得方法は具体的にどうなっているのでしょうか。
認可タイプが Authorization Code (User Account) と JWT Bearer (Service Account) の二通り、認可対象が Cloud SDK と Server Application の二通りあり、その組み合わせとして四通りの認可手段が GCP には存在します。
ここからはこの四通りの方法を見ていきます。
1. User Account — Cloud SDK
User Account を使って Cloud SDK が使うためのアクセストークンを取得する方法です。
ターミナル上で gcloud auth login を実行することで行われます。
$ gcloud auth login
これを実行すると OAuth2 Authorization Code Grant Flow が走ります。まずブラウザが開き、そこでユーザが同意を行うことで、アクセストークンとリフレッシュトークンが得られるという流れです。
以降、gcloud コマンドで GCP サービスを操作しようとすると、このアクセストークンが使われるようになります。
ちなみに gcloud auth login
は RFC 8252 — OAuth 2.0 for Native Apps で定義されている Loopback Interface Redirection という技術を使っています。
これはローカルホスト上にテンポラリなサーバを立て、ブラウザ ↔ ターミナル間で認可レスポンスを授受する方式です。
2. Service Account — Cloud SDK
次に、Service Account を使って Cloud SDK が使うためのアクセストークンを取得する方法です。
ターミナル上で gcloud auth activate-service-account を実行することで行われます。
$ gcloud auth activate-service-account --key-file=KEY_FILE
これを実行すると裏では OAuth2 JWT Bearer Grant Flow が走ります。
JWT Bearer Grant Flow は、事前に認可サーバと握っておいたシークレット情報を用いて、アサーションと呼ばれる JWT 形式の文字列を作り、そのアサーションと引き換えにトークンを取得する方法です。
GCP では Service Account の鍵情報をシークレット情報として使います。
JWT Bearer Grant Flow は上図からわかる通りエンドユーザの介在が必要ないため、二者間の OAuth2 (2-legged OAuth2) と呼ばれることがあります。
このフローは以下の2つのコマンドで試すことができるので、よかったらお手元の Service Account を使って試してみてください。
(create_assertion はこちらから)
ASSERTION=`./create_assertion <SERVICE_ACCOUNT_KEY.json>`curl -s https://oauth2.googleapis.com/token -d grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer -d assertion=$ASSERTION
3. User Account — Server Application
次に、User Account を使って Server Application が使うためのアクセストークンを取得する方法です。
ターミナル上で gcloud auth application-default login を実行することで行われます。
$ gcloud auth application-default login
これは認可対象のクライアントが違うだけで、 gcloud auth login
とやっていることは基本的に変わりません。
つまり OAuth2 Authorization Code Grant Flow で Server Application 用の認可を取得する流れです。
得られたトークンは ~/.config/gcloud/application_default_credentials.json
というファイルに書き出されます(後述する ADC で使われます)。
ここで得られたトークンは、gcloud コマンドといった Cloud SDK の内部では使われません。ローカルで開発しているアプリケーションなどで GCP の API を叩く時に自動で使われます。
4. Service Account — Server Application
最後に、Service Account を使って Server Application が使うためのアクセストークンを取得する方法です。
ここでは Application Default Credentials (ADC) と呼ばれる方式で認可を与えます。
ADC とは Server Application がアクセストークンを取得する際に使われる、デフォルトのクレデンシャル探索のルールのことを指します。
このルールは OAuth2 の各クライアントライブラリ (例: Golang) で実装されているので、このルールに則って Service Account の鍵ファイルを配置しておくと自動的に読み込まれて楽です。
ルールは以下のようになっています。
GOOGLE_APPLICATION_CREDENTIALS
環境変数のパスにある Service Account を探す- なければ
~/.config/gcloud/application_default_credentials.json
を探す - なければ環境ごとの個別のやり方で探す (例: GAE なら App Identity を使う, GCE なら Metadata Server を使う)
つまり、アプリケーションで独自の Service Account を使いたい場合は、鍵ファイルを GOOGLE_APPLICATION_CREDENTIALS
環境変数のパス、もしくは ~/.config/gcloud/application_default_credentials.json
に置いておけば自動的にその Service Account を使ってアクセストークンの取得が走ります。
実際のトークン取得は gcloud auth activate-service-account
と同じく、二者間の OAuth2 JWT Bearer Grant Flow が行われます。
まとめ
少し込み入った話になってしまいましたが、GCP の API を使う時の認証・認可の仕組みについて OAuth2 の視点からまとめてみました。
実際に開発する際は上記で述べた4つの認可手段を理解しておけば大丈夫だと思うので、表を再掲して終わりたいと思います。