向S3 Bucket发送Curl请求时遭遇访问拒绝的技术咨询
我正尝试从EC2实例向S3 Bucket发送Curl请求以获取桶内特定对象:
- 由于需要搭建由NGINX实现缓存的透明代理,因此无法使用AWS CLI。
- 该EC2实例(Linux机器)作为代理服务器,通过NGINX向Bucket发送HTTP请求以实现缓存,实例上未配置SSL证书。
- Bucket内仅存储图片。
我的Curl请求如下:
curl my-bucket.s3.eu-west-1.amazonaws.com/1450/1349/5467_1012.jpg
但我收到了Access Denied错误。
我已为EC2实例角色附加了全读取权限策略,Bucket策略如下:
{ "Version": "2012-10-17", "Id": "MediaStorageBucketPolicy", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::${current_account}:root" }, "Action": [ "s3:GetObject", "s3:ListBucket" ], "Resource": [ "${media_storage_bucket_arn}", "${media_storage_bucket_arn}/*" ] }, { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::${current_account}:root" }, "Action": [ "s3:PutObject" ], "Resource": [ "${media_storage_bucket_arn}", "${media_storage_bucket_arn}/*" ] }, { "Effect": "Allow", "Principal": { "AWS": [ "arn:aws:iam::<account number>:role/ssm-ec2-service-role" ] }, "Action": [ "s3:GetObject", "s3:ListBucket" ], "Resource": [ "${media_storage_bucket_arn}", "${media_storage_bucket_arn}/*" ] } ] }
请问能否在不公开Bucket的前提下实现该需求?
1. 修复Bucket策略的角色ARN格式错误
你的Bucket策略中角色ARN存在格式问题:arn:aws:I am::<account number>:role/ssm-ec2-service-role 里多了空格,正确格式应为 arn:aws:iam::<account number>:role/ssm-ec2-service-role。这个错误会导致策略无法识别目标角色,直接触发权限拒绝。
2. 确认EC2实例绑定正确角色
- 登录EC2控制台,找到目标实例,查看「安全」标签下的IAM角色是否为
ssm-ec2-service-role。 - 检查该角色的信任策略是否允许EC2服务扮演它,信任策略需包含:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "ec2.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }
3. 为Curl请求添加AWS签名
直接用curl发起的普通HTTP请求不会自动携带EC2实例角色的凭证,S3无法识别请求身份,因此会返回拒绝。需生成带签名的请求,以下是几种可行方式:
方式A:用AWS CLI生成带签名的临时URL
虽然无法直接用AWS CLI操作,但可以用它生成带签名的URL供curl或NGINX使用:
aws s3 presign s3://my-bucket/1450/1349/5467_1012.jpg --expires-in 3600
该命令会生成一个有效期1小时的签名URL,直接用curl访问即可。你可以将此逻辑集成到NGINX配置中,比如通过脚本动态生成URL后转发。
方式B:用脚本生成签名请求
如果是Python环境,可使用requests-aws4auth库自动生成带签名的请求,示例代码:
import requests from requests_aws4auth import AWS4Auth aws_auth = AWS4Auth('<account number>', 'eu-west-1', 's3') url = 'http://my-bucket.s3.eu-west-1.amazonaws.com/1450/1349/5467_1012.jpg' response = requests.get(url, auth=aws_auth)
可将此脚本封装为内部API服务,让NGINX代理到该服务获取签名后的请求结果。
方式C:使用NGINX AWS签名模块
第三方NGINX模块(如ngx_http_aws_sign)可自动为转发到S3的请求添加AWS签名,无需手动处理,直接利用EC2实例角色的凭证完成身份验证。
4. 验证权限有效性
在EC2实例上执行以下命令,确认角色权限是否正常:
aws s3api get-object --bucket my-bucket --key 1450/1349/5467_1012.jpg test.jpg
如果命令成功,说明角色权限没问题,问题出在curl请求未签名;如果失败,需重新检查角色权限和Bucket策略配置。
5. 保持Bucket阻止公共访问设置
确保Bucket的「阻止公共访问」功能处于开启状态,这不会影响IAM角色的访问,同时符合不公开Bucket的要求。
内容的提问来源于stack exchange,提问作者Tegue Morrison

