EC2实例PHP程序调用SecretsManager遇400权限错误求助
问题
我在EC2实例上编写了一个独立PHP程序,用于从SecretsManager读取密钥(EC2实例与密钥处于同一区域),AWS SDK PHAR包与程序在同一目录。程序代码如下:
<?php require 'aws.phar'; // Include the AWS SDK for PHP use Aws\SecretsManager\SecretsManagerClient; use Aws\Sts\StsClient; use Aws\Exception\AwsException; $client = new SecretsManagerClient([ 'version' => 'latest', 'region' => 'us-west-1', ]); $result = $client->getSecretValue([ 'SecretId' => 'prod/vserver/api-keys', ]); $secretString = $result['SecretString'];
我希望EC2实例无需提供显式AWS凭证即可获取密钥,已完成以下配置:
- 创建了IAM策略:
{ "Version": "2012-10-17", "Statement": [ { "Sid": "AllowReadSecrets", "Effect": "Allow", "Action": "secretsmanager:GetSecretValue", "Resource": "arn:aws:secretsmanager:us-west-1:ACCOUNTID:secret:prod/vserver/api-keys" } ] }
- 创建名为
EC2_ACCESS_SECRETSMANAGER的IAM角色,将上述策略附加到该角色,并为运行PHP程序的EC2实例关联此角色。
但运行程序时出现400权限拒绝错误:
Next Aws\SecretsManager\Exception\SecretsManagerException: Error executing "GetSecretValue" on "https://secretsmanager.us-west-1.amazonaws.com"; AWS HTTP error: Client error:
POST https://secretsmanager.us-west-1.amazonaws.comresulted in a400 Bad Requestresponse:
{"__type":"AccessDeniedException","Message":"User: arn:aws:sts::<AWS_ACCOUNT_ID>:assumed-role/EC2_ACCESS_SECRETSMANAGER/i-08 (truncated...)
AccessDeniedException (client): User: arn:aws:sts::<AWS_ACCOUNT_ID>:assumed-role/EC2_ACCESS_SECRETSMANAGER/ is not authorized to perform: secretsmanager:GetSecretValue on resource: prod/vserver/api-keys because no identity-based policy allows the secretsmanager:GetSecretValue action - {"__type":"AccessDeniedException","Message":"User: arn:aws:sts::<AWS_ACCOUNT_ID>:assumed-role/EC2_ACCESS_SECRETSMANAGER/ is not authorized to perform: secretsmanager:GetSecretValue on resource: prod/vserver/api-keys because no identity-based policy allows the secretsmanager:GetSecretValue action"} in phar:///var/www/html/aws.phar/Aws/WrappedHttpHandler.php:195
注:<AWS_ACCOUNT_ID>为占位符。
解决方案
1. 替换IAM策略中的账户ID占位符
策略中Resource字段的ACCOUNTID是占位符,必须替换为你的实际AWS账户ID。AWS无法识别占位符,会导致策略无法匹配目标资源,触发权限拒绝。
修正后的策略示例:
{ "Version": "2012-10-17", "Statement": [ { "Sid": "AllowReadSecrets", "Effect": "Allow", "Action": "secretsmanager:GetSecretValue", "Resource": "arn:aws:secretsmanager:us-west-1:123456789012:secret:prod/vserver/api-keys" } ] }
(将123456789012替换为你的实际账户ID)
2. 匹配Secrets Manager密钥的完整ARN
AWS Secrets Manager创建密钥时,会自动为ARN追加随机后缀(例如prod/vserver/api-keys-abc123),如果你的密钥实际ARN包含该后缀,策略的Resource字段必须完全匹配完整ARN,不能只写前缀prod/vserver/api-keys。
你可以在Secrets Manager控制台找到目标密钥的完整ARN,替换到策略的Resource字段中。
3. 验证EC2实例的角色关联状态
- 登录EC2实例,执行以下命令确认实例已获取目标角色:
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/
如果返回EC2_ACCESS_SECRETSMANAGER,说明角色关联正常;如果返回空或其他角色名,需要重新为EC2实例关联该角色。
- 进一步验证权限:
先获取实例的临时凭证:
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/EC2_ACCESS_SECRETSMANAGER
用返回的AccessKeyId、SecretAccessKey和Token执行AWS CLI命令测试:
aws secretsmanager get-secret-value --secret-id prod/vserver/api-keys --region us-west-1 --access-key-id <AccessKeyId> --secret-access-key <SecretAccessKey> --session-token <Token>
如果CLI也报错,说明权限配置存在问题;如果CLI能成功获取密钥,再排查PHP程序的逻辑。
4. 确保PHP SDK使用实例角色凭证
AWS SDK for PHP默认会优先读取环境变量中的凭证(如AWS_ACCESS_KEY_ID、AWS_SECRET_ACCESS_KEY),如果实例上存在这些环境变量,会导致SDK忽略EC2角色凭证。
可以在程序中强制指定使用实例元数据凭证提供者:
$client = new SecretsManagerClient([ 'version' => 'latest', 'region' => 'us-west-1', 'credentials' => new Aws\Credentials\InstanceProfileCredentials() ]);
内容的提问来源于stack exchange,提问作者Rahul Saini

