My FeedDiscussionsHeadless CMS
New
Sign in
Log inSign up
Learn more about Hashnode Headless CMSHashnode Headless CMS
Collaborate seamlessly with Hashnode Headless CMS for Enterprise.
Upgrade ✨Learn more

AWS Lambda Role Execution Example

Nico Coetzee's photo
Nico Coetzee
·Feb 13, 2020

In my first post, I demonstrated how EC2 execution roles allows an EC2 instance access to AWS services without requiring credentials to be used on the actual instance. This is great from a security point of view because there the less you work with credential, the less change is there for it to leak or be exposed in some unintentional way.

That first article also mentioned I was working toward something bigger, and this second short article is therefore along the same lines and will demonstrate the execution roles linked to a Lambda function.

The lambda execution role will use the exact same policy we already defined for the EC2 instance.

The Lambda Development Environment

The actual OS you use doesn't make that much of a difference these days, although the examples I use will be leaning more toward Apple OSX as this is my primary OS.

That being said, the rest of the tooling is pretty standard regardless of OS:

  • You need access to a terminal. For Windows users I can highly recommend the still-in-development Windows Terminal
  • A good IDE that you are comfortable/familiar with. Personally I use VS Code
  • The AWS CLI. On OSX it's easiest to use Homebrew to install the AWS CLI.
  • I used to develop in Python (and I still love Python), but these days I find it convenient to use a language that just works everywhere, and therefore JavaScript is the way to go. To this end, you will need Node.js. Again, on OSX you can use Homebrew to install. Most Linux distributions have Node.js in their repos. However, the tricky bit i that for AWS Lambda development, you need a version that is compatible with the AWS version. Refer to the AWS Documentation to see which versions is supported and ensure you have that version available on your OS. I have a short OSX+Homebrew example below.
    • For this article I will use Node.js version 12

In this article we will be using the command line a lot.

Other Prerequisites

Once you have your environment sorted, you will also need to ensure the following is in place:

  • Create an IAM user with appropriate roles.
    • IMPORTANT : Never use your AWS Root account unless you are busy setting up the account after initial account registration.
    • If you are still new new to AWS, use the AWS Web Console and create a user with the AWS pre-configured role of AdministratorAccess
    • Consult the AWS IAM Documentation for help/assistance
  • Once you have created an administrator user, also create Access Keys. You will need the keys to configure the AWS CLI on your local machine.
    • IMPORTANT : As you become more familiar with AWS, you will always aim for giving users least privilege access - meaning the acocunt has only the policies it requires to perform a certain function. This is partly where this series is working towards! For now, once you are finished with this article and the example, it is highly recommended that you revoke your Access Keys - don't keep them on your machine indefinitely!

Running Node.js version 12 along side the default on OSX, via Homebrew

So, you have this problem:

$ node --version
v13.7.0

Lambda supports Node version 12 and you want to ensure you develop against that. First, see if it's available:

$ brew search node@12
==> Formulae
node@12

And now, install and link to the newly installed Node version 12:

$ brew install node@12
$ brew unlink node
$ brew link --force --overwrite node@12
$ node --version
v12.15.0

Preparing a Directory for Development

The following series of commands prepares a directory that will be home for our source code. There are also commands for installing a number of pre-requisite libraries. The commands assume a Linux or OSX type OS, so Windows users need to adjust the commands slightly - or just use WSL on Windows 10.

$ cd
$ mkdir aws_lambda_execution_role_test
$ cd aws_lambda_execution_role_test
$ touch index.js
$ touch xray_policy.json 
$ touch lambda_execution_policy.json
$ npm install aws-xray-sdk

Note : The AWS X-Ray library is something we will use to build in telemetry in our application. It helps identifying bottlenecks, among various other useful functions.

The Code

Below is some example code to retrieve a secret and return it to the client.

File: index.js

const getMySecret = (secretName) => {
    const AWSXRay = require('aws-xray-sdk-core')
    const AWS = AWSXRay.captureAWS(require('aws-sdk'));
    var region = "us-east-1";
    var client = new AWS.SecretsManager({region: region});
    return new Promise((resolve,reject)=>{
        client.getSecretValue({SecretId: secretName}, function(err, data) {
            if (err) {
                reject(err);
            }
            else {
                if ('SecretString' in data) {
                    resolve(data.SecretString);
                } else {
                    let buff = new Buffer(data.SecretBinary, 'base64');
                    resolve(buff.toString('ascii'));
                }
            }
        });
    });
}

exports.handler = async (event) => {
    var mySecret = await getMySecret('DarkSecret')
    const response = {
        statusCode: 200,
        haveRun: true,
        body: JSON.stringify(mySecret)
    };
    return response;
};

Preparing the Lambda Execution Role

Add the following JSON to the file xray_policy.json:

{
    "Version": "2012-10-17",
    "Statement": {
        "Effect": "Allow",
        "Action": [
            "xray:PutTraceSegments",
            "xray:PutTelemetryRecords"
        ],
        "Resource": [
            "*"
        ]
    }
}

In the previous example explained to some extend the process, but here is a command line example of how to create a policy:

$ aws iam create-policy --policy-name "xray_emit_policy" --policy-document file://xray_policy.json
{
    "Policy": {
        "PolicyName": "xray_emit_policy",
        "PolicyId": "ANPATD4BXMDLKC3CBEOW2",
        "Arn": "arn:aws:iam::xxxxx:policy/xray_emit_policy",
        "Path": "/",
        "DefaultVersionId": "v1",
        "AttachmentCount": 0,
        "PermissionsBoundaryUsageCount": 0,
        "IsAttachable": true,
        "CreateDate": "2020-02-13T05:33:12Z",
        "UpdateDate": "2020-02-13T05:33:12Z"
    }
}

Previously the policy dark-secret-read-policy was also created. The content of that policy:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "secretsmanager:GetSecretValue",
            "Resource": "arn:aws:secretsmanager:us-east-1:xxxxx:secret:DarkSecret-xxxxx"
        }
    ]
}

You need to take note of the ARN's for these two policies. It will look something like:

arn:aws:iam::xxxxx:policy/xray_emit_policy
arn:aws:iam::xxxxx:policy/dark-secret-read-policy

Next create an execution role for the Lambda function.

Create a file lambda_execution_policy.json containing the following:

{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Allow",
        "Principal": {
          "Service": "lambda.amazonaws.com"
        },
        "Action": "sts:AssumeRole"
      }
    ]
  }
$ aws iam create-role --role-name lambda-darksecrets-role --assume-role-policy-document file://lambda_execution_policy.json
{
    "Role": {
        "Path": "/",
        "RoleName": "lambda-darksecrets-role",
        "RoleId": "xxxxx",
        "Arn": "arn:aws:iam::xxxxx:role/lambda-darksecrets-role",
        "CreateDate": "2020-02-13T05:57:46Z",
        "AssumeRolePolicyDocument": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Effect": "Allow",
                    "Principal": {
                        "Service": "lambda.amazonaws.com"
                    },
                    "Action": "sts:AssumeRole"
                }
            ]
        }
    }
}

Again, take note of the ARN.

Finally, add the xray_emit_policy and dark-secret-read-policy to this role:

$ aws iam attach-role-policy --role-name lambda-darksecrets-role --policy-arn arn:aws:iam::xxxxx:policy/xray_emit_policy
$ aws iam attach-role-policy --role-name lambda-darksecrets-role --policy-arn arn:aws:iam::xxxxx:policy/dark-secret-read-policy

Packaging and Uploading

$ zip -r function.zip .
...lots of output omitted....
$ aws lambda create-function --function-name reader-of-dark-secrets --runtime nodejs12.x --role arn:aws:iam::xxxxx:role/lambda-darksecrets-role --handler index.handler --timeout 10 --zip-file fileb://function.zip
{
    "FunctionName": "reader-of-dark-secrets",
    "FunctionArn": "arn:aws:lambda:us-east-1:xxxxx:function:reader-of-dark-secrets",
    "Runtime": "nodejs12.x",
    "Role": "arn:aws:iam::xxxxx:role/lambda-darksecrets-role",
    "Handler": "index.handler",
    "CodeSize": 7979706,
    "Description": "",
    "Timeout": 10,
    "MemorySize": 128,
    "LastModified": "2020-02-13T06:10:47.951+0000",
    "CodeSha256": "PFlWQ6wvbT2JxkmBWuwLeqGVn6uo9a5Ri6B/djutnBA=",
    "Version": "$LATEST",
    "TracingConfig": {
        "Mode": "PassThrough"
    },
    "RevisionId": "657844da-6c92-44ea-8d2f-9ce10a2f7900",
    "State": "Active",
    "LastUpdateStatus": "Successful"
}

Later, once you have updated your code, you can re-create the ZIP file and update the function:

$ aws lambda update-function-code --function-name reader-of-dark-secrets --zip-file fileb://function.zip

The code includes X-Ray tracing, but it is not enabled the way the function was created, since you only really need it ocasionally for troubleshooting. The following command will activate tracing:

$ aws lambda update-function-configuration --function-name reader-of-dark-secrets --tracing-config Mode=Active

Once done, you can just run the update function again with the mode set to PassThrough

Test

$ aws lambda invoke --function-name "reader-of-dark-secrets" /dev/stdout
{"statusCode":200,"haveRun":true,"body":"\"Some heavy dark secret\""}{
    "StatusCode": 200,
    "ExecutedVersion": "$LATEST"
}

You can use the AWS Console to check the x-ray trace - it may look something like this:

xray-trace.png

Conclusion

This was a demonstration on how to set a very restrictive execution role on a Lambda function. You can try calling any other AWS service to test, but the permissions will only allow access to a very specific secret stored in SecretsManager.

There was also a lot more command line demonstration to show how the bulk of work will hardly ever involve the AWS Console.