okigan / awscurl

curl-like access to AWS resources with AWS Signature Version 4 request signing.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

awscurl did not work with Lambda

starkshaw opened this issue · comments

Hi,

I installed the latest version of awscurl, 0.20. I attempt to invoke my Lambda function and it returned some error messages that I can't make out what went wrong.

The same request worked fine in Postman.

$ awscurl --service lambda --region eu-west-1 --profile default 'https://lambda.<region>.amazonaws.com/2015-03-31/functions/<function-name>/invocations?Qualifier=$LATEST' -H 'X-Amz-Invocation-Type:RequestResponse' -H 'X-Amz-Log-Type:Tail' -H 'Content-Type:application/json' -d '{"message":"Whats up."}'

The error message is:

Traceback (most recent call last):
  File "/usr/local/Cellar/python/3.7.6_1/Frameworks/Python.framework/Versions/3.7/bin/awscurl", line 8, in <module>
    sys.exit(main())
  File "/usr/local/lib/python3.7/site-packages/awscurl/awscurl.py", line 500, in main
    inner_main(sys.argv[1:])
  File "/usr/local/lib/python3.7/site-packages/awscurl/awscurl.py", line 463, in inner_main
    headers = {k: v for (k, v) in map(lambda s: s.split(": "), args.header)}
  File "/usr/local/lib/python3.7/site-packages/awscurl/awscurl.py", line 463, in <dictcomp>
    headers = {k: v for (k, v) in map(lambda s: s.split(": "), args.header)}
ValueError: not enough values to unpack (expected 2, got 1)

The doc of the API can be found here: https://docs.aws.amazon.com/lambda/latest/dg/API_Invoke.html

hmm, looks like split is done by ": " vs in your command it is separated only by ":" (note not space).

check if you add a space in there if that works.

Interesting. Can we make it accept either way as long as a ":" is present?

New error after the modification:

$ awscurl --service lambda --region eu-west-1 --profile default 'https://lambda.<region>.amazonaws.com/2015-03-31/functions/<function-name>/invocations?Qualifier=$LATEST' -H 'X-Amz-Invocation-Type: RequestResponse' -H 'X-Amz-Log-Type: Tail' -H 'Content-Type: application/json' -d '{"message":"Whats up."}'
b'<AccessDeniedException>\n  <Message>Unable to determine service/operation name to be authorized</Message>\n</AccessDeniedException>\n'
Traceback (most recent call last):
  File "/usr/local/Cellar/python/3.7.6_1/Frameworks/Python.framework/Versions/3.7/bin/awscurl", line 8, in <module>
    sys.exit(main())
  File "/usr/local/lib/python3.7/site-packages/awscurl/awscurl.py", line 500, in main
    inner_main(sys.argv[1:])
  File "/usr/local/lib/python3.7/site-packages/awscurl/awscurl.py", line 494, in inner_main
    response.raise_for_status()
  File "/usr/local/lib/python3.7/site-packages/requests/models.py", line 940, in raise_for_status
    raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 403 Client Error: Forbidden for url: https://lambda.<region>.amazonaws.com/2015-03-31/functions/<function-name>/invocations?Qualifier=$LATEST

please propose a change -- just trying to foster collaboration ...

your url looks wrong, the and other <> placeholders would require a value

The URL is not wrong. I masked my value out :)

then looks like general AccessDeniedException error

I attempted to do a more robust test. Here's what I have:

  • An API Gateway POST API with no authorization
  • An API Gateway POST API with IAM authorization
  • The Lambda one mentioned above

Testing from regular curl, if -d is enabled, the method will be changed to POST even -XPOST is not explicitly defined.

Testing from awscurl, if -d is enabled, the method will still be GET if -XPOST is not explicitly defined.

Thus, adding -XPOST to the command I used above:

$ awscurl -XPOST --service lambda --region <region> --profile default 'https://lambda.<region>.amazonaws.com/2015-03-31/functions/<function-name>/invocations?Qualifier=$LATEST' -H 'X-Amz-Invocation-Type: RequestResponse' -H 'X-Amz-Log-Type: Tail' -H 'Content-Type: application/json' -d '{"message":"Whats up."}'

The new error message is "The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details."

Comparing the --verbose log of awscurl to --debug output on AWS CLI, I found the following:

In AWS CLI, the canonical request is:

POST
/2015-03-31/functions/<function-name>/invocations

host:lambda.<region>.amazonaws.com
x-amz-date:<masked-out>
x-amz-invocation-type:RequestResponse
x-amz-log-type:Tail

host;x-amz-date;x-amz-invocation-type;x-amz-log-type
<masked out>

In awscurl, the canonical request is:

('\n'
 'CANONICAL REQUEST = POST\n'
 '/2015-03-31/functions/<function-name>/invocations\n'
 'Qualifier=$LATEST\n'
 'host:lambda.<region>.amazonaws.com\n'
 'x-amz-date:<masked-out>\n'
 '\n'
 'host;x-amz-date\n'
 '<masked-out>')

You can see, the canonical request AWS CLI made has the URL query string Qualifier=$LATEST removed. awscurl has not. Also, x-amz-invocation-type:RequestResponse and x-amz-log-type:Tail headers are missing.