简体   繁体   中英

Access Denied to Public S3 Bucket from certain IP addresses

I have tried to make a completely public S3 bucket and access using PHP SDK. This works when I run the access code below from my local machine:

require 'vendor/autoload.php';

use Aws\S3\S3Client;
use Aws\S3\Exception\S3Exception;

$s3 = new S3Client([
    'version' => 'latest',
    'region'  => 'eu-west-2',
    'credentials' => [
        'key'    => "my correct key",
        'secret' => "my correct secret"
        ]
]);

// //publictest2
$bucket = 'mybucketname';
$keyname = 'test_file.txt';

// Upload an object
try {
    // Upload data.
    $result = $s3->putObject([
        'Bucket' => $bucket,
        'Key'    => $keyname,
        'Body'   => 'Hello',
        'ACL'    => 'public-read'
    ]);

    // Print the URL to the object.
    echo $result['ObjectURL'] . PHP_EOL;
} catch (S3Exception $e) {
    echo $e->getMessage() . PHP_EOL;
}

However, as soon as I run it from the test server (an AWS EC2) I get the below error:

Error executing "PutObject" on "mybucketname"; AWS HTTP error: Client error: `PUT https://mybucketname/test_file.txt` resulted in a `403 Forbidden` response: AccessDeniedAccess DeniedVTJX7V (truncated...) AccessDenied (client): Access Denied - AccessDeniedAccess DeniedVTJX7V4CCKZYG7CRTz+dPA7fsZQnFxTERKxxbP+IpTtMIIsS1uu23fvTruHH3w8KxwGIduCntRBM5u6tIfHdusbCoPw=

I have already implemented the following to make the bucket public:

    {
    "Version": "2012-10-17",
    "Id": "S3BukcetPolicyIPAccess",
    "Statement": [
        {
            "Sid": "DenyIfNotFromAllowedVPC",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:*",
            "Resource": [
                "mybucketarn",
                "mybucketarn/*"
            ]
        }
    ]
}

Why is this still not working when run from my EC2 server?

This is not to do with the S3 bucket - the hint here is that it works from your local machine, meaning that the S3 bucket isn't to blame but the instance.

It's to do with the role that your EC2 instance is assuming (or not assuming) - create & assign an IAM role that has access to perform PutObject on mybucketname .

One quick way to test this would be to use a role with the AWS-managed policy AmazonS3FullAccess attached which provides full access to all S3 buckets.

Your EC2 instance will then have access to the S3 bucket.

for HTTP 403 Forbidden error debug these below steps.

  1. Missing permissions to s3:PutObject or s3:PutObjectAcl
  2. Missing permissions to use an AWS Key Management Service (AWS KMS) key
  3. Explicit deny statement in the bucket policy
  4. Bucket access control list (ACL) doesn't allow the AWS account root user to write objects

could you please check this command from your ec2 instance

"aws s3 ls s3://doc-example-bucket/abc/"

If this command is successful, then the credentials or role specified in your application code are causing the "Access Denied" error. Be sure that the instance profile role has the required read and write permissions for the S3 buckets. For example, the S3 actions in the following IAM policy provides the required read and write access to the S3 bucket.

Your problem is that the PHP web-server running on your EC2 instance uses AWS PHP SDK and does NOT have an IAM principal , because there's neither an instance profile (AWS role) attached to your EC2, nor a default profile (AWS user) configured with aws configure .

In other words your AWS API calls are not authenticated, let alone authorized to perform any actions against S3. Even if you allow s3:* for all Principals in the Bucket policy. Remember: the Bucket policy expects an AWS principal, but your request is not authenticated (AWS API can't figure out who is calling). Hence AccessDenied .

There are several solutions to your problem. But all of them can be boiled down to:

  • Using IAM policy (recommended)

  • Using S3 bucket policy

You can check out this AWS blog post as to which approach to follow, but for your particular problem I would go with IAM policies.


Using S3 bucket policy

Let me start with the second solution first.

  1. Create a bucket. Disable all public access to it.
  2. Attach the following S3 bucket policy:
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Statement1",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::xxxxxxxxxxx:role/ec2-instance-role"
            },
            "Action": "s3:*",
            "Resource": [
                "arn:aws:s3:::yyyyyyyyyyy",
                "arn:aws:s3:::yyyyyyyyyyy/*"
            ]
        }
    ]
}
  1. Create a new IAM role named ec2-instance-role . You do NOT need any IAM policies attached to it. Leave it blank.

  2. Go to your EC2 instance, click Actions -> Security -> Modify IAM role and select the role created in step 3.

  3. No need to restart the instance. To verify everything works as expected I am using AWS CLI, but it should work with any SDK (eg PHP) as well. Execute from your EC2 instance:

# Make sure the correct role is assumed
$ aws sts get-caller-identity
{
    "Account": "xxxxxxxxxxx",
    "UserId": "AROAYGJILC6K4AJRGLZTS:i-0aa422c12359ac00e",
    "Arn": "arn:aws:sts::xxxxxxxxxxx:assumed-role/ec2-instance-role/i-0aa422c12359ac00e"
}

# Upload a test file and check the bucket
$ touch test.txt
$ aws s3 cp test.txt s3://yyyyyyyyyyy/test.txt
upload: ./test.txt to s3://yyyyyyyyyyy/test.txt
$ aws s3 ls s3://yyyyyyyyyyy
2021-12-12 11:01:51        0 test.txt

Using IAM policies

Here, everything stays the same with the exception that you do not need any Bucket policy, but you attach the policy directly to the IAM instance profile (role).

  1. Create a bucket. Disable all public access to it. Disable ACL.

  2. Create a new IAM role named ec2-instance-role with the following policy:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Statement1",
            "Effect": "Allow",
            "Action": "s3:*",
            "Resource": [
                "arn:aws:s3:::yyyyyyyyyyy",
                "arn:aws:s3:::yyyyyyyyyyy/*"
            ]
        }
    ]
}
  1. Go to your EC2 instance, click Actions -> Security -> Modify IAM role and select the role created in the previous step.

  2. To verify everything works as expected I am using AWS CLI, but it should work with any SDK (eg PHP) as well. Execute from your EC2 instance:

# Make sure the correct role is assumed
$ aws sts get-caller-identity
{
    "Account": "xxxxxxxxxxx",
    "UserId": "AROAYGJILC6K4AJRGLZTS:i-0aa422c12359ac00e",
    "Arn": "arn:aws:sts::xxxxxxxxxxx:assumed-role/ec2-instance-role/i-0aa422c12359ac00e"
}

# Upload a test file and check the bucket
$ touch test.txt
$ aws s3 cp test.txt s3://yyyyyyyyyyy/test.txt
upload: ./test.txt to s3://yyyyyyyyyyy/test.txt
$ aws s3 ls s3://yyyyyyyyyyy
2021-12-12 11:01:51        0 test.txt

Last but not least for both approaches: do not use ACLs (configure your bucket with ACLs disabled ) since ACLs exist for backward compatibility in favor of Bucket policies, see details in the same blog post :

As a general rule, AWS recommends using S3 bucket policies or IAM policies for access control. S3 ACLs is a legacy access control mechanism that predates IAM.


In the above snippets:

  • xxxxxxxxxxx is your AWS account number, eg 561262107623
  • yyyyyyyyyyy is your bucket name, eg mybucket

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM