Rust Lambda Extension for any Runtime to preload SSM Parameters as ๐Ÿ” Secure Environment Variables!

Overview

Actions Status

๐Ÿ›ก Crypteia

Rust Lambda Extension for any Runtime to preload SSM Parameters as Secure Environment Variables!

Super fast and only performaned once during your function's initialization, Crypteia turns your serverless YAML from this:

Environment:
  Variables:
    SECRET: x-crypteia-ssm:/myapp/SECRET

Into real runtime (no matter the lang) environment variables backed by SSM Parameter Store. For example, assuming the SSM Parameter path above returns 1A2B3C4D5E6F as the value. Your code would return:

process.env.SECRET; // 1A2B3C4D5E6F
ENV['SECRET'] # 1A2B3C4D5E6F

We do this using our shared object library via the LD_PRELOAD environment variable in coordination with our Lambda Extension binary file. See installation & usage sections for more details.

๐Ÿ’• Many thanks to the following projects & people for their work, code, and personal help that made Crypteia possible:

Installation

When building your own Lambda Containers, use both the crypteia binary and libcrypteia.so shared object files that match your platform. Target platform naming conventions include the following:

  • Amazon Linux 2: Uses the -amzn suffix.
  • Debian, Ubuntu, Etc: Uses the -debian suffix.

โš ๏ธ For now our project supports the x86_64 architecture, but we plan to release arm64 variants soon. Follow or contribute in our GitHub Issue which tracks this topic.

Lambda Containers

You have two options here. The easiest is to use Docker's multi stage builds with our Extension Containers to copy the /opt directory matching your platform and Crypteia version number. example below. Remember to use -debian vs -amzn if you are using your own Linux containers. Or change the version number depending on your needs.

FROM ghcr.io/customink/crypteia-extension-amzn:0.90.0 AS crypteia
FROM public.ecr.aws/lambda/nodejs:16
COPY --from=crypteia /opt /opt

Alternatively, you can download your platform's binary and shared object file from our Releases page and place them into your projects Docker build directory. Remember, to remove the platform file suffix. Example:

RUN mkdir -p /opt/lib
RUN mkdir -p /opt/extensions
COPY crypteia /opt/extensions/crypteia
COPY libcrypteia.so /opt/lib/libcrypteia.so
ENV LD_PRELOAD=/opt/lib/libcrypteia.so

โš ๏ธ When building your own Lambda Containers, please make sure glibc is installed since this is used by redhook.

Lambda Extension

Our Amazon Linux 2 files can be used within a Lambda Extension that you can deploy to your own AWS account as a Lambda Layer. You can use this project to build, publish, and deploy that layer since it has the SAM CLI installed. All you need to do is supply your own S3 bucket. For example:

aws configure
./amzn/setup
S3_BUCKET_NAME=my-bucket ./layer/deploy

Usage

First, you will need your secret environment variables setup in AWS Systems Manager Parameter Store. These can be whatever hierarchy you choose. Parameters can be any string type. However, we recommend using SecureString to ensure your secrets are encrypted within AWS. For example, let's assume the following paramter paths and values exists.

  • /myapp/SECRET -> 1A2B3C4D5E6F
  • /myapp/access-key -> G7H8I9J0K1L2
  • /myapp/envs/DB_URL -> mysql2://u:p@host:3306
  • /myapp/envs/NR_KEY -> z6y5x4w3v2u1

Crypteia supports two methods to fetch SSM parameters:

  1. x-crypteia-ssm: - Single path for a single environment variable.
  2. x-crypteia-ssm-path: - Path prefix to fetch many environment variables.

Using whatever serverless framework you prefer, setup your function's environment variables using either of the two SSM interfaces from above. For example, here is a environment variables section for an AWS SAM template that demonstrates all of Crypteia's features.

Environment:
  Variables:
    SECRET: x-crypteia-ssm:/myapp/SECRET
    ACCESS_KEY: x-crypteia-ssm:/myapp/access-key
    X_CRYPTEIA_SSM: x-crypteia-ssm-path:/myapp/envs
    DB_URL: x-crypteia
    NR_KEY: x-crypteia

When your function initializes, each of the four environmet variables (SECRET, ACCESS_KEY, DB_URL, and NR_KEY) will return values from their respective SSM paths.

process.env.SECRET;       // 1A2B3C4D5E6F
process.env.ACCESS_KEY;   // G7H8I9J0K1L2
process.env.DB_URL;       // mysql2://u:p@host:3306
process.env.NR_KEY;       // z6y5x4w3v2u1

Here are a few details about the internal implementation on how Crypteia works:

  1. When accessing a single parameter path via x-crypteia-ssm: the environment variable name available to your runtime is used as is. No part of the parameter path effects the resulting name.
  2. When using x-crypteia-ssm-path: the environment variable name can be anything and the value is left unchanged.
  3. The parameter path hierarchy passed with x-crypteia-ssm-path: must be one level deep and end with valid environment variable names. These names must match environement placeholders using x-crypteia values.

For security, the usage of DB_URL: x-crypteia placeholders ensures that your application's configuration is in full control on which dynamic values can be used with x-crypteia-ssm-path:.

Shown below is a simple Node.js 16 function which has the appropriate IAM Permissions and Crypteia extension via a Lambda Layer installed. Also configured are the needed LD_PRELOAD and SECRET environment variables. The code of this function log the value of the process.env.SECRET which does correctly resolve to the value within SSM Parameter Store.

Screenshot of the Environment variables in the AWS Lambda Console showing LD_PRELOAD to /opt/lib/libcrypteia.so and SECRET to x-crypteia-ssm:/myapp/SECRET.

Screenshot of Code source in the AWS Lambda Console showing the body results of 1A2B3C4D5E6F which is resolved from SSM Parameter Store.

IAM Permissions

Please use AWS' Restricting access to Systems Manager parameters using IAM policies guide for details on what policies your function's IAM Role will need. For an appliction to pull both single parameters as well as bulk paths, I have found the following policy helpful. It assumed the /myapp prefix and using AWS default KMS encryption key.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
        "ssm:GetParameter",
        "ssm:GetParametersByPath",
        "ssm:GetParameters",
        "ssm:GetParameterHistory",
        "ssm:DescribeParameters"
      ],
      "Resource": "arn:aws:ssm:us-east-1:123456789012:parameter/myapp*",
      "Effect": "Allow"
    },
    {
      "Action": "kms:Decrypt",
      "Resource": "*arn:aws:kms:us-east-1:123456789012:key/4914ec06-e888-4ea5-a371-5b88eEXAMPLE*",
      "Effect": "Allow"
    }
  ]
}

Development

This project is built for GitHub Codespcaes which may not be available to everyone. Thankfully you can use this same devcontainer.json specification automatically with VS Code Remote Development which allows you to clone this repo and open the folder in a container.

Our development container is based on the vscode-remote-try-rust demo project. For details on the VS Code Rust development containers, have a look here: https://github.com/microsoft/vscode-dev-containers/tree/main/containers/rust/history. Once you have the repo cloned or setup in a development container, run the following command. This will install and build your project.

./bin/setup

Optionally, you can setup/build the Amazon Linux 2 files. This will use Docker in Docker to download AWS SAM & Lambda images to build cryptia using what is present (like glibc) in those environments.

./amzn/setup

Running Tests

Requires an AWS account to populate test SSM Parameters. The AWS CLI is installed on the devcontainer. Set it up with your test credentials using:

aws configure

Once complete, you can run the tests using the following command. If you make changes to the code, make sure to run bin/setup again whick will run cargo build for you.

./bin/test

Again, if you are working on the Amazon Linux 2 project files, assuming you have already run ./amzn/setup, you can run the same tests above using that environment.

./amzn/test
Comments
  • Add Python to Codespaces & Test

    Add Python to Codespaces & Test

    Testing to make sure Python works as expected with libc. Seems it does not, this will fail on test/libcrypteia/override-python.sh for now and this PR will sort out why. cc @bekiya

    opened by metaskills 18
  • Update ENV Variables w/Values

    Update ENV Variables w/Values

    Thoughts on changing the way this project works by updated each matching ENV variable with the returned value. So in stead of this:

    echo $FOO_PARAM
    ssm_parameter:/my/parameter
    

    We would have this. And every runtime can simply use the environment vs constructing a fetch to localhost.

    echo $FOO_PARAM
    my-parameter
    

    A friend shared this project as an idea on how to tap into LD_PRELOAD to update environments before the "real" lambda function/application starts. Thoughts?

    https://github.com/mmanciop/opentelemetry-injector

    opened by metaskills 3
  • Running setup on a vanilla x86 arch Mac results in errors

    Running setup on a vanilla x86 arch Mac results in errors

    Running the setup command for the first time results in the following error:

    error[E0463]: can't find crate for `core`
      |
      = note: the `x86_64-unknown-linux-gnu` target may not be installed
      = help: consider downloading the target with `rustup target add x86_64-unknown-linux-gnu`
    
    error[E0463]: can't find crate for `compiler_builtins`
    
    For more information about this error, try `rustc --explain E0463`.
    error: could not compile `cfg-if` due to 2 previous errors
    warning: build failed, waiting for other jobs to finish...
    error[E0463]: can't find crate for `core`
        --> /Users/thomasstark/.cargo/registry/src/github.com-1ecc6299db9ec823/pin-project-lite-0.2.9/src/lib.rs:1503:9
    

    I'm assuming this can easily be resolved with a few additional package installs, but I'm leaving this here in case someone wants to smooth out the setup.

    opened by tdstark 2
  • Support Environments Updates & Bulk Fetches

    Support Environments Updates & Bulk Fetches

    Right now the proof of concept will work as long as an existing ENV is set. For example, if HELLO=yall is set then the shared object hook into getenv can override that with WORLD as a value. But if HELLO were not set at all, then the current code will not return the override. Why is this important?

    Let's say you want to batch fetch many parameters using ssm_parameters:/my/application/env as a path. You could have something like this in your template.yml file:

    Globals:
      Function:
        Environment:
          Variables:
            X_CRYPTEIA_ENVIRONMENTS: ssm_parameters:/my/application/env
    

    So this could pull in DATABASE_URL, SECRET_KEY_BASE, and many more. But would fail because there was no previous DATABASE_URL set. We could treat this as a feature and encourage placeholders like so, but that feels a bit superfluous?

    Globals:
      Function:
        Environment:
          Variables:
            DATABASE_URL: placeholder
            SECRET_KEY_BASE: placeholder
            X_CRYPTEIA_ENVIRONMENTS: ssm_parameters:/my/application/env
    
    opened by metaskills 2
  • Shrink extensions with scratch, add ltrace for dev, and format

    Shrink extensions with scratch, add ltrace for dev, and format

    This was mostly separated from #19 because some of it seemed unrelated. I can also fold this back into that branch, but thought it might make sense to make these changes separately.

    Here we use scratch to make the image for extensions as small as possible. The ltrace utility was added for ability to trace shared library calls and there are some minor formatting changes included as well.

    opened by mpeteuil 1
  • Embedded Metrics Logging

    Embedded Metrics Logging

    Fixes #23 and gives us easy metrics on how your team(s) are using Crypteia. Each file's logging is a dimension. So for example, it would be easy for an Infrastructure team to monitor SSM errors and alarm on them. When additional ENV backends like Secrets Manager #16 DynamoDB are added, they will have their own namespaces. Also, there is an All namespace too.

    opened by metaskills 1
  • Do Not Clobber /opt Directory

    Do Not Clobber /opt Directory

    Investigate if our installation guides will clobber an existing opt directory or merge things in. If it does clobber, account for this in a major version change and new guides.

    opened by metaskills 1
  • Lambda Extension Layer

    Lambda Extension Layer

    Expand on the https://github.com/customink/crypteia#development section with a new script and docs to show people how to publish a Lambda Layer to their own account.

    opened by metaskills 1
  • Version of glibc Needed

    Version of glibc Needed

    The redhook crate will output the following on a base NodeJS image.

    /lib64/libc.so.6: version `GLIBC_2.28' not found (required by /opt/lib/libcrypteia.so)
    

    This is what I see on ruby:2.7, nodejs:14, and nodes:16. This version was released on 2017-08-02 :(

    bash-4.2# find / -name "*glibc*"
    /usr/sbin/glibc_post_upgrade.x86_64
    /usr/share/licenses/glibc-2.26
    /usr/share/doc/glibc-common-2.26
    /usr/share/doc/glibc-2.26
    

    So I tried learning if I could find a package (https://gist.github.com/metaskills/14bf3ddb65f7cc1e6156ee1609e64cb3) or build glibc from source (https://www.gnu.org/software/libc/), which both were failures.

    opened by metaskills 1
  • Add Language Tests. Node, PHP, & Ruby.

    Add Language Tests. Node, PHP, & Ruby.

    This is meant to help #19 by adding language specific test patterns. Moved all the language specific test/libcrypteia/* files to have a node convention. Then I created PHP & Ruby test files and wired various things up all the way to the devcontainer which includes modern syntax for features. cc @mpeteuil

    opened by metaskills 0
  • Use GitHub Container as Extension Installation.

    Use GitHub Container as Extension Installation.

    The purpose of this pull request is to provide a simple interface to install the Lambda Extension for Lambda Containers. This post here (https://aws.amazon.com/blogs/compute/working-with-lambda-layers-and-extensions-in-container-images/) does a great job outlining the technique. The learnings here will influence an another open-source project to use Libvips with Ruby here (https://github.com/customink/ruby-vips-lambda).

    @brcarp I will be making some โ„น๏ธ comments on various parts of this and merging right away. Feel free to ask questions tho and see what eventually lands in main after the merge.

    FROM public.ecr.aws/myrepo/shared-lib-layer:1 AS shared-lib-layer
    WORKDIR /opt
    COPY --from=shared-lib-layer /opt/ .
    

    Post Merge TODO

    • Ensure extension package is public.
    • Test container extension usage and update readme.
    opened by metaskills 0
  • Bump tokio from 1.19.2 to 1.20.3

    Bump tokio from 1.19.2 to 1.20.3

    Bumps tokio from 1.19.2 to 1.20.3.

    Release notes

    Sourced from tokio's releases.

    Tokio v1.20.2

    1.20.2 (September 27, 2022)

    This release removes the dependency on the once_cell crate to restore the MSRV of the 1.20.x LTS release. (#5048)

    #5048: tokio-rs/tokio#5048

    Tokio v1.20.1

    1.20.1 (July 25, 2022)

    Fixed

    • chore: fix version detection in build script (#4860)

    #4860: tokio-rs/tokio#4860

    Tokio v1.20.0

    1.20.0 (July 12, 2022)

    Added

    Changed

    • time: remove src/time/driver/wheel/stack.rs (#4766)
    • rt: clean up arguments passed to basic scheduler (#4767)
    • net: be more specific about winapi features (#4764)
    • tokio: use const initialized thread locals where possible (#4677)
    • task: various small improvements to LocalKey (#4795)

    Fixed

    Documented

    • fs: warn about performance pitfall (#4762)
    • chore: fix spelling (#4769)
    • sync: document spurious failures in oneshot (#4777)
    • sync: add warning for watch in non-Send futures (#4741)
    • chore: fix typo (#4798)

    Unstable

    • joinset: rename join_one to join_next (#4755)
    • rt: unhandled panic config for current thread rt (#4770)

    #4677: tokio-rs/tokio#4677 #4741: tokio-rs/tokio#4741 #4755: tokio-rs/tokio#4755 #4758: tokio-rs/tokio#4758 #4762: tokio-rs/tokio#4762

    ... (truncated)

    Commits
    • ba81945 chore: prepare Tokio 1.20.3 release
    • 763bdc9 ci: run WASI tasks using latest Rust
    • 9f98535 Merge remote-tracking branch 'origin/tokio-1.18.x' into fix-named-pipes-1.20
    • 9241c3e chore: prepare Tokio v1.18.4 release
    • 699573d net: fix named pipes server configuration builder
    • 3d95a46 chore: prepare Tokio v1.20.2 (#5055)
    • 2063d66 Merge 'tokio-1.18.3' into 'tokio-1.20.x' (#5054)
    • 5c76d07 chore: prepare Tokio v1.18.3 (#5051)
    • 05e6614 chore: don't use once_cell for 1.18.x LTS release (#5048)
    • c0746b6 chore: prepare Tokio v1.20.1 (#4861)
    • Additional commits viewable in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies rust 
    opened by dependabot[bot] 0
  • No Lambda Runtime when Not Needed

    No Lambda Runtime when Not Needed

    Use this env var AWS_LAMBDA_RUNTIME_API to avoid to avoid running this https://github.com/customink/crypteia/blob/main/src/main.rs#L18 when not present. Thanks @tdstark

    opened by metaskills 0
  • Force AWS Region

    Force AWS Region

    Seems AWS is having a SSM issue in us-east-1 today. Perhaps we can have a CRYPTEIA_AWS_REGION config here https://github.com/customink/crypteia/blob/main/src/ssm.rs#L7 to force another region in case it is needed?

    opened by metaskills 0
  • Thoughts on Path Env/Value Contention

    Thoughts on Path Env/Value Contention

    Thanks to @cameronlynn for pointing this out. It is possible to use two paths where a value like SECRET would be in both.

    X_CRYPTEIA_SSM1: x-crypteia-ssm-path:/myapp/1/env
    X_CRYPTEIA_SSM2: x-crypteia-ssm-path:/myapp/2/env
    SECRET: x-crypteia
    

    In this case, there is no deterministic behavior on what the value of SECRET would be. Should we handle this? Document it? Etc?

    opened by metaskills 0
  • More Hooks for Bulk ENV

    More Hooks for Bulk ENV

    If you were to print out the full ENV object in Ruby it would not show the resolved values because we patch into getenv only which comes into play when you use ENV#[]. The same might be true in other languages.

    opened by metaskills 0
Releases(1.0.0)
Owner
Custom Ink
Custom Ink
The lambda-chaos-extension allows you to inject faults into Lambda functions without modifying the function code.

Chaos Extension - Seamless, Universal & Lightning-Fast The lambda-chaos-extension allows you to inject faults into Lambda functions without modifying

AWS CLI Tools 5 Aug 2, 2023
cargo-lambda a Cargo subcommand to help you work with AWS Lambda

cargo-lambda cargo-lambda is a Cargo subcommand to help you work with AWS Lambda. This subcommand compiles AWS Lambda functions natively and produces

David Calavera 184 Jan 5, 2023
cargo-lambda is a Cargo subcommand to help you work with AWS Lambda.

cargo-lambda cargo-lambda is a Cargo subcommand to help you work with AWS Lambda. The new subcommand creates a basic Rust package from a well defined

null 184 Jan 5, 2023
A Rust runtime for AWS Lambda

Rust Runtime for AWS Lambda This package makes it easy to run AWS Lambda Functions written in Rust. This workspace includes multiple crates: lambda-ru

Amazon Web Services - Labs 2.4k Dec 29, 2022
๐Ÿ“ฆ ๐Ÿš€ a smooth-talking smuggler of Rust HTTP functions into AWS lambda

lando ?? maintenance mode ahead ?? As of this announcement AWS not officialy supports Rust through this project. As mentioned below this projects goal

Doug Tangren 68 Dec 7, 2021
The classic game Pong, written in lambda calculus, and a thin layer of Rust.

What? The good old game Pong, written in lambda calculus, and a thin layer of Rust. Why? I was bored. No, seriously, why? Everyone keeps saying that l

null 2 Aug 14, 2022
Examples of how to use Rust with Serverless Framework, Lambda, API Gateway v1 and v2, SQS, GraphQL, etc

Rust Serverless Examples All examples live in their own directories: project: there is nothing here, just a simple cargo new project_name with a custo

Fernando Daciuk 9 Dec 17, 2022
Serverless setup for activity pub (using lambda+dynamodb) in Rust

Serverless ActivityPub About This is an experiment to have free/cheaper activitypub instances running on AWS (making use of free tiers as much as poss

Conrad Ludgate 3 Dec 30, 2022
A tool to run web applications on AWS Lambda without changing code.

AWS Lambda Adapter A tool to run web applications on AWS Lambda without changing code. How does it work? AWS Lambda Adapter supports AWS Lambda functi

AWS Samples 321 Jan 2, 2023
A high-performance Lambda authorizer for API Gateway that can validate OIDC tokens

oidc-authorizer A high-performance token-based API Gateway authorizer Lambda that can validate OIDC-issued JWT tokens. ?? Use case This project provid

Luciano Mammino 4 Oct 30, 2023
Rust crate that provides a convenient macro to quickly plot variables.

Debug Plotter This crate provides a convenient macro to quickly plot variables. Documentation For more information on how to use this crate, please ta

Fabian Bรถsiger 82 Dec 31, 2022
Fast, initializable, and thread safe static variables

tagged_cell Fast, initializable, and thread safe static variables Overview Borrows the excellent ZST based tagging implementation (linked below) to gu

David Schwarz 8 Nov 10, 2022
Tons of extension utility functions for Rust

LazyExt Tons of extension utility functions for Rust. English | ็ฎ€ไฝ“ไธญๆ–‡ Status Name Status Crate Documents Introduction lazyext-slice Alpha Thousands of

Al Liu 2 Dec 5, 2022
Stopwatch lib for rust. Start, pause, reset and lap like any stopwatch.

Chronometer Stopwatch lib for rust. Start, pause, reset and lap like any stopwatch. Nothing special I'm just learning rust. Getting Started Add this l

Naoufel Berrada 2 Sep 29, 2022
Annotation to easily define ad-hoc / one-shot extension traits

Annotation to easily define ad-hoc / one-shot extension traits

Daniel Henry-Mantilla 2 Apr 19, 2022
Searchbuddy is a browser extension that lets you chat with people that are searching for what you're searching for.

searchbuddy Make friends while searching! Searchbuddy is a browser extension that lets you chat with people that are searching for what you're searchi

Joseph Gerber 14 May 23, 2022
Extension registry for Lapce Registry

Lapce Registry This is the software running the lapce plugin registry, this manages and hosts plugins that the community uploads. Run the registry loc

Lapce 15 Dec 4, 2022
An experimental Athena extension for DuckDB ๐Ÿค

DuckDB Athena Extension WARNING This is a work in progress - things may or may not work as expected ??โ€โ™‚๏ธ Limitations Only the default database is sup

Damon P. Cortesi 34 Apr 3, 2023
Cargo extension to recycle outdated build artifacts

cargo gc Cargo extension to recycle outdated build artifacts. And try the best to avoid recompilation. Usage Install it with cargo: cargo install carg

Ruihang Xia 23 Aug 30, 2023