はじめに (対象読者・この記事でわかること)

この記事は、AWSとGCPの両方を利用していて、クラウドサービス間の連携を実装している開発者、特にPHPで開発を行っているエンジニアを対象としています。

この記事を読むことで、AWS STS(Security Token Service)を利用してGCPリソースにアクセスする方法の基本を理解し、Node.jsでは正常に動作するがPHPではエラーになる問題の原因を把握できるようになります。さらに、実際の現場で役立つ具体的な解決策とコード例を習得し、PHP環境でもAWSとGCPの連携をスムーズに行えるようになります。

前提知識

この記事を読み進める上で、以下の知識があるとスムーズです。 - AWSとGCPの基本的なアーキテクチャと用語 - IAM(Identity and Access Management)の基本概念 - APIキーやサービスアカウントの利用経験 - PHPとNode.jsの基本的なプログラミング知識 - cURLやHTTPクライアントライブラリの利用経験

AWS STSとGCP連携の基本と問題の概要

AWSのSecurity Token Service(STS)は、一時的な認証情報を取得するためのサービスです。これを利用することで、AWSの認証情報を使って他のクラウドサービス(この場合はGCP)にアクセスすることが可能になります。

多くの開発者が利用するNode.js環境では、この連携は比較的スムーズに実装できます。しかし、PHP環境で同じ処理を実装しようとすると、一見同じコードでもエラーが発生するケースが頻繁に見られます。

この問題の主な原因は、AWSとGCPの認証情報形式の違いと、PHPのHTTPクライアントライブラリが持つデフォルトの動作にあります。Node.jsではAWS SDKが内部で適切に処理してくれる部分を、PHPでは手動で実装する必要があるケースがあるのです。

特に、AWS STSが返すトークン形式と、GCPが期待する認証情報形式の変換処理や、HTTPリクエストのヘッダー設定などがエラーの原因となりやすい点に注意が必要です。

具体的な手順と問題解決

ステップ1:AWS STSで一時的な認証情報を取得する

まず、AWS STSを利用して一時的な認証情報を取得する必要があります。Node.jsとPHPで実装してみましょう。

Node.jsでの実装例:

Javascript
const AWS = require('aws-sdk'); const sts = new AWS.STS(); const params = { RoleArn: 'arn:aws:iam::AWS_ACCOUNT_ID:role/ROLE_NAME', RoleSessionName: 'gcp-access-session' }; sts.assumeRole(params, (err, data) => { if (err) console.log(err, err.stack); else { const credentials = data.Credentials; console.log('Access Key:', credentials.AccessKeyId); console.log('Secret Access Key:', credentials.SecretAccessKey); console.log('Session Token:', credentials.SessionToken); } });

PHPでの実装例:

Php
require 'vendor/autoload.php'; use Aws\Sts\StsClient; $client = new StsClient([ 'version' => 'latest', 'region' => 'us-east-1' ]); $result = $client->assumeRole([ 'RoleArn' => 'arn:aws:iam::AWS_ACCOUNT_ID:role/ROLE_NAME', 'RoleSessionName' => 'gcp-access-session' ]); $credentials = $result['Credentials']; echo "Access Key: " . $credentials['AccessKeyId'] . "\n"; echo "Secret Access Key: " . $credentials['SecretAccessKey'] . "\n"; echo "Session Token: " . $credentials['SessionToken'] . "\n";

このステップでは、Node.jsとPHPのどちらでも正常に一時認証情報を取得できるはずです。

ステップ2:取得した認証情報を使ってGCPリソースにアクセスする

次に、取得した一時認証情報を使ってGCPリソースにアクセスします。

Node.jsでの実装例:

Javascript
const {GoogleAuth} = require('google-auth-library'); const credentials = { accessKeyId: ACCESS_KEY, secretAccessKey: SECRET_KEY, sessionToken: SESSION_TOKEN }; const auth = new GoogleAuth(); const client = auth.fromAWS(credentials); // GCP APIへのアクセス client.request({ url: `https://iam.googleapis.com/v1/projects/PROJECT_ID/serviceAccounts/EMAIL:signJwt`, method: 'POST', data: { payload: 'PAYLOAD' } }).then(response => { console.log('Response:', response.data); }).catch(err => { console.error('Error:', err); });

PHPでの実装例:

Php
$credentials = [ 'key' => ACCESS_KEY, 'secret' => SECRET_KEY, 'token' => SESSION_TOKEN ]; $auth = new Google\Auth\AWSCredentials($credentials); $client = new Google\Client(); $client->setAuthConfig($auth); // GCP APIへのアクセス $response = $client->post('https://iam.googleapis.com/v1/projects/PROJECT_ID/serviceAccounts/EMAIL:signJwt', [ 'json' => [ 'payload' => 'PAYLOAD' ] ]); echo "Response: " . $response->getBody() . "\n";

このステップで、Node.jsでは正常に動作するが、PHPではエラーが発生する問題が現れます。

ハマった点やエラー解決

エラーメッセージ例:

Error: Invalid credentials: The provided AWS credentials are not valid.

または

Error: Could not load the credentials: unable to locate credentials

これらのエラーは、AWS STSで取得した一時認証情報がGCP側で正しく認証されていないことを示しています。

問題の原因: 1. AWS SDKとGoogle Auth Libraryの互換性の問題: Node.jsのGoogle Auth LibraryはAWS SDKと互換性があるように設計されていますが、PHP版では完全な互換性が確保されていません。

  1. 認証情報の形式の違い: Node.jsとPHPでAWS SDKが認証情報を処理する方法に微妙な違いがあります。特に、セッショントークンの取り扱いにおいて差異が見られます。

  2. HTTPリクエストのヘッダー設定: PHPのHTTPクライアントがデフォルトで設定するヘッダーと、GCPが期待するヘッダーに不一致がある可能性があります。

解決策

解決策1: AWS SDKのバージョンを統一する

PHPで使用するAWS SDKのバージョンを、Node.jsで使用しているものと同じバージョンに統一します。これにより、認証情報の処理方法が一致しやすくなります。

Php
composer require aws/aws-sdk-php:3.x

解決策2: 認証情報の明示的な設定

AWS SDKで取得した認証情報を、Google Auth Libraryが期待する形式に明示的に変換します。

Php
$awsCredentials = [ 'key' => ACCESS_KEY, 'secret' => SECRET_KEY, 'token' => SESSION_TOKEN ]; // 明示的に認証情報を設定 $auth = new Google\Auth\AWSCredentials($awsCredentials); $auth->updateMetadata([ 'x-goog-iam-authorization' => 'Bearer ' . $auth->fetchAuthToken()['access_token'] ]);

解決策3: カスタムHTTPハンドラの実装

PHPのGoogle Auth Libraryが使用するHTTPハンドラをカスタマイズし、必要なヘッダーを明示的に設定します。

Php
$credentials = [ 'key' => ACCESS_KEY, 'secret' => SECRET_KEY, 'token' => SESSION_TOKEN ]; $auth = new Google\Auth\AWSCredentials($credentials); $httpClient = new GuzzleHttp\Client([ 'headers' => [ 'x-goog-api-client' => 'gl-php/7.4.0 gdcl/0.24.0' ] ]); $client = new Google\Client(); $client->setAuthConfig($auth); $client->setHttpClient($httpClient); $response = $client->post('https://iam.googleapis.com/v1/projects/PROJECT_ID/serviceAccounts/EMAIL:signJwt', [ 'json' => [ 'payload' => 'PAYLOAD' ] ]);

解決策4: 直接的なJWT署名の実装

GCPが提供するJWT署名ライブラリを直接利用し、AWS STSで取得した認証情報を使ってJWTを署名する方法もあります。

Php
use Firebase\JWT\JWT; use Firebase\JWT\Key; $credentials = [ 'key' => ACCESS_KEY, 'secret' => SECRET_KEY, 'token' => SESSION_TOKEN ]; // AWS STSの認証情報を使ってJWTを生成 $payload = [ 'iss' => EMAIL, 'scope' => 'https://www.googleapis.com/auth/cloud-platform', 'aud' => 'https://iam.googleapis.com/v1/projects/PROJECT_ID/serviceAccounts/EMAIL:signJwt', 'exp' => time() + 3600, 'iat' => time() ]; $jwt = JWT::encode($payload, $credentials['secret'], 'RS256'); // GCP APIに送信 $response = $client->post('https://iam.googleapis.com/v1/projects/PROJECT_ID/serviceAccounts/EMAIL:signJwt', [ 'json' => [ 'payload' => $jwt ] ]);

これらの解決策を組み合わせることで、PHP環境でもAWS STS経由でGCPリソースに正常にアクセスできるようになります。

まとめ

本記事では、AWS STSを利用してGCPリソースにアクセスする際に、Node.jsでは正常に動作するがPHPではエラーが発生する問題の原因と解決策について解説しました。

  • AWS STSで取得した一時認証情報をGCPで利用するには、Node.jsとPHPで実装方法に違いがある
  • 主な原因はAWS SDKとGoogle Auth Libraryの互換性の問題と、認証情報の形式の違い
  • 解決策として、AWS SDKのバージョン統一、認証情報の明示的な設定、カスタムHTTPハンドラの実装などがある
  • 直接的なJWT署名の実装も有効な手段となる

この記事を通して、クラウドサービス間連携における認証のベストプラクティスを理解し、PHP環境でもAWSとGCPの連携をスムーズに行えるようになったことが読者のメリットとなります。今後は、認証情報のセキュアな管理方法や、より大規模なシステムでの実装例についても記事にする予定です。

参考資料