TeemuK
TeemuK

Reputation: 2529

AWS Billing Report S3 bucket grant read-permissions to another account

So as the title suggests this is a very specific error that I've managed to find. We have one account that holds all the billing data and which is then stored to S3 bucket as hourly reports.

Then however we have another account that has an EC2 instance for which we'd like to grant GetObject permissions on our billing-bucket. Yet I am unable to do so, is it because AWS CloudWatch as it uploads the reports to S3 it does not set the acl to bucket-owner-full-control? I am at loss why this is happening.

fatal error: An error occurred (403) when calling the HeadObject operation: Forbidden

That is the error as I try to copy a hourly-Manifest.json object from the billing bucket using my EC2 instance's AWS-CLI.

The permissions are set from the billing-account as following where the Principal is the second account root.

{
      "Sid": "ClaudiaReadOnly",
      "Effect": "Allow",
      "Principal": {
          "AWS": "arn:aws:iam::xxxxxxxxxxxx:root"
      },
      "Action": [
        "s3:Get*",
        "s3:List*"
      ],
      "Resource": [
        "arn:aws:s3:::billing-bucket",
        "arn:aws:s3:::billing-bucket/billing/*"
      ]
}

The second account grants permissions to the EC2 instance using CloudFormation template with again same Get* and List* permissions.

If we set single object's Read-permission directly from the console for the second account we are able to copy it using the EC2 instance's AWS-CLI. But then for each other object we get the same 403 error.

Very frustrating to debug this issue and I wish AWS had little better information about this matter...

Upvotes: 2

Views: 1946

Answers (1)

mixja
mixja

Reputation: 7467

The problem here is that a different account (AWS Billing) creates your billing reports in your billing bucket, and this leads you into the murky world of S3 object ACLs - see https://docs.aws.amazon.com/AmazonS3/latest/dev/acl-overview.html

If you were accessing the S3 bucket from your billing account, you won't have issues as the billing account is the bucket owner and is always granted access, however for other accounts, the only way to grant access is for either the writer (AWS Billing) to set appropriate ACLs on copy (this won't happen), or to retrospectively modify the ACLs.

Here is default object ACL that is created for a billing report:

$ aws s3api get-object-acl --bucket my-billing-bucket --key reports/xxx/xxx.json
{
    "Owner": {
        "DisplayName": "aws-billpresentation+artifact-storage",
        "ID": "aaaaaaaaaa"
    },
    "Grants": [
        {
            "Grantee": {
                "DisplayName": "aws-billpresentation+artifact-storage",
                "ID": "aaaaaaaaaa",
                "Type": "CanonicalUser"
            },
            "Permission": "FULL_CONTROL"
        },
        {
            "Grantee": {
                "DisplayName": "mybilling",
                "ID": "xxxxxxxxxxx",
                "Type": "CanonicalUser"
            },
            "Permission": "FULL_CONTROL"
        }
    ]
}

IN the example above, aws-billpresentation+artifact-storage is the name of the AWS billing account that writes your reports, whilst mybiling is the name of the billing account where your bucket resides.

If you wanted to grant read access to another account, you would need to find out the canonical ID of that account and then add a READ grant, repeating this for every file. For example:

aws s3api put-object-acl --bucket my-billing-bucket --key reports/xxx/xxx.json --access-control-policy '{
 "Owner": {
     "DisplayName": "aws-billpresentation+artifact-storage",
     "ID": "aaaaaaaa"
 },
 "Grants": [
     {
         "Grantee": {
             "DisplayName": "aws-billpresentation+artifact-storage",
             "ID": "aaaaaaaa",
             "Type": "CanonicalUser"
         },
         "Permission": "FULL_CONTROL"
     },
     {
         "Grantee": {
             "DisplayName": "mybilling",
             "ID": "xxxxxxxx",
             "Type": "CanonicalUser"
         },
         "Permission": "FULL_CONTROL"
     },
     {
         "Grantee": {
             "DisplayName": "myotheraccount",
             "ID": "yyyyyyyy",
             "Type": "CanonicalUser"
         },
         "Permission": "READ"
     }
   ]
}'

The simplest option is to publish reports into the account where you want to actually process the reports, which avoids all of the S3 object ACL issues.

Upvotes: 2

Related Questions