ci(sandbox): run the egressDeny guard weekly instead of never - #512
Open
ItamarZand88 wants to merge 1 commit into
Open
ci(sandbox): run the egressDeny guard weekly instead of never#512ItamarZand88 wants to merge 1 commit into
ItamarZand88 wants to merge 1 commit into
Conversation
Greptile SummaryThe PR adds a scheduled AWS guard for sandbox egress denial and a defensive cleanup path for the temporary MicroVM infrastructure.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains.
|
| Filename | Overview |
|---|---|
| .github/workflows/egress-deny-guard.yml | Adds the pinned, weekly AWS deployment, validation, and independent cleanup workflow. |
| crates/alien-aws-clients/src/aws/lambda_microvms.rs | Adds the live egress assertion and resource-reclamation checks, including polling asynchronous MicroVM termination before version deletion. |
| crates/alien-build/examples/sandbox-bundle.rs | Adds a small CLI wrapper around the shipped sandbox bundle writer. |
| scripts/egress-deny-guard-teardown.sh | Adds fail-closed teardown that reclaims image dependencies, deletes the stack, and verifies absence. |
| scripts/README.md | Documents the guard teardown script and its external AWS prerequisites. |
Sequence Diagram
sequenceDiagram
participant Schedule as Weekly schedule
participant Build as Build and render
participant AWS as AWS CloudFormation/MicroVMs
participant Guard as egressDeny test
participant Cleanup as Teardown job
Schedule->>Build: Build agent and image bundle
Build->>AWS: Deploy probe stack
AWS-->>Guard: Image and connector identifiers
Guard->>AWS: Start denied and open MicroVMs
Guard->>Guard: Compare internet reachability
Guard->>Cleanup: Run teardown
Cleanup->>AWS: Terminate MicroVMs
Cleanup->>AWS: Delete image versions and stack
Cleanup->>AWS: Verify image and stack are absent
Reviews (2): Last reviewed commit: "ci(sandbox): run the egressDeny guard we..." | Re-trigger Greptile
The one test that proves a sandbox declared egressDeny cannot reach the internet is `#[ignore]`d and named by no workflow, so it runs when someone remembers. A weekly job now builds the probe image, renders the stack with the shipped emitter, runs the check against a real account and removes everything. The test stays `#[ignore]`d and is named exactly by the workflow, with `--no-tests=fail`. Gating it on the probe environment instead would report a provisioning failure as a pass, which is the hole this closes. Teardown runs as a trailing step and again as a dependent job: a cancelled run gives `always()` only whatever window the runner allows, which does not reliably cover deleting a VPC.
ItamarZand88
force-pushed
the
itamar/alien-590-the-only-guard-on-egressdeny-never-runs
branch
from
August 27, 2026 21:43
d9b39c2 to
1a00ca1
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
We publish
egressDenyas an enforced property, and its only automated check is#[ignore]d andnamed by no workflow — so it runs when someone remembers. This adds a weekly job that builds the
probe image, deploys it, runs that check against real AWS, and removes everything afterwards.
Step-by-step flow when the schedule fires:
CloudFormation emitter — so the guard tests the template deployments actually get.
Both try to reach a public address. ← the check lives here
This PR changes a security property verified by hand into one verified on a schedule.
What was broken
a_denied_sandbox_cannot_reach_the_internet_and_an_open_one_canasserts the right thing and neverexecutes: it is
#[ignore]d, andPROBE_IMAGE_NAME/PROBE_CONNECTOR_ARNappear in no workflow.If AWS changed how a network connector governs egress, nothing would tell us and we would keep
reporting
egressDeny: true.What I did
down. Weekly rather than per-PR: it needs a real account and guards against an AWS-side change,
not a change in this repository.
#[ignore]d and named it exactly from the workflow, with--no-tests=fail. A gatethat skipped when the image was missing would report a provisioning failure as a pass, which is
the hole this exists to close; with the filter, a name that matches nothing fails the job.
egressDeny, so an on-call reader knows what regressed without openingthe test.
image is gone once its stack is. Both are best-effort per resource and report everything they
could not clear, so one stubborn MicroVM cannot abandon the rest.
always()only whatever window the runner allows, which does not reliably cover deleting a VPC.Files touched
.github/workflows/egress-deny-guard.yml— the weekly job and the cleanup job.crates/alien-aws-clients/src/aws/lambda_microvms.rs— assertion wording, plusreclaim_the_probe_imageandthe_probe_image_is_gone.crates/alien-build/examples/sandbox-bundle.rs— a CLI oversandbox_bundle::write_bundle, sothe bundle comes from the shipped packaging code rather than a Dockerfile pasted into YAML.
scripts/egress-deny-guard-teardown.sh— reclaim, delete, and verify the stack is gone.scripts/README.md— an entry for it, and the two preconditions that live outside this repo.The teardown is the part worth reviewing
The check itself is short. Nearly all the risk is in not leaking a VPC, and in never reporting a
clean teardown over something that survived. Three things shape it:
(
delete_images_versions_first), so deleting the stack anyway would reachDELETE_COMPLETEwhile MicroVMs kept billing — and every survival check keys on the stack existing, so nothing
would look again. The stack is the handle back to the image; it stays up, loudly, and the next
run retries.
describe-stacksfailing on expiredcredentials or a throttle exits 1 rather than reporting nothing to clean, matching how
cleanup-aws-e2e-resources.shdraws the same distinction.so it falls back to
list-stack-resources. Only a stack that genuinely declares no image mayleave that empty.
How I tested
AWS::Lambda::NetworkConnectorresolvesSecurityGroupIdstoProbeEgressSecurityGroup, whoseonly egress rule is
CidrIp: 127.0.0.1/32. The other0.0.0.0/0hits are two route-tabledefault routes and a separate workload security group the connector does not reference.
aws/cargoonPATHacross nine paths: absent stack,inconclusive
describe-stacks, happy path via outputs, empty outputs falling back tolist-stack-resources, reclaim failure, image-still-present failure, delete failure, outputscarrying no sandbox resource, and both queries returning
None. No path exits 0 with a resourcesurviving; none exits non-zero when everything is clean.
docker build --platform linux/arm64onamazonlinux:2023succeeded — uid 60000 exists,/sandboxisdrwx------, and/usr/bin/curl(which the probe invokes) is present.shellcheckclean,actionlintclean apart from thedepot-*runner label it does not know,cargo clippy -p alien-aws-clients --lib --testsandcargo fmt --checkclean.red rather than green:
the_probe_image_is_goneassumes a deleted image answers 404, and itsexpect_errsays to widen the check if AWS returns a terminal state instead; andpublic.ecr.aws/amazonlinux/amazonlinux:2023was validated against Docker Hub's image, sincepublic.ecr.aws was unreachable from here.
I also ran a security review on the diff. What it checked:
transformed, so GitHub's literal auto-mask covers it.
::add-mask::is registered before any step that can printit, including CloudFormation's
ValidationErrorand waiter-failure paths, and the cleanup jobsets
mask-aws-account-id.--no-capturestreamed a passing run's output live, anda transport error carries the full exec URL. Dropped the flag — nextest prints failing output
anyway.
unset-current-credentials, androle-duration-secondscovers the job budget rather thanexpiring inside it.
unresolved expression passes
"", soclient()now rejects empty as unset rather than signingwith it and surfacing a 403.
Nothing turned up.
Before this can run
An operator has to create the
egress-deny-guardenvironment, setEGRESS_GUARD_AWS_ROLE_ARN,EGRESS_GUARD_ARTIFACT_BUCKETandEGRESS_GUARD_AWS_REGION, and grant the role the MicroVMsactions including
lambda:ListMicrovms. The role needsMaxSessionDurationof at least 5400seconds. A lifecycle rule expiring the bucket's
egress-deny-guard/prefix is what reclaims abundle from a cancelled run — nothing in this diff can.