AWS assumed-role unable to perform secretsmanager:GetSecretValue in serverless project - amazon-iam

I have a serverless project written with node.js.
This service defines an IAM role for use at runtime with the following policy:
{
"Version": "2012-10-17",
"Statement": [
// statement to allow logging omitted
// statement for VPC stuff omitted (CreateNetworkInterface, etc)
{
"Effect": "Allow",
"Action": [
"secretsmanager:GetSecretValue"
],
"Resource": "arn:aws:secretsmanager:eu-west-1:*:secret:my_secret_name-*"
}
]
}
I have a lambda that then tries to read that secrets:
import SecretsManager from 'aws-sdk/clients/secretsmanager' // v2
...
export const handler: ValidatedEventAPIGatewayProxyEvent<typeof schema> = async (event, context: any) => {
try {
const sm = new SecretsManager({ region }) // region is defined as "eu-west-1"
const secret = await sm.getSecretValue({
SecretId: 'my_secret_name'
})
} catch (e) {
console.error(e)
}
This errors with the following:
AccessDeniedException: User: arn:aws:sts::{accountId}:assumed-role/my-service-lambda-role/my-service-my-stage-my-function is not authorized to perform: secretsmanager:GetSecretValue
I'm not sure why the permissions would not allow me to retrieve the secret. I'm further not sure why this is using sts and using an assumed-role rather than just using the serverless lambda role directly. Can someone explain this to me and how to fix this?
NOTE: using the policysimulator, I can confirm the role created with the above policy does have access to read the defined secret, so this must be to do with assumed roles?

Related

List IAM Permissions for User/Role using AWS SDK

We're using AWS Cognito and AWS IAM to manage our users and their permissions to access certain resources in our static website built in JavaScript. Before we make certain requests using the AWS SDK we'd like to know what permissions the user has via their Role.
For example in AWS Console we can see the following:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"cognito-idp:GetUser",
"iam:ListPoliciesGrantingServiceAccess",
"cognito-idp:ListUsers"
],
"Resource": "*"
}
]
}
Using the SDK we've found a method to get the Policies for the User/Role:
const iam = new AWS.IAM();
iam.listPoliciesGrantingServiceAccess({
Arn: arn,
ServiceNamespaces: [
"iam",
"cognito-idp"
]
}, (err, data) => {
if (err) {
console.log(err);
}
else {
console.log(data);
}
})
However this doesn't return the permissions these policies contain and only returns the actual policies themselves...
[
{
"ServiceNamespace":"iam",
"Policies":[
{
"PolicyName":"admin-policy",
"PolicyType":"INLINE",
"EntityType":"ROLE",
"EntityName":"Cognito_Auth_Role"
}
]
},
{
"ServiceNamespace":"cognito-idp",
"Policies":[
{
"PolicyName":"admin-policy",
"PolicyType":"INLINE",
"EntityType":"ROLE",
"EntityName":"Cognito_Auth_Role"
}
]
}
We haven't been able to find any methods that get permissions for a policy... How can we access this information for a given User/Role?
Here are the relevant SDK v3 methods. For the above example, you would use GetRolePolicy or ListRolePolicies:
PolicyType: INLINE
Get[User|Role|Group]PolicyCommand: Retrieves the specified inline policy document that is embedded in the specified entity.
List[User|Role|Group]PoliciesCommand: Lists the names of the inline policies embedded in the specified entity.
PolicyType: MANAGED
GetPolicyCommand to get the default version and GetPolicyVersionCommand to retrieve the policy document.
ListAttached[User|Role|Group]PoliciesCommand: Lists all managed policies that are attached to the specified IAM entity.

Lambda that copy object from one bucket to another in different accounts using AWS SDK copyobject method in javascript

I would like to copy objects from the bucket in account-1 to the other buckets in account-2 using the copyObject method of the AWSJavaScriptSDK in a lambda. Right now it works by allowing the lambda role in account-1 to write in the bucket in account account-2 using the s3 bucket policies. The thing is that in the account-2 we have a lot of buckets and we want to avoid adding permission to each of them every time that we create a lambda that writes or read in one of them.
I'm trying that the lambda in the account-1 assume the role with the permissions to write and read in the account-2 as follows.
In the account-2 I have the role called s3-account-2-full-access with the AmazonS3FullAccess policy and the following trusted relationship:
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::11111111111:role/lambda-account-1-role"
},
"Action": "sts:AssumeRole",
"Condition": {}
}
]
}
In the account-1 I have the lambda-account-1-role with the following policy attached.
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::22222222222:role/s3-account-2-full-access"
}
]
}
In the lambda I'm assuming the role as follows:
const sts = new AWS.STS();
exports.handler = async (event) => {
const timestamp = (new Date()).getTime();
var sts_params = {
RoleArn: 'arn:aws:iam::22222222222:role/s3-account-2-full-access',
RoleSessionName: `id-${timestamp}`,
DurationSeconds: 3600,
};
const { Credentials } = await sts.assumeRole(sts_params).promise();
const { AccessKeyId, SecretAccessKey, SessionToken } = Credentials;
const accessparams2 = {
accessKeyId: AccessKeyId,
secretAccessKey: SecretAccessKey,
sessionToken: SessionToken,
};
var s3 = new AWS.S3(accessparams2);
var params = {
Bucket: "account-2-bucket",
CopySource: "/account-1-bucket/file.txt",
Key: "file.txt"
};
const result = await s3.copyObject(params).promise();
const response = {
statusCode: 200,
body: JSON.stringify(result),
};
return response;
};
The thing here is that I'm getting the error AccessDenied during the copyObject. Maybe I'm misunderstanding something, but I can't figure out how I could copy the objects from account-1 to account-2 without having to edit the bucket policy in account-2.
You need to execute S3 with the assume role permissions that you just get, I think that you are missing it :)
var s3 = new AWS.S3(accessparams2);
As bonus points:
You are assuming role and instantiate the s3 object in every lambda execution, this is not necessary, you can do it out of your handler function to improve performance
nodejs SDK use to have a .promise() method for every object so you probably can do something like the following to avoid the ugly Promise/callback thing in your getCrossAccountCredentials function (as you do with your s3 call): const data = await sts.assumeRole(sts_params).promise();

AWS Elastic search change policy using lambda function

I am trying to change the elastic search access policy through lambda function using node js currently access policy looks like bellow
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "*"
},
"Action": "es:*",
"Resource": "arn:aws:es:us-east-1:XXXX:domain/YYY/*"
}
]
}
the code which i have tried in lambda
var params = {
DomainName: 'YYYY'
};
const es = new AWS.ES();
es.upgradeElasticsearchDomain(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});
this always throw error
is not authorized to perform: es:UpdateElasticsearchDomainConfig on resource with error code "code": "AccessDeniedException",
in the param i will add AccessPolicies this is same as the policy added in my question but the Effect will be Deny
After discussing it further with the OP, it turned out it was the lack of permissions on the IAM role attached to the Lambda function.
For others facing the same issue, make sure to attach ESFullAccess to the Lambda function that is playing with ElasticSearch.
To do so, go to IAM -> Roles and select the role attached to your Lambda function.
Click on attach policies and attach ESFullAccess, like the image below:

Add authorization to to function created with amplify cli

I've created a lambda function to add new users created in my React app to a group in my cognito user pool.
I used this guide from the docs to be able to call the the lambda function via AppSync.
Now when I run the function I get the following expected error:
User: [redacted] is not authorized to perform: cognito-idp:AdminAddUserToGroup on resource: [redacted]
So far I've added this additional policy to the role in CustomResources.json:
{
"PolicyName": "CognitoAuthLambdaFunction",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["cognito-idp:AdminAddUserToGroup"],
"Resource": [
{
"Fn::Sub": [
"arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:setUsersGroup-${env}",
{ "env": { "Ref": "env" } }
]
}
]
}
]
}
}
How do I dynamically reference the cognito user pool so that I can switch between envs?
I assume the policy you've given in the question is the Lambda Execution policy, i.e. the one passed to Lambda at runtime. This policy will authorise your code running in Lambda to call the given list of API.
If my assumption is correct, there is a mistake in the policy, as the resource property refers to your Lambda function itself (ARN starts with arn:aws:lambda), while it should refer to the User Pool you want to grant access to (ARN should start with arn:aws:cognito-identity).
See https://docs.aws.amazon.com/cognito/latest/developerguide/resource-permissions.html
Assuming the Cognito User Pool is created in the same CloudFormation template, you can access the Cognito User Pool ARN using the Fn::GetAtt intrinsic function, such as
{ "Fn::GetAtt" : [ "logical_resource_id_cognito_user_pool", "ARN" ] }
See https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cognito-userpool.html for attributes available on AWS::Cognito::UserPool resource type.

AccessDenied: User is not authorized to perform: cloudfront:CreateInvalidation

I'm trying to deploy an ember app to AWS CloudFront using ember-cli-deploy and ember-cli-deploy-cloudfront.
I set up my bucket and user in AWS, gave my user AmazonS3FullAccess policy.
Set up my .env.deploy.production file to look like this:
AWS_KEY=<my key>
AWS_SECRET=<my secret>
PRODUCTION_BUCKET=<app.<my domain>.com
PRODUCTION_REGION=us-east-1
PRODUCTION_DISTRIBUTION=<my cloudfront distribution id>
My config/default.js looks like this:
/* jshint node: true */
module.exports = function(deployTarget) {
var ENV = {
build: {},
pipeline: {
activateOnDeploy: true
},
s3: {
accessKeyId: process.env.AWS_KEY,
secretAccessKey: process.env.AWS_SECRET,
filePattern: "*"
},
cloudfront: {
accessKeyId: process.env.AWS_KEY,
secretAccessKey: process.env.AWS_SECRET
}
};
if (deployTarget === 'staging') {
ENV.build.environment = 'production';
ENV.s3.bucket = process.env.STAGING_BUCKET;
ENV.s3.region = process.env.STAGING_REGION;
ENV.cloudfront.distribution = process.env.STAGING_DISTRIBUTION;
}
if (deployTarget === 'production') {
ENV.build.environment = 'production';
ENV.s3.bucket = process.env.PRODUCTION_BUCKET;
ENV.s3.region = process.env.PRODUCTION_REGION;
ENV.cloudfront.distribution = process.env.PRODUCTION_DISTRIBUTION;
}
return ENV;
};
I installed ember-cli-deploy, ember-cli-deploy-cloudfront and ember install ember-cli-deploy-aws-pack.
When I run ember deploy production
I get this error:
AccessDenied: User: arn:aws:iam::299188948670:user/Flybrary is not authorized to perform: cloudfront:CreateInvalidation
It's my understanding that ember-cli-deploy-cloudfront handles creating invalidations for you but when I saw this error I went into the AWS IAM console and created an invalidation myself. I still get the same error when I try to run ember deploy production.
IAM Policies do not allow restriction of access to specific CloudFront distributions. The work around is to use a wildcard for the resource, instead of only referencing a specific CloudFront resource. Adding that to your IAM policy will work around the issue you're having.
Here is an example of that in a working IAM policy:
{
"Statement": [
{
"Effect": "Allow",
"Action": "s3:*",
"Resource": "arn:aws:s3:::*"
},
{
"Effect": "Allow",
"Action": [
"cloudfront:CreateInvalidation",
"cloudfront:GetInvalidation",
"cloudfront:ListInvalidations"
],
"Resource": "*"
}
]
}
Docs:
AWS Services That Work with IAM
CloudFront API Permissions
Using Identity-Based Policies (IAM Policies) for CloudFront