Vault AWS Lambda extension | Vault | HashiCorp Developer (original) (raw)

AWS Lambda lets you run code without provisioning and managing servers. Lambda functions perform processing when you need it, and now can use Vault secrets through the Vault Lambda Extension.

A database within your AWS infrastructure maintains sensitive information. A periodic function audits and updates the stored data. This function must authenticate with Vault and receive database credentials.

Lambda Extensions are Lambda layers used to augment Lambda functions, and you can use them to give the Lambda function access to the Vault cluster. During execution, the Lambda layer packages the extension code with the main Lambda function code.

In this lab you will create an AWS Lambda function that uses the Vault Lambda Extension to authenticate with Vault, receive the database credentials, and perform the necessary queries.

This tutorial requires an AWS account, Terraform, go, AWS CLI, Docker, and the provided Terraform configuration to create a demonstration environment.

You will learn about the Vault Lambda Extension by creating a local scenario environment. The provided Terraform configuration deploys supporting AWS-based infrastructure and Vault configuration for use with the AWS auth method.

You will then build an example Lambda function that uses the Vault Lambda Extension to read secrets from Vault, and use Terraform to deploy it.

You can retrieve the lambda function and Terraform configuration by cloning thehashicorp-education/learn-vault-lambda-extension repository from GitHub.

$ git clone https://github.com/hashicorp-education/learn-vault-lambda-extension

This repository has the supporting content for Vault tutorials. The content specific to this tutorial is in a sub-directory.

Change into the learn-vault-lambda-extension directory.

$ cd learn-vault-lambda-extension

Working directory

This tutorial assumes that you execute all following commands from within this directory.

The demonstration environment sets up a Vault server, a PostgreSQL database, and configures the database secrets engine to generate dynamic credentials. The Terraform configuration in this directory creates the required infrastructure for the tutorial.

Create an environment variable named AWS_ACCESS_KEY_ID with your AWS access key ID.

$ export AWS_ACCESS_KEY_ID = "<YOUR_AWS_ACCESS_KEY_ID>"

Create an environment variable named AWS_SECRET_ACCESS_KEY with your AWS secret access key.

$ export AWS_SECRET_ACCESS_KEY = "<YOUR_AWS_SECRET_ACCESS_KEY>"

The AWS CLI, and Terraform use these environment variables to authenticate and create resources.

Tip

The above example uses IAM user authentication. You can use any authentication method described in the AWS provider documentation.

Copy terraform.tfvars.example and rename to terraform.tfvars.

$ cp terraform.tfvars.example terraform.tfvars

Edit terraform.tfvars to override the default settings that describe your environment.

# AWS region and AZs in which to deploy
# default: 'us-east-1'
aws_region = "us-east-1"

# All resources will be tagged with this
# default: 'vault-lambda-extension-demo'
environment_name = "vault-lambda-extension-demo"

# URL for Vault binary
# default: Vault v1.10.0
vault_zip_file = "https://releases.hashicorp.com/vault/1.10.0/vault_1.10.0_linux_amd64.zip"

# Instance size
# default: 't2.micro'
instance_type = "t2.micro"

# DB instance size
# default: 'db.t2.micro'
db_instance_type = "db.t2.micro"

Initialize Terraform.

The initialization retrieves the modules required to apply the resources defined within the configuration.

Apply Terraform to review the planned actions.

The terminal output displays the plan that it found and the resources it creates.

Enter yes to confirm and resume.

Note

Keep in mind that answering yes at this time will create actual resources with associated costs.

When the terraform apply command completes, the Terraform output displays the IP addresses of the Vault server and other services created.

PostgreSQL database address
    terraform-20201118041217579300000001.ci12bl452q7n.us-east-1.rds.amazonaws.com

Elastic Container Registry (ECR) repository address
    906192817044.dkr.ecr.us-east-1.amazonaws.com/demo-function

Vault Server IP (public)
    3.82.241.157

Vault UI URL
    http://3.82.241.157:8200/ui

You can SSH into the Vault EC2 instance using private.key:
    ssh -i private.key ubuntu@3.82.241.157

The output displays the database address, the container repository address, and connection information for the Vault server. The Terraform configuration also generated a private key and enabled SSH login on the Vault server. The output shows an example SSH command to connect to the Vault server.

In a new terminal session, change into the scenario directory.

$ cd learn-vault-lambda-extension

Then SSH into the Vault Server.

$ ssh -i private.key ubuntu@3.82.241.157

A single Vault server is running with the filesystem storage backend. The Terraform configuration automatically initialized and unsealed the server.

Verify that Vault is ready, initialized, and unsealed.

$ vault status
Key                      Value
---                      -----
Recovery Seal Type       shamir
Initialized              true
Sealed                   false
Total Recovery Shares    5
Threshold                3
Version                  1.10.0
Storage Type             file
Cluster Name             vault-cluster-07d67d93
Cluster ID               04353a37-6208-775d-4655-b5e3bfb53d5f
HA Enabled               false

If the value of Initialized is true and the value of Sealed is false, then the Vault server is ready for use.

Note

The Vault server configuration uses the template found in ./templates/userdata-vault-server.tpl.

The database secrets engine is already enabled and configured to communicate with the database through credentials generated for the role lambda-function.

Read credentials from the lambda-function database role.

$ vault read database/creds/lambda-function
Key                Value
---                -----
lease_id           database/creds/lambda-function/V2KfrawfpkJqGYr2N5cFaSH0
lease_duration     1h
lease_renewable    true
password           A1a-9ihozcMd4RGqmgfA
username           v-root-lambda-f-ilX2242kMoLeWnY7ZqKS-1605567098

The output displays the PostgreSQL username and password credentials.

Set the value of environment variables DB_USER and DB_PASSWORD to another set of credentials read from the role.

$ read -d "\n" DB_USER DB_PASSWORD <<<$(vault read database/creds/lambda-function -format=json | jq -r ".data.username,.data.password")

The command returns and parses the results for the username and password fields. These two values are then read into the values of variables DB_USER and DB_PASSWORD.

The PostgreSQL client tool requires this username, password, and host address to connect with its command-line tool. You stored the database servers address in an environment variable named DB_HOST.

Display the database address.

Connect to the PostgreSQL database via the CLI with the credentials and host.

$ PGPASSWORD=$DB_PASSWORD psql -h <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>D</mi><msub><mi>B</mi><mi>H</mi></msub><mi>O</mi><mi>S</mi><mi>T</mi><mo>−</mo><mi>d</mi><mi>p</mi><mi>o</mi><mi>s</mi><mi>t</mi><mi>g</mi><mi>r</mi><mi>e</mi><mi>s</mi><mo>−</mo><mi>U</mi></mrow><annotation encoding="application/x-tex">DB_HOST -d postgres -U </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8333em;vertical-align:-0.15em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">D</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.05017em;">B</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em;"><span style="top:-2.55em;margin-left:-0.0502em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.08125em;">H</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord mathnormal" style="margin-right:0.13889em;">OST</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">d</span><span class="mord mathnormal">p</span><span class="mord mathnormal">os</span><span class="mord mathnormal">t</span><span class="mord mathnormal" style="margin-right:0.03588em;">g</span><span class="mord mathnormal">res</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.10903em;">U</span></span></span></span>DB_USER

This command authenticates and connects you to the PostgerSQL database server, represented by system prompt root=#. You are now issuing commands in the PostgreSQL database running within the container.

List all the database users.

$ SELECT usename, valuntil FROM pg_user;
                       usename                       |        valuntil
-----------------------------------------------------+------------------------
 vaultadmin                                          | infinity
 rdsadmin                                            | infinity
 v-root-lambda-f-0BtQGCRTzGm2QgWHgv09-1647293547     | 2022-03-14 22:32:32+00
 v-root-lambda-f-DBN5PYDVkZ7u7yYymt47-1647293577     | 2022-03-14 22:33:02+00
(4 rows)

The output displays a table of all the database credentials generated. The credentials generated appear in this list.

Disconnect from the PostgreSQL database.

Vault issued database credentials, connected to the database and queried the users within the database.

The AWS Lambda function created in this step performs the same operation of requesting credentials and performing a query. The function needs its own set of Vault credentials that it requests through the AWS authentication engine.

Enable the AWS authentication engine.

Configure the AWS client to use the default options.

$ vault write -force auth/aws/config/client

The lambda function needs credentials to query the database credentials at the configured path.

Create a policy named lambda-function.

$ vault policy write lambda-function - <<EOF
path "database/creds/lambda-function" {
    capabilities = ["read"]
}
EOF

The policy grants the read capability to the database/creds/lambda-function. Vault attaches a policy to a token during the authentication through a role.

Read the Vault Policies tutorial to learn about defining Vault policies.

Create a role prefixed with the AWS environment name.

$ vault write auth/aws/role/$AWS_IAM_ROLE_LAMBDA_NAME \
    auth_type=iam \
    bound_iam_principal_arn="arn:aws:iam::$AWS_ACCOUNT_ID:role/$AWS_IAM_ROLE_LAMBDA_NAME" \
    policies=lambda-function \
    ttl=5m

The Vault role connects the AWS IAM role, created by the Terraform configuration in ./iam.tf and the Vault policy, lambda-function. The token returned after authentication is valid for 5 minutes.

You can observe the requests and responses associated with deploying the Lambda in a Vault audit device log. If you choose to try the caching example later in the scenario, the audit log is helpful for observing the lack of requests as well.

Enable a filesystem audit device.

$ vault audit enable file file_path=/tmp/vault_audit.log

You configured the Vault Server and it is ready for the AWS Lambda function to authenticate.

Tail the audit device log.

$ sudo tail -f /tmp/vault_audit.log

Successful output:

{"time":"2022-03-21T15:57:12.602098086Z","type":"request","auth":{"token_type":"default"},"request":{"id":"77f6f837-5b68-6097-5b02-5c37ccc4e124","operation":"update","namespace":{"id":"root"},"path":"sys/audit/test"}}
{"time":"2022-03-21T15:57:12.60420102Z","type":"response","auth":{"client_token":"hmac-sha256:2bfa837462c4c3997ca6b82aaf2047bd20a3c886198d2db81b240a798a591642","accessor":"hmac-sha256:6a8a815bc0554a66b8b5ebfc82641b03297cda2acfe285efe1ec6de7cf7a1f25","display_name":"root","policies":["root"],"token_policies":["root"],"token_type":"service","token_issue_time":"2022-03-21T15:27:59Z"},"request":{"id":"aa7dc060-69ab-f436-7fca-0fa67c16ca7b","operation":"update","mount_type":"system","client_token":"hmac-sha256:2bfa837462c4c3997ca6b82aaf2047bd20a3c886198d2db81b240a798a591642","client_token_accessor":"hmac-sha256:6a8a815bc0554a66b8b5ebfc82641b03297cda2acfe285efe1ec6de7cf7a1f25","namespace":{"id":"root"},"path":"sys/audit/file","data":{"description":"hmac-sha256:155ca16daa7e61da1d2e81b5bf7681dabe180d00d063784fdbd2ad1ab8309413","local":false,"options":{"file_path":"hmac-sha256:b91e77649a56647b56e76d65267801b02211b3f6ef79e85ddc6e462b90a6dc5e"},"type":"hmac-sha256:e4b3338f90bf149a84f11ea34df4b00667ddcff000f288f41fe90153beaa2f65"},"remote_address":"172.31.25.24","remote_port":58854},"response":{"mount_type":"system"}}

There is a single request and response pair present in the log.

Return to your original terminal session for the next steps.

A AWS Lambda function executes packaged code in a specific environment. The package is an archive or a container image.

The Lambda function for this tutorial is written in Go. It reads in the location of the Vault server and credentials provided to it. It requests dynamic credentials to gain access to the PostgreSQL database and performs a query to display the current list of database users.

Clean, build, and archive the lambda function.

The build script creates the demo function archive at the path demo-function/demo-function.zip.

Display the archived lambda function.

$ ls demo-function/demo-function.zip
demo-function/demo-function.zip

The archived function can now become an AWS lambda function.

The Lambda function is ready for the creation of an AWS Lambda resource. Terraform provides the aws_lambda_function resourcethat enables you to create the lambda function with an archive or container image.

Rename the file lambda-as_an_archive.tf.disabled to lambda.tf.

$ cp lambda-as_an_archive.tf.disabled lambda.tf

Display the contents of the lambda.tf file.

Successful output:

lambda.tf

resource "aws_lambda_function" "function" {
  function_name = "${var.environment_name}-function"
  description   = "Demo Vault AWS Lambda extension"
  role          = aws_iam_role.lambda.arn
  filename      = "./demo-function/demo-function.zip"
  handler       = "main"
  runtime       = "provided.al2"
  layers        = ["arn:aws:lambda:${var.aws_region}:634166935893:layer:vault-lambda-extension:13"]

  environment {
    variables = {
      VAULT_ADDR           = "http://${aws_instance.vault-server.public_ip}:8200",
      VAULT_AUTH_ROLE      = aws_iam_role.lambda.name,
      VAULT_AUTH_PROVIDER  = "aws",
      VAULT_SECRET_PATH_DB = "database/creds/lambda-function",
      VAULT_SECRET_FILE_DB = "/tmp/vault_secret.json",
      DATABASE_URL         = aws_db_instance.main.address
    }
  }
}

output "lambda" {
  value = <<EOF

The lambda function is ready to run.

    aws lambda invoke --function-name ${aws_lambda_function.function.function_name} /dev/null \
        --log-type Tail \
        --region ${var.aws_region} \
        | jq -r '.LogResult' \
        | base64 --decode

EOF

This file has two resources. The resource to create the Lambda function and the output that displays how to execute the Lambda function using the AWS CLI.

The aws_lambda_function loads the packaged function found at the path defined at `filename. The Lambda function uses several environment variables to connect and authenticate with Vault:

Run terraform apply and review the planned actions. Your terminal output indicates the plan that it found and what resources Terraform will create.

Enter yes to confirm and resume.

The terraform apply command creates the lambda function and the output displays how to invoke it.

lambda =
The lambda function is ready to run.

    aws lambda invoke --function-name vault-lambda-extension-demo-function /dev/null \
        --log-type Tail \
        --region us-east-1 \
        | jq -r '.LogResult' \
        | base64 --decode

Invoke the lambda function.

$ aws lambda invoke --function-name vault-lambda-extension-demo-function /dev/null \
        --log-type Tail \
        --region us-east-1 \
        | jq -r '.LogResult' \
        | base64 --decode

The output displays the logging and the current accounts found within the database.

START RequestId: 0999f92b-1a57-4e77-a47a-2e17682038a2 Version: $LATEST
c487ab6c-e834-4d1f-a333-8fcae8e1c0e1[vault-lambda-extension] 2022/03/14 21:50:55 Initialising
[vault-lambda-extension] 2022/03/14 21:50:55 attemping Vault login...
[runtime] handler in bootstrap: main
[runtime] Initializing...
[runtime] Waiting for invocation...
[vault-lambda-extension] 2022/03/14 21:50:55 Initialised
[vault-lambda-extension] 2022/03/14 21:50:55 Waiting for event...
EXTENSION   Name: vault-lambda-extension    State: Ready    Events: [INVOKE,SHUTDOWN]
[vault-lambda-extension] 2022/03/14 21:50:55 Received event
[vault-lambda-extension] 2022/03/14 21:50:55 Waiting for event...
[runtime] Received invocation: {}
[runtime] Executing function: main
[demo-function] Received:
[demo-function] Reading file /tmp/vault_secret.json
[demo-function] users:
[demo-function]      vaultadmin
[demo-function]      rdsadmin
[demo-function]      v-root-lambda-f-0BtQGCRTzGm2QgWHgv09-1647293547
[demo-function]      v-root-lambda-f-DBN5PYDVkZ7u7yYymt47-1647293577
[demo-function]      v-aws-vaul-lambda-f-P0akiqTACqHLWDCssTyj-1647294655
END RequestId: 0999f92b-1a57-4e77-a47a-2e17682038a2
REPORT RequestId: 0999f92b-1a57-4e77-a47a-2e17682038a2  Duration: 1076.90 ms    Billed Duration: 1185 ms    Memory Size: 128 MBMax Memory Used: 46 MB   Init Duration: 107.88 ms

The Lambda function executed.

Check the other terminal session, and you will observe a pair of new requests and responses corresponding to the Lambda execution.

Example output:

{"time":"2022-03-21T16:04:47.651293247Z","type":"request","auth":{"token_type":"default"},"request":{"id":"96877994-df04-1053-2fe5-d139cf9159e2","operation":"update","mount_type":"aws","namespace":{"id":"root"},"path":"auth/aws/login","data":{"iam_http_request_method":"hmac-sha256:05864ff6246edd8910fd4f6c4e04b75c7b1dcad3c0333940b87bf5fbf46f3bea","iam_request_body":"hmac-sha256:a6b996161178463d6e4cda2e9176c129c21bd3e8105a8053fba90fdbd1069117","iam_request_headers":"hmac-sha256:4d22c5076f549af3e00ed95f943481f166cf5a484dacda60653abf64d688a750","iam_request_url":"hmac-sha256:de80ceb22a3568566f33a623d7bcf03cc0933c5439e9ded75013ba07236f6451","role":"hmac-sha256:0796f22fa9fecbc94c1ab6c60c15e88d6eb61cbd552600a4a6373b5268cc8228"},"remote_address":"3.81.200.22","remote_port":58258}}
{"time":"2022-03-21T16:04:47.677761356Z","type":"response","auth":{"client_token":"hmac-sha256:1ec29e3cd8d52420bedbc4408c0cb2bbe31a6ecc31988de6c8c7dc54936e0857","accessor":"hmac-sha256:4ed70834f9ef6189d250f3b4f8a3068b75c50334f4535a4f426120ff426795c2","display_name":"aws-vault-lambda-extension-brian-lambda-role","policies":["default","lambda-function"],"token_policies":["default","lambda-function"],"metadata":{"account_id":"561656980159","auth_type":"iam","role_id":"b395511b-5c8c-943e-2bcc-0c4add9755d4"},"entity_id":"3895cdbc-6a17-631b-4224-a8d7cccc5225","token_type":"service","token_ttl":300},"request":{"id":"96877994-df04-1053-2fe5-d139cf9159e2","operation":"update","mount_type":"aws","namespace":{"id":"root"},"path":"auth/aws/login","data":{"iam_http_request_method":"hmac-sha256:05864ff6246edd8910fd4f6c4e04b75c7b1dcad3c0333940b87bf5fbf46f3bea","iam_request_body":"hmac-sha256:a6b996161178463d6e4cda2e9176c129c21bd3e8105a8053fba90fdbd1069117","iam_request_headers":"hmac-sha256:4d22c5076f549af3e00ed95f943481f166cf5a484dacda60653abf64d688a750","iam_request_url":"hmac-sha256:de80ceb22a3568566f33a623d7bcf03cc0933c5439e9ded75013ba07236f6451","role":"hmac-sha256:0796f22fa9fecbc94c1ab6c60c15e88d6eb61cbd552600a4a6373b5268cc8228"},"remote_address":"3.81.200.22","remote_port":58258},"response":{"auth":{"client_token":"hmac-sha256:1ec29e3cd8d52420bedbc4408c0cb2bbe31a6ecc31988de6c8c7dc54936e0857","accessor":"hmac-sha256:4ed70834f9ef6189d250f3b4f8a3068b75c50334f4535a4f426120ff426795c2","display_name":"aws-vault-lambda-extension-brian-lambda-role","policies":["default","lambda-function"],"token_policies":["default","lambda-function"],"metadata":{"account_id":"561656980159","auth_type":"iam","role_id":"b395511b-5c8c-943e-2bcc-0c4add9755d4"},"entity_id":"3895cdbc-6a17-631b-4224-a8d7cccc5225","token_type":"service","token_ttl":300},"mount_type":"aws"}}
{"time":"2022-03-21T16:04:47.681203967Z","type":"request","auth":{"client_token":"hmac-sha256:1ec29e3cd8d52420bedbc4408c0cb2bbe31a6ecc31988de6c8c7dc54936e0857","accessor":"hmac-sha256:4ed70834f9ef6189d250f3b4f8a3068b75c50334f4535a4f426120ff426795c2","display_name":"aws-vault-lambda-extension-brian-lambda-role","policies":["default","lambda-function"],"token_policies":["default","lambda-function"],"metadata":{"account_id":"561656980159","auth_type":"iam","role_id":"b395511b-5c8c-943e-2bcc-0c4add9755d4"},"entity_id":"3895cdbc-6a17-631b-4224-a8d7cccc5225","token_type":"service","token_ttl":300,"token_issue_time":"2022-03-21T16:04:47Z"},"request":{"id":"a81b297f-f25c-5b72-9e61-f0d75ec59f50","client_id":"3895cdbc-6a17-631b-4224-a8d7cccc5225","operation":"read","mount_type":"database","client_token":"hmac-sha256:60df7c00cd51d1b90731b88c2f8f8df9e6c458fa884873fd5c28206a13cc08e8","client_token_accessor":"hmac-sha256:4ed70834f9ef6189d250f3b4f8a3068b75c50334f4535a4f426120ff426795c2","namespace":{"id":"root"},"path":"database/creds/lambda-function","remote_address":"3.81.200.22","remote_port":58258}}
{"time":"2022-03-21T16:04:47.689257463Z","type":"response","auth":{"client_token":"hmac-sha256:1ec29e3cd8d52420bedbc4408c0cb2bbe31a6ecc31988de6c8c7dc54936e0857","accessor":"hmac-sha256:4ed70834f9ef6189d250f3b4f8a3068b75c50334f4535a4f426120ff426795c2","display_name":"aws-vault-lambda-extension-brian-lambda-role","policies":["default","lambda-function"],"token_policies":["default","lambda-function"],"metadata":{"account_id":"561656980159","auth_type":"iam","role_id":"b395511b-5c8c-943e-2bcc-0c4add9755d4"},"entity_id":"3895cdbc-6a17-631b-4224-a8d7cccc5225","token_type":"service","token_ttl":300,"token_issue_time":"2022-03-21T16:04:47Z"},"request":{"id":"a81b297f-f25c-5b72-9e61-f0d75ec59f50","operation":"read","mount_type":"database","client_token":"hmac-sha256:60df7c00cd51d1b90731b88c2f8f8df9e6c458fa884873fd5c28206a13cc08e8","client_token_accessor":"hmac-sha256:4ed70834f9ef6189d250f3b4f8a3068b75c50334f4535a4f426120ff426795c2","namespace":{"id":"root"},"path":"database/creds/lambda-function","remote_address":"3.81.200.22","remote_port":58258},"response":{"mount_type":"database","secret":{"lease_id":"database/creds/lambda-function/ygpRENQOeivgbsYdInv0kJob"},"data":{"password":"hmac-sha256:a3bf456c0ed398d712d78fdbc5732525999540d9b6e11caa916619262178a1d4","username":"hmac-sha256:0957fb3df371718a327630bfc5563b609bfe29523b3ddba775e8887deb02a287"}}}

If your use case can benefit from it, you can configure caching for the Vault Lambda Extension. The local proxy server handles requests so that it does not forward every request to the Vault server.

You can enable this caching by setting a VAULT_DEFAULT_CACHE_TTL variable in your lambda.tf configuration with a value used as a Go time duration.

For example, to specify a 5 minute TTL you can add VAULT_DEFAULT_CACHE_TTL = "5m" to your variables. Now when the extension runs and clients make GET requests using the header X-Vault-Cache-Control: cache, the proxy handles requests directly from the cache if there's a cache hit. On a cache miss the proxy forwards requests to Vault and the response returned and cached.

Setting VAULT_DEFAULT_CACHE_ENABLED to true, as in this example, caches all requests. If you do not set VAULT_DEFAULT_CACHE_ENABLEDyou must set the HTTP headerX-Vault-Cache-Control: cache on requests that you want to cache. Consult the Vault Lambda Extensiondocumentation for more detail.

To try caching with this tutorial, you can rename the file lambda-as_an_archive_caching.tf.disabled to lambda.tf.

$ cp lambda-as_an_archive_caching.tf.disabled lambda.tf

Display the contents of the lambda.tf file.

Successful output:

lambda.tf

resource "aws_lambda_function" "function" {
  function_name = "${var.environment_name}-function"
  description   = "Demo Vault AWS Lambda extension"
  role          = aws_iam_role.lambda.arn
  filename      = "./demo-function/demo-function.zip"
  handler       = "main"
  runtime       = "provided.al2"
  layers        = ["arn:aws:lambda:${var.aws_region}:634166935893:layer:vault-lambda-extension:13"]

  environment {
    variables = {
      VAULT_ADDR           = "http://${aws_instance.vault-server.public_ip}:8200",
      VAULT_AUTH_ROLE      = aws_iam_role.lambda.name,
      VAULT_AUTH_PROVIDER  = "aws",
      VAULT_SECRET_PATH_DB = "database/creds/lambda-function",
      VAULT_SECRET_FILE_DB = "/tmp/vault_secret.json",
      VAULT_DEFAULT_CACHE_ENABLED = true,
      VAULT_DEFAULT_CACHE_TTL = "5m",
      DATABASE_URL         = aws_db_instance.main.address
    }
  }
}

output "lambda" {
  value = <<EOF

The lambda function is ready to run.

    aws lambda invoke --function-name ${aws_lambda_function.function.function_name} /dev/null \
        --log-type Tail \
        --region ${var.aws_region} \
        | jq -r '.LogResult' \
        | base64 --decode

EOF
}

Notice the caching specific entries in this configuration, which set the variables on lines 17 and 18.

Run terraform apply and review the planned actions. Your terminal output indicates the resources Terraform will create.

Enter yes to confirm and resume.

The terraform apply command modifies the lambda function and the output displays how to invoke it.

lambda =
The lambda function is ready to run.

    aws lambda invoke --function-name vault-lambda-extension-demo-function /dev/null \
        --log-type Tail \
        --region us-east-1 \
        | jq -r '.LogResult' \
        | base64 --decode

Invoke the lambda function.

$ aws lambda invoke --function-name vault-lambda-extension-demo-function /dev/null \
        --log-type Tail \
        --region us-east-1 \
        | jq -r '.LogResult' \
        | base64 --decode

Now check the other terminal with audit device log for entries. You will note that there is another pair of request and responses logged.

{"time":"2022-03-21T16:10:12.559749467Z","type":"request","auth":{"token_type":"default"},"request":{"id":"1ddb4b61-ca47-b134-794e-39b0b4f81831","operation":"update","mount_type":"aws","namespace":{"id":"root"},"path":"auth/aws/login","data":{"iam_http_request_method":"hmac-sha256:05864ff6246edd8910fd4f6c4e04b75c7b1dcad3c0333940b87bf5fbf46f3bea","iam_request_body":"hmac-sha256:a6b996161178463d6e4cda2e9176c129c21bd3e8105a8053fba90fdbd1069117","iam_request_headers":"hmac-sha256:5bcfee0729c73dd2f8355e5d1e97cb628fb25304cdc9e228c5b5cfcae8f6b487","iam_request_url":"hmac-sha256:de80ceb22a3568566f33a623d7bcf03cc0933c5439e9ded75013ba07236f6451","role":"hmac-sha256:0796f22fa9fecbc94c1ab6c60c15e88d6eb61cbd552600a4a6373b5268cc8228"},"remote_address":"3.236.58.198","remote_port":33606}}
{"time":"2022-03-21T16:10:12.582299763Z","type":"response","auth":{"client_token":"hmac-sha256:bb2320516bb2fba6bf4b9115a62999183ad158ac54a8a5a13956ed89ec77cf2f","accessor":"hmac-sha256:7221d375c4c0a76ff60d076d89f97706e3a5ec8ecc34fc9a273c13d1753a89ad","display_name":"aws-vault-lambda-extension-brian-lambda-role","policies":["default","lambda-function"],"token_policies":["default","lambda-function"],"metadata":{"account_id":"561656980159","auth_type":"iam","role_id":"b395511b-5c8c-943e-2bcc-0c4add9755d4"},"entity_id":"3895cdbc-6a17-631b-4224-a8d7cccc5225","token_type":"service","token_ttl":300},"request":{"id":"1ddb4b61-ca47-b134-794e-39b0b4f81831","operation":"update","mount_type":"aws","namespace":{"id":"root"},"path":"auth/aws/login","data":{"iam_http_request_method":"hmac-sha256:05864ff6246edd8910fd4f6c4e04b75c7b1dcad3c0333940b87bf5fbf46f3bea","iam_request_body":"hmac-sha256:a6b996161178463d6e4cda2e9176c129c21bd3e8105a8053fba90fdbd1069117","iam_request_headers":"hmac-sha256:5bcfee0729c73dd2f8355e5d1e97cb628fb25304cdc9e228c5b5cfcae8f6b487","iam_request_url":"hmac-sha256:de80ceb22a3568566f33a623d7bcf03cc0933c5439e9ded75013ba07236f6451","role":"hmac-sha256:0796f22fa9fecbc94c1ab6c60c15e88d6eb61cbd552600a4a6373b5268cc8228"},"remote_address":"3.236.58.198","remote_port":33606},"response":{"auth":{"client_token":"hmac-sha256:bb2320516bb2fba6bf4b9115a62999183ad158ac54a8a5a13956ed89ec77cf2f","accessor":"hmac-sha256:7221d375c4c0a76ff60d076d89f97706e3a5ec8ecc34fc9a273c13d1753a89ad","display_name":"aws-vault-lambda-extension-brian-lambda-role","policies":["default","lambda-function"],"token_policies":["default","lambda-function"],"metadata":{"account_id":"561656980159","auth_type":"iam","role_id":"b395511b-5c8c-943e-2bcc-0c4add9755d4"},"entity_id":"3895cdbc-6a17-631b-4224-a8d7cccc5225","token_type":"service","token_ttl":300},"mount_type":"aws"}}
{"time":"2022-03-21T16:10:12.585414511Z","type":"request","auth":{"client_token":"hmac-sha256:bb2320516bb2fba6bf4b9115a62999183ad158ac54a8a5a13956ed89ec77cf2f","accessor":"hmac-sha256:7221d375c4c0a76ff60d076d89f97706e3a5ec8ecc34fc9a273c13d1753a89ad","display_name":"aws-vault-lambda-extension-brian-lambda-role","policies":["default","lambda-function"],"token_policies":["default","lambda-function"],"metadata":{"account_id":"561656980159","auth_type":"iam","role_id":"b395511b-5c8c-943e-2bcc-0c4add9755d4"},"entity_id":"3895cdbc-6a17-631b-4224-a8d7cccc5225","token_type":"service","token_ttl":300,"token_issue_time":"2022-03-21T16:10:12Z"},"request":{"id":"bc1a6869-c41f-f128-04d8-c44634521ff3","client_id":"3895cdbc-6a17-631b-4224-a8d7cccc5225","operation":"read","mount_type":"database","client_token":"hmac-sha256:9c46ca0cf4cfba2401142d2567acc446e08783439d8e5a8a50455a887cea0b45","client_token_accessor":"hmac-sha256:7221d375c4c0a76ff60d076d89f97706e3a5ec8ecc34fc9a273c13d1753a89ad","namespace":{"id":"root"},"path":"database/creds/lambda-function","remote_address":"3.236.58.198","remote_port":33606}}
{"time":"2022-03-21T16:10:12.593161077Z","type":"response","auth":{"client_token":"hmac-sha256:bb2320516bb2fba6bf4b9115a62999183ad158ac54a8a5a13956ed89ec77cf2f","accessor":"hmac-sha256:7221d375c4c0a76ff60d076d89f97706e3a5ec8ecc34fc9a273c13d1753a89ad","display_name":"aws-vault-lambda-extension-brian-lambda-role","policies":["default","lambda-function"],"token_policies":["default","lambda-function"],"metadata":{"account_id":"561656980159","auth_type":"iam","role_id":"b395511b-5c8c-943e-2bcc-0c4add9755d4"},"entity_id":"3895cdbc-6a17-631b-4224-a8d7cccc5225","token_type":"service","token_ttl":300,"token_issue_time":"2022-03-21T16:10:12Z"},"request":{"id":"bc1a6869-c41f-f128-04d8-c44634521ff3","operation":"read","mount_type":"database","client_token":"hmac-sha256:9c46ca0cf4cfba2401142d2567acc446e08783439d8e5a8a50455a887cea0b45","client_token_accessor":"hmac-sha256:7221d375c4c0a76ff60d076d89f97706e3a5ec8ecc34fc9a273c13d1753a89ad","namespace":{"id":"root"},"path":"database/creds/lambda-function","remote_address":"3.236.58.198","remote_port":33606},"response":{"mount_type":"database","secret":{"lease_id":"database/creds/lambda-function/n8hQDKq1y6H7XxksNMkkiCgw"},"data":{"password":"hmac-sha256:51d179715a37095c95b10373e02ced4ba6338414d5fb19d16a5455ac0d8f44e6","username":"hmac-sha256:f20d229069a6e82b6f3f087fe2e3ed584cf59f96150e2f15de2286cd0a1f7d1a"}}}

Invoke the lambda function once again.

$ aws lambda invoke --function-name vault-lambda-extension-demo-function /dev/null \
        --log-type Tail \
        --region us-east-1 \
        | jq -r '.LogResult' \
        | base64 --decode

Invoke the lambda function a final time.

$ aws lambda invoke --function-name vault-lambda-extension-demo-function /dev/null \
        --log-type Tail \
        --region us-east-1 \
        | jq -r '.LogResult' \
        | base64 --decode

If you check the other terminal with audit device log now, you'll note that there were no entries for the last two invocations of the Lambda.

This is because they used the cache, and did not need to authenticate with Vault.

You can learn more about the caching functionality in the Vault Lambda Extension documentation.

In the second terminal displaying the audit log, press CTRL+C to stop tailing the log and exit from the ssh session.

Return to the first terminal where you created the cluster and use Terraform to destroy the cluster.

Destroy the AWS resources provisioned by Terraform.

$ terraform destroy -auto-approve

Delete the state file.

You built, packaged, and deployed a Lambda function written in Go that uses the Vault Lambda Extension, and deployed it with Terraform.

Learn more by reading the blog post Use AWS Lambda Extensions to Securely Retrieve Secrets From HashiCorp Vault or by reviewing the Vault Lambda Extension code repository.

You deployed a Vault server and database with the provided Terraform configuration. Learn more about deploying infrastructure with Terraform Getting Started - AWS.

You used the AWS lambda to authenticate with Vault, request credentials for the PostgreSQL database, and perform a Query. Learn more aboutDynamic Secrets: Database Secrets Engine.

You also learned about the caching functionality available in the Vault Lambda Extension, and demonstrated it in action in the audit device logs.