Help us understand the problem. What is going on with this article?

GCP のAPIをcurlコマンドで実行するときのメモ

概要

GCPのAPIを実行するにはgcloud コマンドやSDKを使うやり方もありますがcurlコマンドを使ったやり方について調べたのでそのメモです。

GCPで作成したサービスアカウントを使います。方法は2つあります。
1) 事前にOAuth 2.0の認証を行い、トークンをもらってからAPIにアクセスする方法
2) 事前の認証無しに、認証情報を署名してトークンとして使うことで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. 認証に必要な情報をまとめる
  2. 1.からJSON Web Token (以後 JWT )を作成する
  3. 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

以上でした。
間違いのご指摘や質問大歓迎です。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした