AWS Cognito and API Gateway Authentication - amazon-web-services

I have a GET method setup under API gateway (Auth: AWS_IAM) and have a Cognito pool with developer identity. I have a lambda behind get method.
When I call Cognito I get the temporary credentials and I assume a role. My assumed role has the proper permission to execute and access everything on API gateway.
...
{
"Effect": "Allow",
"Action": [
"execute-api:Invoke"
],
"Resource": [
"*"
]
},
{
"Effect": "Allow",
"Action": [
"apigateway:GET"
],
"Resource": [
"*"
]
}
...
When I call the API gateway with this setup I get a 500, Internal Server Error.
If I remove the above API Gateway permissions from the policy then I get 403 error forbidden (User: arn:aws:sts::xxxxx:assumed-role/Cogn_Auth_Role/xxx is not authorized to perform: execute-api:Invoke on resource: arn:aws:execute-api:us-east-1:xxxx:xxx/xxx/GET/events
If I go and attached the AdminAccess to this role then everything works fine. what is the deal here? Am I missing something?

So after modifying the policy of cognito role like this, it start working fine.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"lambda:InvokeFunction"
],
"Resource": [
"*"
]
},
{
"Effect": "Allow",
"Action": [
"execute-api:Invoke"
],
"Resource": [
"*"
]
}
]
}
the important piece that make it work:
{
"Effect": "Allow",
"Action": [
"lambda:InvokeFunction"
],
"Resource": [
"*"
]
}
still not sure why I should have invoke permission for all lambdas.

If you are just trying to invoke your API Gateway API with Cognito credentials, then you may not need "apigateway:GET" in your policy. Since that is used to manage your API, e.g. to get information about your API resources.
If you are just trying to create a role so that your API can be invoked, you could try removing "apigateway:GET" from your policy and see if it works. More information.

This could also be due to the case that you have a Condition in your API Gateway resource policy which only allows access by clients that satisfy the requirement. If you were accessing the API Gateway for testing with Postman, then you would get the Unauthorized error. For example,
"Condition": {
"StringLike": {
"aws:Referer": [
"https://example.com/*",
"example.com/*"
]
}
}

Related

IAM policy to specific S3 bucket not effective without ListAllMyBuckets

I've specified the IAM access policy for one specific S3 bucket that's working fine with ListAllMyBuckets action. However I don't want to list all buckets to the user. If I remove LisAllBuckets action then I get the error,
Access Denied (Service: Amazon S3; Status Code: 403; Error Code: AccessDenied; Request ID: 37A0TA0JGKQA56FJ; S3 Extended Request ID: yWLJEG4RSqGKXjphkcvfOUTCqPe6Qtq/aZUKek1LJ
error when trying to access using access key id & Secret access key thru my application. It looks this policy should work as per AWS guidelines https://aws.amazon.com/blogs/security/writing-iam-policies-how-to-grant-access-to-an-amazon-s3-bucket/ - but its not working as expected. Can you pls help me to resolve this issue? Thanks.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:ListAllMyBuckets"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::ohdart-dev-assessments"
]
},
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject"
],
"Resource": [
"arn:aws:s3:::ohdart-dev-assessments/*"
]
}
]
}
TL;DR: This isn't supported by AWS.
I'm trying to set up the same scenario, both for least-priveleged access as well as for providing the simplest ease of use.
According to the AWS knowedlege centre (as of 2022-07-22) if you do not want to allow the s3:ListAllMyBuckets action the recommended alternative is to allow the s3:ListBucket action (possibly providing a Condition so as to limit the paths accessible):
{
"Effect": "Allow",
"Action": [
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::DOC-EXAMPLE-BUCKET"
],
"Condition": {
"StringLike": {
"s3:prefix": [
"folder1/folder2/*"
]
}
}
}
and then to:
Provide the user with a direct console link to the S3 bucket or folder. The direct console link to an S3 bucket looks like this:
https://s3.console.aws.amazon.com/s3/buckets/DOC-EXAMPLE-BUCKET/folder1/folder2/

API Gateway does not have permission to assume the provided role

I am trying to invoke a lambda function from an API Gateway. I have followed the next tutorial: https://docs.aws.amazon.com/apigateway/latest/developerguide/integrating-api-with-aws-services-lambda.html
However, I get the following error when I test it from the web of API Gateway:
Execution failed due to configuration error: API Gateway does not have permission to assume the provided role
I have search in google and I have not been able to solve it (this, for instance).
If I go to the IAM Management Console, I can see that the trust relationship allows API Gateway to assume the rol, and the JSON of the trust relationship is the following:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"apigateway.amazonaws.com",
"lambda.amazonaws.com"
]
},
"Action": "sts:AssumeRole"
}
]
}
I have tried also with:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Service": [
"lambda.amazonaws.com",
"apigateway.amazonaws.com"
]
},
"Action": "sts:AssumeRole"
}
]
}
The policy of the role is the next:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"lambda:InvokeFunction"
],
"Resource": [
"*"
]
}
]
}
What is wrong here? Thank you
To fix this go to the role in your IAM and select the “Trust Relationships” tab. From here edit the policy and for the Principal Service add in “apigateway.amazonaws.com” as seen below. This will grant the API Gateway the ability to assume roles to run your function in addition to the existing lambda permission.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"apigateway.amazonaws.com",
"lambda.amazonaws.com"
]
},
"Action": "sts:AssumeRole"
}
]
}
I guess you have not attached the role to the invoking method i.e the api gateway
Attaching the created role to the api gateway is needed for api to execute the lamda.
Under Execution role, choose Choose an existing role.
Enter the role ARN for the lambda_invoke_function_assume_apigw_role
role you created earlier.
Choose Save.
AWS Link

AWS Cognito role restrict permissions to invoke Lambda

I have a Cognito Identity Pool with an Authenticated role with a policy applied as per below:
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"execute-api:Invoke"
],
"Resource": "*",
"Effect": "Allow"
},
{
"Action": "lambda:InvokeFunction",
"Resource": "arn:aws:lambda:ab:asdf:function:b",
"Effect": "Allow"
}
]
}
I have an API Gateway resource which has AWS_IAM Authorization applied.
Now this only gives access to invoke 1 specific lambda function.
However, I'm able to invoke other lambda functions, I am not restricted to this function only.
When I try the policy simulator, it states that other functions will be denied, however this is not the case.
I've confirmed that the request is assuming the correct role, this is what comes through in the request:
userArn: "arn:aws:sts::00000000:assumed-role/appAuthRole-dev/CognitoIdentityCredentials"
Why would this be?

cognito fine grained access control and API gateway

In api gateway, I have the following resource ARN:
arn:aws:execute-api:us-east-2:XXXXXXXXXXXXX:syx381ecq9/*/GET/members/*
which provides a link to get a list of members based on a class_id - /members/{id}
A user that is in a class can only see the list of members that belong into that class.
I have specified cognito user pool with the following IAM policy (assume that class1 is class_id)
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"cognito-identity:*",
"mobileanalytics:PutEvents",
"cognito-sync:*",
"lambda:*",
"execute-api:*"
],
"Resource": [
"arn:aws:execute-api:us-east-2:XXXXXXXXXXXXX:syx381ecq9/*/GET/members/class1"
]
}
]
}
however, when used the link GET /members/class1, I get the following message:
Execution failed due to configuration error: API Gateway could not determine the callers credentials
I checked in cloudwatch, no log from lambda, therefore I think lambda was not executed.
I continued trying with class2. This time the following message was shown:
User:arn:aws:sts::XXXXXXXXXXXX:assumed-role/Cognito-sample_client1/CognitoIdentityCredentials is not authorized to perform: execute-api:Invoke on resource: arn:aws:execute-api:us-east-2:********8469:syx381ecq9/sample/GET/inspectors/client2
I have checked in policy stimulate and everything worked fine with message Allowed
I have no idea why I could not call lambda? how can I fix this problem?
Thanks
OK, I found the answer. The above policy only allows calling lambda function for
arn:aws:execute-api:us-east-2:XXXXXXXXXXXXX:syx381ecq9/*/GET/members/class1
therefore, when cognito credential has passed, api will try to call lambda but unfortunately, the policy restricts that. In order to get through it, we need to separate it into another statement like the following:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"cognito-identity:*",
"mobileanalytics:PutEvents",
"cognito-sync:*",
"execute-api:*"
],
"Resource": [
"arn:aws:execute-api:us-east-2:XXXXXXXXXXXXX:syx381ecq9/*/GET/members/class1"
]
},
{
"Effect": "Allow",
"Action": [
"lambda:*"
],
"Resource": [
"*"
]
}
]
}
we can customized specific lambda's arn if required

Permissions to access ElasticSearch from Lambda?

I'm trying to use Elasticsearch for data storage for a Lambda function connected to Alexa Skills Kit. The Lambda works alright without Elasticsearch but ES provides much-needed fuzzy matching.
The only way I've been able to access it from Lambda is by enabling Elasticsearch global access but that's a really bad idea. I've also been able to access from my computer via open access policy or IP address policy. Is there a way to do read-only access via Lambda and read-write via IP?
On IAM I granted my Lambda role AmazonESReadOnlyAccess. On the ES side I tried this but it only worked for IP address:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": [
"arn:aws:iam::NUMBER:root",
"arn:aws:iam::NUMBER:role/lambda_basic_execution"
]
},
"Action": "es:*",
"Resource": "arn:aws:es:us-east-1:NUMBER:domain/NAME/*"
},
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"AWS": "*"
},
"Action": "es:*",
"Resource": "arn:aws:es:us-east-1:NUMBER:domain/NAME/*",
"Condition": {
"IpAddress": {
"aws:SourceIp": "MY IP"
}
}
}
]
}
This forum post asks the same question but went unanswered.
The only way I know of to do this is to use a resource-based policy or an IAM-based policy on your ES domain. This would restrict access to a particular IAM user or role. However, to make this work you also need to sign your requests to ES using SigV4.
There are libraries that will do this signing for you, for example this one extends the popular Python requests library to sign ElasticSearch requests via SigV4. I believe similar libraries exist for other languages.
Now it's possible from your code with elasticsearch.js. Before you try it, you must install http-aws-es module.
const AWS = require('aws-sdk');
const httpAwsEs = require('http-aws-es');
const elasticsearch = require('elasticsearch');
const client = new elasticsearch.Client({
host: 'YOUR_ES_HOST',
connectionClass: httpAwsEs,
amazonES: {
region: 'YOUR_ES_REGION',
credentials: new AWS.EnvironmentCredentials('AWS')
}
});
// client.search({...})
Of course, before using it, configure access to elasticsearch domain:
For external (outside AWS) access to your Elasticsearch cluster, you want to create the cluster with an IP-based access policy. Something like the below:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"AWS": "*"
},
"Action": "es:*",
"Condition": {
"IpAddress": {
"aws:SourceIp": [
"<<IP/CIDR>>"
]
}
},
"Resource": "arn:aws:es:<<REGION>>:<<ACCOUNTID>>:domain/<<DOMAIN_NAME>>/*"
}
]
}
For your Lambda function, create the role that the Lambda function will assume with the below policy snippet.
{
"Sid": "",
"Effect": "Allow",
"Action": [
"es:DescribeElasticsearchDomain",
"es:DescribeElasticsearchDomains",
"es:DescribeElasticsearchDomainConfig",
"es:ESHttpPost",
"es:ESHttpPut"
],
"Resource": [
"arn:aws:es:<<REGION>>:<<ACCOUNTID>>:domain/<<DOMAIN_NAME>>",
"arn:aws:es:<<REGION>>:<<ACCOUNTID>>:domain/<<DOMAIN_NAME>>/*"
]
},
{
"Sid": "",
"Effect": "Allow",
"Action": [
"es:ESHttpGet"
],
"Resource": [
"arn:aws:es:<<REGION>>:<<ACCOUNTID>>:domain/<<DOMAIN_NAME>>/_all/_settings",
"arn:aws:es:<<REGION>>:<<ACCOUNTID>>:domain/<<DOMAIN_NAME>>/_cluster/stats",
"arn:aws:es:<<REGION>>:<<ACCOUNTID>>:domain/<<DOMAIN_NAME>>/<<INDEX>>*/_mapping/<<TYPE>>",
"arn:aws:es:<<REGION>>:<<ACCOUNTID>>:domain/<<DOMAIN_NAME>>/_nodes",
"arn:aws:es:<<REGION>>:<<ACCOUNTID>>:domain/<<DOMAIN_NAME>>/_nodes/stats",
"arn:aws:es:<<REGION>>:<<ACCOUNTID>>:domain/<<DOMAIN_NAME>>/_nodes/*/stats",
"arn:aws:es:<<REGION>>:<<ACCOUNTID>>:domain/<<DOMAIN_NAME>>/_stats",
"arn:aws:es:<<REGION>>:<<ACCOUNTID>>:domain/<<DOMAIN_NAME>>/<<INDEX>>*/_stats"
]
}
I think you could more easily condense the above two policy statements into the following:
{
"Sid": "",
"Effect": "Allow",
"Action": [
"es:DescribeElasticsearchDomain",
"es:DescribeElasticsearchDomains",
"es:DescribeElasticsearchDomainConfig",
"es:ESHttpPost",
"es:ESHttpGet",
"es:ESHttpPut"
],
"Resource": [
"arn:aws:es:<<REGION>>:<<ACCOUNTID>>:domain/<<DOMAIN_NAME>>",
"arn:aws:es:<<REGION>>:<<ACCOUNTID>>:domain/<<DOMAIN_NAME>>/*"
]
}
I managed to piece the above together from the following sources:
https://aws.amazon.com/blogs/security/how-to-control-access-to-your-amazon-elasticsearch-service-domain/
How to access Kibana from Amazon elasticsearch service?
https://forums.aws.amazon.com/thread.jspa?threadID=217149
You need to go to the access policy of Lambda and provide the AWS ARN to connect
http://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/es-aws-integrations.html#es-aws-integrations-s3-lambda-es-authorizations
AWS Lambda runs on public EC2 instances. So simply adding a whitelist of IP addresses to the Elasticsearch access policy will not work. One way to do this will be to give the Lambda execution role appropriate permissions to the Elasticsearch domain. Make sure that the Lambda Execution role has permissions to the ES domain and the ES domain access policy has a statement which allows this Lambda Role ARN to do the appropriate actions. Once this is done all you would have to do is sign your request via SigV4 while accessing the ES endpoint Hope that helps!