はじめに (対象読者・この記事でわかること)
この記事は、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での実装例:
Javascriptconst 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での実装例:
Phprequire '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での実装例:
Javascriptconst {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版では完全な互換性が確保されていません。
-
認証情報の形式の違い: Node.jsとPHPでAWS SDKが認証情報を処理する方法に微妙な違いがあります。特に、セッショントークンの取り扱いにおいて差異が見られます。
-
HTTPリクエストのヘッダー設定: PHPのHTTPクライアントがデフォルトで設定するヘッダーと、GCPが期待するヘッダーに不一致がある可能性があります。
解決策
解決策1: AWS SDKのバージョンを統一する
PHPで使用するAWS SDKのバージョンを、Node.jsで使用しているものと同じバージョンに統一します。これにより、認証情報の処理方法が一致しやすくなります。
Phpcomposer 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を署名する方法もあります。
Phpuse 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の連携をスムーズに行えるようになったことが読者のメリットとなります。今後は、認証情報のセキュアな管理方法や、より大規模なシステムでの実装例についても記事にする予定です。
参考資料
- AWS STSドキュメント
- Google Auth Library for PHP
- Google Cloud IAMドキュメント
- AWS SDK for PHPドキュメント
- PHPでAWSとGCP連携する際のベストプラクティス
