IAM Role Policy that limits access to a ec2-instance-named s3 folder (via aws:username?)

0

I'm attempting to create s3 folders (prefixes) within a bucket that is only accessible to specific EC2 instances via IAM Role policies based on their name. The idea would be something like s3://mybucket/i-1234567890/* would only be accessible to EC2 instance i-1234567890 and s3://mybucket/i-987654321/* would only be accessible to EC2 instance i-987654321. Rather then create a custom IAM Role for each instance (they are pets), I thought I might be able to use policy variables to limit access to the prefix. It looks like the only variable this may work with is aws:username, which with IAM Role is set to be role-id:ec2-instance-id according to https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_variables.html#policy-vars-infotouse . So I created a folder named "AROAxxxxxxxxxxxx:i-1234567890/" in that s3 bucket and attached this policy to the IAM Role:

{	"Version": "2012-10-17",
	"Statement": [ {
			"Sid": "AllowListingOfInstanceFolder",
			"Effect": "Allow",
			"Action": "s3:ListBucket",
			"Resource": "arn:aws:s3:::mybucket",
			"Condition": {	"StringLike": { "s3:prefix": "${aws:username}/*" }			}
		},
		{	"Sid": "AllowAllS3ActionsInInstanceFolder",
			"Effect": "Allow",
			"Action": "s3:*",
			"Resource": "arn:aws:s3:::mybucket/${aws:username}/*"
		}
	]
}

I got the Role ID by running this powershell command: (Get-IAMRole <RoleName>).RoleId

I'm unable to get this working. If I hardcode "AROAxxxxxxxxxxxxxx:i-xxxxxxxxxxxxxx" in place of ${aws:username} it works as expected. It is as if ${aws:username} is not returning what I expect it to be. I'm unable to determine what is returning instead though.

I setup CloudTrail Trail on the s3 bucket and did a aws s3 cp download attempt. The log shows the correct RoleId:InstanceId as the PrincipalId but I get access denied.

    {
      "userIdentity": {
        "type": "AssumedRole",
        "principalId": "AROAxxxxxxxxxxxxxx:i-xxxxxxxxxxxxxx",
        "arn": "arn:aws:sts::xxxxxxxxxxxxxx:assumed-role/ROLENAME/i-xxxxxxxxxxxxxx",
        "sessionContext": {
          "sessionIssuer": {
            "type": "Role",
            "principalId": "AROAxxxxxxxxxxxxxx",
            "arn": "arn:aws:iam::xxxxxxxxxxxxxx:role/ROLENAME",
            "userName": "ROLENAME"
          }
        }
      },
      "eventSource": "s3.amazonaws.com",
      "eventName": "HeadObject",
      "errorCode": "AccessDenied",
      "errorMessage": "User: arn:aws:sts::xxxxxxxxxxxxxx:assumed-role/ROLENAME/i-xxxxxxxxxxxxxx is not authorized to perform: s3:GetObject on resource: \"arn:aws:s3:::MYBUCKET/AROAxxxxxxxxxxxxxx:i-xxxxxxxxxxxxxx/test.txt\" because no identity-based policy allows the s3:GetObject action",
      "requestParameters": {
        "bucketName": "MYBUCKET",
        "Host": "MYBUCKET.s3.us-east-1.amazonaws.com",
        "key": "AROAxxxxxxxxxxxxxx:i-xxxxxxxxxxxxxx/test.txt"
      },
      "resources": [
        {
          "type": "AWS::S3::Object",
          "ARN": "arn:aws:s3:::MYBUCKET/AROAxxxxxxxxxxxxxx:i-xxxxxxxxxxxxxx/test.txt"
        },
        {
          "type": "AWS::S3::Bucket",
          "ARN": "arn:aws:s3:::MYBUCKET"
        }
      ],
      "eventType": "AwsApiCall",
      "managementEvent": false,
      "eventCategory": "Data"
    }

removed some properties to save space

Is there a way to determine what aws:username is exactly returning in this case? I'm also not tied to using aws:username if there is another variable that would help tie the ec2 instance to a folder. I did try using ec2:* variables and tags but since this is a s3 policy I don't think that will work. This is basically attempting to implement that is brought up in this stackoverflow question but I can't get it to work. https://stackoverflow.com/questions/67564420/is-using-tag-values-to-control-access-to-a-resource-possible

1 Answer
2
Accepted Answer

Use ${aws:userid} instead of ${aws:username}. The ${aws:userid} variable resolves to the format RoleId:InstanceId, which matches the S3 prefix you want to control.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowListingOfInstanceFolder",
      "Effect": "Allow",
      "Action": "s3:ListBucket",
      "Resource": "arn:aws:s3:::mybucket",
      "Condition": {
        "StringLike": {
          "s3:prefix": "${aws:userid}/*"
        }
      }
    },
    {
      "Sid": "AllowAllS3ActionsInInstanceFolder",
      "Effect": "Allow",
      "Action": "s3:*",
      "Resource": "arn:aws:s3:::mybucket/${aws:userid}/*"
    }
  ]
}

Apply the updated IAM policy.

Ensure your S3 bucket has the appropriate folder structure.

Test the access from your EC2 instances to verify it works as expected

profile pictureAWS
EXPERT
Deeksha
answered 19 days ago
profile picture
EXPERT
reviewed 13 days ago
profile picture
EXPERT
reviewed 17 days ago
EXPERT
Leo K
reviewed 19 days ago
  • HA! Of course it's something obvious I've completely missed. Changing it to userid worked immediately. Thanks!

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.

Guidelines for Answering Questions