diff --git a/README.md b/README.md index 67c2f2fb..3c5d58d3 100644 --- a/README.md +++ b/README.md @@ -28,10 +28,11 @@ The same docker image can run on AWS Lambda, Amazon EC2, AWS Fargate, and local ### Docker Images -Add one line to your Dockerfile: +Copy the adapter into your Dockerfile and identify the OS-only runtime to AWS SDKs: ```dockerfile COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:1.1.0 /lambda-adapter /opt/extensions/lambda-adapter +ENV AWS_EXECUTION_ENV=AWS_Lambda_provided_custom ``` Pre-compiled multi-arch images (x86_64 and arm64) are available at [public.ecr.aws/awsguru/aws-lambda-adapter](https://gallery.ecr.aws/awsguru/aws-lambda-adapter). [Non-AWS base images](https://docs.aws.amazon.com/lambda/latest/dg/images-create.html) may be used since the [Runtime Interface Client](https://docs.aws.amazon.com/lambda/latest/dg/images-create.html#images-ric) ships with the Lambda Web Adapter. diff --git a/docs/guide/src/getting-started/docker-images.md b/docs/guide/src/getting-started/docker-images.md index da6f78d9..7786a1e1 100644 --- a/docs/guide/src/getting-started/docker-images.md +++ b/docs/guide/src/getting-started/docker-images.md @@ -1,11 +1,14 @@ # Docker Images -To use Lambda Web Adapter with Docker images, package your web app in a Dockerfile and add one line to copy the adapter binary: +To use Lambda Web Adapter with Docker images, package your web app in a Dockerfile, copy the adapter binary, and identify the OS-only runtime to AWS SDKs: ```dockerfile COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:1.1.0 /lambda-adapter /opt/extensions/lambda-adapter +ENV AWS_EXECUTION_ENV=AWS_Lambda_provided_custom ``` +`AWS_EXECUTION_ENV` allows AWS SDK user agents to identify requests from OS-only Lambda runtimes. It must be set in the application image because a multi-stage `COPY --from` transfers the adapter binary, not environment metadata from the adapter image. + [Non-AWS base images](https://docs.aws.amazon.com/lambda/latest/dg/images-create.html) can be used since the [Runtime Interface Client](https://docs.aws.amazon.com/lambda/latest/dg/images-create.html#images-ric) ships with the adapter. ## Example: Node.js Express App @@ -13,6 +16,7 @@ COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:1.1.0 /lambda-adapter /opt ```dockerfile FROM public.ecr.aws/docker/library/node:20-slim COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:1.1.0 /lambda-adapter /opt/extensions/lambda-adapter +ENV AWS_EXECUTION_ENV=AWS_Lambda_provided_custom ENV PORT=7000 WORKDIR "/var/task" ADD src/package.json /var/task/package.json diff --git a/docs/guide/src/getting-started/quick-start.md b/docs/guide/src/getting-started/quick-start.md index 3cc95b7e..c4b63d95 100644 --- a/docs/guide/src/getting-started/quick-start.md +++ b/docs/guide/src/getting-started/quick-start.md @@ -11,13 +11,14 @@ AWS Lambda Web Adapter works with Lambda functions packaged as both Docker image ## Minimal Example (Docker) -Add one line to your existing Dockerfile: +Copy the adapter into your existing Dockerfile and identify the OS-only runtime to AWS SDKs: ```dockerfile COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:1.1.0 /lambda-adapter /opt/extensions/lambda-adapter +ENV AWS_EXECUTION_ENV=AWS_Lambda_provided_custom ``` -That's it. Your web app now runs on Lambda. +Your web app can now run on Lambda, and AWS SDK user agents can identify Lambda as its execution environment. ## Minimal Example (Zip) diff --git a/docs/guide/src/getting-started/zip-packages.md b/docs/guide/src/getting-started/zip-packages.md index b99e57b9..e6b46120 100644 --- a/docs/guide/src/getting-started/zip-packages.md +++ b/docs/guide/src/getting-started/zip-packages.md @@ -32,6 +32,8 @@ AWS_LAMBDA_EXEC_WRAPPER=/opt/bootstrap Set your function handler to your web application's startup script, e.g. `run.sh`. +The layer bootstrap sets `AWS_EXECUTION_ENV` to `AWS_Lambda_provided_custom` when the variable is otherwise unset. This allows AWS SDK user agents to identify requests from OS-only Lambda runtimes. An explicitly configured value is preserved. + ## SAM Template Example ```yaml diff --git a/layer/bootstrap b/layer/bootstrap index 53c613c1..973b258f 100755 --- a/layer/bootstrap +++ b/layer/bootstrap @@ -1,3 +1,8 @@ #!/bin/bash -exec -- "${LAMBDA_TASK_ROOT}/${_HANDLER}" \ No newline at end of file +# OS-only runtimes do not define AWS_EXECUTION_ENV. Set the standard custom-runtime +# value so AWS SDK user agents can identify requests originating from Lambda, while +# preserving a value explicitly configured by the customer. +export AWS_EXECUTION_ENV="${AWS_EXECUTION_ENV:-AWS_Lambda_provided_custom}" + +exec -- "${LAMBDA_TASK_ROOT}/${_HANDLER}" diff --git a/tests/integ_tests/main.rs b/tests/integ_tests/main.rs index ba0f1c5d..a67c4864 100644 --- a/tests/integ_tests/main.rs +++ b/tests/integ_tests/main.rs @@ -1389,3 +1389,44 @@ async fn test_no_tenant_id_header_when_absent() { endpoint.assert(); assert_eq!(200, response.status()); } + +#[test] +fn test_layer_bootstrap_sets_execution_environment() { + use std::fs; + use std::os::unix::fs::PermissionsExt; + use std::process::Command; + use std::time::{SystemTime, UNIX_EPOCH}; + + let unique = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_nanos(); + let task_root = std::env::temp_dir().join(format!("lambda-web-adapter-bootstrap-{}-{unique}", std::process::id())); + fs::create_dir(&task_root).unwrap(); + + let handler = task_root.join("handler"); + fs::write(&handler, "#!/bin/sh\nprintf '%s' \"$AWS_EXECUTION_ENV\"\n").unwrap(); + fs::set_permissions(&handler, fs::Permissions::from_mode(0o755)).unwrap(); + + let bootstrap = concat!(env!("CARGO_MANIFEST_DIR"), "/layer/bootstrap"); + let run = |execution_env: Option<&str>| { + let mut command = Command::new(bootstrap); + command.env("LAMBDA_TASK_ROOT", &task_root).env("_HANDLER", "handler"); + match execution_env { + Some(value) => { + command.env("AWS_EXECUTION_ENV", value); + } + None => { + command.env_remove("AWS_EXECUTION_ENV"); + } + } + command.output().unwrap() + }; + + let defaulted = run(None); + assert!(defaulted.status.success()); + assert_eq!(defaulted.stdout, b"AWS_Lambda_provided_custom"); + + let configured = run(Some("customer-configured-value")); + assert!(configured.status.success()); + assert_eq!(configured.stdout, b"customer-configured-value"); + + fs::remove_dir_all(task_root).unwrap(); +}