GCP Cloud FunctionのJWT認証
- estoy
- 2022年7月2日
最近、自宅IoTの一環でGCPを使っているのですが、その中でもCloud Functionsが便利でよく使っています。Javascript(Node.js)、Python、Javaなどの言語が使えて、簡単にインターネット経由のWebAPIが作成できることからとても重宝しています。
ただ、インターネットから呼び出すにあたって、ライブラリを使わない手動トークン認証を使った際の呼び出し方が一癖あったため、その呼び出し方について、書いていきたいと思います。(プロジェクトID等、外部に公開させたくない部分に関しては、塗りつぶしていますので、その点はご承知おきください。)
1. Cloud Functionの作成
まずは、呼び出すためのCloud Functionを作成します。Cloud Functionsの画面から、「関数の作成」をクリックして、作成していきます。
正直、ここは「Googleのクイックスタート」が詳しく記載してくれているので、これを見ながらやるのが一番良いです。唯一異なる点としては「認証が必要」を選択するように注意してください。(認証については、後から権限で変えることもできますが面倒なので。。)
Cloud Functionの作成が完了したら、関数を開き、下の図のように自分が作成した関数にURLが振られていることを確認したら、ここの作業は完了です。
2. サービスアカウント・公開鍵の作成
次は、Cloud Functionを起動するためのサービスアカウントの作成になります。既存のIAMを使うこともできますが、できるだけ最小権限で行うほうが良いので、専用のアカウントを作成します。
左のナビゲーションメニューから、「IAMと管理」を選択し、左のメニューから、「サービスアカウント」を選択します。サービスアカウント名をTESTとし、サービスIDが自動設定されたら、「作成して続行」します。
作成ができたら、権限設定にて、アカウントに必要な権限を付与します。今回は、Cloud Functionの起動が目的なので、 ロールに「Cloud Function起動元」だけを付与し、後は続行を選んで完了になります。
サービスアカウントの作成・権限付与が完了したら、作成したサービスアカウントを作成して「キー」タブを選択。 選択した画面から「鍵の作成」をクリックし、「新しい鍵の作成」を選択して、キーのタイプを「JSON」にして、作成したキーをダウンロードしたら、完了です。本画面に作成したキーが表示されることが確認できます。
3. クライアント側の処理
サーバー側の準備が完了したので、後はクライアント側の処理になります。Cloud Functionの認証はJWTというトークンを使った認証になっており、 {ヘッダ}.{ペイロード}.{署名} の3つのセクションからなる文字列を生成する必要があります。Cloud Functionで必要な設定としては、以下のようになります。(Googleのマニュアル参照)
alg | RS256 |
---|---|
typ | JWT |
iat | トークンの発行日時 |
---|---|
exp | トークンの有効期限を表す日時 |
iss | サービスアカウントのメールアドレス |
aud | https://www.googleapis.com/oauth2/v4/token(※固定) |
sub | サービスアカウントのメールアドレス |
target_audience | Cloud FunctionsのURL |
ヘッダーとペイロードの設定ができたら、上記をダウンロードした秘密鍵で署名を行い、JWTを作成します。以下のソースはGoogleのサンプルソースを参考に、JWTを自動で生成するpythonのスクリプトになります。
JWT作成
#!/usr/bin/env python
import time
import json
import jwt
# 秘密鍵
key = "ダウンロードファイルの「private_key」の値"
#JWTの設定値
head = {
"alg": "RS256",
"typ": "JWT"
}
sa_email='サービスアカウントのメールアドレス'
audience='Cloud FunctionのURL'
#JWT生成処理
def plain_jwt(expiry_length=3600):
"""Generates a signed JSON Web Token using a Google API Service Account."""
now = int(time.time())
global head
global sa_email
global audience
#ペイロード部分
payload = {
'iat': now,
"exp": now + expiry_length,
'iss': sa_email,
'aud': 'https://www.googleapis.com/oauth2/v4/token',
'sub': sa_email,
'target_audience':audience
}
token = jwt.encode(payload,key,algorithm="RS256",headers=head)
return token
#メイン部分
if __name__ == '__main__':
expiry_length = 3600
plain = plain_jwt(expiry_length)
print(plain)
上記を実行するとJWTのトークンが標準出力されるので、それをコピーして、Fiddlerなどに貼り付けて下記のように貼り付けて、「https://www.googleapis.com/oauth2/v4/token」に送信します。
問題が無ければ、id_token="GCPで認証されたJWT”というレスポンスがJSON形式で帰ってきます。それをコピーして、今度はCloud FunctionのURLに送信すると、認証が通って、下記のようにCloud Function側から「Hello World!」の文字が返却されることが確認できました。
4. 最後に
JWT認証でCloud Functionを実行する方法を紹介しました。JWTを使った基本的な認証方法なので、Google Cloudのライブラリが使えない場面でも、直接Cloud Functionを起動できるようになるのが、一番のメリットだと思います。
ただ、認証するに当たって、一回余計に通信をしなければいけないのが、少し面倒くさい点ではありますね。API Gatewayを使えば同じ方式でも、一回の通信で処理できるようになるので、近いうちにその方法についても書いていきたいと思います。