This repository has been archived by the owner. It is now read-only.

After add lambda function in x-amazon-apigateway-integration, don't have access to run test on AWS console #9

Closed
Garyguo2011 opened this Issue Jul 30, 2015 · 35 comments

Comments

Projects
None yet
@Garyguo2011

Garyguo2011 commented Jul 30, 2015

Removed

@Vilsepi

This comment has been minimized.

Vilsepi commented Jul 30, 2015

I am having the same problem. I am trying to make the simplest possible API, which takes a GET request to /test, and forwards it to a Lambda function. I can make it just fine manually with the web interface, but not with Swagger.

---
swagger: '2.0'
info:
  version: 0.0.1
  title: First API app
  description: "It is alive!"
  schemes: 
    - "http"
  consumes:
    - "application/json"
  produces: 
    - "application/json"
paths:
  /test:
    get:
      summary: "Summary of get"
      description: "Description of get"
      responses:
        200:
          description: "A response"
          headers: 
            test-method-response-header:
              type: string
      x-amazon-apigateway-auth":
        type: none
      x-amazon-apigateway-integration:
        type: aws
        uri: arn:aws:apigateway:eu-west-1:lambda:path/2015-03-31/functions/arn:aws:lambda:eu-west-1:MY_ACCOUNT_ID:function:echoLambda/invocations
        credentials: "arn:aws:iam::MY_ACCOUNT_ID:role/lambda_basic_execution"
        httpMethod: "POST"
        requestTemplates:
          application/json: "json request template 2"
        responses:
          2//d{2}: 
            statusCode: "200"
            responseParameters: 
              method.response.header.test-method-response-header: "integration.response.header.integrationResponseHeaderParam1"
            responseTemplates: 
              application/json: "json 200 response template"
          default: 
            statusCode: "400"
            responseParameters: 
              method.response.header.test-method-response-header: "'static value'"
            responseTemplates: 
              application/json: "json 400 response template"

If I include the "credentials" parameter, I get:

API Gateway does not have permission to assume the provided role.

If I don't include it, I get:

Invalid permissions on Lambda function.

If I do include it, deploy the API and use the HTTP interface, I get a response:

{"message":"Missing Authentication Token"}

Even though x-amazon-apigateway-auth is set to none.

I do not understand what is the credential used for: is it a credential that the API gateway uses to call the Lambda function, or is it the credential that the Lambda function uses to execute? I don't think it's the latter, as isn't that already defined in the Lambda function configuration? If it is for the API gateway, I am guessing that using the web interface adds some magic credential.

I feel dumb. :(

@Vilsepi

This comment has been minimized.

Vilsepi commented Jul 30, 2015

I noticed that there was an extra quote after the x-amazon-apigateway-auth which may or may not have cause some of the issues. In any case, if I use this Swagger file:

---
swagger: '2.0'
info:
  version: 0.0.1
  title: First Swagger API
  schemes:
    - "http"
  consumes:
    - "application/json"
  produces:
    - "application/json"
paths:
  /test:
    get:
      responses:
        200:
          description: "A response"
      x-amazon-apigateway-auth:
        type: none
      x-amazon-apigateway-integration:
        type: aws
        uri: arn:aws:apigateway:eu-west-1:lambda:path/2015-03-31/functions/arn:aws:lambda:eu-west-1:MY_ACCOUNT_ID:function:echoLambda/invocations
        credentials: null
        httpMethod: POST
        responses:
          default:
            statusCode: "200"
            responseTemplates:
              application/json: "json 200 response template"

The Test gives "Invalid permissions on Lambda function". The Integration seems fine in the API Gateway Console:

/test - GET - Integration Request
Integration type: Lambda Function
Lambda Region: eu-west-1
Lambda Function: echoLambda

But, when I go check the Lambda Console -> API endpoints, there are none specified. If I go back to the API Gateway Console -> Integration Request, click the Lambda Function field, do not change the name but just click Update, a dialog will appear:

You are about to give API Gateway permission to invoke your Lambda function:
arn:aws:lambda:eu-west-1:MY_ACCOUNT_ID:function:echoLambda

Once I click OK, the endpoint becomes visible in the Lambda Console, and while nothing has seemingly changed in the API Gateway Console, the Test now works. So how do I do think linkage with Swagger?

I have tried passing all kinds of roles in the credentials parameter, but they all return the same "API Gateway does not have permission to assume the provided role."

@rpgreen

This comment has been minimized.

Contributor

rpgreen commented Jul 31, 2015

Hi folks,

First of all, I apologize for the sparse documentation - we are working on it!

These concepts are more specific to the API Gateway service and not particularly related to the Swagger importer - you would run into the same issues using our API. I realize this can be difficult to set up, but luckily this is usually a one-time setup per account. I'll try to clear a few things up.

There are currently 2 different ways to invoke a lambda function:

  1. Using credentials, whereby API Gateway assumes a role in your account, and then invokes the lambda function in your account. In Swagger or the API, this is achieved by referencing a role ARN in the credentials field. This role is typically not the lambda function "execution role" and its policy must at a minimum grant permission for API Gateway to assume it and to invoke lambda functions.

At a minimum, the role must have these actions allowed:

...
"lambda:InvokeFunction"
"iam:PassRole"
...

as well as a trust-relationship defined for API Gateway:

...
"Effect": "Allow",
"Principal": {
"Service": "apigateway.amazonaws.com"
},
"Action": "sts:AssumeRole"
...

See http://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html

  1. Using "resource-based" permissions, whereby you set a policy on the lambda function itself, granting the API Gateway account permission to invoke it. In Swagger or the API, this is achieved by omitting the credentials field.

When you setup a method with a lambda function using the API Gateway management console, we automatically add a permission to the lambda function. However, the Swagger importer or the SDK does not add this permission - this is why you might see different results when using the console vs using the importer.

When using resource-based permissions, you must explicitly update the policy on the lambda function so that API Gateway can invoke it from the specified API/resource/method combination. To see an example policy, attach your lambda function using the API Gateway management console, then use the Lambda API or CLI to view the function policy (See http://docs.aws.amazon.com/lambda/latest/dg/API_GetPolicy.html). You can then replicate this policy for APIs managed by the Swagger importer. Note that wildcards can be used for API ID, resource, method, etc. in the function policy.

I hope this helps with the above questions.

Another point - if you get 403 "Missing authentication token" when calling your API, it may be related to an error in the URL (api id/stage/resource path) or http method, rather than an auth error. We send 403 in all cases when the resource cannot be located.

Please let me know if this helps!

Cheers,
Ryan

@rpgreen

This comment has been minimized.

Contributor

rpgreen commented Aug 3, 2015

This looks like a signature issue when calling lambda. I'll look into this and get back to you

@kmrgithub

This comment has been minimized.

kmrgithub commented Aug 4, 2015

Hi rpgreen -

I attempted to use the CLI to add the permission after I ran my swagger file. I get the same error as shown above.

I ran the following command:
aws lambda add-permission --function-name BuyerFirstRS_Get --statement-id 123456789 --action
lambda:* --principal "apigateway.amazonaws.com"

Here is the policy entry after I ran get-policy.
{
"Action":"lambda:*",
"Resource":"arn:aws:lambda:us-east-1:ACCOUNT_ID:function:BuyerFirstRS_Get",
"Effect":"Allow",
"Principal":{"Service":"apigateway.amazonaws.com"},
"Sid":"123456789"
}

Thanks in advance.

@Vilsepi

This comment has been minimized.

Vilsepi commented Aug 5, 2015

Thanks for the help, Ryan. I am just chiming in to let you know that I managed to get mine working. Indeed I was missing the trust relationship from the role, so API Gateway could not assume the role by itself. I have now gone back to reading the docs.

@Garyguo2011 Garyguo2011 closed this Aug 5, 2015

@Garyguo2011 Garyguo2011 reopened this Aug 5, 2015

@johntitus

This comment has been minimized.

johntitus commented Aug 6, 2015

Similar issue. My swagger file:

{
    "swagger": "2.0",
    "info": {
        "title": "fishstore"
    },
    "paths": {
        "/signup": {
            "get": {
                "responses": {
                    "200": {
                        "description": "ok"
                    }
                },
                "x-amazon-apigateway-auth": {
                    "type": "none"
                },
                "x-amazon-apigateway-integration": {
                    "type": "aws",
                    "uri": "arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:<my_account_id>:function:hocus_5f9s1xy8pd_controller_listFish/invocations",
                    "httpMethod": "GET",
                    "credentials": "arn:aws:iam::<my_account_id>:role/ExecLambdaRole",
                    "requestTemplates": {
                        "application/json": "Empty"
                    },
                    "responses": {
                        "default": {
                            "statusCode": "200"
                        }
                    }
                }
            }
        }
    }
}

That ExecLambdaRole has the following policy:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "logs:*"
      ],
      "Resource": "arn:aws:logs:*:*:*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "lambda:InvokeFunction"
      ],
      "Resource": [
        "*"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "apigateway:*",
        "iam:PassRole"
      ],
      "Resource": [
        "*"
      ]
    }
  ]
}

That role also has the following Trust Relationship:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Service": [
          "lambda.amazonaws.com",
          "apigateway.amazonaws.com"
        ]
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

I run the following command:
./aws-api-import.sh --create /tmp/swagger.json

The resulting API in the console looks ok:
image

But the Test response results in:

<AccessDeniedException>
  <Message>Unable to determine service/operation name to be authorized</Message>
</AccessDeniedException>

And the logs are:

                    Execution log for request test-request
Thu Aug 06 18:20:04 UTC 2015 : Starting execution for request: test-invoke-request
Thu Aug 06 18:20:04 UTC 2015 : API Key: test-invoke-api-key
Thu Aug 06 18:20:04 UTC 2015 : Method request path: {}
Thu Aug 06 18:20:04 UTC 2015 : Method request query string: {}
Thu Aug 06 18:20:04 UTC 2015 : Method request headers: {}
Thu Aug 06 18:20:04 UTC 2015 : Method request body before transformations: null
Thu Aug 06 18:20:04 UTC 2015 : Endpoint request URI: https://lambda.us-east-1.amazonaws.com/2015-03-31/functions/arn:aws:lambda:us-east-1:<my_account_id>:function:hocus_5f9s1xy8pd_controller_listFish/invocations
Thu Aug 06 18:20:04 UTC 2015 : Endpoint request headers: {Authorization=AWS4-HMAC-SHA256 Credential=ASIAJYO22ALFEROHAWOQ/20150806/us-east-1/lambda/aws4_request, SignedHeaders=accept;content-type;host;user-agent;x-amz-content-sha256;x-amz-date, Signature=2f7d34a6f6eefedf8585e97c622bfd0dd11f32546e8d42b33b9da28d519d0c72, X-Amz-Date=20150806T182004Z, Accept=application/json, User-Agent=AmazonAPIGateway_0ss8j7bbod, X-Amz-Security-Token=AQoDYXdzEDMaoALpewKb93Zai1Pem3Oc1Ezvq43wSjO8+P1giS21vzu3o9HC6Hv4A0J1NMvLEhvLjA+n0HSMwBdpdT5fqFypWni8y+HlXxtBBZIOw6xsqs0hVJ4dFgc6nqCzBDqaz/r7mkhDxTp44XWPLCtBNdT+W471/PCcW8HEGfbhEskHx0L4kFaC3dWhdMcfuQAbk5JAcJYWovmnZHg11sOMKOwYCc1/iZChGxDJ1byw+BOfeEkKD8+hY8Vm1zHXC1fTh9ygB240DGZxEYyFwmad+PFKTJiznrvlCHAWH+sCwGJzglzwUFx6Xl4qEcT28VcQNr/AjKLAFD4Ez+rWhseEXJ3t6u3Vj4unUc2/VZF7i1LuTuIt48A+yxZFR2XPMpFJtExF1GEg1MqOrgU=, Host=lambda.us-east-1.amazonaws.com, X-Amz-Content-Sha256=c6c094bc0054f9cbe34102ff49f86b3928b5ac09f3d2ac87e170d0500675921f, Content-Type=application/json}
Thu Aug 06 18:20:04 UTC 2015 : Endpoint request body after transformations: Empty
Thu Aug 06 18:20:04 UTC 2015 : Endpoint response body before transformations: <AccessDeniedException>
  <Message>Unable to determine service/operation name to be authorized</Message>
</AccessDeniedException>

Thu Aug 06 18:20:04 UTC 2015 : Endpoint response headers: {x-amzn-RequestId=c27c931d-3c67-11e5-98a0-5de465424063, Connection=keep-alive, Content-Length=130, Date=Thu, 06 Aug 2015 18:20:04 GMT}
Thu Aug 06 18:20:04 UTC 2015 : Method response body after transformations: <AccessDeniedException>
  <Message>Unable to determine service/operation name to be authorized</Message>
</AccessDeniedException>

Thu Aug 06 18:20:04 UTC 2015 : Method response headers: {Content-Type=application/json}
Thu Aug 06 18:20:04 UTC 2015 : Successfully completed execution
@rpgreen

This comment has been minimized.

Contributor

rpgreen commented Aug 6, 2015

We are actively looking into this. Can you confirm that you get the same
response message when deploying this API and invoking it through an http
client?

On Thursday, August 6, 2015, John notifications@github.com wrote:

Similar issue. My swagger file:

{
"swagger": "2.0",
"info": {
"title": "fishstore"
},
"paths": {
"/signup": {
"get": {
"responses": {
"200": {
"description": "ok"
}
},
"x-amazon-apigateway-auth": {
"type": "none"
},
"x-amazon-apigateway-integration": {
"type": "aws",
"uri": "arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:<my_account_id>:function:hocus_5f9s1xy8pd_controller_listFish/invocations",
"httpMethod": "GET",
"credentials": "arn:aws:iam::<my_account_id>:role/ExecLambdaRole",
"requestTemplates": {
"application/json": "Empty"
},
"responses": {
"default": {
"statusCode": "200"
}
}
}
}
}
}
}

That ExecLambdaRole has the following policy:

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:"
],
"Resource": "arn:aws:logs:
::"
},
{
"Effect": "Allow",
"Action": [
"lambda:InvokeFunction"
],
"Resource": [
""
]
},
{
"Effect": "Allow",
"Action": [
"apigateway:
",
"iam:PassRole"
],
"Resource": [
"*"
]
}
]
}

That role also has the following Trust Relationship:

{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Service": [
"lambda.amazonaws.com",
"apigateway.amazonaws.com"
]
},
"Action": "sts:AssumeRole"
}
]
}

I run the following command:
./aws-api-import.sh --create /tmp/swagger.json

The resulting API in the console looks ok:
[image: image]
https://cloud.githubusercontent.com/assets/590334/9119719/2fd7b426-3c46-11e5-9ebb-7681e121472a.png

But the Test response results in:

Unable to determine service/operation name to be authorized

And the logs are:

                Execution log for request test-request

Thu Aug 06 18:20:04 UTC 2015 : Starting execution for request: test-invoke-request
Thu Aug 06 18:20:04 UTC 2015 : API Key: test-invoke-api-key
Thu Aug 06 18:20:04 UTC 2015 : Method request path: {}
Thu Aug 06 18:20:04 UTC 2015 : Method request query string: {}
Thu Aug 06 18:20:04 UTC 2015 : Method request headers: {}
Thu Aug 06 18:20:04 UTC 2015 : Method request body before transformations: null
Thu Aug 06 18:20:04 UTC 2015 : Endpoint request URI: https://lambda.us-east-1.amazonaws.com/2015-03-31/functions/arn:aws:lambda:us-east-1:<my_account_id>:function:hocus_5f9s1xy8pd_controller_listFish/invocations
Thu Aug 06 18:20:04 UTC 2015 : Endpoint request headers: {Authorization=AWS4-HMAC-SHA256 Credential=ASIAJYO22ALFEROHAWOQ/20150806/us-east-1/lambda/aws4_request, SignedHeaders=accept;content-type;host;user-agent;x-amz-content-sha256;x-amz-date, Signature=2f7d34a6f6eefedf8585e97c622bfd0dd11f32546e8d42b33b9da28d519d0c72, X-Amz-Date=20150806T182004Z, Accept=application/json, User-Agent=AmazonAPIGateway_0ss8j7bbod, X-Amz-Security-Token=AQoDYXdzEDMaoALpewKb93Zai1Pem3Oc1Ezvq43wSjO8+P1giS21vzu3o9HC6Hv4A0J1NMvLEhvLjA+n0HSMwBdpdT5fqFypWni8y+HlXxtBBZIOw6xsqs0hVJ4dFgc6nqCzBDqaz/r7mkhDxTp44XWPLCtBNdT+W471/PCcW8HEGfbhEskHx0L4kFaC3dWhdMcfuQAbk5JAcJYWovmnZHg11sOMKOwYCc1/iZChGxDJ1byw+BOfeEkKD8+hY8Vm1zHXC1fTh9ygB240DGZxEYyFwmad+PFKTJiznrvlCHAWH+sCwGJzglzwUFx6Xl4qEcT28VcQNr/AjKLAFD4Ez+rWhseEXJ3t6u3Vj4unUc2/VZF7i1LuTuIt48A+yxZFR2XPMpFJtExF1GEg1MqOrgU=, Host=lambda.us-east-1.amazonaws.com, X-Amz-Content-Sha256=c6c094bc0054f9cbe34102ff49f86b3928b5ac09f3d2ac87e170d0500675921f, Content-Type=application/json
}
Thu Aug 06 18:20:04 UTC 2015 : Endpoint request body after transformations: Empty
Thu Aug 06 18:20:04 UTC 2015 : Endpoint response body before transformations:
Unable to determine service/operation name to be authorized

Thu Aug 06 18:20:04 UTC 2015 : Endpoint response headers: {x-amzn-RequestId=c27c931d-3c67-11e5-98a0-5de465424063, Connection=keep-alive, Content-Length=130, Date=Thu, 06 Aug 2015 18:20:04 GMT}
Thu Aug 06 18:20:04 UTC 2015 : Method response body after transformations:
Unable to determine service/operation name to be authorized

Thu Aug 06 18:20:04 UTC 2015 : Method response headers: {Content-Type=application/json}
Thu Aug 06 18:20:04 UTC 2015 : Successfully completed execution


Reply to this email directly or view it on GitHub
#9 (comment)
.

@johntitus

This comment has been minimized.

johntitus commented Aug 6, 2015

Yes

john@john-VirtualBox ~/aws-apigateway-swagger-importer $ curl https://<my_api_id>.execute-api.us-east-1.amazonaws.com/test/signup
<AccessDeniedException>
  <Message>Unable to determine service/operation name to be authorized</Message>
</AccessDeniedException>
@rpgreen

This comment has been minimized.

Contributor

rpgreen commented Aug 10, 2015

I just set up a simple example from scratch with a successful response from both the console test invoke and from curl.

Swagger:

...
                "x-amazon-apigateway-auth" : {
                    "type" : "none"
                },
                "x-amazon-apigateway-integration" : {
                    "type" : "aws",
                    "uri" : "arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:ACCT_ID:function:rg-no-permissions/invocations",
                    "httpMethod" : "POST",
                    "credentials" : "arn:aws:iam::ACCT_ID:role/apiGatewayLambdaInvokeRole",
                    "responses" : {
                        ".*error.*" : {
                            "statusCode" : "400"
                        },
                        "default" : {
                            "statusCode" : "200"
                        }
                    }
                }
       ...

with the following policy for apiGatewayLambdaInvokeRole:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "logs:*"
            ],
            "Resource": "arn:aws:logs:*:*:*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "lambda:InvokeFunction"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}

and the following trust relationship:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Service": "apigateway.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

Are you able to reproduce this with a new API and lambda function?

The error seems to be coming from lambda. Would you mind sending your lambda function execution role?

@rpgreen

This comment has been minimized.

Contributor

rpgreen commented Aug 10, 2015

If you can recreate this, please send the x-amzn-RequestId response header so I can investigate further

@rpgreen

This comment has been minimized.

Contributor

rpgreen commented Aug 11, 2015

Hi all,

I found the issue in the swagger definition.

Http method should be POST to invoke a lambda function, as per the Lambda API. See http://docs.aws.amazon.com/lambda/latest/dg/API_Invoke.html

Change the http method on your integration to POST and this should work.

@rpgreen rpgreen closed this Aug 11, 2015

@johntitus

This comment has been minimized.

johntitus commented Aug 11, 2015

I think I see what you mean - that while the API Gateway method is GET, internally it needs to send a POST to the lambda? I had been under the impression that the two methods had to match.

I don't have a built swagger importer here at home, but I'll test first thing in the morning. Thank you for looking into this - it's been driving me crazy.

@Garyguo2011

This comment has been minimized.

Garyguo2011 commented Aug 11, 2015

That works perfectly. Awesome!!!!

@johntitus

This comment has been minimized.

johntitus commented Aug 11, 2015

Thank you, works just as you said!

@Sam-Martin

This comment has been minimized.

Contributor

Sam-Martin commented Sep 12, 2015

I had a similar issue and it was because my uri read:
arn:aws:apigateway:eu-west-1:lambda:path/2015-03-31/functions/arn:aws:lambda:eu-west-1:<accountid>:function:ephemera-getsignedurl
instead of
arn:aws:apigateway:eu-west-1:lambda:path/2015-03-31/functions/arn:aws:lambda:eu-west-1:<accountid>:function:ephemera-getsignedurl/invocations
(i.e. it was missing the trailing /invocations)

@jontg

This comment has been minimized.

jontg commented Oct 16, 2015

I just reproduced this by copying an existing API into a new API and deleting the old one (which was instantiated via cloudformation).

@raptortech-js

This comment has been minimized.

raptortech-js commented Nov 11, 2015

If lambda always needs POST requests, then would it make sense for httpMethod be hardcoded and not required in every x-amazon-apigateway-integration definition?

@jackrk

This comment has been minimized.

Contributor

jackrk commented Dec 15, 2015

The integration definition applies to Lambda, HTTP backends, and also other AWS services. Yes Lambda does require POST requests, but I don't believe it makes sense to go with that assumption behind the scenes.

As to the previous comment on the URI missing '/invocations', I agree the format is complex and easy to mess up. We are working on improving documentation across the board.

@cosbor11

This comment has been minimized.

cosbor11 commented Feb 18, 2016

@rpgreen

Im trying to make a declarative and repeatable way to deploy my entire AWS env and am struggling to wire the api gateway to the lambdas.
Im finding that I need to programmatically give API Gateway permission to invoke my lambda function using the second methodology ("resource-based" permission settings) that you mentioned...

When I do the following awscli command:

aws lambda get-policy --function-name MyLambda

I get a response like this:

{
   "Version":"2012-10-17",
   "Statement":[
      {
         "Condition":{
            "ArnLike":{
               "AWS:SourceArn":"arn:aws:execute-api:us-west-2:{account-id}:d6v9eiz6we/*/POST/{path}"
            }
         },
         "Action":"lambda:InvokeFunction",
         "Resource":"arn:aws:lambda:us-west-2:394393457149:function:MyLambda",
         "Effect":"Allow",
         "Principal":{
            "Service":"apigateway.amazonaws.com"
         },
         "Sid":"5d320ee4ee34cesdq43564......"
      }
   ],
   "Id":"default"
}

The Sid, I'm assuming is the Service ID. Is that needed to create this Policy?

Also, How can I create this policy and apply it to the service before the service is created?
I guess Im just confused about whether to put the chicken before the egg and why the aws-apigateway-importer doesn't do this automatically.

Here is the general process Im thinking I need to do to get my api wired to the lambdas?

  1. Create the Roles and Policies for the Lambda using aws cloudformation create-stack
  2. Upload a jar with all my lambdas to an S3 bucket
  3. Update or create the lambda configuration to use the latest jar/handler.
    aws lambda create-function or aws lambda update-function-code.
  4. Use the aws-apigateway-importer to generate the API
  5. Get the Sid of the gateway I just created
  6. Create a policy similar to the one above? How would I do that? aws lambda add-permission --function-name MyFunction ... or is there a better way to do this?
@rpgreen

This comment has been minimized.

Contributor

rpgreen commented Feb 18, 2016

Yeah that's pretty much it. Few notes below:

Source ARN identifies an API Gateway method and should be structured like this:

arn:aws:execute-api:[REGION]:[ACCOUNT_ID]:[API_ID]/[STAGE]/[HTTP_METHOD]/[FULL_RESOURCE_PATH]

(Note wildcard (*) can be used for the value of STAGE)

You may need to modify the importer code in order to dynamically build a list of the resource paths/methods and lambda functions to add the permissions. Or you could simply hardcode them into your add permission script, but that would need to be maintained when you change the API definition.

Sid is the "statement ID" and must be unique - it's not related to the API.

Keep in mind that there is a limit to the overall size of the function policy, so if you keep adding permissions to the same function you will eventually hit the limit.

@rpgreen

This comment has been minimized.

Contributor

rpgreen commented Feb 18, 2016

Related to #111

@cosbor11

This comment has been minimized.

cosbor11 commented Feb 18, 2016

Thanks. I think I will write a bash script that pulls the path from the swagger file using jq (a bash json parsing tool) and do this for each path.

Im wondering tho, which of those ARN sections can I add a wild card to? and which can I not?

Can I create a policy like this:

arn:aws:execute-api:*:[ACCOUNT_ID]:[API_ID]/*/*/*

Thus granting all methods to each lambda associated with the API. Does this present any real security concern (when a restful endpoint has invocation access to a lambda it's not linked to)?

@rpgreen

This comment has been minimized.

Contributor

rpgreen commented Feb 18, 2016

Does this present any real security concern (when a restful endpoint has invocation access to a lambda it's not linked to)?

Yes, absolutely. I believe wildcards are accepted in any element, but I would strongly suggest to be as specific as possible when building the source ARN. At a bare minimum you should explicitly set the account ID and the API ID.

Hope this helps. If you think your script could be re-used a PR would be much appreciated

@cosbor11

This comment has been minimized.

cosbor11 commented Feb 18, 2016

Ok, thanks Ryan. I'll consider adding a PR to importer. I have a local snapshot and I'll see If that might be a better solution with my timeframe

@cosbor11

This comment has been minimized.

cosbor11 commented Feb 19, 2016

The version of AWS SDK (1.9.6), that the aws-apigateway-importer is using does not support creating a policy via the awsIdentityManagementClient or awsLambdaClient. Support is there for the latest (v1.10.53) version. Any plans on upgrading the SDK version? I can upgrade the pom.xml if you're okay with that.

@rpgreen

This comment has been minimized.

Contributor

rpgreen commented Feb 19, 2016

Yeah that should be straightforward, go for it.

Alternatively, if you wanted to use the CLI you could have any arbitrary version running in your environment.

@cosbor11

This comment has been minimized.

cosbor11 commented Feb 19, 2016

I wired in a awsLambdaClient, when I invoke the AddPermission, I get and InternalFailure and cant figure out why.

Upgrading the sdk version worked fine:

<properties>
    <aws-sdk.version>1.10.53</aws-sdk.version>
</properties>
...
<dependency>
    <groupId>com.amazonaws</groupId>
    <artifactId>aws-java-sdk-core</artifactId>
    <version>${aws-sdk.version}</version>
</dependency>

<dependency>
    <groupId>com.amazonaws</groupId>
    <artifactId>aws-java-sdk</artifactId>
    <version>${aws-sdk.version}</version>
</dependency>

Here is the ApiGatewaySdkSwaggerApiImporter code that I added (it looks at integration extenstion in swagger for a resourcePolicyConfig):

private void createResourcePolicy(RestApi api, Resource resource, Operation op, Method method, Map<String, String> resourcePolicyConfig){
    LOG.info("Creating policy for " + method.getHttpMethod() + " /" + resource.getPathPart() + "...");

    AddPermissionRequest permissionRequest = new AddPermissionRequest();
    permissionRequest.setAction("lambda:InvokeFunction");
    permissionRequest.setPrincipal("apigateway.amazonaws.com");
    permissionRequest.setStatementId(UUID.randomUUID().toString());

    String functionName = resourcePolicyConfig.get("functionName");
    if(functionName == null || functionName.isEmpty()){
        throw new IllegalArgumentException("resourcePolicyConfig must have functionName defined.");
    }
    permissionRequest.setFunctionName(functionName);

    String accountId = resourcePolicyConfig.get("accountId");
    if(accountId == null || accountId.isEmpty()){
        throw new IllegalArgumentException("resourcePolicyConfig must have accountId defined.");
    }
    permissionRequest.setSourceAccount(accountId);

    final String apiId = api.getId();
    String sourceArn = MessageFormat.format("arn:aws:execute-api:us-west-2:{0}:{1}/*/{2}/{3}", accountId, apiId, method.getHttpMethod(), resource.getPathPart());
    //LOG.info("SourceArn = " + sourceArn);
    permissionRequest.setSourceArn(sourceArn);

    try {
        AddPermissionResult result = awsLambda.addPermission(permissionRequest);
        LOG.info(result.toString());
    } catch(AmazonServiceException e) {
        LOG.error(e);
    }
}

And here is the Provider configuration:

@Provides
protected AWSLambda provideAWSLambda(AWSCredentialsProvider credsProvider,
                                             RetryPolicy.BackoffStrategy backoffStrategy,
                                             @Named("region") String region) {

    final RetryPolicy retrypolicy = new RetryPolicy(PredefinedRetryPolicies.DEFAULT_RETRY_CONDITION, backoffStrategy, 5, true);

    final ClientConfiguration clientConfig = new ClientConfiguration().withUserAgent(USER_AGENT).withRetryPolicy(retrypolicy);
    clientConfig.setProtocol(Protocol.HTTPS);

    AWSLambdaClient awsLambdaClient = new AWSLambdaClient(credsProvider.getCredentials(), clientConfig);

    awsLambdaClient.setEndpoint(getEndpoint(region));

    return awsLambdaClient;
}

But I keep getting this error when invoking addPermission:

com.amazonaws.AmazonClientException: Unable to unmarshall error response (Unable to parse error response: '<InternalFailure/>'). Response Code: 500, Response Text: Internal Server Error
    at com.amazonaws.http.AmazonHttpClient.handleErrorResponse(AmazonHttpClient.java:1363)
    at com.amazonaws.http.AmazonHttpClient.executeOneRequest(AmazonHttpClient.java:913)
    at com.amazonaws.http.AmazonHttpClient.executeHelper(AmazonHttpClient.java:631)
    at com.amazonaws.http.AmazonHttpClient.doExecute(AmazonHttpClient.java:400)
    at com.amazonaws.http.AmazonHttpClient.executeWithTimer(AmazonHttpClient.java:362)
    at com.amazonaws.http.AmazonHttpClient.execute(AmazonHttpClient.java:311)
    at com.amazonaws.services.lambda.AWSLambdaClient.invoke(AWSLambdaClient.java:2031)
    at com.amazonaws.services.lambda.AWSLambdaClient.addPermission(AWSLambdaClient.java:450)
    at com.amazonaws.service.apigateway.importer.impl.sdk.ApiGatewaySdkSwaggerApiImporter.createResourcePolicy(ApiGatewaySdkSwaggerApiImporter.java:349)
    at com.amazonaws.service.apigateway.importer.impl.sdk.ApiGatewaySdkSwaggerApiImporter.createMethod(ApiGatewaySdkSwaggerApiImporter.java:285)
    at com.amazonaws.service.apigateway.importer.impl.sdk.ApiGatewaySdkSwaggerApiImporter.lambda$createMethods$3(ApiGatewaySdkSwaggerApiImporter.java:208)
    at com.amazonaws.service.apigateway.importer.impl.sdk.ApiGatewaySdkSwaggerApiImporter$$Lambda$4/1576277927.accept(Unknown Source)
    at java.util.HashMap$EntrySet.forEach(HashMap.java:1035)
    at com.amazonaws.service.apigateway.importer.impl.sdk.ApiGatewaySdkSwaggerApiImporter.createMethods(ApiGatewaySdkSwaggerApiImporter.java:206)
    at com.amazonaws.service.apigateway.importer.impl.sdk.ApiGatewaySdkSwaggerApiImporter.createResources(ApiGatewaySdkSwaggerApiImporter.java:197)
    at com.amazonaws.service.apigateway.importer.impl.sdk.ApiGatewaySdkSwaggerApiImporter.createApi(ApiGatewaySdkSwaggerApiImporter.java:76)
    at com.amazonaws.service.apigateway.importer.impl.ApiGatewaySwaggerFileImporter.importApi(ApiGatewaySwaggerFileImporter.java:48)
    at com.amazonaws.service.apigateway.importer.ApiImporterMain.importSwagger(ApiImporterMain.java:155)
    at com.amazonaws.service.apigateway.importer.ApiImporterMain.execute(ApiImporterMain.java:145)
    at com.amazonaws.service.apigateway.importer.ApiImporterMain.main(ApiImporterMain.java:83)
Caused by: com.amazonaws.AmazonClientException: Unable to parse error response: '<InternalFailure/>'
    at com.amazonaws.http.JsonErrorResponseHandler.handle(JsonErrorResponseHandler.java:55)
    at com.amazonaws.http.JsonErrorResponseHandler.handle(JsonErrorResponseHandler.java:29)
    at com.amazonaws.http.AmazonHttpClient.handleErrorResponse(AmazonHttpClient.java:1338)
    ... 19 more
Caused by: com.amazonaws.util.json.JSONException: A JSONObject text must begin with '{' at 1 [character 2 line 1]
    at com.amazonaws.util.json.JSONTokener.syntaxError(JSONTokener.java:422)
    at com.amazonaws.util.json.JSONObject.<init>(JSONObject.java:196)
    at com.amazonaws.util.json.JSONObject.<init>(JSONObject.java:323)
    at com.amazonaws.http.JsonErrorResponseHandler.handle(JsonErrorResponseHandler.java:53)
    ... 21 more

This is using the following integration config in swagger:

"x-amazon-apigateway-integration": {
    "responses": {
        "default": {
            "statusCode": "200",
            "responseParameters": {
                "method.response.header.Access-Control-Allow-Origin": "'*'"
            },
            "responseTemplates": {
                "application/json": "__passthrough__"
            }
        }
    },
    "uri": "arn:aws:apigateway:us-west-2:lambda:path/2015-03-31/functions/arn:aws:lambda:us-west-2:{account}:function:MyLambda/invocations",
    "httpMethod": "POST",
    "type": "aws",
    "resourcePolicyConfig": {
        "accountId" : "{account}",
        "functionName" : "MyLambda"
    }
}

Any idea why I would be getting an Internal Error? My code matches up with the documentation; Example 2: Grant Amazon API Gateway Permissions to Invoke Your Lambda Function

I'll see what happens when I try with awscli.

@rpgreen

This comment has been minimized.

Contributor

rpgreen commented Feb 19, 2016

That looks like a fault on the Lambda service side. Are you able to get it working with simple test input for source arn, etc?

Your code looks good except that the source ARN should include the full resource path, not just the path part.

Are you sure that the credentials used have IAM policy to allow them to add permission?

This looks like a Lambda bug to me - I would suggest to follow up with the Lambda team with your stack trace: https://forums.aws.amazon.com/forum.jspa?forumID=186

@cosbor11

This comment has been minimized.

cosbor11 commented Feb 20, 2016

I am able to get it working using awscli with the following command:

 aws lambda add-permission --region us-west-2 --function-name  MyLambda --statement-id 3C64D50A-7FEA-4AC6-8331-99751E22DFDA --principal apigateway.amazonaws.com --action lambda:InvokeFunction --source-arn arn:aws:execute-api:us-west-2:{accountId}:7gshcby1c6/*/POST/my-resource

The response looks like this:

{
     "Statement": "{\"Condition\":{\"ArnLike\":{\"AWS:SourceArn\":\"arn:aws:execute-api:us-west-   2:394393457149:7gshcby1c6/*/POST/my-resource\"}},\"Action\":    [\"lambda:InvokeFunction\"],\"Resource\":\"arn:aws:lambda:us-west-2:{accoutId}:function:MyLambda\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"apigateway.amazonaws.com\"},\"Sid\":\"3C64D50A-7FEA-4AC6-8331-99751E22DFDA\"}"
}

It looks like the bug is with the java sdk.

@lony

This comment has been minimized.

lony commented Mar 8, 2016

+1

@shubhpatel108

This comment has been minimized.

shubhpatel108 commented Mar 29, 2016

If you are using boto3 for adding an integration of lambda function to APIGatway, make sure httpMethod and integrationHttpMethod both are set to 'POST'.

@BerndWessels

This comment has been minimized.

BerndWessels commented Oct 10, 2016

Has this been resolved? I cannot find a way to make it work using terraform. See here for details.

@4lex-gs

This comment has been minimized.

4lex-gs commented Nov 23, 2016

I had a similar issue.
My wrong setting on "Integration Request" caused it.

I had to set "Use path override" but I chose another one.
I resolved the problem when I change setting.

Wed Nov 23 15:14:16 UTC 2016 : Endpoint response body before transformations: <AccessDeniedException>
  <Message>Unable to determine service/operation name to be authorized</Message>
</AccessDeniedException>

Wed Nov 23 15:14:16 UTC 2016 : Endpoint response headers: {x-amzn-RequestId=************* Connection=keep-alive, Content-Length=130, Date=Wed, 23 Nov 2016 15:14:15 GMT}
Wed Nov 23 15:14:16 UTC 2016 : Method response body after transformations: <AccessDeniedException>
  <Message>Unable to determine service/operation name to be authorized</Message>
</AccessDeniedException>

Wed Nov 23 15:14:16 UTC 2016 : Method response headers: {X-Amzn-Trace-Id=Root=1-*************************, Content-Type=application/json}
Wed Nov 23 15:14:16 UTC 2016 : Successfully completed execution
Wed Nov 23 15:14:16 UTC 2016 : Method completed with status: 200
@calebickler

This comment has been minimized.

calebickler commented Apr 17, 2018

I was finally able to get my API Gateway custom authorizer to work because of this issue, so thanks!

But I wish this was simpler and in the AWS docs, would of saved tons of time, AWS needs to improve their API Gateway docs/experience, its quite frustrating.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.