Skip to content

feat(lambda): give function containers the AWS environment - #2562

Merged
vieiralucas merged 5 commits into
faiscadev:mainfrom
Sorttech:feat/lambda-container-aws-endpoint
Sep 30, 2026
Merged

vieiralucas merged 5 commits into
faiscadev:mainfrom
Sorttech:feat/lambda-container-aws-endpoint

Conversation

@Sorttech

@Sorttech Sorttech commented Sep 25, 2026 •

Copy link
Copy Markdown

Real Lambda injects region and execution-role credentials, and function
code relies on them: an SDK client built with no region or credentials
fails before it reaches an endpoint. fakecloud injected none of the
standard variables, only the function's own environment, so handler code
that called AWS did nothing useful -- CDK's BucketDeployment reported
success having copied no files.

Inject AWS_REGION, AWS_DEFAULT_REGION and credentials, plus
AWS_ENDPOINT_URL pointing at the backend's host alias and this server's
port. The endpoint is the one deliberate deviation from AWS: there the
variable is absent and the SDK's defaults are correct, whereas here the
container has to be pointed back at fakecloud or the handler reaches out
to the real internet. localhost would resolve to the container itself,
so the existing host alias is reused. Defaults are emitted before the
function's own environment, so a function that sets any of them wins.

The E2E asserts the environment the container actually observes. It stops
short of a full SDK round-trip from inside the container: that also needs
container-to-host reachability on an arbitrary host port, which does not
hold on every developer machine (WSL2 here), and would make the test flaky
for reasons unrelated to this change.

Test plan

Regression test: lambda_container_receives_the_aws_environment in crates/fakecloud-e2e/tests/lambda_aws_env.rs

Verification

On this exact head, rebased onto current main:

  • cargo fmt --all --check - clean.
  • cargo clippy --workspace --all-targets -- -D warnings - clean.
  • cargo test --workspace excluding fakecloud-e2e, fakecloud-conformance,
    fakecloud-tfacc and fakecloud-parity (the same set CI's test job runs),
    plus cargo test -p fakecloud-conformance --lib - 9931 passed, 0 failed.
  • cargo test -p fakecloud-e2e --test lambda_aws_env - the regression test
    above passes in a real Lambda container under Docker against a freshly
    built target/debug/fakecloud.

Found by deploying a CDK CloudFront + S3 SPA against fakecloud.


Summary by cubic

Injects the AWS environment real Lambda provides into function containers — region, reserved variables, and real execution-role session credentials — pointed back at fakecloud, so handler code that calls AWS works against the emulator instead of silently reaching the real internet.

  • Execution-role credentials are minted as real STS assumed-role sessions and revoked when an instance retires, when a launch fails or is cancelled, and on an IAM reset.
  • One environment is built for the docker and k8s backends; credentials travel as container env on docker and as a per-Pod Secret on k8s.
  • Reserved environment keys are now rejected, and execution-role trust checks run in the session's account on CreateFunction, UpdateFunctionConfiguration, and AWS::Lambda::Function; cross-account roles are refused under --iam strict and soft-logged otherwise.
  • Warm instances are keyed by qualified function ARN so configuration changes start a fresh instance; busy instances are never evicted, instance age is judged from the credentials' expiration, and only the k8s backend folds tags into the deploy fingerprint.
  • The k8s backend now needs create/patch/delete on Secrets (RBAC docs updated).

Written for commit 7a03a17. Summary will update on new commits.

Review in cubic

Real Lambda injects region and execution-role credentials, and function
code relies on them: an SDK client built with no region or credentials
fails before it reaches an endpoint. fakecloud injected none of the
standard variables, only the function's own environment, so handler code
that called AWS did nothing useful -- CDK's `BucketDeployment` reported
success having copied no files.

Inject `AWS_REGION`, `AWS_DEFAULT_REGION` and credentials, plus
`AWS_ENDPOINT_URL` pointing at the backend's host alias and this server's
port. The endpoint is the one deliberate deviation from AWS: there the
variable is absent and the SDK's defaults are correct, whereas here the
container has to be pointed back at fakecloud or the handler reaches out
to the real internet. `localhost` would resolve to the container itself,
so the existing host alias is reused. Defaults are emitted before the
function's own environment, so a function that sets any of them wins.

The E2E asserts the environment the container actually observes. It stops
short of a full SDK round-trip from inside the container: that also needs
container-to-host reachability on an arbitrary host port, which does not
hold on every developer machine (WSL2 here), and would make the test flaky
for reasons unrelated to this change.
- Give each function instance a real STS session for its execution role
  (assumed-role/<role>/<function>, minted like /_fakecloud/credentials and
  revoked on retire) instead of static keys that matched the root bypass.
- Build one environment for the docker and k8s backends: region from the
  function ARN, reserved Lambda variables that always win, AWS_ENDPOINT_URL
  pointed back at fakecloud. Credentials travel as docker -e names with
  values on the CLI's environment, and as a per-pod Secret on k8s.
- Reject reserved environment keys on CreateFunction,
  UpdateFunctionConfiguration and AWS::Lambda::Function as AWS does, and run
  the execution-role trust check on update and CloudFormation too; a
  cross-account role is refused under IAM enforcement.
- Key the warm pool by qualified function ARN and fold the launch config
  into deploy_id so config changes start a fresh instance; never evict a
  busy instance; an IAM reset retires credentialed instances.
- Unit tests for environment assembly, the k8s spec, the pool and issuer;
  e2e for the observed environment under --verify-sigv4, reserved keys, and
  the role checks in default and enforcement modes.
- Refuse a cross-account role only under --iam strict; soft logs it.
- Run the trust check in the account the session is minted in.
- Mark instances retiring synchronously on an IAM reset.
- Revoke a launch's credentials if the launch fails or is cancelled.
- Judge instance age from the credentials' expiration.
- Retire busy instances of a deleted version when they are released.
- Fold tags into the deploy fingerprint only for the k8s backend.
- Share one ARN account parser (fakecloud_aws::arn::account_of).
- Carry the InvalidParameterValueException code on the CloudFormation
  reserved-key failure.
@vieiralucas
vieiralucas merged commit 6ad4c97 into faiscadev:main Sep 30, 2026
159 checks passed
@vieiralucas

Copy link
Copy Markdown
Member

Thanks a lot for this, @Sorttech. Giving function containers the region and credentials real Lambda injects is what makes handler code like CDK's BucketDeployment actually reach AWS APIs locally, and pointing AWS_ENDPOINT_URL back at fakecloud through the host alias was the right call. I pushed a few follow-ups before merging: the static test/test keys matched fakecloud's root bypass, so each instance now gets a real STS session for its execution role (assumed-role//, revoked when the instance retires, with any launch that fails or is cancelled cleaned up); the k8s backend gets the same environment, with credentials in a per-pod Secret rather than inline, and Docker passes them via the CLI environment rather than argv; reserved Lambda variables always win over the function's own and are rejected on CreateFunction, UpdateFunctionConfiguration and AWS::Lambda::Function as AWS does; the region comes from the function ARN; the execution-role trust check now also runs on update and CloudFormation, and a cross-account role is refused under --iam strict; and the warm pool is keyed by qualified function ARN with the launch config in the deploy fingerprint, so config changes start a fresh instance without ever killing a busy one. Unit tests for each plus e2e coverage under --verify-sigv4 (the handler calls STS and sees its assumed role). Merged. Really appreciate the contribution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants