
{"id":2277,"date":"2016-10-26T14:05:28","date_gmt":"2016-10-26T21:05:28","guid":{"rendered":"http:\/\/briantroy.com\/?p=2277"},"modified":"2025-01-03T04:41:20","modified_gmt":"2025-01-03T04:41:20","slug":"series-part-4-serverless-architecture-a-practical-implementation-securing-a-serverless-rest-api","status":"publish","type":"post","link":"https:\/\/blogarchive.briantroy.com\/index.php\/2016\/10\/26\/series-part-4-serverless-architecture-a-practical-implementation-securing-a-serverless-rest-api\/","title":{"rendered":"Series &#8211; Part 4: Serverless Architecture &#8211; a practical implementation: Securing a Serverless REST API"},"content":{"rendered":"<p>In <a href=\"http:\/\/briantroy.com\/2016\/10\/24\/series-part-3-serverless-architecture-a-practical-implementation-serverless-rest-api\/\">part three of the series<\/a> I discussed creating a serverless REST API &#8211; using Lambda and API Gateway &#8211; to serve the collected IoT data from the security camera devices.<\/p>\n<p>In this post I will cover securing the REST API.<\/p>\n<p><!--more--><\/p>\n<p>As a review, the image below provides an overview of the serverless web application that will be implemented using this REST API:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-2024 size-full\" src=\"https:\/\/blogarchive.briantroy.com\/wp-content\/uploads\/2016\/10\/serverless-app-arch-001-2.png\" alt=\"serverless-app-arch-001\" width=\"1024\" height=\"768\" \/><\/p>\n<h1>Securing the REST API<\/h1>\n<p>The API we created in part three needs to be secured to ensure that the videos &#8211; and the metadata about those videos &#8211; is inaccessible to API users who haven&#8217;t been granted access. As importantly, I didn&#8217;t want to inject a server to manage user accounts and rights, nor did I want to take on managing another set of user accounts.<\/p>\n<p>The solution: <a href=\"https:\/\/developers.google.com\/api-client-library\/javascript\/features\/authentication#authorized-access\">Google Authentication API<\/a><\/p>\n<p>I won&#8217;t go into significant detail in this post (I will, however, in the next) about implementing the configuration and javascript to authenticate a user with Google and obtain a user token &#8211; but suffice it to say once we have the token we can pass it to the REST API and use that to allow\/disallow access.<\/p>\n<p>Note: I did look at <a href=\"https:\/\/console.aws.amazon.com\/cognito\/home?region=us-east-1#\">Cognito<\/a>, but it seemed like overkill for my purposes.<\/p>\n<h2>Creating a API Gateway\u00a0Custom Authenticator<\/h2>\n<p>This is a relatively straightforward process since we will be using a Lambda function as our custom authenticator. So, step one is the creation of another Lambda function using <a href=\"https:\/\/github.com\/briantroy\/sendftpfilestos3\/blob\/master\/lambda-functions\/gAppsFamilyAuthenticator.py\">this code from GitHub<\/a>.<\/p>\n<h3>Custom Authenticator Functionality<\/h3>\n<p>The custom authenticator does 3 things. First it gets the token sent by the client and makes a HTTP request to google to validate the token and get the user&#8217;s information:<\/p>\n<pre>    # Get the user info from Google for the recieved token...\n    id_token = event['authorizationToken']\n    google_token_helper_uri = \"https:\/\/www.googleapis.com\/oauth2\/v3\/tokeninfo?id_token=\" + id_token\n\n    result = json.loads(urllib2.urlopen(google_token_helper_uri).read())\n\n    domain = result['hd']\n    user = result['sub']\n    effect = 'Deny'\n<\/pre>\n<p>It also sets the default effect to &#8220;Deny&#8221; &#8211; meaning the user will not be allowed access.<\/p>\n<p>The second thing it does is compare some of the user information to a whitelist. In my case I&#8217;m granting access to everyone in the\u00a0<em>brianandkelly.ws<\/em> domain.<\/p>\n<pre>    allowed_domain = \"brianandkelly.ws\"\n    if domain == allowed_domain:\n        effect = 'Allow'\n<\/pre>\n<p>This is <em>very simple<\/em>. This can easily be extended to have a more complex whitelist &#8211; including a dynamic one stored in DynamoDB, ActiveDirectory, OpenLDAP or any other backing store.<\/p>\n<p>Third, the authenticator constructs an IAM Policy which either allows or denies access &#8211; based on the value of the <em>effect <\/em>variable &#8211; and returns that policy to API Gateway<em>:<\/em><\/p>\n<pre>    respond = {\n        \"principalId\": user,\n        \"policyDocument\": {\n            \"Version\": \"2012-10-17\",\n            \"Statement\": [\n                {\n                    \"Action\": \"execute-api:Invoke\",\n                    \"Effect\": effect,\n                    \"Resource\": \"arn:aws:execute-api:us-east-1:*:!!your api info!!*\"\n                }\n            ]\n        }\n    }\n\n    return respond\n<\/pre>\n<p>In the script itself you&#8217;ll need to replace <em>!!your api info!!<\/em> with information from the heading of the API Gateway screen:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-2339 size-full\" src=\"https:\/\/blogarchive.briantroy.com\/wp-content\/uploads\/2016\/10\/screen-shot-2016-10-25-at-6-36-33-pm1-2.png\" alt=\"screen-shot-2016-10-25-at-6-36-33-pm\" width=\"2538\" height=\"366\" \/><\/p>\n<p>The format is:<\/p>\n<pre>part in parenthesis\/api name - part before parenthisis<\/pre>\n<p>in my\u00a0case:<\/p>\n<pre>\"Resource\": \"arn:aws:execute-api:us-east-1:*:7k8o0sgjli\/securityvideos\/*\"<\/pre>\n<p>I won&#8217;t go into detail about creating this function (we&#8217;ve done several in the series) but here is how mine looks fully configured:<\/p>\n<figure id=\"attachment_2308\" aria-describedby=\"caption-attachment-2308\" style=\"width: 2154px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-2308 size-full\" src=\"https:\/\/blogarchive.briantroy.com\/wp-content\/uploads\/2016\/10\/screen-shot-2016-10-25-at-6-21-58-pm-2.png\" alt=\"screen-shot-2016-10-25-at-6-21-58-pm\" width=\"2154\" height=\"1298\" \/><figcaption id=\"caption-attachment-2308\" class=\"wp-caption-text\">Authenticator Lambda Function<\/figcaption><\/figure>\n<p>Using the existing IAM role we created for the other REST API Lambda Functions will work fine.<\/p>\n<p>You should note that this Lambda Function has no Trigger &#8211; since it will be called by API Gateway once we apply the lambda function as a custom authorizer.<\/p>\n<h3>Applying the Custom Authenticator to your REST API<\/h3>\n<p>The first step is to tell API Gateway to use the Lambda Function as an Authorizer. Click on Authorizers under the API:<\/p>\n<figure id=\"attachment_2352\" aria-describedby=\"caption-attachment-2352\" style=\"width: 298px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-2352\" src=\"https:\/\/briantroy.com\/wp-content\/uploads\/2016\/10\/screen-shot-2016-10-25-at-6-44-41-pm.png\" alt=\"Screen Shot 2016-10-25 at 6.44.41 PM.png\" width=\"298\" height=\"563\" \/><figcaption id=\"caption-attachment-2352\" class=\"wp-caption-text\">Adding Authorizer<\/figcaption><\/figure>\n<p>Now select &#8220;Create&#8221; on the right and select &#8220;Custom Authorizer&#8221;<\/p>\n<figure id=\"attachment_2356\" aria-describedby=\"caption-attachment-2356\" style=\"width: 415px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-2356\" src=\"https:\/\/briantroy.com\/wp-content\/uploads\/2016\/10\/screen-shot-2016-10-25-at-6-46-00-pm.png\" alt=\"Screen Shot 2016-10-25 at 6.46.00 PM.png\" width=\"415\" height=\"182\" \/><figcaption id=\"caption-attachment-2356\" class=\"wp-caption-text\">Custom Authorizer<\/figcaption><\/figure>\n<p>In the &#8220;Update Custom Authorizer&#8221; screen select the region your Lambda function is deployed in, the name of the function\u00a0and give the authorizer a name.<\/p>\n<p>You can leave the default\u00a0<em>method.request.header.Authorization<\/em> value in the &#8220;Identity token source&#8221; field. We will discuss how to set the header in the next post in this series. I strongly recommend that you set &#8220;Result TTL in seconds&#8221; to a value\u00a0<em>no less than 300.<\/em> This is \u00a0important for both the performance of your API and to avoid sending excessive requests to Google.<\/p>\n<figure id=\"attachment_2360\" aria-describedby=\"caption-attachment-2360\" style=\"width: 1626px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-2360 size-full\" src=\"https:\/\/blogarchive.briantroy.com\/wp-content\/uploads\/2016\/10\/screen-shot-2016-10-25-at-6-46-55-pm-2.png\" alt=\"Screen Shot 2016-10-25 at 6.46.55 PM.png\" width=\"1626\" height=\"908\" \/><figcaption id=\"caption-attachment-2360\" class=\"wp-caption-text\">Authorizer Configuration<\/figcaption><\/figure>\n<p>Finally, you will need to apply the authenticator to all the active methods of your API (or at least the ones you want secure):<\/p>\n<figure id=\"attachment_2370\" aria-describedby=\"caption-attachment-2370\" style=\"width: 2524px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-2370 size-full\" src=\"https:\/\/blogarchive.briantroy.com\/wp-content\/uploads\/2016\/10\/screen-shot-2016-10-25-at-6-51-50-pm-2.png\" alt=\"Screen Shot 2016-10-25 at 6.51.50 PM.png\" width=\"2524\" height=\"1210\" \/><figcaption id=\"caption-attachment-2370\" class=\"wp-caption-text\">Auth setting for a GET Method<\/figcaption><\/figure>\n<p>You can do that by clicking on &#8220;Method Request&#8221; and setting the authenticator as follows:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-2374 size-full\" src=\"https:\/\/blogarchive.briantroy.com\/wp-content\/uploads\/2016\/10\/screen-shot-2016-10-25-at-6-54-07-pm-2.png\" alt=\"Screen Shot 2016-10-25 at 6.54.07 PM.png\" width=\"2538\" height=\"956\" \/><\/p>\n<p>NOTE: You should NOT apply the authenticator to the OPTIONS method. That method is used for CORS and should not be secured.<\/p>\n<h1>Deploying the REST API<\/h1>\n<p>Once you&#8217;ve done that for all the resources you can deploy your API &#8211; which will make it available. API Gateway uses &#8220;Stage&#8221; to denote various types of deployments for your API. You can use these stages to have separate DEV\/QA and production stages for your API.<\/p>\n<p>To deploy your API select the root of the resource tree and select &#8220;Actions&#8221; -&gt; &#8220;Deploy API&#8221;:<\/p>\n<figure id=\"attachment_2377\" aria-describedby=\"caption-attachment-2377\" style=\"width: 2524px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-2377 size-full\" src=\"https:\/\/blogarchive.briantroy.com\/wp-content\/uploads\/2016\/10\/screen-shot-2016-10-25-at-6-56-45-pm-2.png\" alt=\"screen-shot-2016-10-25-at-6-56-45-pm\" width=\"2524\" height=\"822\" \/><figcaption id=\"caption-attachment-2377\" class=\"wp-caption-text\">API Deployment<\/figcaption><\/figure>\n<p>In the &#8220;Deploy API&#8221; dialog select &#8220;[New Stage]&#8221;:<\/p>\n<figure id=\"attachment_2389\" aria-describedby=\"caption-attachment-2389\" style=\"width: 1174px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-2389 size-full\" src=\"https:\/\/blogarchive.briantroy.com\/wp-content\/uploads\/2016\/10\/screenshot-2016-10-26-13-08-21-2.png\" alt=\"screenshot-2016-10-26-13-08-21\" width=\"1174\" height=\"634\" \/><figcaption id=\"caption-attachment-2389\" class=\"wp-caption-text\">New Stage from Deploy<\/figcaption><\/figure>\n<p>You can then give your stage a name (note: this will appear in the URI), description and a deployment description:<\/p>\n<figure id=\"attachment_2393\" aria-describedby=\"caption-attachment-2393\" style=\"width: 1182px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-2393 size-full\" src=\"https:\/\/blogarchive.briantroy.com\/wp-content\/uploads\/2016\/10\/screenshot-2016-10-26-13-08-45-2.png\" alt=\"screenshot-2016-10-26-13-08-45\" width=\"1182\" height=\"798\" \/><figcaption id=\"caption-attachment-2393\" class=\"wp-caption-text\">Creating Deploy Stage &amp; Deploying API<\/figcaption><\/figure>\n<p>Pressing the &#8220;Deploy&#8221; button completes the process. You should see a stage editor similar to the following:<\/p>\n<figure id=\"attachment_2399\" aria-describedby=\"caption-attachment-2399\" style=\"width: 2796px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-2399 size-full\" src=\"https:\/\/blogarchive.briantroy.com\/wp-content\/uploads\/2016\/10\/screenshot-2016-10-26-13-16-29-2.png\" alt=\"screenshot-2016-10-26-13-16-29\" width=\"2796\" height=\"1694\" \/><figcaption id=\"caption-attachment-2399\" class=\"wp-caption-text\">Deployment Stage Editor<\/figcaption><\/figure>\n<p>I strongly recommend you tick the boxes for &#8220;Enable CloudWatch Logs&#8221; and &#8220;Enable Detailed CloudWatch Metrics&#8221; at the beginning as this will make the logging to CloudWatch verbose enough for you to troubleshoot any issues that arise.<\/p>\n<p>At the top of the stage editor you will find the URI to your new API. You should visit this URI and make sure you are denied access!<\/p>\n<p>Last, but not least, you can test your new API using Postman or Swagger by exporting the API in those formats on the &#8220;Export&#8221; tab:<\/p>\n<figure id=\"attachment_2407\" aria-describedby=\"caption-attachment-2407\" style=\"width: 2796px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-2407 size-full\" src=\"https:\/\/blogarchive.briantroy.com\/wp-content\/uploads\/2016\/10\/screenshot-2016-10-26-13-16-291-2.png\" alt=\"screenshot-2016-10-26-13-16-29\" width=\"2796\" height=\"1694\" \/><figcaption id=\"caption-attachment-2407\" class=\"wp-caption-text\">Exporting Swagger or Postman API Specification<\/figcaption><\/figure>\n<p>AWS provides some helpful documentation for <a href=\"http:\/\/docs.aws.amazon.com\/apigateway\/latest\/developerguide\/how-to-use-postman-to-call-api.html\">testing your API with Postman here<\/a>. In order to access the API and test it with Postman you&#8217;ll need a valid google account and a way to get a valid id_token. This will be discussed in detail in part five of this series when we implement the web application using this API. That being said, if you&#8217;d like to jump ahead check out the <a href=\"https:\/\/developers.google.com\/identity\/sign-in\/web\/sign-in\">google documentation<\/a> and my <a href=\"https:\/\/github.com\/briantroy\/SecurityVideos\">GitHub repository containing the web application code<\/a> (the most interesting bit for getting a google id_token is <a href=\"https:\/\/github.com\/briantroy\/SecurityVideos\/blob\/master\/init.js\">found in init.js<\/a>).<\/p>\n<h1>Summary<\/h1>\n<p>We now have fully secured and deployed the REST API created <a href=\"http:\/\/briantroy.com\/2016\/10\/24\/series-part-3-serverless-architecture-a-practical-implementation-serverless-rest-api\/\">in part three\u00a0of the series<\/a>. In part five of the series we will discuss the application built over this API and how to deploy that application serverless.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In part three of the series I discussed creating a serverless REST API &#8211; using Lambda and API Gateway &#8211; to serve the collected IoT data from the security camera devices. In this post I will cover securing the REST API.<\/p>\n","protected":false},"author":1,"featured_media":1744,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[9,11,18,19,28],"tags":[236,249],"_links":{"self":[{"href":"https:\/\/blogarchive.briantroy.com\/index.php\/wp-json\/wp\/v2\/posts\/2277"}],"collection":[{"href":"https:\/\/blogarchive.briantroy.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogarchive.briantroy.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogarchive.briantroy.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blogarchive.briantroy.com\/index.php\/wp-json\/wp\/v2\/comments?post=2277"}],"version-history":[{"count":2,"href":"https:\/\/blogarchive.briantroy.com\/index.php\/wp-json\/wp\/v2\/posts\/2277\/revisions"}],"predecessor-version":[{"id":3326,"href":"https:\/\/blogarchive.briantroy.com\/index.php\/wp-json\/wp\/v2\/posts\/2277\/revisions\/3326"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blogarchive.briantroy.com\/index.php\/wp-json\/wp\/v2\/media\/1744"}],"wp:attachment":[{"href":"https:\/\/blogarchive.briantroy.com\/index.php\/wp-json\/wp\/v2\/media?parent=2277"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogarchive.briantroy.com\/index.php\/wp-json\/wp\/v2\/categories?post=2277"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogarchive.briantroy.com\/index.php\/wp-json\/wp\/v2\/tags?post=2277"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}