I'm building an app that uses Amazon's Security Token Service to create temporary users to access a subdirectory on an S3 bucket. The users are created by an IAM user that has full read/write access to the bucket (as well as the permissions necessary to create users).
I have the creation of users working perfectly along with session expiration and more, but I'm having problems getting the right policy in place to allow for prefix-based listing of keys. The permissions that I want the end user to have are:
I managed to get read and write working but somehow no matter what I try the list access isn't working properly. Here's the Ruby code that I was using when I was closest:
AWS::STS::Policy.new do |policy|
policy.allow(
actions: ["s3:GetObject*", "s3:PutObject*", "s3:DeleteObject*"],
resources: "arn:aws:s3:::#{ENV['PROJECT_BUCKET']}/#{folder_path}/*"
)
policy.allow(
actions: ["s3:*"],
resources: ["arn:aws:s3:::#{ENV['PROJECT_BUCKET']}/*", "arn:aws:s3:::#{ENV['PROJECT_BUCKET']}"]
).where(:s3_prefix).like("#{folder_path}/*")
end
If I recall, this allowed me to do reading and writing but not listing. Since I'm still in development I've changed the code to this:
AWS::STS::Policy.new do |policy|
# FIXME: This is way too permissive, but it's not working to be more specific.
policy.allow(
actions: ["s3:*"],
resources: ["arn:aws:s3:::#{ENV['PROJECT_BUCKET']}/*", "arn:aws:s3:::#{ENV['PROJECT_BUCKET']}"]
)
end
This works 100% fine with the obvious problem that nothing is constrained to a prefix which would allow users to clobber each other's work.
What am I doing wrong in my policy?
Short description. The "403 Access Denied" error can occur due to the following reasons: Your AWS Identity and Access Management (IAM) user or role doesn't have permissions for both s3:GetBucketPolicy and s3:PutBucketPolicy.
S3 bucket policies (as the name would imply) only control access to S3 resources, whereas IAM policies can specify nearly any AWS action.
If you're getting Access Denied errors on public read requests that are allowed, check the bucket's Amazon S3 Block Public Access settings. Review the S3 Block Public Access settings at both the account and bucket level. These settings can override permissions that allow public read access.
You can do this by using policy variables, which allow you to specify placeholders in a policy. When the policy is evaluated, the policy variables are replaced with values that come from the request itself. Note: Only StringLike recognizes an asterisk (*) as wildcard. StringEquals doesn't.
To expand on Bob Kinney's referenced article and fragments (+1), I'd like to explain what I consider the likely cause of your problem, which is actually not related to using the AWS Security Token Service (STS), but involves a few subtleties frequently encountered with Amazon S3 IAM policies in general:
The Example Policies for Amazon S3 cover various use cases similar or related to yours - specifically your use cases apparently includes Example 2: Allow a group to have a shared folder in Amazon S3 - you have effectively implemented that in the first policy of the first fragment already (modulo GetObjectVersion
, DeleteObjectVersion
, which are only relevant when using Object Versioning).
What's missing now is ListBucket - please note the following subtleties:
prefix
parameter Limits the response to keys which begin with the indicated prefix. You can use prefixes to separate a bucket into different sets of keys in a way similar to how a file system uses folders., which implies that a prefix cannot contain a wildcard specification, or rather *
is just treated as part of the name, see What characters are allowed in a bucket or object name?.
Many use cases like yours therefore require two distinct policy fragments to separately address the object and the bucket related operations, accordingly you'll likely need something like the following:
AWS::STS::Policy.new do |policy|
policy.allow(
actions: ["s3:GetObject*", "s3:PutObject*", "s3:DeleteObject*"],
resources: "arn:aws:s3:::#{ENV['PROJECT_BUCKET']}/#{folder_path}/*"
)
policy.allow(
actions: ["s3:ListBucket"],
resources: ["arn:aws:s3:::#{ENV['PROJECT_BUCKET']}"]
).where(:s3_prefix).like("#{folder_path}/")
end
You might find this article of interest, as it specifically discusses creating a policy to restrict users to a prefix in an S3 Bucket.
Credential Management for Mobile Applications
You'll most likely just need to refer to the second policy.
{
"Statement":
[
{
"Effect":"Allow",
"Action":["s3:PutObject","s3:GetObject","s3:DeleteObject"],
"Resource":"arn:aws:s3:::__MY_APPS_BUCKET_NAME__/__USERNAME__/*"
},
{
"Effect":"Allow",
"Action":"s3:ListBucket",
"Resource":"arn:aws:s3:::__MY_APPS_BUCKET_NAME__",
"Condition":{"StringLike":{"s3:prefix":"__USERNAME__/"}}
},
{
"Effect":"Deny",
"Action":["sts:*", "iam:*", "sdb:*"],
"Resource":"*"
}
]
}
With the first 2 statements being what you'd be most interested in.
Hope this helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With