How to Grant EC2 IAM Role Read Access to S3 Bucket in AWS Elastic Beanstalk


2 views

When deploying a Rails application through AWS Elastic Beanstalk, a common challenge arises when EC2 instances attempt to access resources in S3 buckets. The error typically manifests as:

HTTP Error 403 : <?xml version="1.0" encoding="UTF-8"?>
<Error><Code>AccessDenied</Code><Message>Access Denied</Message>

The setup described includes:

  • An Elastic Beanstalk environment using aws-elasticbeanstalk-ec2-role
  • An IAM policy attached to this role granting S3 read permissions
  • A bucket policy attempting to allow access to the EC2 role

Several factors could be causing the access denial:

// Potential issue 1: Bucket policy principal format
"Principal": {
    "AWS": "arn:aws:iam:::role/aws-elasticbeanstalk-ec2-role"
}

// Potential issue 2: Resource ARN format
"Resource": "arn:aws:s3:::my.bucket/*"

Here's a comprehensive fix that addresses multiple potential issues:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::ACCOUNT_ID:role/aws-elasticbeanstalk-ec2-role"
      },
      "Action": [
        "s3:GetObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::my.bucket",
        "arn:aws:s3:::my.bucket/*"
      ]
    }
  ]
}

When configuring S3 access for EC2 roles:

  • Always specify both bucket and object permissions separately
  • Use the full ARN format including account ID
  • Consider adding conditionals for enhanced security:
"Condition": {
    "IpAddress": {"aws:SourceIp": ["EC2_INSTANCE_IP_RANGE"]},
    "StringEquals": {"aws:Referer": "REFERER_VALUE"}
}

After applying changes:

  1. SSH into your EC2 instance
  2. Run AWS CLI commands to test access:
aws s3 ls s3://my.bucket/
aws s3 cp s3://my.bucket/bootstrap.sh /tmp/test-file

If the solution still doesn't work:

  • Create a dedicated IAM role instead of using the default EB role
  • Consider temporary credentials via Instance Metadata Service
  • Verify VPC endpoints if using private subnets

When your Elastic Beanstalk EC2 instances fail to access S3 resources with HTTP 403 errors, it typically indicates an IAM permissions mismatch. Let's break down the complete solution.

For EC2 instances to access S3, you need both:

  1. IAM role permissions attached to the EC2 instance
  2. S3 bucket policy granting access to that IAM role

First, check if your EC2 instance has the correct IAM role assigned:

aws ec2 describe-instances --instance-ids i-1234567890abcdef0 \
--query 'Reservations[].Instances[].IamInstanceProfile.Arn'

Even with seemingly correct policies, these issues often cause problems:

  • Region-specific S3 endpoints not covered
  • Missing s3:ListBucket permission on the bucket ARN itself
  • Conflicting Deny statements in other policies

Here's the corrected IAM policy for your EC2 role:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::my.bucket",
        "arn:aws:s3:::my.bucket/*"
      ]
    }
  ]
}

And the corresponding S3 bucket policy:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::123456789012:role/aws-elasticbeanstalk-ec2-role"
      },
      "Action": [
        "s3:GetObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::my.bucket",
        "arn:aws:s3:::my.bucket/*"
      ]
    }
  ]
}

Test permissions directly from the EC2 instance:

aws s3 ls s3://my.bucket/
aws s3 cp s3://my.bucket/bootstrap.sh /tmp/test-download

For EB environments, also ensure:

  • The IAM role is specified in your EB environment configuration
  • No service control policies (SCPs) are restricting access
  • Bucket ownership and ACLs aren't causing conflicts

If IAM roles aren't working, consider temporary alternatives:

# Using instance profile credentials
export AWS_ACCESS_KEY_ID=$(curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/ | xargs -I {} curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/{})
export AWS_SECRET_ACCESS_KEY=$(curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/$ROLE | jq -r '.SecretAccessKey')
export AWS_SESSION_TOKEN=$(curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/$ROLE | jq -r '.Token')