AWS Chalice, Amazon API Gateway, and AWS IAM Authorization
I’ve wanted to take a hands-on look at Amazon API Gateway and the recent 1.0 release of aws/chalice: Python Serverless Microframework for AWS pushed me over the edge.
The python serverless microframework for AWS allows you to quickly create and deploy applications that use Amazon API Gateway and AWS Lambda.
Since I am using the Anaconda Distribution of Python, I needed to consult Managing environments — Conda documentation for setting things up without using virtualenv.
$ conda create --name chalice python=3
$ source activate chalice
$ pip install chalice
$ chalice new-project helloworld
A simple app.py
file was created for me…
from chalice import Chalice
app = Chalice(app_name='helloworld')
@app.route('/')
def index():
return {'hello': 'world'}
…which I was able to easily deploy to our AWS account.
$ chalice deploy
Initial creation of lambda function.
Creating role
Creating deployment package.
Initiating first time deployment...
Deploying to: api
https://fw7xjo57bc.execute-api.us-east-1.amazonaws.com/api/
I tested with curl and got the result I was expecting.
$ curl https://fw7xjo57bc.execute-api.us-east-1.amazonaws.com/api/
{"hello": "world"}
However, this was now publicly exposed and I don’t want everyone to know about “Hello World”. It was time to consult the documentation on how to secure my new API:
I needed the resource-id
of my GET method to add an authorizationType of AWS_IAM
so only a valid AWS user in this account could call my API.
$ aws apigateway get-resources --rest-api-id fw7xjo57bc
{
"items": [
{
"id": "9y11coidb1",
"path": "/",
"resourceMethods": {
"GET": {}
}
}
]
}
$ aws apigateway update-method \
--rest-api-id fw7xjo57bc \
--resource-id 9y11coidb1 \
--http-method GET \
--patch-operations op="replace",path="/authorizationType",value="AWS_IAM"
But this didn’t work right away, so I had to deploy my changes before my curl was rejected.
$ aws apigateway create-deployment --rest-api-id fw7xjo57bc --stage-name api
{
"id": "5pr2ed",
"createdDate": 1502815254
}
$ curl https://fw7xjo57bc.execute-api.us-east-1.amazonaws.com/api/
{"message":"Missing Authentication Token"}
The docs for Signing AWS API Requests had Examples of the Complete Version 4 Signing Process (Python) but they weren’t working for me, so I used jmenga/requests-aws-sign: This package allows for AWS V4 request signing using the Python requests library instead.
$ pip install requests
$ pip install requests_aws_sign
Since naming things is hard, I created fw7xjo57bc.py
to make the signed request.
import requests
from requests_aws_sign import AWSV4Sign
from boto3 import session
session = session.Session()
credentials = session.get_credentials()
region = session.region_name or 'us-east-1'
service = 'execute-api'
url = "https://fw7xjo57bc.execute-api.us-east-1.amazonaws.com/api/"
auth = AWSV4Sign(credentials, region, service)
response = requests.get(url, auth=auth)
print(response.text)
It works!
$ python3 fw7xjo57bc.py
{"hello": "world"}
I then set authorizationType back to NONE, deployed, and tested again.
$ aws apigateway update-method \
--rest-api-id fw7xjo57bc \
--resource-id 9y11coidb1 \
--http-method GET \
--patch-operations op="replace",path="/authorizationType",value="NONE"
$ aws apigateway create-deployment --rest-api-id fw7xjo57bc --stage-name api
{
"id": "xxtj1w",
"createdDate": 1502816441
}
$ curl https://fw7xjo57bc.execute-api.us-east-1.amazonaws.com/api/
{"hello": "world"}
Using an authorizationType
of AWS_IAM
is only interesting for more “internal” or “infrastructure” APIs. Most cases where API Gateway come up is for customer-facing, external API which needs either API Keys for Usage Plans or Custom Authorizers.
Lastly, it was also just as easy to clean up after myself
$ chalice delete
Deleting rest API fw7xjo57bc
Deleting lambda function helloworld-dev
Delete the role helloworld-dev? [y/N]: y
Deleting role name helloworld-dev