💡

IAPで保護されたAPIをcurlで叩いてみる

2023/05/22に公開

はじめに

業務でAPIにIAPを使用して制限をかけて使用する機会がありました。その際にユーザーアカウントを使用してcurlを叩こうと思ったのですが、少し詰まったのでその経験を記事にしました。
この記事では、IAPを使用して制限をかけたAPIにcurlでアクセスする方法を解説します。IAPの作成と設定については省略します。
ユーザーアカウントを使用してトークンを取得する方法と、サービスアカウントを使用してトークンを取得する方法を混同したのが今回の詰まりポイントです。
解説が間違っていたり不足している場合は指摘いただけると助かります。

IAPについて

IAP(Identity-Aware Proxy)とは、GCP(Google Cloud Platform)のセキュリティサービスの1つで、選択したアカウントのみアクセスできるようにしてくれる便利なサービスです。
以下は公式より抜粋。

https://cloud.google.com/iap?hl=ja

より簡単なクラウド管理

VPN を実装する場合よりも短い時間で、アプリへのアクセスを保護できます。認証や認可は IAP に任せて、開発者はアプリケーション ロジックに専念できます。

詳しくは公式を見ればわかりますが、内部用などの非公開APIを作成する際にも使えて、とても便利です。

curlで叩くには

IAPはブラウザ上からアクセスする場合はとてもわかりやすいです。リダイレクトが発生し、アカウントを選択して、閲覧できるアカウントの場合確認できるみたいな流れです。
しかし、curl等でアクセスする場合は自分でトークンを取得する必要があり、結構手間がかかります。アカウントの種類ごとにトークン取得方法が異なるので、その方法を順番に解説していきます。

アカウントの種類について

GCPには、Googleアカウントに関連付いたユーザー(人間)が使用するユーザーアカウントと、プロジェクト固有のメールアドレスが割り振られサービス間の認証等に使用されるサービスアカウントがあります。
それぞれ同じようにアクセス権限を制御できますが、トークンを取得する方法が異なります。

サービスアカウントを使用してトークンを取得する

サービスアカウントの方法の方が簡単なので先に紹介します。
gcloudの用意されているコマンドをたたいて取得したトークンをheaderに渡すだけです。
この方法の欠点としては、どうしてもローカルにkeyfileが必要な点です。プロジェクトの決まりでkeyfileのダウンロードが禁止されている場合はこの方法は使用できません。
動作確認でいちいちユーザーアカウントの方法を行うのは大変ですので、アクセスするだけの権限をもったアカウントを使って見逃してもらったり、別プロジェクトにアカウントを作成してそのアカウントを使用したりで乗り切ることをお勧めします。

まず、使用するサービスアカウントのキーファイルをダウンロードします。

次にkeyfileを使用して、ローカルの gcloud auth に登録します。

gcloud auth activate-service-account --key-file 保存したkeyfileのpath

gcloud auth list を使用して、登録したサービスアカウントを使用しているか確認します。
登録したサービスアカウントにアスタリスクがついていればOKです。

gcloud auth list                                                                                                       
Credentialed Accounts 
ACTIVE  ACCOUNT
        xxx@xxxx
*       xxx@xxxx.iam.gserviceaccount.com

To set the active account, run:
    $ gcloud config set account `ACCOUNT`

最後に gcloud auth print-identity-token コマンドを使用して、IDトークンを取得します。
https://cloud.google.com/sdk/gcloud/reference/auth/print-identity-token

gcloud auth print-identity-token --audiences="xxx.apps.googleusercontent.com"

audiencesは、IAPの「OAuth構成に移動」を押して移動したページで確認できるクライアントIDです。

あとは、headerに先ほど取得したIDトークンを渡せばcurlを叩けます。

curl --header 'Authorization: Bearer 取得したIDトークン' "https://example.com/dummy.json"

ユーザーアカウントを使用してトークンを取得する

サービスアカウントの章で紹介したコマンドはサービスアカウントでしか使用できないので、ユーザーアカウント用の方法を紹介します。
サービスアカウントで使用したgcloud コマンドを使用しようとすると、以下のエラーが発生します。

 (gcloud.auth.print-identity-token) Invalid account Type for `--audiences`. Requires valid service account.

このコマンドはユーザーアカウントにそもそも対応していないのです。ここでやや詰まりしました。
ユーザーアカウントからトークンを取得する場合、いくつか準備が必要になります。
下に続く解説は、この公式ドキュメントを参考にしているので、より正確な情報を知りたい方はこちらを参考にしてください。
https://cloud.google.com/iap/docs/authentication-howto?hl=ja

まず、IAPの設定をローカルサーバーを許可するように変更します。ポートは適当です。

次にトークンを取得するためのコードを確認するために、ローカルサーバーを起動します。
ncコマンドはデータを送ったり待ち受けたりできるコマンドです。
オプションでlistenモードをkeepしています。

nc -k -l 3333

公式サイトで紹介されている下記のサイトにアクセスします。
知らない人が貼り付けているURLが不安な方は、先ほどの公式サイトのこちらの3番と同じなので、そちらを使用してください。(ポートは違います。)
CLIENT_IDには、IAPの「OAuth構成に移動」を押して移動したページで確認できるクライアントIDを入れてください。(xxx.apps.googleusercontent.comのようなもの)

https://accounts.google.com/o/oauth2/v2/auth?client_id=CLIENT_ID&response_type=code&scope=openid%20email&access_type=offline&redirect_uri=http://localhost:3333&cred_ref=true

アクセスすると、よく見るGoogleアカウント選択画面になります。
使用したいユーザーアカウントを選択してください。
使用したいユーザーアカウントを選択すると、ローカルでlistenしていた箇所に下記のような出力があります。
その中の「code=xxx」のxxxを全て選択しコピペしておいてください。

GET /?code=xxx&scope=emailxxxxhttps://www.googleapis.com/auth/userinfo.email&authuser=xxxxxx HTTP/1.1
Host: localhost:3333
.
.
.

最後にこちらの4番に記載されている、下記のコマンドでトークンを取得します。

curl --verbose \
      --data client_id=CLIENT_ID \
      --data client_secret=CLIENT_SECRET \
      --data code="上記で取得したcode" \
      --data audience=CLIENT_IDの「.apps.googleusercontent.com」を取り除いたもの \
      --data redirect_uri=http://localhost:3333 \
      --data grant_type=authorization_code \
      https://oauth2.googleapis.com/token

CLIENT_SECRETは、CLIENT_IDを取得したページで確認できる、IAPのクライアント シークレットです。

以下のようなレスポンスを取得できます

{
  "access_token": "xxx",
  "expires_in": 3563,
  "scope": "openid https://www.googleapis.com/auth/userinfo.email",
  "token_type": "Bearer",
  "id_token": "xxx",
}

あとはこのid_tokenをheaderに渡せば、curlでAPIを叩けます。

curl --header 'Authorization: Bearer 取得したIDトークン' "https://example.com/dummy.json"

まとめ

IAPで保護されたAPIをcurlで叩く方法を紹介しました。
長々と書きましたが、ユーザーアカウントではgcloud auth print-identity-tokenが使えないというのがこの記事で伝えたかったことです。
サービスアカウントを使用した方法が簡単で、そちらを使いたいという人の説明資料にもなるかなと思っています。
ユーザーアカウントで簡単にトークンを取得する方法を知っている方は、教えていただけると嬉しいです!
以上です!

xtone tech blog

Discussion