Sqs.IO in Apache Beam and Session Credentials - amazon-web-services

I would like to access AWS SQS with short lived credentials from an Apache Beam Pipleline.
In AWS IAM I have created a role with the following trust relationship:
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:sts::xxxxxx:assumed-role/gcp_role/gcp-project-session-name",
"Service": "sqs.amazonaws.com"
},
"Action": "sts:AssumeRole"
},
With this role I am able to access SQS from my local machine.
I used AWS BasicSessionCredentials as followed:
BasicSessionCredentials refreshedAWSCredentials = new BasicSessionCredentials(
refreshedCredentials.getAccessKeyId(),
refreshedCredentials.getSecretAccessKey(),
refreshedCredentials.getSessionToken());
AWSSecurityTokenService service = AWSSecurityTokenServiceClientBuilder.standard()
.withCredentials(new AWSStaticCredentialsProvider(refreshedAWSCredentials))
.withRegion(options.getAwsRegion()).build();
I add the credentials object to the pipeline options:
options.setAwsSessionToken(refreshedAWSCredentials.getSessionToken());
options.setAwsCredentialsProvider(new AWSStaticCredentialsProvider(refreshedAWSCredentials));
return Pipeline.create(options);
At the end I always run into the following error:
Caused by: org.apache.beam.sdk.util.UserCodeException: com.amazonaws.services.sqs.model.AmazonSQSException:
The security token included in the request is invalid. (Service: AmazonSQS; Status Code: 403; Error Code:
InvalidClientTokenId; Request ID: 501e9869-ea58-5e80-9ec1-c1exxxx; Proxy: null
I assume that the AWSStaticCredentialsProvider does not know about the AWS_SECRET_TOKEN.
That's why I setup a STSAssumeRoleSessionCredentialsProvider which should be work with temporary credentials
STSAssumeRoleSessionCredentialsProvider stsSessionProvider = new STSAssumeRoleSessionCredentialsProvider
.Builder(awsRoleArn, awsRoleSession)
.withStsClient(service)
.build();
This is the associated pipeline code
p.apply(SqsIO.read().withQueueUrl(options.getSourceQueueUrl())
.withMaxNumRecords(options.getNumberOfRecords()))
.apply(ParDo.of(new SqsMessageToJson()))
.apply(TextIO.write()
.to(options.getDestinationBucketUrl() + "/purchase_intent/")
.withSuffix(".json"));
Even if I used the above provider which worked locally as well, I got the sam exception shown above. So, I am wondering how to setup SqsIO with temp credentials.

Related

AWS.ApiGatewayManagementApi() postToConnection is not working

I am trying to send a message to a client connected through the socket by using **AWS.ApiGatewayManagementApi() postToConnection** method. It's working fine in local, but not working in ec2. The only difference is VPC. Is that would be a problem?
var AWS = require('aws-sdk');
var awsGW = new AWS.ApiGatewayManagementApi({
endpoint: "https://endpoint",
accessKeyId: "accessKeyId",
secretAccessKey: "secretAccessKey",
region: "region"
});
awsGW.postToConnection({
ConnectionId: 'ConnectionId',
Data: '{"key1":"msg1","key2":"msg2"}'
}, (err, success) => {
if (err) {
console.log('Socket error', err);
} else {
}
});
ERROR:
{ ForbiddenException: Forbidden
at Object.extractError (/home/ubuntu/git/backend/node_modules/aws-sdk/lib/protocol/json.js:51:27)
at Request.extractError (/home/ubuntu/git/backend/node_modules/aws-sdk/lib/protocol/rest_json.js:55:8)
at Request.callListeners (/home/ubuntu/git/backend/node_modules/aws-sdk/lib/sequential_executor.js:106:20)
at Request.emit (/home/ubuntu/git/backend/node_modules/aws-sdk/lib/sequential_executor.js:78:10)
at Request.emit (/home/ubuntu/git/backend/node_modules/aws-sdk/lib/request.js:683:14)
at Request.transition (/home/ubuntu/git/backend/node_modules/aws-sdk/lib/request.js:22:10)
at AcceptorStateMachine.runTo (/home/ubuntu/git/backend/node_modules/aws-sdk/lib/state_machine.js:14:12)
at /home/ubuntu/git/backend/node_modules/aws-sdk/lib/state_machine.js:26:10
at Request.<anonymous> (/home/ubuntu/git/backend/node_modules/aws-sdk/lib/request.js:38:9)
at Request.<anonymous> (/home/ubuntu/git/backend/node_modules/aws-sdk/lib/request.js:685:12)
message: 'Forbidden',
code: 'ForbiddenException',
time: 2019-06-10T07:40:02.214Z,
requestId: 'f4caef03-8b52-11e9-9cc3-91bfe40b8eb0',
statusCode: 403,
retryable: false,
retryDelay: 37.0834357877396 }
I ran into a similar problem and it was because I was using the wrong endpoint. I was generating it from the event object but on a normal API request, so I was getting an endpoint for a different service (the normal API, and not the websocket).
If your websockets service is deployed to a URL like wss://abcd then your endpoint should be https://abcd.
In my case, there was just an error/undefined value in the endpoint I was using to instantiate the
new AWS.ApiGatewayManagementApi({
endpoint,
region,
})
Note: endpoint can provided in 2 forms:
custom domain name+ base path mapping, for example: wss.my-super-website-with-websocket.com/v1 (where v1 is the configured base path mapping)
default aws-style url like https://********.execute-api.us-east-5.amazonaws.com/prod where prod is the stage configured in API Gateway.
More importantly, for your debugging purposes, if you get desperate, I found out that you can quite easily log the request made by aws-sdk to make sure you are making the correct request (using the aws cli to try the request is also a good idea):
node_modules\aws-sdk\lib\event_listeners.js
function executeSend() {
var http = AWS.HttpClient.getInstance();
var httpOptions = resp.request.service.config.httpOptions || {};
try {
console.log('event listener execute send', resp.request.httpRequest); // only this line is added
...
403 sounds like you need to update the policies for the IAM role calling your postToConnection
In my case I had explicit missing rules, hence I had to attach authorizations for ExecuteAPI
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": "execute-api:*",
"Resource": "arn:aws:execute-api:eu-west-1:###########:*/*/*/*"
}
]
}
I faced the same issue - I have Lambda inside VPC in private subnet and there was VPC endpoint for API Gateway but it always returned 403 Forbidden.
It's weird, but after replacing VPC endpoint with NAT Gateway issue has gone.

How to get/generate aws quicksight secure dashboard url

I want to embed Quicksight dashboard to an application. I have gone through the AWS quicksight documents, I did not get where I will find secure signed dashboard url.
In order to generate Quicksight secure dashboard url, follow the below steps:
Step 1: Create a new Identity Pool. Go to https://console.aws.amazon.com/cognito/home?region=us-east-1 , click ‘Create new Identity Pool’
Give an appropriate name.
Go to the Authentication Providers section, select Cognito.
Give the User Pool ID(your User pool ID) and App Client ID (go to App Clients in userpool and copy id).
Click ‘Create Pool’. Then click ‘Allow’ to create roles of the identity pool in IAM.
Step 2: Assign Custom policy to the Identity Pool Role
Create a custom policy with the below JSON.
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "quicksight:RegisterUser",
"Resource": "*",
"Effect": "Allow"
},
{
"Action": "quicksight:GetDashboardEmbedUrl",
"Resource": "*",
"Effect": "Allow"
},
{
"Action": "sts:AssumeRole",
"Resource": "*",
"Effect": "Allow"
}
]
}
Note: if you want to restrict the user to only one dashboard, replace the * with the dashboard ARN name in quicksight:GetDashboardEmbedUrl,
then goto the roles in IAM.
select the IAM role of the Identity pool and assign custom policy to the role.
Step 3: Configuration for generating the temporary IAM(STS) user
Login to your application with the user credentials.
For creating temporary IAM user, we use Cognito credentials.
When user logs in, Cognito generates 3 token IDs - IDToken, AccessToken, RefreshToken. These tokens will be sent to your application server.
For creating a temporary IAM user, we use Cognito Access Token and credentials will look like below.
AWS.config.region = 'us-east-1';
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId:"Identity pool ID",
Logins: {
'cognito-idp.us-east-1.amazonaws.com/UserPoolID': AccessToken
}
});
For generating temporary IAM credentials, we call sts.assume role method with the below parameters.
var params = {
RoleArn: "Cognito Identity role arn",
RoleSessionName: "Session name"
};
sts.assumeRole(params, function (err, data) {
if (err) console.log( err, err.stack); // an error occurred
else {
console.log(data);
})
You can add additional parameters like duration (in seconds) for the user.
Now, we will get the AccessKeyId, SecretAccessKey and Session Token of the temporary user.
Step 4: Register the User in Quicksight
With the help of same Cognito credentials used in the Step 3, we will register the user in quicksight by using the quicksight.registerUser method with the below parameters
var params = {
AwsAccountId: “account id”,
Email: 'email',
IdentityType: 'IAM' ,
Namespace: 'default',
UserRole: ADMIN | AUTHOR | READER | RESTRICTED_AUTHOR | RESTRICTED_READER,
IamArn: 'Cognito Identity role arn',
SessionName: 'session name given in the assume role creation',
};
quicksight.registerUser(params, function (err, data1) {
if (err) console.log("err register user”); // an error occurred
else {
// console.log("Register User1”);
}
})
Now the user will be registered in quicksight.
Step5: Update AWS configuration with New credentials.
Below code shows how to configure the AWS.config() with new credentials generated Step 3.
AWS.config.update({
accessKeyId: AccessToken,
secretAccessKey: SecretAccessKey ,
sessionToken: SessionToken,
"region": Region
});
Step6: Generate the EmbedURL for Dashboards:
By using the credentials generated in Step 3, we will call the quicksight.getDashboardEmbedUrl with the below parameters
var params = {
AwsAccountId: "account ID",
DashboardId: "dashboard Id",
IdentityType: "IAM",
ResetDisabled: true,
SessionLifetimeInMinutes: between 15 to 600 minutes,
UndoRedoDisabled: True | False
}
quicksight.getDashboardEmbedUrl(params,
function (err, data) {
if (!err) {
console.log(data);
} else {
console.log(err);
}
});
Now, we will get the embed url for the dashboard.
Call the QuickSightEmbedding.embedDashboard from front end with the help of the above generated url.
The result will be the dashboard embedded in your application with filter controls.
this link will give you what you need from aws cli https://aws.amazon.com/blogs/big-data/embed-interactive-dashboards-in-your-application-with-amazon-quicksight/
this is the step 3 aws cli cmd to give you embeded URL ( i was able to excecute)
aws quicksight get-dashboard-embed-url --aws-account-id (your account ID) --dashboard-id (your dashgboard ID) --identity-type IAM
there are many other dependence to enable the embeded dashboard per aws dcouments. i have not able to successfully doen that. GL and let me know if you make it happen!
PHP implementation
(in addition to Siva Sumanth's answer)
https://gist.github.com/evgalak/d0d1adf099e2d7bff741c16a89bf30ba

AWS generate dynamic credential for S3 folder level access?

I'm new to AWS and still figuring out how to do things.
Part of my web application is using AWS S3 for file storage, but I want each user to be only able to access specific folders(for CRUD) in the bucket.
The backend server will track what folders the user will be able to access.
I know it is possible to define policies that allow access to specific folders(by matching prefix of objects), but can I generate these policies dynamically and get credentials with these policies attached (probably with Cognito?). So that these credentials could be passed to client-side to enable access to S3 folders.
I'm wondering if it is possible to do that and what services are required to achieve this.
You should change your view, each time you want to share a file with one of your users, you should check your database about their permissions( folders they have access) and if logical things on your side are correct, generate a presigned URL for access to that object.
How presigned URL works.
When you generate a presigned URL for accessing to an object, you can set the time limit too, it means after that time, the URL not work and expired.
For more information about the presigned URL, read the following documents on Amazon Web services website:
Generate a Pre-signed Object URL Using the AWS SDK for Java
Generate a Pre-signed Object URL Using AWS SDK for .NET
Also, if you want to create users and assign the right policy for access them to their folder you can follow these instructions:
You can use the IAM API to creating a user for each of your users, and attach the right policy for each of them.
For example, for creating the new user, you should use the following API
/* The following create-user command creates an IAM user named Bob in the current account. */
var params = {
UserName: "Bob"
};
iam.createUser(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
/*
data = {
User: {
Arn: "arn:aws:iam::123456789012:user/Bob",
CreateDate: <Date Representation>,
Path: "/",
UserId: "AKIAIOSFODNN7EXAMPLE",
UserName: "Bob"
}
}
*/
});
For more info about the Create user API, read the following
https://docs.aws.amazon.com/IAM/latest/APIReference/API_CreateUser.html
After creating a user, you should create a policy for each of them with CreatePolicy API.
var params = {
PolicyDocument: 'STRING_VALUE', /* required */
PolicyName: 'STRING_VALUE', /* required */
Description: 'STRING_VALUE',
Path: 'STRING_VALUE'
};
iam.createPolicy(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});
For more info about the Create policy read the following doc:
https://docs.aws.amazon.com/IAM/latest/APIReference/API_CreatePolicy.html
And finally, you should assign the policy you created before to each user by the AttachUserPolicy API.
/* The following command attaches the AWS managed policy named AdministratorAccess to the IAM user named Alice. */
var params = {
PolicyArn: "arn:aws:iam::aws:policy/AdministratorAccess",
UserName: "Alice"
};
iam.attachUserPolicy(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});
For more info about the AttachUserPolicy API read the following doc:
https://docs.aws.amazon.com/IAM/latest/APIReference/API_AttachUserPolicy.html
The last part is about the which policy you should create and assign to each of them; we use the following policy for listing objects in each folder:
{
"Sid": "AllowListingOfUserFolder",
"Action": ["s3:ListBucket"],
"Effect": "Allow",
"Resource": ["arn:aws:s3:::my-company"],
"Condition":{"StringLike":{"s3:prefix":["home/David/*"]}}
}
And the following policy for actions in each folder:
{
"Sid": "AllowAllS3ActionsInUserFolder",
"Effect": "Allow",
"Action": ["s3:*"],
"Resource": ["arn:aws:s3:::my-company/home/David/*"]
}
For more detailed info about that policies read the following article by Jim Scharf:
https://aws.amazon.com/blogs/security/writing-iam-policies-grant-access-to-user-specific-folders-in-an-amazon-s3-bucket/

How to move AWS Elasticsearch into another account

I'm moving all the instances under each service from old AWS account into new AWS account. I've found ways to move EC2 and RDS into another account.
To move EC2 instance, I have created an AMI and shared with the new AWS account. Using that image I've created an instance
To move RDS instance, I've created a snapshot and shared with the new AWS account. I've restored the shared snapshot in the new account
Now I need to move Elasticsearch from old account to the new one. I couldn't able to figure out a way to move my Elasticsearch. Can anyone help me on this?
Create a role with Elasticsearch permission. You may also use the existing role with the following trust relationship,
{
"Effect": "Allow",
"Principal": {
"Service": "es.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
Provide the iam:PassRole for the iam user whose access/secret keys will be using to take snapshot.
{
"Version": "2012-10-17",
"Statement": {
"Effect": "Allow",
"Action": "iam:PassRole",
"Resource": "arn:aws:iam::accountID:role/TheServiceRole"
}
}
Change the access & secret key, host, region, path, and payload in the below code and execute it.
import requests
from requests_aws4auth import AWS4Auth
AWS_ACCESS_KEY_ID=''
AWS_SECRET_ACCESS_KEY=''
region = 'us-west-1'
service = 'es'
awsauth = AWS4Auth(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, region, service)
host = 'https://elasticsearch-domain.us-west-1.es.amazonaws.com/' # include https:// and trailing /
# REGISTER REPOSITORY
path = '_snapshot/my-snapshot-repo' # the Elasticsearch API endpoint
url = host + path
payload = {
"type": "s3",
"settings": {
"bucket": "s3-bucket-name",
"region": "us-west-1",
"role_arn": "arn:aws:iam::accountID:role/TheServiceRole"
}
}
headers = {"Content-Type": "application/json"}
r = requests.put(url, auth=awsauth, json=payload, headers=headers) # requests.get, post, put, and delete all have similar syntax
print(r.text)
To take the snapshot and store it in the S3
path = '_snapshot/my-snapshot-repo/my-snapshot'
url = host + path
r = requests.put(url, auth=awsauth)
print(r.text)
Now the snapshot is ready. Share this snapshot to another account and use the same code with new account keys and endpoint to restore it using the below code snippet.
To restore all indices from the snapshot
path = '_snapshot/my-snapshot-repo/my-snapshot/_restore'
url = host + path
r = requests.post(url, auth=awsauth)
print(r.text)
To restore single index from the snapshot
path = '_snapshot/my-snapshot-repo/my-snapshot/_restore'
url = host + path
payload = {"indices": "my-index"}
headers = {"Content-Type": "application/json"}
r = requests.post(url, auth=awsauth, json=payload, headers=headers)
print(r.text)
Reference: AWS docs.

aws lambda function getting access denied when getObject from s3

I am getting an acccess denied error from S3 AWS service on my Lambda function.
This is the code:
// dependencies
var async = require('async');
var AWS = require('aws-sdk');
var gm = require('gm').subClass({ imageMagick: true }); // Enable ImageMagick integration.
exports.handler = function(event, context) {
var srcBucket = event.Records[0].s3.bucket.name;
// Object key may have spaces or unicode non-ASCII characters.
var key = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, " "));
/*
{
originalFilename: <string>,
versions: [
{
size: <number>,
crop: [x,y],
max: [x, y],
rotate: <number>
}
]
}*/
var fileInfo;
var dstBucket = "xmovo.transformedimages.develop";
try {
//TODO: Decompress and decode the returned value
fileInfo = JSON.parse(key);
//download s3File
// get reference to S3 client
var s3 = new AWS.S3();
// Download the image from S3 into a buffer.
s3.getObject({
Bucket: srcBucket,
Key: key
},
function (err, response) {
if (err) {
console.log("Error getting from s3: >>> " + err + "::: Bucket-Key >>>" + srcBucket + "-" + key + ":::Principal>>>" + event.Records[0].userIdentity.principalId, err.stack);
return;
}
// Infer the image type.
var img = gm(response.Body);
var imageType = null;
img.identify(function (err, data) {
if (err) {
console.log("Error image type: >>> " + err);
deleteFromS3(srcBucket, key);
return;
}
imageType = data.format;
//foreach of the versions requested
async.each(fileInfo.versions, function (currentVersion, callback) {
//apply transform
async.waterfall([async.apply(transform, response, currentVersion), uploadToS3, callback]);
}, function (err) {
if (err) console.log("Error on excecution of watefall: >>> " + err);
else {
//when all done then delete the original image from srcBucket
deleteFromS3(srcBucket, key);
}
});
});
});
}
catch (ex){
context.fail("exception through: " + ex);
deleteFromS3(srcBucket, key);
return;
}
function transform(response, version, callback){
var imageProcess = gm(response.Body);
if (version.rotate!=0) imageProcess = imageProcess.rotate("black",version.rotate);
if(version.size!=null) {
if (version.crop != null) {
//crop the image from the coordinates
imageProcess=imageProcess.crop(version.size[0], version.size[1], version.crop[0], version.crop[1]);
}
else {
//find the bigger and resize proportioned the other dimension
var widthIsMax = version.size[0]>version.size[1];
var maxValue = Math.max(version.size[0],version.size[1]);
imageProcess=(widthIsMax)?imageProcess.resize(maxValue):imageProcess.resize(null, maxValue);
}
}
//finally convert the image to jpg 90%
imageProcess.toBuffer("jpg",{quality:90}, function(err, buffer){
if (err) callback(err);
callback(null, version, "image/jpeg", buffer);
});
}
function deleteFromS3(bucket, filename){
s3.deleteObject({
Bucket: bucket,
Key: filename
});
}
function uploadToS3(version, contentType, data, callback) {
// Stream the transformed image to a different S3 bucket.
var dstKey = fileInfo.originalFilename + "_" + version.size + ".jpg";
s3.putObject({
Bucket: dstBucket,
Key: dstKey,
Body: data,
ContentType: contentType
}, callback);
}
};
This is the error on Cloudwatch:
AccessDenied: Access Denied
This is the stack error:
at Request.extractError (/var/runtime/node_modules/aws-sdk/lib/services/s3.js:329:35)
at Request.callListeners (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:105:20)
at Request.emit (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:77:10)
at Request.emit (/var/runtime/node_modules/aws-sdk/lib/request.js:596:14)
at Request.transition (/var/runtime/node_modules/aws-sdk/lib/request.js:21:10)
at AcceptorStateMachine.runTo (/var/runtime/node_modules/aws-sdk/lib/state_machine.js:14:12)
at /var/runtime/node_modules/aws-sdk/lib/state_machine.js:26:10
at Request.<anonymous> (/var/runtime/node_modules/aws-sdk/lib/request.js:37:9)
at Request.<anonymous> (/var/runtime/node_modules/aws-sdk/lib/request.js:598:12)
at Request.callListeners (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:115:18)
Without any other description or info
on S3 bucket permissions allow to everyone put list and delete.
What can I do to access the S3 bucket?
PS: on Lambda event properties the principal is correct and has administrative privileges.
Interestingly enough, AWS returns 403 (access denied) when the file does not exist. Be sure the target file is in the S3 bucket.
If you are specifying the Resource don't forget to add the sub folder specification as well. Like this:
"Resource": [
"arn:aws:s3:::BUCKET-NAME",
"arn:aws:s3:::BUCKET-NAME/*"
]
Your Lambda does not have privileges (S3:GetObject).
Go to IAM dashboard, check the role associated with your Lambda execution. If you use AWS wizard, it automatically creates a role called oneClick_lambda_s3_exec_role. Click on Show Policy. It should show something similar to the attached image. Make sure S3:GetObject is listed.
I ran into this issue and after hours of IAM policy madness, the solution was to:
Go to S3 console
Click bucket you are interested in.
Click 'Properties'
Unfold 'Permissions'
Click 'Add more permissions'
Choose 'Any Authenticated AWS User' from dropdown. Select 'Upload/Delete' and 'List' (or whatever you need for your lambda).
Click 'Save'
Done.
Your carefully written IAM role policies don't matter, neither do specific bucket policies (I've written those too to make it work). Or they just don't work on my account, who knows.
[EDIT]
After a lot of tinkering the above approach is not the best. Try this:
Keep your role policy as in the helloV post.
Go to S3. Select your bucket. Click Permissions. Click Bucket Policy.
Try something like this:
{
"Version": "2012-10-17",
"Id": "Lambda access bucket policy",
"Statement": [
{
"Sid": "All on objects in bucket lambda",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::AWSACCOUNTID:root"
},
"Action": "s3:*",
"Resource": "arn:aws:s3:::BUCKET-NAME/*"
},
{
"Sid": "All on bucket by lambda",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::AWSACCOUNTID:root"
},
"Action": "s3:*",
"Resource": "arn:aws:s3:::BUCKET-NAME"
}
]
}
Worked for me and does not require for you to share with all authenticated AWS users (which most of the time is not ideal).
If you have encryption set on your S3 bucket (such as AWS KMS), you may need to make sure the IAM role applied to your Lambda function is added to the list of IAM > Encryption keys > region > key > Key Users for the corresponding key that you used to encrypt your S3 bucket at rest.
In my screenshot, for example, I added the CyclopsApplicationLambdaRole role that I have applied to my Lambda function as a Key User in IAM for the same AWS KMS key that I used to encrypt my S3 bucket. Don't forget to select the correct region for your key when you open up the Encryption keys UI.
Find the execution role you've applied to your Lambda function:
Find the key you used to add encryption to your S3 bucket:
In IAM > Encryption keys, choose your region and click on the key name:
Add the role as a Key User in IAM Encryption keys for the key specified in S3:
If all the other policy ducks are in a row, S3 will still return an Access Denied message if the object doesn't exist AND the requester doesn't have ListBucket permission on the bucket.
From https://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectGET.html:
...If the object you request does not exist, the error Amazon S3
returns depends on whether you also have the s3:ListBucket permission.
If you have the s3:ListBucket permission on the bucket, Amazon S3 will
return an HTTP status code 404 ("no such key") error. if you don’t
have the s3:ListBucket permission, Amazon S3 will return an HTTP
status code 403 ("access denied") error.
I too ran into this issue, I fixed this by providing s3:GetObject* in the ACL as it is attempting to obtain a version of that object.
I tried to execute a basic blueprint Python lambda function [example code] and I had the same issue. My execition role was lambda_basic_execution
I went to S3 > (my bucket name here) > permissions .
Because I'm beginner, I used the Policy Generator provided by Amazon rather than writing JSON myself: http://awspolicygen.s3.amazonaws.com/policygen.html
my JSON looks like this:
{
"Id": "Policy153536723xxxx",
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Stmt153536722xxxx",
"Action": [
"s3:GetObject"
],
"Effect": "Allow",
"Resource": "arn:aws:s3:::tokabucket/*",
"Principal": {
"AWS": [
"arn:aws:iam::82557712xxxx:role/lambda_basic_execution"
]
}
}
]
And then the code executed nicely:
I solved my problem following all the instruction from the AWS - How do I allow my Lambda execution role to access my Amazon S3 bucket?:
Create an AWS Identity and Access Management (IAM) role for the Lambda function that grants access to the S3 bucket.
Modify the IAM role's trust policy.
Set the IAM role as the Lambda function's execution role.
Verify that the bucket policy grants access to the Lambda function's execution role.
I was trying to read a file from s3 and create a new file by changing content of file read (Lambda + Node). Reading file from S3 did not had any problem. As soon I tried writing to S3 bucket I get 'Access Denied' error.
I tried every thing listed above but couldn't get rid of 'Access Denied'. Finally I was able to get it working by giving 'List Object' permission to everyone on my bucket.
Obviously this not the best approach but nothing else worked.
After searching for a long time i saw that my bucket policy was only allowed read access and not put access:
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicListGet",
"Effect": "Allow",
"Principal": "*",
"Action": [
"s3:List*",
"s3:Get*",
"s3:Put*"
],
"Resource": [
"arn:aws:s3:::bucketName",
"arn:aws:s3:::bucketName/*"
]
}
]
}
Also another issue might be that in order to fetch objects from cross region you need to initialize new s3 client with other region name like:
const getS3Client = (region) => new S3({ region })
I used this function to get s3 client based on region.
I was struggling with this issue for hours. I was using AmazonS3EncryptionClient and nothing I did helped. Then I noticed that the client is actually deprecated, so I thought I'd try switching to the builder model they have:
var builder = AmazonS3EncryptionClientBuilder.standard()
.withEncryptionMaterials(new StaticEncryptionMaterialsProvider(encryptionMaterials))
if (accessKey.nonEmpty && secretKey.nonEmpty) builder = builder.withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(accessKey.get, secretKey.get)))
builder.build()
And... that solved it. Looks like Lambda has trouble injecting the credentials in the old model, but works well in the new one.
I was getting the same error "AccessDenied: Access Denied" while cropping s3 images using lambda function. I updated the s3 bucket policy and IAM role inline policy as per the document link given below.
But still, I was getting the same error. Then I realised, I was trying to give "public-read" access in a private bucket. After removed ACL: 'public-read' from S3.putObject problem get resolved.
https://aws.amazon.com/premiumsupport/knowledge-center/access-denied-lambda-s3-bucket/
I had this error message in aws lambda environment when using boto3 with python:
botocore.exceptions.ClientError: An error occurred (AccessDenied) when calling the GetObject operation: Access Denied
It turns out I needed an extra permission because I was using object tags. If your objects have tags you will need
s3:GetObject AND s3:GetObjectTagging for getting the object.
I have faced the same problem when creating Lambda function that should have read S3 bucket content. I created the Lambda function and S3 bucket using AWS CDK. To solve this within AWS CDK, I used magic from the docs.
Resources that use execution roles, such as lambda.Function, also
implement IGrantable, so you can grant them access directly instead of
granting access to their role. For example, if bucket is an Amazon S3
bucket, and function is a Lambda function, the code below grants the
function read access to the bucket.
bucket.grantRead(function);