how to send post requests to AWS Lambda function created with serverless - post

TLDR
I cant figure out how to format my post request for my AWS Lambda API created with serverless
Current Implementation
I have a Lambda function created using the serverless framework. I have two functions; a function which prints out all modules and their version, and another that does a prediction on data sent via a post request. It handles dependency storage by mounting an EFS volume, here's the serverless.yml
service: test3Predict
plugins:
- serverless-pseudo-parameters
custom:
efsAccessPoint: fsap-**********
LocalMountPath: /mnt/efs
subnetsId: subnet-**********
securityGroup: sg-**********
provider:
name: aws
runtime: python3.6
region: us-east-2
timeout: 20
package:
exclude:
- node_modules/**
- .vscode/**
- .serverless/**
- .pytest_cache/**
- __pychache__/**
functions:
ohPredict:
handler: handler.lambda_handler_OH
environment: # Service wide environment variables
MNT_DIR: ${self:custom.LocalMountPath}
vpc:
securityGroupIds:
- ${self:custom.securityGroup}
subnetIds:
- ${self:custom.subnetsId}
iamManagedPolicies:
- arn:aws:iam::aws:policy/AmazonElasticFileSystemClientReadWriteAccess
events:
- http:
path: ohPredict
method: get
fileSystemConfig:
localMountPath: '${self:custom.LocalMountPath}'
arn: 'arn:aws:elasticfilesystem:${self:provider.region}:#{AWS::AccountId}:access-point/${self:custom.efsAccessPoint}'
test:
handler: handler.test
environment: # Service wide environment variables
MNT_DIR: ${self:custom.LocalMountPath}
vpc:
securityGroupIds:
- ${self:custom.securityGroup}
subnetIds:
- ${self:custom.subnetsId}
iamManagedPolicies:
- arn:aws:iam::aws:policy/AmazonElasticFileSystemClientReadWriteAccess
events:
- http:
path: test
method: get
fileSystemConfig:
localMountPath: '${self:custom.LocalMountPath}'
arn: 'arn:aws:elasticfilesystem:${self:provider.region}:#{AWS::AccountId}:access-point/${self:custom.efsAccessPoint}'
It seems like the test is working; If I run sls deploy and enter the URL for my test function in a browser, I get the expected output.
Problem
The ohPredict function is not working as well. When I send it a post request with the requests python module,
url = 'https://****.execute-api.****.amazonaws.com/dev/ohPredict'
myobj = {"data": data}
x = requests.post(url, json=myobj)
res = eval(x.text)
print(res)
I get the following error:
{'message': 'Missing Authentication Token'}
I ran a test with a simple lambda function and a post request without using serverless, and that was all the code I needed. I figured I would try experimenting and supplying a few values, and I ended up with this:
url = 'https://****.execute-api.****.amazonaws.com/dev/ohPredict'
myobj = {"data": data}
x = requests.post(url, headers={'Authorization': 'FOO'}, json=myobj)
res = eval(x.text)
print(res)
which resulted in:
{'message': "Authorization header requires 'Credential' parameter. Authorization header requires 'Signature' parameter. Authorization header requires 'SignedHeaders' parameter. Authorization header requires existence of either a 'X-Amz-Date' or a 'Date' header. Authorization=FOO"}
I tried playing around with supplying other values, and didn't get any change in the output. How can I get my post request to be recieved by my function?

As mentioned by yvesonline, the problem was the method. This was fixed by simply replacing method: get to method: post

Related

Configure `:path` header for envoy ratelimiting

I'm trying to use envoy ratelimiting functionality and need to ratelimit based on the entire url in my request, e.g. https://myenvoy.com/path/to/smth
Here is a part of my envoy.yaml
routes:
- match: { prefix: "/" }
route:
cluster: backend
rate_limits:
- stage: 0
actions:
- {request_headers: {header_name: ":path", descriptor_key: "path"}}
When I run
curl -k https://myenvoy.com/path/to/smth
The above configuration creates descriptor value /path/to/smth whereas I would like to have descriptor of value https://myenvoy.com/path/to/smth
Is it possible to configure that with envoy?
Thank you
PS: I looked at these header values and tried to use some, but it didn't help
https://github.com/envoyproxy/envoy/blob/master/source/common/http/headers.h
From further investigation,
- {request_headers: {header_name: "host", descriptor_key: "host"}}
does the job

Is it possible to set the Micronaut OAuth2 callback-uri as an absolute URL?

I have a Micronaut web-app that uses OpenId / OAuth2 / JWT. In some environments, everything works really well with this set up, however, in other environments, auth fails during the step where the configured callback-uri is called. For some reason, in these environments, the URL generated is "http" instead of "https". This causes the call to fail since my application is only accessible over https.
I have no clue why it is trying to use http in the first place, however, if I was able to specify the callback-uri as an absolute / full URL, then I could probably work around this anomaly in these environments.
An example yml config that I use:
application:
name: xxxxx
security:
authentication: idtoken
oauth2:
enabled: true
clients:
azure:
client-id: ${OAUTH_CLIENT_ID}
client-secret: ${OAUTH_CLIENT_SECRET}
openid:
issuer: https://login.microsoftonline.com/xxx
callback-uri: ${OAUTH_CALLBACK_URI}
redirect:
login-success: ${LOGIN_SUCCESS_URL}
logout: '/logout-handler/logout-success'
endpoints:
logout:
get-allowed: true
token:
jwt:
cookie:
cookie-same-site: none
cookie-secure: true
In this config if I set the callback-uri environment variable (OAUTH_CALLBACK_URI) to /oauth/callback/azure, for example, then the full URL that seems to be used is http://xxxxx/oauth/callback/azure. However, if I use a full URL for the environment variable, e.g. https://xxxxx/oauth/callback/azure then the full URL it uses still appends that as opposed to using it as an absolute URL, i.e. http://xxxxx/https://xxxxx/oauth/callback/azure.
Is it possible to specify this uri as an absolute one and not have it append it like the above effectively duplicating it?
Good news. This was fixed in micronaut-security 2.3.4
https://github.com/micronaut-projects/micronaut-security/pull/644

Deploy api gateway with terraform based on a swagger file

I want to deploy my api gateway with terraform using a swagger file to describe my api. The swagger.yaml looks like this:
swagger: '2.0'
info:
version: '1.0'
title: "CodingTips"
schemes:
- https
paths:
"/api":
get:
description: "Get coding tips"
produces:
- application/json
x-amazon-apigateway-integration: ${apiIntegration}
responses:
'200':
description: "Codingtips were successfully requested"
Terraform is giving me a BadRequestException saying that The REST API doesn't contain any methods.
Because of this I am thinking that it is trying to deploy the REST api without waiting for the methods and integrations of this api to be created.
This made me think in the direction of having to add DEPENDS_ON to the aws_api_gateway_deployment. However I do not know what to depend on since I don't define the method and integration resource using swagger. They should be automatically deducted from the swagger definition.
Am I thinking in the right direction and if so, what do I have to make my aws_api_gateway_deployment depend on? Or is something else wrong with the way I am trying to deploy this api.
My apigateway.tf file looks like this:
resource "aws_api_gateway_rest_api" "codingtips-api-gateway" {
name = "ServerlessExample"
description = "Terraform Serverless Application Example"
body = "${data.template_file.codingtips_api_swagger.rendered}"
}
locals{
"get_codingtips_arn" = "${aws_lambda_function.get-tips-lambda.invoke_arn}"
"x-amazon-coding-tips-apigateway-integration" = <<EOF
#
uri = "${local.get_codingtips_arn}"
passthroughBehavior: when_no_match
httpMethod: POST
type: aws_proxy
credentials: "${aws_iam_role.api_gateway_role.arn}"
EOF
}
data "template_file" codingtips_api_swagger{
template = "${file("./swagger.yaml")}"
vars {
apiIntegration = "${indent(8, local.x-amazon-coding-tips-apigateway-integration)}"
}
}
resource "aws_api_gateway_deployment" "codingtips-api-gateway-deployment" {
rest_api_id = "${aws_api_gateway_rest_api.codingtips-api-gateway.id}"
stage_name = "test"
}
How can I fix the BadRequestException: The REST API doesn't contain any methods ?
I found out what was wrong. It is a syntactical error in the locals{} block.
uri = should be uri: . Using a colon instead of an equal sign. The block then looks like this:
locals{
"get_codingtips_arn" = "${aws_lambda_function.get-tips-lambda.invoke_arn}"
"x-amazon-codingtips-get-apigateway-integration" = <<EOF
# comment for new line
uri: "${aws_lambda_function.get-tips-lambda.invoke_arn}"
passthroughBehavior: when_no_match
httpMethod: POST
type: aws_proxy
EOF
}
Researching this I found that it reads easier when you specify the x-amazon-apigateway-integration in the swagger.yaml like this:
swagger: '2.0'
info:
version: '1.0'
title: "CodingTips"
schemes:
- https
paths:
"/api":
get:
description: "Get coding tips"
produces:
- application/json
responses:
'200':
description: "The codingtips request was successful."
x-amazon-apigateway-integration:
uri: ${uri_arn}
passthroughBehavior: "when_no_match"
httpMethod: "POST"
type: "aws_proxy"
The data{} and locals{} blocks in your terraform then look like:
data "template_file" codingtips_api_swagger{
template = "${file("swagger.yaml")}"
vars {
uri_arn = "${local.get_codingtips_arn}"
}
}
locals {
"get_codingtips_arn" = "${aws_lambda_function.get-tips-lambda.invoke_arn}"
}

Swagger generated Java Client has no overloaded calls, even though the query parameter is marked as optional

I'm introducing an optional "query parameter" (with default value) for one of my API endpoint ( i.e. /foo2) but hoping that existing API consumers don't have to make any changes in their application when they regenerate the java client stub from the new swagger file.
I was expecting that the generated java client will offer overloaded methods, to operate with and without a method parameter, so that my existing API consumers can work without any changes to their call. At the same time, they can switch to its more generic offering.
public void foo2Get() throws ApiException {
public void foo2Get(String type) throws ApiException {
But it generates ONLY the latter. I don't see the former.
At the same time, generated python client from same swagger throws a var arg and this way it more flexible and hence old and new offerings will work without any issues.
def foo2_get(self, **kwargs):
(swagger sample used: attached)
** Description**
Is there a way to overcome this limitation with java code generator.
swagger-generator version
swagger 2.0
Swagger declaration file content or url
swagger: '2.0'
info:
title: identity
version: Unknown
consumes:
- application/json
produces:
- application/json
parameters:
typeParam:
name: type
in: query
required: false
allowEmptyValue: true
default: basic
type: string
enum:
- basic
- advanced
paths:
/foo2:
get:
consumes:
- application/json
parameters:
- "$ref": "#/parameters/typeParam"
responses:
'200':
description: OK

Why does API Gateway not have permissions for my Authorizer lambda when using Swagger?

I have an API defined using Swagger, which defines the following API Key authorizer:
securityDefinitions:
api_key:
type: apiKey
name: x-api-key
in: header
x-amazon-apigateway-authtype: "oauth2"
x-amazon-apigateway-authorizer:
type: token
authorizerUri: arn:aws:apigateway:eu-west-1:lambda:path/2015-03-31/functions/arn:aws:lambda:eu-west-1:[accountid]:function:ApiKeyAuthorizerLambdaFunction/invocations
authorizerResultTtlInSeconds: 0
However, when I pass this Swagger definition into my CloudFormation script and call an endpoint secured with this authorizer, I get a 500. The API Gateway logs show the following:
Incoming identity: ***key
Execution failed due to configuration error: Invalid permissions on Lambda function
Execution failed due to configuration error: Authorizer error
Note that I have given API Gateway permission to execute this lambda:
LambdaPermissionAuthorizerApiGateway:
Type: AWS::Lambda::Permission
Properties:
FunctionName:
Fn::GetAtt:
- ApiKeyAuthorizerLambdaFunction
- Arn
Action: lambda:InvokeFunction
Principal: apigateway.amazonaws.com
In fact, when I compare the CloudFormation script which Serverless generates for authoized endpoints (using Serverless's notation) with my own CloudFormation script using Swagger, I see little difference between them, except that my authorizer is defined with Swagger rather than directly as a CF resource.
Can anyone shed any light on this? Is this a bug when using Swagger with CloudFormation?
Can you try setting the authorizerCredentials parameter of your x-amazon-apigateway-authorizer to a valid IAM role that has permission to execute the authorizer lambda? Not sure the standard AWS::Lambda::Permission applies for this, though you probably want to keep it for now just in case it is still required.
The x-amazon-apigateway-authorizer docs show an example.

Resources