Cloud Run:PHPから認証サービスにデータを送る

Cloud Run:PHPから認証サービスにデータを送る

前記事ではデータを POST 受信するアプリを Cloud Run にデプロイしましたので、次は PHP 側の実装を考えます。関連の過去記事は次のとおりです。

01PHP から Cloud Run 認証サービスにデータを送る方法

Gemini に「外部の PHP から認証が必要な Cloud Run サービスに JSON データを送る方法」を聞いてみました。

答えは「Google発行のIDトークン(OIDCトークン)を取得し、それをリクエストの Authorization ヘッダーに含める必要がある」でした。具体的には、

  1. Google Cloud 側の準備
    • 外部 PHP からのアクセス用に専用のサービスアカウントを作成し、対象の Cloud Run サービスで「Cloud Run 起動元(roles/run.invoker)」のロールを付与します
    • サービスアカウントの JSON キーファイルをダウンロードし、外部サーバーの安全な場所に配置します
  2. PHP での実装手順
    • Google公式の Google Auth Library for PHP を使用するのが最も確実です
    • Google Auth Library for PHP のインストール(コード付き)
    • 実装コード例(コード付き)

Gemini の指示にしたがって順番にやってきましょう。

02Google Cloud 側の準備

サービスアカウントに Cloud Run 起動元ロールを付加する

まずサービスアカウントを作成します。

コンソールのどこに何があるかを理解するためにコンソールでやってみます。既存のサービスを構成する場合です。Cloud Run のサービスを選択し、該当のサービスをクリックします。

「新しいリビジョンの編集とデプロイ(えんぴつアイコン)」をクリックします。

「セキュリティ」タブをクリックし、「サービス アカウント」プルダウンをクリックしますと既存のサービスアカウントが表示されます。「新しいサービスアカウントの作成」か「手動で入力」で作成してもいいのですが、とりあえずは既存のものを利用してみます。

続いて、そのサービスアカウントに Cloud Run 起動元ロールを付与します。

サービスに戻り、該当のサービスの左の端の □ にチェックを入れますとタブが変わり権限が現れますのでクリックします。

右側からにょろっとダイアログみたいなものが出てきますので「プリンシパルの追加」をクリックしますと上の画像になります。既存のサービスアカウントを入力し、「Cloud Run 起動元」のロールを与えます。

サービスアカウントの JSON キーファイルの取得

ナビゲーションメニューから「IAM と管理 > サービスアカウント > 該当のサービスアカウント > 鍵 > キーを追加 > 新しい鍵を作成 > JSON を選択して作成」と進みます。

「サービスアカウントキーの作成が無効になっています」と表示されます。どうやらこれはキーの流出の危険があるからということでデフォルトで無効になっているからのようです。このダイアログが出る前に次の表示がありました。

サービス アカウント キーは、不正使用されるとセキュリティ上のリスクになる可能性があります。サービス アカウント キーをダウンロードするのではなく、代わりに Workload Identity 連携 を使用することをおすすめします。
Google は公開リポジトリで検出されたサービス アカウント キーを自動的に無効にします。組織のポリシー「iam.serviceAccountKeyExposureResponse」を使用してこの動作をカスタマイズできます。

これは組織ポリシーで iam.disableServiceAccountKeyCreation が有効になっているからだそうで、これを解除すればキーファイルが取得できます。確認してみましょう。ナビゲーションメニューから「IAM と管理 > 組織のポリシー」に進み、フィルタに iam.disableServiceAccountKeyCreation を入れてみますと、

たしかにアクティブになっています。これを非アクティブにすれば鍵は取得できるのですが、組織全体を変更するのか、プロジェクトだけを変更するのか、よく検討すべきとあります。詳細は、

にあります。組織レベルでは非アクティブのままプロジェクトでアクティブにしてみます。

なお、この設定変更には roles/orgpolicy.policyAdmin(組織ポリシー管理者)が必要です。変更する場合は、IAM と管理 > IAM と進み操作するアカウントに組織レベルでの組織ポリシー管理者を付与してください。

上の画像のプロジェクト選択を組織から該当プロジェクトに変更します。組織とプロジェクトではアイコンが異なります。アクティブとなっている「Disable service account key creation」をクリックします。「ポリシーを管理」をクリックします。

「親のポリシーをオーバーライドする」にチェックを入れ、「ルールの追加」をクリックして「オフ」にし、「ポリシーを設定」します。再度ナビゲーションメニューから「IAM と管理 > サービスアカウント > 該当のサービスアカウント > 鍵 > キーを追加 > 新しい鍵を作成 > JSON を選択して作成」と進みますと、

と、JSON キーファイルがローカルにダウンロードされます。

03PHP での実装手順

Composer のインストール

Google Auth Library for PHP はこれですね。Google Cloud ドキュメントにありました。インストールは composer で行いますので、まずは composer のインストールです。

インストール方法は Google Auth Library for PHP のページにもありますが、本家のインストール手順でやってみます。同じことです。

php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php -r "if (hash_file('sha384', 'composer-setup.php') === 'c8b085408188070d5f52bcfe4ecfbee5f727afa458b2573b8eaaf77b3419b0bf2768dc67c86944da1544f06fa544fd47') { echo 'Installer verified'.PHP_EOL; } else { echo 'Installer corrupt'.PHP_EOL; unlink('composer-setup.php'); exit(1); }"
php composer-setup.php
php -r "unlink('composer-setup.php');"

この4行がやっていることは次のとおりです。

  • インストーラーを現在のディレクトリにダウンロードする
  • インストーラーを SHA-384 で検証する
  • インストーラーを実行する
  • インストーラーを削除する
sudo mv composer.phar /usr/local/bin/composer

実行ファイルディレクトリに移動しグローバルで使えるようにします。

composer --version

バージョン確認して表示されれば完了です。現在の composer のバージョンは 2.9.5 です。

Google Auth Library for PHP インストール

composer require google/auth

カレントディレクトリ配下に vender ディレクトリが作成されその中にライブラリがインストールされます。

実装コード

外部 PHP から Cloud Run の認証サービスにアクセスするコードは Google Auth Library for PHP のドキュメント内にあります。Gemini もほぼ同じコードを教えてくれます。

require 'vendor/autoload.php';

use Google\Auth\ApplicationDefaultCredentials;
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;

// サービスアカウントのJSONキーファイルパス
$keyFilePath = '/path/to/your-service-account-key.json';
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $keyFilePath);

// Cloud Run のURL
$serviceUrl = 'https://your-cloud-run-url-xxxx.a.run.app';
$receiveDir = '/items/'; // これは例です。前記事参照

// create middleware
$middleware = ApplicationDefaultCredentials::getIdTokenMiddleware($serviceUrl);
$stack = HandlerStack::create();
$stack->push($middleware);

// create the HTTP client
$client = new Client([
  'handler' => $stack,
  'auth' => 'google_auth',
  'base_uri' => $serviceUrl,
]);

// make the request
try {
  $response = $client->post($receiveDir, [
    'json' => [
      'key1' => 'value1',
      'key2' => 'value2
    ]
  ]);

  // show the result!
  echo "Status Code: " . $response->getStatusCode() . "\n"; 
  echo "Body: " . $response->getBody();
} catch (Exception $e) {
  echo "Error: " . $e->getMessage();
}

前記事で Cloud Run にデプロイしたサービスに JSON データを送ってみます。

まず意図的にエラーを出してみました。受信側のキーは asin ですので key1, key2 と名前も数も違ったデータを送りますとエラーになります。

2回めは “asin:12345678” を送っていますので成功しています。

ということで一歩進みました。