AWS API Gateway CORS from Swagger - amazon-web-services

I am importing a Swagger file into a Terraform created AWS API Gateway deployment, and everything seems to work fine apart from enabling CORS. Once I have finished deploying, then I always receive the 403 CORS error from any endpoint. I have to manually then go through an enable CORS within the AWS console.
I import the swagger file via the AWS CLI like this;
aws apigateway put-rest-api \
--body file://swagger.json \
--mode overwrite \
--rest-api-id ${REST_API_ID}
The swagger file uses the below to map the correct headers;
"x-amazon-apigateway-integration": {
"responses": {
"2\\d{2}": {
"statusCode": "200",
"responseParameters": {
"method.response.header.Access-Control-Allow-Headers" : "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token,X-Correlation-ID'",
"method.response.header.Access-Control-Allow-Methods" : "'*'",
"method.response.header.Access-Control-Allow-Origin": "'*'"
}
}
}
}
This maps to response codes within the top level Swagger file;
"responses": {
"200": {
"description": "Succesfully created Report",
"headers": {
"Access-Control-Allow-Origin": {
"type": "string"
},
"Access-Control-Allow-Headers": {
"type": "string"
},
"Access-Control-Allow-Methods": {
"type": "string"
}
}
}
}
Despite doing this, I still get CORS erros straight after importing the Swagger file.
Is there anything I am missing here?

Related

AWS Lambda Controller route not matching, with {proxy+} - 404 Not Found

I have a .NET core 3.1 project with a GET endpoint. Locally the route works fine - "/api" GET request returns JSON string.
After publishing to my AWS Lambda function, and invoking the lambda via Postman, I get a 404 Not Found response to the "/api" GET request. Full URL: "https://(lambda domain)/default/MyLambda2/api"
In my project's Startup.cs ConfigureServices and Configure methods, I added "LambdaLogger.Log" statements. My log lines show up in CloudWatch (so I am definitely reaching the app). It's just after that, the route fails.
In ConfigureServices method, I have
services.AddControllers();
In Configure method, I have
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
And in the Controller file
namespace LambdaTest.Controllers
{
[Route("api")]
public class ValuesController : ControllerBase
{
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
}
}
I have an API Gateway trigger defined for the lambda, with
API endpoint: "https://(lambda domain)/default/MyLambda2/{proxy+}"
API type: REST
Authorization: NONE
Method: ANY
In Resources tab, I have...
/MyLambda2
ANY
/{proxy+}
ANY
OPTIONS
... and I have all Resources deployed to "default" stage (via Actions > Deploy API).
Here is the default auto-generated serverless.template file...
{
"AWSTemplateFormatVersion": "2010-09-09",
"Transform": "AWS::Serverless-2016-10-31",
"Description": "An AWS Serverless Application that uses the ASP.NET Core framework running in Amazon Lambda.",
"Parameters": {},
"Conditions": {},
"Resources": {
"AspNetCoreFunction": {
"Type": "AWS::Serverless::Function",
"Properties": {
"Handler": "LambdaTest::LambdaTest.LambdaEntryPoint::FunctionHandlerAsync",
"Runtime": "dotnetcore3.1",
"CodeUri": "",
"MemorySize": 256,
"Timeout": 30,
"Role": null,
"Policies": [
"AWSLambda_FullAccess"
],
"Events": {
"ProxyResource": {
"Type": "Api",
"Properties": {
"Path": "/{proxy+}",
"Method": "ANY"
}
},
"RootResource": {
"Type": "Api",
"Properties": {
"Path": "/",
"Method": "ANY"
}
}
}
}
}
},
"Outputs": {
"ApiURL": {
"Description": "API endpoint URL for Prod environment",
"Value": {
"Fn::Sub": "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/"
}
}
}
}
What could be causing the route to not get picked up in lambda?
Thanks
Found the issue. I needed my route on the controller to be
[Route("MyLambda2/api")]

How to reference existing API Gateway Authorizer in OpenApi endpoint definition

I've got an AWS SAM/CloudFormation template written in JSON which contains:
A definition of an API Gateway API, with endpoints defined using the
OpenAPI DefinitionBody format
A definition of an API Gateway Authorizer, defined using SAM/CF format
I'm trying to find a way to reference the Authorizer (2) for each of the endpoints (1) I wish to use the Authorizer for.
Here's the code:
Authorizer definition:
"LambdaAuthorizer":{
"Type": "AWS::ApiGateway::Authorizer",
"Properties":{
"IdentitySource":"method.request.header.Authorization",
"Type":"TOKEN",
"RestApiId":{
"Ref": "ApiName"
},
"AuthorizerUri": {
"Fn::Join" : ["", ["arn:aws:apigateway:", {"Ref": "AWS::Region"}, ":lambda:path/2015-03-31/functions/", {"Fn::GetAtt": ["AuthLambda", "Arn"]}, "/invocations"]]
},
"IdentityValidationExpression": "^[a-zA-Z0-9]{3,32}$",
"AuthorizerResultTtlInSeconds": 300,
"AuthorizerCredentials": {
"Fn::GetAtt": ["LambdaAuthorizerRole", "Arn"]
},
"Name":"lambda-authorizer"
}
},
Endpoint definition
"API": {
"Type": "AWS::Serverless::Api",
"Properties": {
...
"DefinitionBody": {
...
"paths": {
"/endpoint": {
"post": {
"responses": {
"200": {
"description": "200 response"
}
},
"x-amazon-apigateway-integration": {
"uri": {
"Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${AuthLambda.Arn}/invocations"
},
"responses": {
"default": {
"statusCode": "200",
"contentHandling": "CONVERT_TO_TEXT"
}
},
"passthroughBehavior": "when_no_match",
"httpMethod": "POST",
"contentHandling": "CONVERT_TO_TEXT",
"type": "aws_proxy"
},
"security" : [{
"NAME OF OPEN API SECURITY DEFINITION":[] // Can I reference my existing Authorizer?
}]
}
},
}
}
}
}
As you can see I've found the OpenAPI security property, which according to the AWS documentation is used as a reference to a securityDefinitions where the Authorizer can be defined.
But I've already got my Authorizer defined in CloudFormation JSON. Can't I reference that instead?
API Gateway does not allow you to directly reference an existing Authorizer ID in the OpenAPI paths. However, there is a workaround. You can provide a securitySchemes definition. The format of securitySchemes will vary between OpenAPI 2.0 (aka Swagger) and OpenAPI 3.0 (documentation here).
Make sure that the details of the securitySchemes exactly match the existing API Authorizer. If everything matches, then API Gateway will use the existing Authorizer. If there are differences, then it will create a new Authorizer or overwrite the existing one. Ensure the following:
the security scheme object name is the same as the Authorizer name. In this case it is "lambda-authorizer"
type is "apiKey" for lambda authorizers
name is the HTTP header you're using for authorization (typically the "Authorization" header)
x-amazon-apigateway-authtype is "custom" for lambda authorizers
x-amazon-apigateway-authorizer has the correct type ("request" or "token"), the correct uri, ttl, and identity source
I've tested this with a regional REST API, so it may behave differently for other API Gateway types. The example below is a simplified version of the AWS PetStore example API.
{
"openapi" : "3.0.1",
"info" : {
"description" : "Your first API with Amazon API Gateway. This is a sample API that integrates via HTTP with our demo Pet Store endpoints",
"version" : "1",
"title" : "PetStore"
},
...
"paths" : {
"/pets" : {
"get" : {
...
"security" : [ {
"lambda-authorizer" : [ ]
} ],
"x-amazon-apigateway-integration" : {
...
}
}
}
},
"components" : {
"schemas" : {
...
},
"securitySchemes" : {
"lambda-authorizer" : {
"type" : "apiKey",
"name" : "Authorization",
"in" : "header",
"x-amazon-apigateway-authtype" : "custom",
"x-amazon-apigateway-authorizer" : {
"type" : "request",
"authorizerUri" : "arn:aws:apigateway:${region}:lambda:path/2015-03-31/functions/${lambda_authorizer_arn}/invocations",
"authorizerResultTtlInSeconds" : 300,
"identitySource" : "method.request.header.Authorization"
}
}
}
}
}
In this example, you'd have to replace ${region} with your API Gateway region and ${lambdaauthorizer_arn} with the full ARN of your lambda function. The full string will look like this: arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:123456789012:function:lambda-authorizer-function/invocations
TL;DR: If you want to use an existing Authorizer, you have to fully define it in the OpenAPI file.

List Documentation Parts AWS apigateway

Through Terraform I have created an AWS apigateway based on a swagger file.
I that swagger file I have added the specific AWS endpoints to document the API, like:
"x-amazon-apigateway-documentation": {
"documentationParts": [
{
"location": {
"type": "API"
},
"properties": {
"description": "This is the API description"
}
},
{
"location": {
"type": "METHOD",
"method": "GET",
"path": "/foo/{bar}"
},
"properties": {
"description": "This is the method description"
}
}
]
}
Then I have published the documentation version 1.0 through AWS console and I am trying to fetch that documentation via HTTP as stated in :
List Documentation Parts
But, unfortunately, I can't. I am doing a GET to the endpoint
http://apigateway.eu-central1.amazonaws.com/restapis/<TheIdOfMyApiGateway>/documentation/parts
And it doesn't even give me a 4XX code but neither response nor HTTP code at all.
What I am doing wrong?
My bad was a typo in the region.
I was missing a dash, the correct endpoint is:
http://apigateway.eu-central-1.amazonaws.com/restapis/<TheIdOfMyApiGateway>/documentation/parts
With eu-central-1 instead of eu-central1
I check the naming of the regions in AWS Regions

AWS Api gateway write to S3 with timestamp as filename

I'm trying to create an endpoint on Api Gateway that writes on AWS s3 bucket using a filename like data/$timestamp where timestamp is the $context.requestTimeEpoch variable that should be available in each request
My api gateway request integration is this
{
"type": "AWS",
"httpMethod": "PUT",
"uri": "arn:aws:apigateway:eu-west-1:bucketname.s3:path/data/{reqTimestamp}",
"credentials": "arn:aws:iam::xxxxx:role/ApiGatewayWriteToDemoS3Bucket",
"requestParameters": {
"integration.request.path.reqTimestamp": "context.requestTime"
},
"passthroughBehavior": "WHEN_NO_MATCH",
"timeoutInMillis": 29000,
"cacheNamespace": "5f92eajcg9",
"cacheKeyParameters": [],
"integrationResponses": {
"200": {
"statusCode": "200",
"responseParameters": {
"method.response.header.Access-Control-Allow-Origin": "'*'"
},
"responseTemplates": {
"application/json": null
}
}
}
}
However I get this error
Execution failed due to configuration error: Illegal character in path at index 49: https://bucketname.s3-eu-west-1.amazonaws.com/data/{reqTimestamp}
it worked only once, then I had an S3 authentication error but that's another story
You need to add a value in the URL Path Parameter configuration of the Integration Request in your API Gateway resource method, setting the name to reqTimestamp, and value to method.request.path.reqTimestamp.
This will map the value received by API Gateway in the URL to the path override parameter you defined.

AWS API Gateway and EC2 Service Proxy

I am trying to POST a json string to API Gateway and in turn have API Gateway send the JSON to an EC2 server.
My issue is I can't find good documentation from Amazon on how to accomplish this.
When I test the setup I get this
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Response><Errors><Error><Code>InvalidHttpRequest</Code><Message>The HTTP request is invalid. Reason: Unable to parse request</Message></Error></Errors><RequestID>1fa47f52-d75c-4ff8-8992-3eac11a79015</RequestID></Response>"
Which means very little to me. I assume it is an issue with API Gateway trying to send the request to EC2 and it can't so it generates this error. So perhaps I am setting up the EC2 AWS Service Proxy in API Gateway incorrectly. Which is likely because I have no idea what I am supposed to set 'Action' to right now I have it pointing to the EC2 instance, only cause i don't see any other place to put that info.
This really shouldn't be that hard I have successfully done this thing connecting to Lambda and have looked through all the documentation and all I can find is this: http://docs.aws.amazon.com/apigateway/latest/developerguide/getting-started-aws-proxy.html#getting-started-aws-proxy-add-resources
Which is less than helpful for this scenario. Any Ideas?
I think you confused AWS Service Proxy and HTTP Service proxy.
API Gateway can forward API calls to different type of backends:
- a lambda function
- an AWS Service (see http://docs.aws.amazon.com/apigateway/latest/developerguide/integrating-api-with-aws-services-s3.html for an example)
- an existing API, running on AWS or on premises (your use case)
When defining you API, be sure to define a POST verb and point the Endpoint URL to your EC2 instance URL
I just made a test using the JSON POST service available online at http://gurujsonrpc.appspot.com/ and it works as expected.
Here is the Swagger export of my test API.
{
"swagger": "2.0",
"info": {
"version": "2016-04-11T20:46:13Z",
"title": "test"
},
"host": "c22wfjg4d7.execute-api.eu-west-1.amazonaws.com",
"basePath": "/prod",
"schemes": [
"https"
],
"paths": {
"/": {
"post": {
"produces": [
"application/json"
],
"responses": {
"200": {
"description": "200 response",
"schema": {
"$ref": "#/definitions/Empty"
}
}
},
"x-amazon-apigateway-integration": {
"responses": {
"default": {
"statusCode": "200"
}
},
"uri": "http://gurujsonrpc.appspot.com/guru",
"httpMethod": "POST",
"type": "http"
}
}
}
},
"definitions": {
"Empty": {
"type": "object"
}
}
}