I have an API invoked lambda that generates signed getObject and putObject URLs. GET and PUT on my restricted bucket (contains zip files) work fine, but PUT on my public bucket (contains images) returns a "SignatureDoesNotMatch" error. There is no GET on the public bucket, images from that bucket are referenced directly. Do I need extra configuration to PUT on a public bucket? I've tried giving the most generous permissions I can think without luck.
EDIT: I did end up having to send in the specific image MIME type to the endpoint that generates the signed URL. image/* didn't work, unfortunately.
Signed URL Generation (called by getSignedImgUploadUrl)
let params = {
Bucket: "public-bucket",
Key: `${folder}/${key}.jpg`, // Ideally without extension
Expires: 30,
ContentType: "image/jpeg", // Ideally image/*
ACL: "public-read" // Tried with and without this
};
let url = s3.getSignedUrl("putObject", params);
let result = {
signedUrl: url,
key: key
};
return result;
Use Signed URL
public uploadImg(folder: string, file: any, key: string): Observable<any> {
return this._spinnerService.spinObservable(
new Observable(subscriber => {
this.getSignedImgUploadUrl(folder, key)
.subscribe(result => {
// put to signedUrl fails with 403 SignatureDoesNotMatch
this._httpClient.put(result["signedUrl"], file, { headers: { "x-amz-acl": "public-read" } })
.subscribe(() => {
subscriber.next(result["key"]);
subscriber.complete();
}, err => {
console.log(err);
subscriber.error(err);
});
}, err => {
console.log(err);
subscriber.error(err);
});
}));
}
Lambda Role
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject"
],
"Resource": "arn:aws:s3:::restricted-bucket/*"
},
{
"Sid": "VisualEditor1",
"Effect": "Allow",
"Action": [
"s3:ListAllMyBuckets"
],
"Resource": "*"
},
{
"Sid": "VisualEditor2",
"Effect": "Allow",
"Action": "s3:*", (Ideally just getObject/putObject)
"Resource": "arn:aws:s3:::public-bucket/*"
}
]
}
Public Bucket Policy
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::public-bucket/*"
},
// Also tried adding s3:* for the lambda role without luck
{
"Sid": "Stmt1624999949645",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::account:role/service-role/lambda-role"
},
"Action": "s3:*",
"Resource": "arn:aws:s3:::public-bucket/*"
}
]
}
According to OP's comment, explicitly sending Content-Type HTTP header worked. The reason why this header is sometimes required is because the HTTP client cannot correctly infer the MIME type from the PUT payload.
Related
I have a user pool and an Identity pool, where the role i am giving the authenticating users in the identity pool has the following policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:PutBucketPolicy",
"s3:CreateBucket"
],
"Resource": [
"arn:aws:s3:::testbucket123",
"arn:aws:s3:::testbucket456",
"arn:aws:s3:::testbucket987"
]
}
]
}
I have created a new role called Role_testbucket456_User_X using Web Identity and added a condition where cognito-identity.amazonaws.com:sub is stringEquals to 8e23d688-1f28-445c-8966-fdcb967c8e3c, and attach to it the following policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:ListBucket"
],
"Resource": "arn:aws:s3:::testbucket456"
}
]
}
Then I have added the Cognito user Y that has the sub 8e23d688-1f28-445c-8966-fdcb967c8e3c to a Cognito User Pool Group called testbucket456_Users
And then attached the role Role_testbucket456_User_X to this group testbucket456_Users
What I am expecting is that none of the Cognito users will have Read/Write access on any S3 bucket, except the user Y that has sub 8e23d688-1f28-445c-8966-fdcb967c8e3c to be able to access Read/Write on testbucket456 bucket. But that didn't work unfortunately.
So I have added the following Bucket Policy to the testbucket456 bucket:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowCognitoUserAccess",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::5555555555555:role/Role_testbucket456_User_X"
},
"Action": [
"s3:PutObject",
"s3:GetObject"
],
"Resource": "arn:aws:s3:::testbucket456/*"
},
{
"Sid": "AllowCognitoUserAccess",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::5555555555555:role/Role_testbucket456_User_X"
},
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::testbucket456"
}
]
}
But that still didn't work, I am still getting Access Denied issue whenever I try to call this method:
const listObjectParams = {
Bucket: 'testbucket456',
};
s3.listObjects(listObjectParams, (err: any, data: any) => {
if (err) {
console.log(err);
return;
}
console.log(data);
console.log(`Successfully listed objects in `);
});
Note
When I set the testbucket456 bucket's policy to
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowCognitoUserAccess",
"Effect": "Allow",
"Principal": "*",
"Action": [
"s3:PutObject",
"s3:GetObject"
],
"Resource": "arn:aws:s3:::testbucket456/*"
},
{
"Sid": "AllowCognitoUserAccess",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::testbucket456"
}
]
}
I am then able to access(list objects) the bucket using the Cognito users, I think the issue is with the bucket's policy itself and in the Principal field specifically.
Possible issues
Maybe the authenticated role must have permissions to assume the custom role
{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::5555555555555:role/Role_testbucket456_User_X"
}
to be like the following:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:CreateBucket"
],
"Resource": [
"arn:aws:s3:::testbucket456"
]
},
{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::5555555555555:role/Role_testbucket456_User_X"
}
]
}
Can anybody confirm please?
This answer was the solution, I had to change the default role given to the Cognito Users
We have an Amazon S3 static website and it was working fine until we introduced a policy where we allow only secure data transfer by enforcing aws:SecureTransport. A policy with simply denies access to the site when the aws:SecureTransport fails. But when we access the site now, it says 403 Forbidden error.
The set up is that we have CloudFront fronted to this site, so the traffic routes through the CloudFront, not sure if this has to do something with the issue we are facing, where the traffic route between CloudFront and Amazon S3 is http only. Having said that, strangely when we tweek the policy to have aws:SecureTransport:true and allow such requests to the site, it works fine, but when we have deny policy aws:SecureTransport:false, then we end up getting 403 error. Sharing both the policies here.
When we have this the static website works fine:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowOriginAccessIdentity",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity XXXXXXXXXXX"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::example.domain.com/*"
},
{
"Sid": "ForceSSLRequest",
"Effect": "Allow",
"Principal": {
"AWS": "*"
},
"Action": "s3:*",
"Resource": "arn:aws:s3:::example.domain.com/*",
"Condition": {
"Bool": {
"aws:SecureTransport": true
}
}
}
]
}
Where as when we have as below, it fails, and we want this to be implemented to be absolutely sure:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowOriginAccessIdentity",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity XXXXXXXXXXX"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::example.domain.com/*"
},
{
"Sid": "ForceSSLRequest",
"Effect": "Deny",
"Principal": {
"AWS": "*"
},
"Action": "s3:*",
"Resource": "arn:aws:s3:::example.domain.com/*",
"Condition": {
"Bool": {
"aws:SecureTransport": false
}
}
}
]
}
Update: I have a policy as below allowing an OAI accessing the S3. This does not work, but when we read/understand the policy it makes sense to allow access the webiste, but it fails :(.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowOriginAccessIdentity",
"Effect": "Deny",
"Principal": {
"AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity XXXXXXXXXXX"
},
"Action": "s3:*",
"Resource": "arn:aws:s3:::example-domain.com/*",
"Condition": {
"Bool": {
"aws:SecureTransport": "false"
}
}
}
]
}
But the below works, even though logical readability is same according to my understanding.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowOriginAccessIdentity",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity XXXXXXXXXXX"
},
"Action": "s3:*",
"Resource": "arn:aws:s3:::example-domain.com/*",
"Condition": {
"Bool": {
"aws:SecureTransport": "true"
}
}
}
]
}
Any help in understanding this behavior is much appreciated.
The policy logic is:
Explicit Deny by default
Use Allow to grant desired access
Use Deny to override access given by Allow
Therefore, the better policy is to Allow access if aws:SecureTransport is true, since it is less permissive and doesn't need any Deny statements (which are always confusing!).
See: Policy evaluation logic - AWS Identity and Access Management
I would like to deny public access to an AWS API Gateway and only allow access when the API is invoked with a specific role. In my test there are two gateways, and one calls the other:
Public Gateway -> Private Gateway
I want to be able to visit Public Gateway endpoints in a browser and receive a 2XX response, and when visiting the Private Gateway directly I should receive a 4XX response. The only way to access the Private Gateway should be via the Public Gateway (which proxies to the Private Gateway with each endpoint).
I've tried several policies. All of these always result in the Public Gateway error logs showing the following:
User: anonymous is not authorized to perform: execute-api:Invoke on resource: arn:aws:execute-api:us-east-1:********9012:abcd123456/dev/GET/products
That error message is received by the Public Gateway as a response from the Private Gateway.
Here are policies I've tried (separately):
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Principal": "*",
"Action": "execute-api:Invoke",
"Resource": "arn:aws:execute-api:us-east-1:123456789012:abcd123456/*",
"Condition": {
"StringNotEquals": {
"aws:PrincipalArn": "arn:aws:iam::123456789012:role/test-apigateway-role"
}
}
}
]
}
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"NotPrincipal": {
"AWS": [
"arn:aws:iam::123456789012:role/test-apigateway-role",
"arn:aws:iam::123456789012:root"
]
},
"Action": "execute-api:Invoke",
"Resource": "arn:aws:execute-api:us-east-1:123456789012:abcd123456/*"
}
]
}
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Principal": "*",
"Action": "execute-api:Invoke",
"Resource": "arn:aws:execute-api:us-east-1:123456789012:abcd123456/*/*/*",
"Condition": {
"ArnNotEquals": {
"aws:PrincipalArn": "arn:aws:iam::123456789012:role/test-apigateway-role"
}
}
},
{
"Effect": "Allow",
"Principal": "*",
"Action": "execute-api:Invoke",
"Resource": "arn:aws:execute-api:us-east-1:123456789012:abcd123456/*/*/*"
}
]
}
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Principal": "*",
"Action": "execute-api:Invoke",
"Resource": "arn:aws:execute-api:us-east-1:123456789012:abcd123456/*",
"Condition": {
"StringNotEquals": {
"aws:PrincipalArn": "arn:aws:iam::123456789012:role/test-apigateway-role"
}
}
},
{
"Effect": "Allow",
"Principal": "*",
"Action": "execute-api:Invoke",
"Resource": "arn:aws:execute-api:us-east-1:123456789012:abcd123456/*/*/*"
}
]
}
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Principal": "*",
"Action": "execute-api:Invoke",
"Resource": "arn:aws:execute-api:us-east-1:123456789012:abcd123456/*",
"Condition": {
"StringNotEquals": {
"aws:PrincipalArn": "arn:aws:iam::123456789012:role/test-apigateway-role"
}
}
},
{
"Effect": "Allow",
"Principal": "*",
"Action": "execute-api:Invoke",
"Resource": "arn:aws:execute-api:us-east-1:123456789012:abcd123456/*/*/*",
"Condition": {
"StringEquals": {
"aws:PrincipalArn": "arn:aws:iam::123456789012:role/test-apigateway-role"
}
}
}
]
}
I've redeployed with each Resource Policy change and waited one minute before testing.
The role is assigned in the Public Gateway's serverless.yml settings:
service: test-gateway
provider:
name: aws
runtime: nodejs12.x
apiGateway:
shouldStartNameWithService: true
role: arn:aws:iam::123456789012:role/test-apigateway-role
How about trying this?
According to the docs, if you don't specify an explicit Deny, and then provide a specific Allow, it should work. If it doesn't, keep sharing your outputs, I'm intrigued.
Update: I removed the Deny * part, this means we'll get an implicit deny for requests that are not specifically declared in an Allow statement. This is according to Sessions policies (see docs link)
Update 2: Check this answer's comments, the author also mentioned - added authorizer: aws_iam to serverless.yml
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": [
"arn:aws:iam::123456789012:role/test-apigateway-role"
]
},
"Action": "execute-api:Invoke",
"Resource": [
"arn:aws:execute-api:us-east-1:123456789012:abcd123456/*"
]
}
]
}
I have this simple script in Ruby to upload a file on S3 I got from AWS documentation:
def object_uploaded?(s3_resource, bucket_name, object_key, file_path)
object = s3_resource.bucket(bucket_name).object(object_key)
File.open(file_path, 'rb') do |file|
object.put(body: file, acl: 'public-read')
end
return true
rescue StandardError => e
puts "Error uploading object: #{e.message}"
return false
end
The script gives me Access Denied when i add acl: 'public-read'. Works fine if I remove that.
Only way for this to work is to make my S3 bucket public.
This is my bucket policy:
{
"Version": "2012-10-17",
"Id": "Policy1610635552932",
"Statement": [
{
"Sid": "Stmt1610635551842",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::[mybucket]/*"
}
]
}
And this is my IAM policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"s3:ListStorageLensConfigurations",
"s3:GetAccessPoint",
"s3:PutAccountPublicAccessBlock",
"s3:GetAccountPublicAccessBlock",
"s3:ListAllMyBuckets",
"s3:ListAccessPoints",
"s3:ListJobs",
"s3:PutStorageLensConfiguration",
"s3:CreateJob"
],
"Resource": "*"
},
{
"Sid": "VisualEditor1",
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:PutObjectAcl"
],
"Resource": [
"arn:aws:s3:::[mybucket]",
"arn:aws:s3:::[mybucket]/*"
]
}
]
}
Is there any way I can turn on Block public access as recommended by AWS and have public accessible files by setting the acl? What is wrong with my policy?
You cannot upload a file with ACL public-read if the bucket's block public access setting is on. Per the documentation: "Amazon S3 block public access prevents the application of any settings that allow public access to data within S3 buckets." (https://docs.aws.amazon.com/AmazonS3/latest/user-guide/block-public-access.html)
Is there a way to create a custom authorizer that returns policies allowing resources paths and its path params?
Example:
Allow: GET /stores, GET /stores/{storeId}
Deny: GET /stores/{storeId}/products
I'm having problems with Path Parameters, because when I return a policy like arn:...:.../stage/GET/stores/{storeId}, API gateway blocks calls to GET /stores/123 or GET /stores/555123
Such a policy is possible. You can return the following structure as custom Authorizer policy to achieve this:
{
"principalId": "user",
"policyDocument": {
"Version": "2012-10-17",
"Statement": [{
"Action": "execute-api:Invoke",
"Effect": "Allow",
"Resource": "arn:aws:execute-api:eu-central-1:1234567890:9f4xsv4jbl/prod/GET/stores"
},
{
"Action": "execute-api:Invoke",
"Effect": "Deny",
"Resource": "arn:aws:execute-api:eu-central-1:1234567890:9f4xsv4jbl/*/GET/stores/{id}"
},
{
"Action": "execute-api:Invoke",
"Effect": "Allow",
"Resource": "arn:aws:execute-api:eu-central-1:1234567890:9f4xsv4jbl/prod/GET/stores/*/products"
}
]
}
}