概要
GCPのAPIを実行するにはgcloud コマンドやSDKを使うやり方もありますがcurlコマンドを使ったやり方について調べたのでそのメモです。
GCPで作成したサービスアカウントを使います。方法は2つあります。
- 事前にOAuth 2.0の認証を行い、トークンをもらってからAPIにアクセスする方法
- 事前の認証無しに、認証情報を署名してトークンとして使うことでAPIのリクエストする方法
後者の方が事前の認証の手間がなく今回はこちらを利用します。
OSはLinuxの CentOS7を使いました。
この記事ではサービスアカウントの作成については省きます。また後に登場するJWT ですが詳細については https://jwt.io/introduction/ を参照してください。
(参考:HTTP/RESTを使った呼び出し
https://developers.google.com/identity/protocols/OAuth2ServiceAccount#authorizingrequests :
直接APIを呼び出しする方法については
https://developers.google.com/identity/protocols/OAuth2ServiceAccount#jwt-auth)
Google APIs GitHub repositoryに利用したいAPIの定義ファイルがない場合は利用できないようです。
この記事ではloggingのAPIを呼び出します。loggingのAPIについては
https://github.com/googleapis/googleapis/blob/master/google/logging/logging.yaml
で確認することができました。
- 認証に必要な情報をまとめる
- 1.からJSON Web Token (以後 JWT )を作成する
- JWTを使ったGCP APIの呼び出し
のようにGCPのAPIをcurlにて呼び出ししたいと思います。
1. 認証情報(JWT)の作成
項目 | 入力値 |
---|---|
iss | service accountの認証ファイル(JSON)内のclient_emailの値 |
sub | service accountの認証ファイル(JSON)内のclient_emailの値 |
aud | 「https://SERVICE_NAME/API_NAME」 のように作成します。今回の場合、https://github.com/googleapis/googleapis/blob/master/google/logging/logging.yaml 内にSERVICE_NAME とAPI_NAME があります。 |
iat | アサーションが発行された時刻。1970年1月1日00:00:00 UTCからの秒数で指定されます。 |
exp | アサーションの有効期限。1970年1月1日00:00:00 UTCからの秒数として指定されます。この値は、発行された時刻の最大1時間後です。 作成したトークンには有効期限があります。期限は最大1時間。 |
kid | サービスアカウントの認証情報JSON 内のprivate_key_id |
参考:https://developers.google.com/identity/protocols/OAuth2ServiceAccount
2. 1.からJSON Web Token (以後 JWT )を作成する
Python 2.7.5を使います。pip を使ってインストールします。
python get-pip.py
pip install pyjwt
pip install cryptography
pythonのCode
https://developers.google.com/identity/protocols/OAuth2ServiceAccount#jwt-auth のコードを元にpythonのコードを完成させます。signed_jwtが出力されるのでそれをコピーします。
# -*- coding: utf-8 -*-
import jwt
import time
iat = time.time()
exp = iat + 3600
payload = {'iss': 'stackdriver-logging-read@xxxxx-xxxxx-xxxx..iam.gserviceaccount.com',
'sub': 'stackdriver-logging-read@xxxxx-xxxxx-xxxx.iam.gserviceaccount.com',
'aud': 'https://logging.googleapis.com/google.logging.v2.LoggingServiceV2',
'iat': iat,
'exp': exp}
additional_headers = {'kid': 'XXXXXXXXXXXXX'}
signed_jwt = jwt.encode(payload, '-----BEGIN PRIVATE KEY-----\nXXXXXXXXXXXXXXXXXXXXXXXXXXX\n-----END PRIVATE KEY-----\n', headers=additional_headers,algorithm='RS256')
print signed_jwt
3. JWTを使ったGCP APIの呼び出し
curlに指定するURIはGCPのマニュアルページ調べるか、gcloud のコマンドに --log-http をつけて実行すると下のようにわかります。
gcloud logging read 'timestamp<="2019-07-30T23:59:59Z"' --format="json" --log-http
...
==== request start ====
uri: https://logging.googleapis.com/v2/entries:list?alt=json
method: POST
...
curlコマンドは以下です。 python で作ったsigned_jwt とGCPのproject-name を適宜入れてください。
curl -X POST \
'https://logging.googleapis.com/v2/entries:list?alt=json' \
-H 'Authorization: Bearer <signed_jwt>' \
-H 'Content-Type: application/json' \
-H 'Host: logging.googleapis.com' \
-d '{ "filter": '\''timestamp<="2019-07-30T23:59:59Z"'\'', "orderBy": "timestamp desc", "pageSize": 1000, "resourceNames": ["projects/<project-name>"]}'
1. 認証情報(JWT)の作成のexp の項にも書きましたがトークンには有効期限があります。失効した場合はもう一度signed_jwt を作成する必要があります。
出力は以下のような感じです。gcloud コマンド実行時と変わりません。
{
"entries": [
{
"insertId": "..........................Aj_..",
"jsonPayload": {
"client.received.end.timestamp": "1564465946852",
"request.verb": "GET",
...
補足
JWTをpythonのツールで生成しましたが、Linuxのコマンドで生成する方法も調べたのでメモ
JWTは
- Header
- Payload
- Signature
で構成されていて、
base64UrlEncode(header) + "." + base64UrlEncode(payload) + "." + Signature
Signature は
base64UrlEncode(header) + "." + base64UrlEncode(payload)
に秘密鍵で署名をしてbase64UrlEncodeしたものになります。
秘密鍵はGCPのサービスアカウントの認証情報(json)にあります。
-----BEGIN PRIVATE KEY-----から始まる文字列です。これをコピーしてテキストファイル(secret_key)を作ります。
header='{
"alg": "RS256",
"typ": "JWT",
"kid": "0123456789abcdef01234567"
}'
payload='{
"iss": "stackdriver-logging-read@project-name.iam.gserviceaccount.com",
"iat": 1565869581.475549,
"sub": "stackdriver-logging-read@project-name.iam.gserviceaccount.com",
"exp": 1565873181.475549,
"aud": "https://logging.googleapis.com/google.logging.v2.LoggingServiceV2"
}'
base64UrlEncHeader=`echo -n ${header}|tr -d " \n"|openssl base64 |tr -- '+/=' '-_ '|tr -d " \n"`
base64UrlEncPayload=`echo -n ${payload}|tr -d " \n"|openssl base64 |tr -- '+/=' '-_ '|tr -d " \n"`
signature=`echo -n "${base64UrlEncHeader}.${base64UrlEncPayload}" | openssl dgst -sha256 -sign secret_key -binary |openssl base64 |tr -- '+/=' '-_ '|tr -d " \n"`
echo "${base64UrlEncHeader}.${base64UrlEncPayload}.${signature}"
[参考]
https://jwt.io/introduction/
https://qiita.com/kunichiko/items/3c0b1a2915e9dacbd4c1
https://crypto.stackexchange.com/posts/68400/revisions
以上でした。
間違いのご指摘や質問大歓迎です。