For a Kinesis stream, I created a proxy API using AWS API Gateway. I added a custom authorizer using python Lambda for the proxy.
After publish of lambda function and deploy of API, I was able to successfully test the API using Gateway Test functionality. I could see the logs in cloudwatch which had detailed prints from custom auth lambda function. After successful authentication, API Gateway pushed the record to my Kinesis stream
However when I call the same API from Chrome Postman client, I get 500 Internal Server Error and response headers includes X-Cache → Error from cloudfront, x-amzn-ErrorType → AuthorizerConfigurationException
Lambda auth function returns the policy which allows execute request for my API. Policy Document returned is:
{
"policyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": "execute-api:Invoke",
"Resource": [
"arn:aws:execute-api:us-east-1:1234567:myapiId/staging/POST/*"
],
"Effect": "Allow"
}
]
},
"principalId": "Foo"
}
Why does the request fail from Chrome or curl but the same API test works fine from API Gateway?
AuthorizerConfigurationException is usually an indication that API Gateway failed to call your authorizer due a permissions error.
Please either make sure you've properly configured your function to be invoked by API Gateway. An easy to reset this is by removing and re-adding the function to your authorizer. The console will then prompt you to add the necessary permissions.
I was facing the same error, in my case a nodejs function, I was adding one context key as array.
{
policyDocument: {
Version: '2012-10-17',
Statement: [{
Action: 'execute-api:Invoke',
Effect: effect,
Resource: `${arn.split('/').slice(0, 2).join('/')}/*`,
}],
},
context: {
roles: ['admin']
}
As doc says:
You can access the stringKey, numberKey, or booleanKey value (for example, "value", "1", or "true") of the context map in a mapping template by calling $context.authorizer.stringKey, $context.authorizer.numberKey, or $context.authorizer.booleanKey, respectively. The returned values are all stringified. Notice that you cannot set a JSON object or array as a valid value of any key in the context map.
Remove the role key and it's working.
Figured out what was causing the issue. From python lambda function, I was returning a json string instance. Instead it should be json object.
Its strange that the same lambda function did not error when I tested the API from API Gateway "test" feature. But when the API was called from internet (curl or chrome) it failed.
#return policy_string ... this is incorrect.
return json.loads(policy_string)
In my case i was not returning a properly formatted IAM policy document. My Authorizer function was doing wrong assumptions on how to get some parameters from request, and default result was not proper policy (this was my specific case). I managed to debug it using CloudWatch log service, with traditional logging instructions coming from my function code.
Related
I have a post endpoint in API gateway with the HTTP request integration. It's not a proxy endpoint. The URL it's pointed at is the search URL for my AWS ElasticSearch domain. I'm taking the body of the post request and mapping it to a template to query the ES domain with.
Right now I'm getting this response back from ES: {"Message":"User: anonymous is not authorized to perform: es:ESHttpPost"}.
I don't have fine grained access control enabled on the ES Domain, and it's not in a VPC. My JSON access policy looks like this:
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "*"
},
"Action": "es:ESHttp*",
"Resource": "arn:aws:es:xx-xxxx-x:999999999999:domain/XXXXX/*",
"Condition": {
"IpAddress": {
"aws:SourceIp": [
"X.X.X.X/X",
"X.X.X.X/X"
]
}
}
}
]
}
With this policy, I'm able to reach out and query the domain directly and navigate to the Kibana dashboard while on the IP's I have in the allow list. However, I'm assuming the query request coming from my gateway endpoint has a different IP address as that's where I get the 403 response mentioned above.
I know I can find the origin IP address in the request in the x-forwarded-for header, but I don't think I'm able to add that as a condition to the ES access policy, I've tried a few other policy conditions as well, such as a StringLike for the principle ARN and looking for the method request ARN, but these don't work.
Ideally, I would like to just be able to override the sourceIP with the actual origin and not gateway's IP address, but I don't know if this is possible. I also know I could just use a lambda function here, but I would like to avoid needing a function here, to avoid having code / another service to maintain.
Basically, I'm hoping to be able to still allow anonymous user access when requests are coming from certain IP's, but I want to create and abstract certain frequently used queries with a few gateway endpoints. Is there a way I can add the API endpoint to the allow list, or do I have to enable fine-grained access control?
Hi I am trying to implement Custom authorization using api gateway and lambda. My current understanding is as follows. I have created simple GET method and deployed to Dev Enviroment. Create lambda authorize to return the IAM policy. I used python blue print api-gateway-authorizer-python. Below is the format of response we should get.
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "execute-api:Invoke",
"Effect": "Deny",
"Resource": "arn:aws:execute-api:us-east-1:{ACCOUNTID}:{APIID}/ESTestInvoke-stage/GET/"
}
]
}
In the above IAM policy, Resource is ARN of my Api Dev stage. What is Action indicates? Also to test this now, How can I get token? I want to test it from postman? I am just confused here. I have my AWS account and authorization is nothing but my current account has access to this Dev stage? How internally it works? To store all the permissions do we need to maintain any other DB? Can someone help me to understand this? Any help would be appreciated. Thanks
To get a token you need an identity provider. Amazon Cognito is one of those (Google, Facebook works as well). To understand that policy you have to understand the chain of commands.
Suppose a client calls an API endpoint (GET /orders), this will trigger a service Lambda so the token can be verified. If the verification is successful, another Lambda (GetOrder a business Lambda this time) will be invoked by Api Gateway.
If your service Lambda (Lambda authorizer) will return a policy like this:
{
"principalId": "apigateway.amazonaws.com",
"policyDocument": {
"Version": "2012-10-17",
"Statement": [{
"Action": "execute-api:Invoke",
"Effect": "Allow",
"Resource": "arn:aws:execute-api:{REGION}:{ACCOUNT_ID}:{API_ID}/Prod/GET/"
}]
}
}
the API Gateway service (i.e. the principalId equals to apigateway.amazonaws.com) is allowed (i.e. Effect equals to Allow) to invoke (i.e. Action equals to execute-api:Invoke) the given API resource (e.g. Resource equals to arn:aws:execute-api:{REGION}:{ACCOUNT_ID}:{API_ID}/Prod/GET/).
In your case the ARN that you return is related to the tester of API Gateway, but it should point to your real function.
This article may help.
Ok, what are custom authorisers for API gateway: custom authorisers let you define your own authentication & authorisation logic.
How do you get the token: Thats part of your authentication and authorisation logic, If you are deploying your services on AWS, you can use AWS cognito. API gateway also supports cognito authorization.
https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-integrate-with-cognito.html
Using postman to test? it's handy to use postman. I use it.
How does the permission internally work: You can use a token to authenticate a user. (If you are using a JWT token, you can also verify the user's claims).
Do you need an internal db? this is entirely depends on your use case. if your use case is simple as all users treated equal, you might not need a db. lets say some users can access some additional features, you may still not need a db (you can use claims). but if your application becomes complicated and you have to manage different access permissions, users, groups, etc, you may surely need a db.
I have an API Gateway api setup that I want to limit access to. I have a subdomain setup in AWS Route 53 that points to a CloudFront distribution where my app lives. This app makes a POST request to the API.
I have looked into adding a resource policy for my api based on the example 'AWS API Whitelist' but I can't seem to get the syntax correct, I constantly get errors.
I also tried creating an IAM user and locking down the API with AWS_IAM auth but then I need to create a signed request which seems like a lot of work that should be a lot easier via resource policies?
This is an example of the resource policy I tried to attach to my API:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity {{CloudFrontID}}"
},
"Action": "execute-api:Invoke",
"Resource": [
"execute-api:/*/*/*"
]
}
]
}
This returns the following error:
Invalid policy document. Please check the policy syntax and ensure that Principals are valid.
"AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity {{CloudFrontID}}"
The problem with this concept is that this is a public HTTP request. Unless it's a signed request, AWS will not know about any IAM or ARN resources, it just knows it has a standard HTTP request. If you make the request with a curl -v command you will see the request parameters look something like this:
GET
/test/mappedcokerheaders
HTTP/2
Host: APIID.execute-api.REGION.amazonaws.com
User-Agent: curl/7.61.1
Accept: */*
It's possible you could filter the user Agent as I do see that condition defined here.
I would check all of the values that are coming in the request from cloudfront vs the request from your curl directly to the API by trapping the api gw request id in the response headers, and looking for those in your API Gateway Access Logs. You'll have to enable Access Logs though, and define what parameters you want logged, which you can see how to do here.
The problem is that OAI cannot be used in CustomOrigin. If you are not forwarding User-Agent to the API Gateway CustomOrigin, then the simplest approach for you is to add a resource policy in API Gateway which only allows aws:UserAgent: "Amazon CloudFront".
Be careful: User-Agent can very easily be spoofed. This approach is designed to only prevent "normal access" like a random bot on the web is trying to scrape your site.
The User-Agent header is guaranteed to be Amazon CloudFront. See the quote from Request and Response Behavior for Custom Origins.
Behavior If You Don't Configure CloudFront to Cache Based on Header Values: CloudFront replaces the value of this header field with Amazon CloudFront. If you want CloudFront to cache your content based on the device the user is using, see Configuring Caching Based on the Device Type.
Here is how the full resource policy looks like:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": "execute-api:Invoke",
"Resource": "arn:aws:execute-api:us-west-2:123456789012:abcdefghij/*/*/*",
"Condition": {
"StringEquals": {
"aws:UserAgent": "Amazon CloudFront"
}
}
}
]
}
Here is how to configure it in serverless.yml:
provider:
resourcePolicy:
- Effect: Allow
Principal: "*"
Action: execute-api:Invoke
Resource:
- execute-api:/*/*/*
Condition:
StringEquals:
aws:UserAgent: "Amazon CloudFront"
I have a subdomain setup in AWS Route 53 that points to a CloudFront
distribution where my app lives. This app makes a POST request to the
API.
What I understand is that you have a public service that can be called from the web browser ( https://your-service.com )
You want the service to respond only when the client's browser is at https://your-site.com. The service will not respond when the browser for example is on https://another-site.com
If that is the case,
you will need to read more about CORS
This will not prevent a random guy / web client to go to and call your service directly to https://your-service.com however. To protect the service from that, you need proper authentication system
I've set up an API with a single method on AWS API gateway to an HTTP service using a VPC link.
I've also set up a TOKEN custom authorizer for that API method using Lambda.
The custom authorizer is based on the AWS Node.js custom authorizer blueprint, where I'm basically allowing all methods through and returning a 'context' section along with an IAM.
{
"principalId": "user|a1b2c3d4",
"policyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": "execute-api:Invoke",
"Effect": "Allow",
"Resource": [
"arn:aws:execute-api:[region]:[account_id]:[restApiId]/[stage]/*/*"
]
}
]
},
"context": {
"key": "new-token",
"number": 1,
"bool": true
}
}
That's the result from the Lambda when I test it, both on the Lambda test console and one the API gateway custom authorizer console.
Finally, I mapped the Authorization header to 'context.authorizer.key' in the Integration Request section.
Now, when I execute the request, I expect that the Authorization header would be populated with the value from the 'context' section of the authorizer's response, but it's never populated.
What am I doing wrong?
It turns out that my setup was correct all along.
I was testing using the AWS API Gateway testing tool (using the TEST button in one of the screenshots), but that tool does not really allow you to test this out end-to-end. I ended up deploying my stage, and then testing it using Postman. Then it started working. Weird.
This is driving me totally crazy
Im trying to call a api-gateway rest service from an angular app that i have restricted from API-GATEWAY with IAM access. So i need to call with IAM authentication. Im using temporary IAM credentials that i have already obtained
My call to the service fails saying there is no Access-Control-Allow-Origin header. When i try and call my service from postman i dont see the required header on the response. On my postman call I get a 403 status back since my authentication actually failed, but i still expected the header. If i remove the IAM authentication on the method it works, i get back the response string and the header im looking for.
What am i missing here? Surely even if my authentication failed i must still get back that header so that i can actually see the message that says your authentication failed.
Any help will be much appreciated
Thanks
From this link on the AWS forum it appears that there is an open issue related to exactly what im experiencing here... Not sure if what im looking for is currently possible
Did you allow your Cognito role to access to the API endpoints?
You have to use a policy similar to this one for the Cognito role:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"execute-api:invoke"
],
"Resource": [
"arn:aws:execute-api:<API_REGION>:<ACCOUNT_ID>:<API_ID>/*"
]
}
}
}
If you use IAM authentication for your endpoint and if you do not allow the Cognito role to access to your API, API Gateway will generate a 403 response. This response is not defined by your API definition because the request is refused by API Gateway before it could get to the point of been processed by your API. That is why your CORS configuration will not apply.
Please ensure you do not have AWS_IAM authentication enabled on your OPTIONS method, otherwise the browser will not be able to make the pre-flight request and your request will fail. You should still have AWS_IAM enabled on your other methods.