Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
260 commits
Select commit Hold shift + click to select a range
bbf7b53
test(spec): cover trailing-dot metadata hosts in allowlist validation
senamakel Oct 23, 2026
b745626
fix(spec): reject metadata hosts with trailing root dots
senamakel Oct 23, 2026
b56273a
merge: stack metadata IPv4 aliases on TinyBox #127
senamakel Oct 23, 2026
de10751
merge: stack metadata DNS and IPv4 alias fixes
senamakel Oct 23, 2026
1fd72be
merge: stack pinned egress planning on TinyBox #130
senamakel Oct 23, 2026
63eea80
feat(egress): add pinned egress allowlist planning
senamakel Oct 23, 2026
9f0dda8
docs(egress): document resolve error condition
senamakel Oct 23, 2026
4620053
fix(egress): preserve trailing dots when normalizing hostnames
senamakel Oct 23, 2026
51606b9
test(egress): cover ip literal pinning and resolver error paths
senamakel Oct 23, 2026
aa95b43
test(host): cover execution location reporting
senamakel Oct 23, 2026
0c53ae8
feat(runtime): add HostExecutionLocation capability to Host
senamakel Oct 23, 2026
d86fa6d
feat(local): report local execution location
senamakel Oct 23, 2026
6ef019d
feat(ssh): report remote execution location for ssh hosts
senamakel Oct 23, 2026
171879c
test(sandbox): cover egress controller prepare and cleanup ordering
senamakel Oct 23, 2026
62771b9
feat(docker): expose native linux egress controller
senamakel Oct 23, 2026
a259425
feat(docker): add pluggable egress controller to sandbox
senamakel Oct 23, 2026
261a654
refactor(sandbox): simplify container removal command construction
senamakel Oct 23, 2026
efeba54
feat(egress): add host validation hook to docker egress controller
senamakel Oct 23, 2026
fac41f0
feat(sandbox): route docker commands through the egress controller
senamakel Oct 23, 2026
72d08de
feat(sandbox): support allowlist network policy in docker backend
senamakel Oct 23, 2026
548863d
style: format egress enforcement and sandbox code
senamakel Oct 23, 2026
1b7ea68
fix(egress): return owned stdout from firewall output
senamakel Oct 23, 2026
7a460fc
fix(sandbox): roll back egress state when container creation fails
senamakel Oct 23, 2026
ad3750c
test(sandbox): cover native controller rejecting hosts without local …
senamakel Oct 23, 2026
bcce19b
test(sandbox): use std::io::Result in egress resolver stub
senamakel Oct 23, 2026
8acafa7
test(docker): add egress controller imports to live tests
senamakel Oct 23, 2026
40399c2
test(docker): cover native egress allowlist against raw tcp
senamakel Oct 23, 2026
8025f57
test(docker): cover egress rollback and host-gateway allowlist denial
senamakel Oct 23, 2026
67c9073
docs(specs): document docker allowlist enforcement
senamakel Oct 23, 2026
2692fff
test(sandbox): cover unknown and remote host locations
senamakel Oct 23, 2026
a08371e
test(sandbox): reformat imports and loop array
senamakel Oct 23, 2026
71fe942
test(tinybox-docker): unset proxy vars in live allowlist probes
senamakel Oct 23, 2026
7be9921
test(docker): run pinned allowlist proof in hosted live lane
senamakel Oct 23, 2026
c5b304f
test(egress): wire up egress enforcement test module
senamakel Oct 23, 2026
7e95600
fix(egress): verify docker forward hook is first in the chain
senamakel Oct 23, 2026
6172539
refactor(sandbox): extract create rollback and network policy helpers
senamakel Oct 23, 2026
01e03ea
refactor(sandbox): extract egress and failed-create helpers
senamakel Oct 23, 2026
8b38bda
test(tinybox-ssh): bind inner host in name test
senamakel Oct 23, 2026
360aa64
test: construct test ip addresses without parsing strings
senamakel Oct 23, 2026
6b0e24f
chore(deps): update tokio and pin windows-sys to 0.52.0
senamakel Oct 23, 2026
043b362
test(sandbox): cover destroy refusal for persisted allowlist without …
senamakel Oct 23, 2026
3af991c
fix(sandbox): reject destroy for allowlist boxes without egress contr…
senamakel Oct 23, 2026
d5b1cda
fix(limited): silence deprecation warning for async pipe registration
senamakel Oct 23, 2026
06e16f0
fix(egress): verify docker bridge link before installing firewall
senamakel Oct 23, 2026
5ce2df0
test(egress): compare allowlist protocols by slice
senamakel Oct 23, 2026
6e3226e
fix(egress): restrict allowlist protocols to tcp
senamakel Oct 23, 2026
b538f11
test: tighten host trait signatures and live docker test setup
senamakel Oct 23, 2026
c92d250
test(sandbox): annotate empty command vector type in assertion
senamakel Oct 23, 2026
5e4efd2
test(egress): cover native controller allowlist prepare and cleanup
senamakel Oct 23, 2026
9a91319
test(egress): assert prepare succeeds before unwrapping
senamakel Oct 23, 2026
9ec20d5
test(egress): match docker subcommands by position-independent search
senamakel Oct 23, 2026
1098304
fix(egress): route network inspect through rootful docker helper
senamakel Oct 23, 2026
2feec32
test(sandbox): cover controller host forwarding and egress rollback
senamakel Oct 23, 2026
f3025bc
test(sandbox): tidy host capability assertions and egress rollback check
senamakel Oct 23, 2026
bd643a4
test(egress): rename docker args binding in fake host
senamakel Oct 23, 2026
9cc4e45
feat(docker): pin egress hostnames via /etc/hosts
senamakel Oct 23, 2026
4097e7a
feat(tinybox-docker): expose prepared egress network from sandbox
senamakel Oct 23, 2026
ad65298
test(egress): cover pinned host mappings and dns blocking
senamakel Oct 23, 2026
83f0a58
docs(tinybox-docker): describe egress plan as consumed by the native …
senamakel Oct 23, 2026
a7fbda7
fix(sandbox): skip egress cleanup for non-allowlist boxes
senamakel Oct 23, 2026
6b08860
test(sandbox): cover allowlist run args pinning resolved addresses
senamakel Oct 23, 2026
fa5d1a4
fix(egress): skip firewall cleanup when network creation fails
senamakel Oct 23, 2026
276c2d7
refactor(sandbox): drop dns pinning and host mappings from docker args
senamakel Oct 23, 2026
5aa6143
fix(sandbox): pin allowlist network dns to loopback
senamakel Oct 23, 2026
af47a8d
docs(specs): clarify docker dns upstream rationale
senamakel Oct 23, 2026
2e00f4a
fix(egress): roll back network setup and allow IPv6 neighbour discovery
senamakel Oct 23, 2026
cf45605
test(docker): assert firewall rule ordering and published-port reacha…
senamakel Oct 23, 2026
e76e1eb
test(docker): cover ipv6 allowlist egress in live test
senamakel Oct 23, 2026
c6193aa
test(docker): fix live egress allowlist probe cleanup
senamakel Oct 23, 2026
735bcae
docs: document allowlist bridge-to-host exceptions
senamakel Oct 23, 2026
076be2d
refactor(tinybox-docker): extract pinned destination rules into helper
senamakel Oct 23, 2026
c5d229b
test(sandbox): assert rollback failure reports its own operation
senamakel Oct 23, 2026
4f79487
test(sandbox): cover failed create cleanup retaining the record
senamakel Oct 23, 2026
e78512e
test(sandbox): update insert_new call in cleanup retry test
senamakel Oct 23, 2026
505fdfb
test(sandbox): recover poisoned lock in failed create cleanup test
senamakel Oct 23, 2026
d1d3c9f
test(tinybox-docker): fix escaped quotes in live network inspect format
senamakel Oct 23, 2026
e310460
test(tinybox-docker): extract ipv6 inspect template helper
senamakel Oct 23, 2026
f48aeb9
test(tinybox-docker): read a single line from the published port
senamakel Oct 23, 2026
8d39638
test(tinybox-docker): drop unused mut in live docker test
senamakel Oct 23, 2026
9dd619d
test(live-docker): serve published port over http
senamakel Oct 23, 2026
11a2901
test(tinybox-docker): make stream binding mutable in live test
senamakel Oct 23, 2026
535cbd1
test(tinybox-docker): read published-port response incrementally
senamakel Oct 23, 2026
cbe0bc0
test(tinybox-docker): tolerate read timeouts while polling published …
senamakel Oct 23, 2026
5c86897
test(tinybox-docker): collapse match arm onto one line
senamakel Oct 23, 2026
27e867b
test(limited): cover local execution location reporting
senamakel Oct 23, 2026
f509d91
feat(limited): report execution location for limited local host
senamakel Oct 23, 2026
9d758b9
test(egress): cover ipv4-mapped ipv6 answers
senamakel Oct 23, 2026
c256479
fix(egress): normalize ipv4-mapped addresses in allowlist
senamakel Oct 23, 2026
87e7362
test(egress): cover ipv4-mapped answers in native controller
senamakel Oct 23, 2026
b697808
fix(egress): make ipv4 arm of normalize_ip_address exhaustive
senamakel Oct 23, 2026
94f4bc3
test(docker): assert established input rule precedes drop rules
senamakel Oct 23, 2026
6127f90
fix(egress): allow established return traffic to the host
senamakel Oct 23, 2026
c54c060
test(egress): extract input rule assertions into a helper
senamakel Oct 23, 2026
50d033d
docs: clarify egress enforcement comments and contract wording
senamakel Oct 23, 2026
704000d
test(tinybox-docker): retry reads on connection reset in live test
senamakel Oct 23, 2026
f75425b
test(tinybox-docker): start published-port httpd detached via docker …
senamakel Oct 23, 2026
0ffed93
test(tinybox-docker): fix stderr_lossy ownership in live test
senamakel Oct 23, 2026
e3c2f8c
test(live-docker): serve published port with nc loop
senamakel Oct 23, 2026
1eedbc9
test(tinybox-docker): simplify published-port probe payload
senamakel Oct 23, 2026
a292574
test(tinybox-docker): cover both listener probe ports in allowlist
senamakel Oct 23, 2026
40e98f7
test(tinybox-docker): cover additional allowlist probe port
senamakel Oct 23, 2026
42a115b
test(docker): cover cleanup refusing a foreign network owner
senamakel Oct 23, 2026
8445400
test(egress): cover network scope and chain owner tokens
senamakel Oct 23, 2026
510a171
fix(egress): scope firewall chain cleanup to the owning box
senamakel Oct 23, 2026
3c8a1a5
test(docker): track deleted iptables chains in egress test host
senamakel Oct 23, 2026
b75f26c
test(egress): track created firewall chains in prepared host
senamakel Oct 23, 2026
d46f959
fix(tinybox-docker): scope owner tokens and tighten egress deny rules
senamakel Oct 23, 2026
8ce6b3c
test(tinybox-docker): track firewall chains per iptables variant
senamakel Oct 23, 2026
6fc2345
test(egress): import deny_rules in enforcement tests
senamakel Oct 23, 2026
973d525
test(egress): simplify destination assertions in metadata deny test
senamakel Oct 23, 2026
cd2dfd5
test(egress): cover scope token sha256 identity
senamakel Oct 23, 2026
e3e777a
style: fix indentation and import ordering
senamakel Oct 23, 2026
c613673
refactor(egress): build scope tokens and fake output without intermed…
senamakel Oct 23, 2026
4c0db33
refactor(egress): replace fmt::Write hex formatting with direct push
senamakel Oct 23, 2026
96cd674
refactor(egress): hoist hex table to module scope
senamakel Oct 23, 2026
8fe4d5c
test(tinybox-docker): cover busybox nc ipv6 flag handling
senamakel Oct 23, 2026
69b5a7d
test(live-docker): drop ipv6 flag from listener probe
senamakel Oct 23, 2026
9a1af48
test(egress): cover chain marker install failure rollback
senamakel Oct 23, 2026
46b0dd1
test(egress): fix chain owner token comparison in native host
senamakel Oct 23, 2026
2f39145
fix(egress): track chains created during a failed setup attempt
senamakel Oct 23, 2026
4381551
test(docker): track owner-marked chains in egress enforcement fake
senamakel Oct 23, 2026
14f9e9a
test(egress): clone firewall when removing chain in test host
senamakel Oct 23, 2026
8c9ab9d
fix(egress): use cleanup_native on setup failure
senamakel Oct 23, 2026
2b5bc49
fix(egress): clean up only chains owned by the failed attempt
senamakel Oct 23, 2026
11c48e2
test(egress): gate native controller tests to linux
senamakel Oct 23, 2026
c6f01d0
chore(egress): allow too_many_lines on install_family
senamakel Oct 23, 2026
67eb415
test(egress): gate linux-only helpers behind cfg
senamakel Oct 23, 2026
51f20dc
fix(docker): reject IPv6 link-local egress targets and reconcile ambi…
senamakel Oct 23, 2026
bbc20ec
test(egress): cover ambiguous network create with absent or foreign n…
senamakel Oct 23, 2026
95dcde9
test(tinybox-docker): model network create outcomes with an enum
senamakel Oct 23, 2026
1e317f5
test(egress): reformat network create failure check
senamakel Oct 23, 2026
da175d8
test(egress): cover network inspect outcomes in rollback tests
senamakel Oct 23, 2026
0995ddc
fix(egress): reconcile failed docker network creation
senamakel Oct 23, 2026
800c922
fix(egress): report docker stderr when network inspection fails
senamakel Oct 23, 2026
ded8757
fix(egress): require the final drop rule in each chain
senamakel Oct 23, 2026
42161c9
test(sandbox): cover docker argv rewriting for non-idempotent control…
senamakel Oct 23, 2026
eab6f87
refactor(sandbox): drop unused docker argv rewriting
senamakel Oct 23, 2026
5473691
test(egress): cover pinned targets inside auto-selected subnets
senamakel Oct 23, 2026
2407dd9
fix(egress): reject allowlist targets inside the docker network subnet
senamakel Oct 23, 2026
4df125e
test(egress): tighten assertion for iptables rule commands
senamakel Oct 23, 2026
281cc29
test(tinybox-docker): silence dead code warnings on non-linux
senamakel Oct 23, 2026
48382a6
test(sandbox): use assert_ne for docker command count
senamakel Oct 23, 2026
7706604
docs(egress): document ipam subnet refusal for pinned destinations
senamakel Oct 23, 2026
aa97407
test(egress): cover owner marker restore when chain delete fails
senamakel Oct 23, 2026
c30bcce
test(egress): guard fail_chain_delete_once with a mutex
senamakel Oct 23, 2026
27fea97
fix(egress): restore ownership marker when chain deletion fails
senamakel Oct 23, 2026
877357c
test(egress): fix host mutability in enforcement tests
senamakel Oct 23, 2026
3d88625
style(tinybox-docker): reformat egress enforcement code
senamakel Oct 23, 2026
fb45030
test(egress): gate chain delete retry test to linux
senamakel Oct 23, 2026
fed0596
chore: add rustc ice reports
senamakel Oct 23, 2026
7a33ef7
chore: remove rustc ice dump files
senamakel Oct 23, 2026
1035a75
chore: add rustc ice report
senamakel Oct 23, 2026
234e47b
chore: remove stray rustc ICE dump file
senamakel Oct 23, 2026
8b01db2
test(limited): cover jailed local host execution location
senamakel Oct 23, 2026
68ce742
feat(host): report local execution location for jailed host
senamakel Oct 23, 2026
df370e8
test(egress): assert same-owner network conflict is not removed
senamakel Oct 23, 2026
74f7388
fix(egress): return original error on network create failure
senamakel Oct 23, 2026
302ab4a
test(egress): capture iptables-restore scripts in prepared host
senamakel Oct 23, 2026
1ec263e
test(egress): cover atomic chain creation and restart-safe cleanup
senamakel Oct 23, 2026
8de0a4e
fix(egress): create firewall chains atomically with owner marker
senamakel Oct 23, 2026
d138253
fix(egress): append owner marker without flushing the chain
senamakel Oct 23, 2026
88a2343
test(egress): assert per-chain restore scripts and binaries
senamakel Oct 23, 2026
dbcccc6
test(egress): reformat restore program assertion
senamakel Oct 23, 2026
d435d30
test(egress): assert no inspect runs on network name conflict
senamakel Oct 23, 2026
a7d4238
test(egress): reformat inspect assertion to match rustfmt
senamakel Oct 23, 2026
d517027
refactor(egress): key firewall chains by per-attempt token
senamakel Oct 23, 2026
d9fa023
fix(egress): label egress networks with the attempt token
senamakel Oct 23, 2026
f82ebf6
fix(egress): include attempt label in network inspect format
senamakel Oct 23, 2026
c15a250
fix(docker): verify egress network ownership before cleanup
senamakel Oct 23, 2026
1efe331
refactor(egress): remove redundant network existence check
senamakel Oct 23, 2026
087c771
test(egress): split restart cleanup coverage into empty and nonempty …
senamakel Oct 23, 2026
2df862a
fix(egress): match attempt label when reconciling ambiguous network c…
senamakel Oct 23, 2026
39fec21
test(egress): cover ambiguous create preserving older attempt network
senamakel Oct 23, 2026
87febd7
test(egress): inline attempt label and mark prepared hosts mutable
senamakel Oct 23, 2026
68d7897
test(egress): fix mutability of prepared host bindings
senamakel Oct 23, 2026
d19c9fd
test(egress): fix mutability of prepared host bindings in tests
senamakel Oct 23, 2026
484de6d
docs(egress_enforcement): describe per-attempt chain ownership
senamakel Oct 23, 2026
a87f185
test(docker): track chains with rules in egress enforcement fake host
senamakel Oct 23, 2026
813a341
test(egress): reuse resolved firewall binary in chain bookkeeping
senamakel Oct 23, 2026
dc13f6a
test(egress): reuse firewall value when clearing chain state
senamakel Oct 23, 2026
9e089c6
style(tinybox-docker): reformat egress enforcement code
senamakel Oct 23, 2026
ab87d25
test(egress): remove duplicate firewall lookup in prepared host
senamakel Oct 23, 2026
df474af
test(egress): mark chains as owned in cleanup ownership test
senamakel Oct 23, 2026
fd6b891
test(egress): rename marker failure test to describe rollback
senamakel Oct 23, 2026
f60d878
test(egress): cover cleanup when no orphaned chains exist
senamakel Oct 23, 2026
c51b031
test(egress): relax iptables chain count assertion
senamakel Oct 23, 2026
e8023a0
test(egress): cover rollback failure when owned network removal fails
senamakel Oct 23, 2026
a2566e9
test(egress): inject one-shot network removal failure
senamakel Oct 23, 2026
ff5bbd6
test(egress): report prepare result and commands on rollback failure
senamakel Oct 23, 2026
a7daa81
test(egress): match simulated lost response in rollback assertion
senamakel Oct 23, 2026
aece4b6
test(egress): gate network removal assertion to linux
senamakel Oct 23, 2026
3bd7f10
test(egress): fix indentation of linux cfg attribute
senamakel Oct 23, 2026
1923fef
test(egress): gate no_matching_hooks field to linux
senamakel Oct 23, 2026
b9a5a70
Merge pull request #132 from tinyhumansai/tinybox-27-linux-egress
senamakel Oct 23, 2026
b497130
Merge pull request #131 from tinyhumansai/tinybox-27-pinned-egress-plan
senamakel Oct 23, 2026
e7bc505
Merge pull request #130 from tinyhumansai/tinybox-27-metadata-ip-alias
senamakel Oct 23, 2026
d298ab6
Merge TinyBox main into Linux egress stack
senamakel Oct 23, 2026
237aa68
test(egress): cover cleanup ignoring another box's chain
senamakel Oct 23, 2026
cdea2e5
test(egress): import stable_hash in enforcement tests
senamakel Oct 23, 2026
2b5190a
fix(egress): widen chain identity tokens to avoid orphan misattribution
senamakel Oct 23, 2026
420069f
test(egress): assert forward chain length in cleanup test
senamakel Oct 23, 2026
e5d4cdb
test(docker): cover neighbor-advertisement ordering and retry after e…
senamakel Oct 23, 2026
9766ebe
test(tinybox-docker): make live retry test resilient to accept timing
senamakel Oct 23, 2026
afb7dd8
test(tinybox-docker): read and half-close the first probe before drop…
senamakel Oct 23, 2026
baa3328
fix(egress): allow ipv6 neighbor advertisements
senamakel Oct 23, 2026
da8bee6
docs(egress_enforcement): document sudo and IPv6 neighbor discovery r…
senamakel Oct 23, 2026
00ea240
test(tinybox-docker): cover stale neighbor revalidation in live allow…
senamakel Oct 23, 2026
d8a97c9
test(tinybox-docker): fix escaped newline in live docker assertion
senamakel Oct 23, 2026
c2e8634
fix(egress): install ipv6 nd rules per icmpv6 type
senamakel Oct 23, 2026
d8dd92e
test(tinybox-docker): propagate errors in response retry test
senamakel Oct 23, 2026
f1722e4
docs(egress_enforcement): clarify setup requirements wording
senamakel Oct 23, 2026
79a6ef7
chore: add pr 127 body notes
senamakel Oct 23, 2026
c6cc485
chore: remove pr127 body file
senamakel Oct 23, 2026
92848fc
test(sandbox): use controller-specific rollback operation in egress test
senamakel Oct 23, 2026
d6d635a
refactor(error): add dedicated egress rollback failure variant
senamakel Oct 23, 2026
9564ea4
test(sandbox): match egress rollback failures on the typed error
senamakel Oct 23, 2026
fe7312d
test(sandbox): cover preservation of requested docker variables
senamakel Oct 23, 2026
2306667
fix(sandbox): inline docker env vars into workload run argv
senamakel Oct 23, 2026
8a047b1
test(egress): match rollback failures against EgressRollbackFailed
senamakel Oct 23, 2026
f7211a5
refactor(sandbox): stop rewriting workload docker env vars
senamakel Oct 23, 2026
8968f79
style: format egress enforcement code and document docker host handling
senamakel Oct 23, 2026
dcbc3a5
chore: add pr 127 body notes
senamakel Oct 23, 2026
0ec9359
docs: update pr 127 body with rollback and coverage details
senamakel Oct 23, 2026
099073d
chore: remove stale pr body file
senamakel Oct 23, 2026
9b435a4
test(egress): cover idempotence of local docker daemon pinning
senamakel Oct 23, 2026
482ae83
fix(egress): skip forcing docker host when already set
senamakel Oct 23, 2026
474a01f
chore: add pr 127 body notes
senamakel Oct 23, 2026
2924de8
chore: remove stale pr body file
senamakel Oct 23, 2026
e759902
test(docker): cover daemon pinning against workload host args
senamakel Oct 23, 2026
9ae0d94
fix(docker): pin local daemon when docker host option is present
senamakel Oct 23, 2026
ba2bc04
fix(tinybox-docker): pass workload Docker credentials via env file
senamakel Oct 23, 2026
4ee20a9
fix(sandbox): rename env_file field to _env_file
senamakel Oct 23, 2026
05ae13e
fix(sandbox): only write docker env file for docker commands
senamakel Oct 23, 2026
7c87097
refactor(sandbox): simplify docker env file flag insertion
senamakel Oct 23, 2026
6099e96
test(sandbox): allow long test for docker variable assertions
senamakel Oct 23, 2026
2044a23
feat(docker): gate docker host rewriting on local execution
senamakel Oct 23, 2026
4aa549c
test(sandbox): add local host constructor to scripted test host
senamakel Oct 23, 2026
9d0b39a
test(sandbox): cover nonlocal docker passthrough and env file newline…
senamakel Oct 23, 2026
943e4ff
test(sandbox): drop assertion on removed env file field
senamakel Oct 23, 2026
ff3d6b7
refactor(egress): drop redundant tls flags from forced docker argv
senamakel Oct 23, 2026
303bb3b
test(sandbox): assert workload env file permissions
senamakel Oct 23, 2026
06db5e4
test(sandbox): cover env file rewrite preserving exec arguments
senamakel Oct 23, 2026
e2327ff
fix(docker): require -- separator for run workload env files
senamakel Oct 23, 2026
4b931f6
test(egress): cover network create failure after side effect
senamakel Oct 23, 2026
dc79eee
fix(egress): reconcile failed network create on non-zero exit
senamakel Oct 23, 2026
be5efc7
test(sandbox): cover remote docker tls env without local env file
senamakel Oct 23, 2026
fa3bdd4
fix(sandbox): skip local env file for remote docker hosts
senamakel Oct 23, 2026
9b9bedb
test(sandbox): cover tls env preservation with a custom controller
senamakel Oct 23, 2026
90a9e3d
refactor(sandbox): gate env file on local docker host requirement
senamakel Oct 23, 2026
8e9043e
docs(egress_enforcement): clarify docker socket and sudo requirements
senamakel Oct 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
*.profraw
*.profdata
lcov.info
rustc-ice-*.txt

# Local secrets — never commit; copy .env.example to .env
.env
Expand Down
14 changes: 8 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions crates/tinybox-core/src/error/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,16 @@ pub enum Error {
message: String,
},

/// A Docker egress controller could not undo resources created for an
/// allowlist network, so the sandbox must retain the box record for retry.
#[error("{sandbox} could not roll back egress network setup: {message}")]
EgressRollbackFailed {
/// The sandbox whose egress resources could not be removed.
sandbox: String,
/// The setup and cleanup failures that require a later retry.
message: String,
},

/// Reading or writing the box store failed, or its contents were not
/// valid.
#[error("box store {operation} failed: {message}")]
Expand Down
2 changes: 1 addition & 1 deletion crates/tinybox-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ pub use identity::{BoxId, HostRef, ProcessId, SandboxRef, SnapshotId, TemplateNa
pub use passthrough::PassthroughSandbox;
pub use runtime::{
BoxInfo, BoxState, ExecOutput, ExecRequest, ExecutionObserver, Forward, ForwardGuard, Host,
OutputStream, Sandbox, WorkspaceFileReader, WorkspaceFileWriter,
HostExecutionLocation, OutputStream, Sandbox, WorkspaceFileReader, WorkspaceFileWriter,
};
pub use spec::{
BoxSpec, EgressAllowlist, Lifecycle, NetworkPolicy, Placement, PortMapping, Resources,
Expand Down
24 changes: 24 additions & 0 deletions crates/tinybox-core/src/runtime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,21 @@ mod types;
pub use forward::{Forward, ForwardGuard};
pub use types::{BoxInfo, BoxState, ExecOutput, ExecRequest};

/// Where a host executes commands relative to the tinybox process.
///
/// Security-sensitive controllers use this explicit capability instead of
/// guessing from a host's registration name. Custom hosts default to
/// [`Unknown`](Self::Unknown) until they can state their location.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HostExecutionLocation {
/// Commands execute in this process's operating-system environment.
Local,
/// Commands execute on another machine.
Remote,
/// The host cannot make a trustworthy locality claim.
Unknown,
}

/// A readable file opened beneath a host- or sandbox-mounted workspace.
#[async_trait]
pub trait WorkspaceFileReader: std::fmt::Debug + Send + Sync {
Expand Down Expand Up @@ -90,6 +105,15 @@ pub trait Host: std::fmt::Debug + Send + Sync + 'static {
/// [`HostRef`](crate::identity::HostRef).
fn name(&self) -> &str;

/// The machine on which commands run.
///
/// This defaults to [`HostExecutionLocation::Unknown`]; a controller that
/// requires local machine access must refuse hosts that do not explicitly
/// attest to it.
fn execution_location(&self) -> HostExecutionLocation {
HostExecutionLocation::Unknown
}

/// Run a command to completion and collect its output.
///
/// # Errors
Expand Down
50 changes: 50 additions & 0 deletions crates/tinybox-core/src/spec/mod_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,8 +228,15 @@ fn allowlist_validation_rejects_malformed_and_metadata_hosts() {
);
for host in [
"169.254.169.254",
"0251.0376.0251.0376",
"0xa9fea9fe",
"0xa9feaa02",
"0x646464c8",
"[fd00:ec2::254]",
"Metadata.Google.Internal",
"metadata.google.internal.",
"Metadata.Google.Internal.",
"metadata.",
] {
assert!(
EgressAllowlist::new([host], [80]).validate().is_err(),
Expand All @@ -238,6 +245,49 @@ fn allowlist_validation_rejects_malformed_and_metadata_hosts() {
}
}

#[test]
fn allowlist_validation_rejects_loopback_targets() {
use super::EgressAllowlist;

for host in [
"127.0.0.1",
"[::1]",
"[::ffff:127.0.0.1]",
"localhost",
"LOCALHOST.",
"service.localhost",
] {
assert!(
EgressAllowlist::new([host], [80]).validate().is_err(),
"{host} must be refused"
);
}
}

#[test]
fn allowlist_validation_rejects_legacy_numeric_loopback_targets() {
use super::EgressAllowlist;

for host in ["127.1", "2130706433", "0x7f000001", "0177.0.0.1"] {
assert!(
EgressAllowlist::new([host], [80]).validate().is_err(),
"{host} must be refused"
);
}
}

#[test]
fn allowlist_validation_rejects_scoped_ipv6_loopback_targets() {
use super::EgressAllowlist;

for host in ["::1%1", "[::1%1]"] {
assert!(
EgressAllowlist::new([host], [80]).validate().is_err(),
"{host} must be refused"
);
}
}

#[test]
fn allowlist_policy_allows_egress_but_open_is_highest_risk() {
use super::EgressAllowlist;
Expand Down
129 changes: 124 additions & 5 deletions crates/tinybox-core/src/spec/types.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! The parts a [`BoxSpec`](crate::spec::BoxSpec) is assembled from.

use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use std::path::PathBuf;
use std::time::Duration;

Expand Down Expand Up @@ -252,8 +253,16 @@ pub enum NetworkPolicy {
/// `Unsupported`, never `Enforced`.
///
/// The cloud metadata endpoints in [`EgressAllowlist::is_metadata_host`] are
/// refused at validation time and must also be blocked by the backend even
/// when a hostname resolves to one of them.
/// refused at validation time, including legacy numeric IPv4 spellings such as
/// `0251.0376.0251.0376` and `0xa9fea9fe` for `169.254.169.254`. They must also
/// be blocked by the backend even when a hostname resolves to one of them.
///
/// Explicit loopback addresses and aliases are also invalid. This includes
/// IPv4/IPv6 loopback literals, IPv4-mapped loopback literals, scoped IPv6
/// literals such as `::1%1`, `localhost` names, and legacy numeric IPv4
/// spellings accepted by common resolvers (for example `127.1`, `2130706433`,
/// `0x7f000001`, and `0177.0.0.1`). These are checked as explicit allowlist
/// entries; enforcing backends must also reject resolved loopback addresses.
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
pub struct EgressAllowlist {
/// Host names or IP literals the box may connect to.
Expand All @@ -276,10 +285,15 @@ impl EgressAllowlist {
}

/// Whether `host` names a cloud instance-metadata endpoint, which no
/// policy may ever allow.
/// policy may ever allow. Matching ignores DNS case and terminal root dots,
/// and recognizes legacy numeric IPv4 spellings of metadata addresses.
#[must_use]
pub fn is_metadata_host(host: &str) -> bool {
let host = host.trim().trim_matches(['[', ']']).to_ascii_lowercase();
let host = host
.trim()
.trim_end_matches('.')
.trim_matches(['[', ']'])
.to_ascii_lowercase();
matches!(
host.as_str(),
"169.254.169.254"
Expand All @@ -289,9 +303,109 @@ impl EgressAllowlist {
| "metadata.google.internal"
| "metadata"
) || host.starts_with("::ffff:169.254.169.254")
|| Self::parse_legacy_ipv4(&host).is_some_and(|address| {
address == Ipv4Addr::new(169, 254, 169, 254)
|| address == Ipv4Addr::new(169, 254, 170, 2)
|| address == Ipv4Addr::new(100, 100, 100, 200)
})
}

/// Whether an allowlist entry explicitly names a loopback address or alias.
fn is_loopback_host(host: &str) -> bool {
let host = host
.trim()
.trim_matches(['[', ']'])
.trim_end_matches('.')
.to_ascii_lowercase();
if host == "localhost" || host.ends_with(".localhost") {
return true;
}

match host.parse::<IpAddr>() {
Comment thread
senamakel marked this conversation as resolved.
Ok(IpAddr::V4(address)) => address.is_loopback(),
Ok(IpAddr::V6(address)) => Self::is_loopback_ipv6(address),
Err(_) => {
Self::parse_scoped_ipv6(&host).is_some_and(Self::is_loopback_ipv6)
|| Self::parse_legacy_ipv4(&host).is_some_and(|address| address.is_loopback())
}
}
}

/// Check the list is non-empty, well formed and names no metadata endpoint.
fn is_loopback_ipv6(address: Ipv6Addr) -> bool {
address.is_loopback()
|| address
.to_ipv4_mapped()
.is_some_and(|mapped| mapped.is_loopback())
}

/// Parse an IPv6 address with a non-empty zone identifier, if present.
fn parse_scoped_ipv6(host: &str) -> Option<Ipv6Addr> {
let (address, scope_id) = host.split_once('%')?;
if scope_id.is_empty() || scope_id.contains('%') {
return None;
}
address.parse().ok()
}

/// Parse the legacy numeric IPv4 formats accepted by common resolvers.
///
/// These formats are still relevant to validation because a backend may
/// resolve the input through a system resolver that treats them as IP
/// addresses rather than DNS names.
fn parse_legacy_ipv4(host: &str) -> Option<Ipv4Addr> {
let components: Vec<_> = host.split('.').collect();
if !(1..=4).contains(&components.len()) || components.iter().any(|part| part.is_empty()) {
return None;
}

let numbers: Vec<u32> = components
.iter()
.map(|part| {
let number = if let Some(hex) =
part.strip_prefix("0x").or_else(|| part.strip_prefix("0X"))
{
u64::from_str_radix(hex, 16).ok()
} else if part.len() > 1 && part.starts_with('0') {
u64::from_str_radix(&part[1..], 8).ok()
} else {
part.parse().ok()
}?;
u32::try_from(number).ok()
})
.collect::<Option<_>>()?;

if numbers[..numbers.len() - 1]
.iter()
.any(|number| *number > u8::MAX.into())
{
return None;
}
let last = *numbers.last()?;
let last_max = match numbers.len() {
1 => u32::MAX,
2 => 0x00ff_ffff,
3 => 0x0000_ffff,
4 => u8::MAX.into(),
_ => return None,
};
if last > last_max {
return None;
}

let address = match numbers.as_slice() {
[whole] => *whole,
[first, last] => (*first << 24) | *last,
[first, second, last] => (*first << 24) | (*second << 16) | *last,
[first, second, third, last] => {
(*first << 24) | (*second << 16) | (*third << 8) | *last
}
_ => return None,
};
Some(Ipv4Addr::from(address))
}

/// Check the list is non-empty, well formed, and names no metadata or
/// explicit loopback endpoint.
///
/// # Errors
///
Expand Down Expand Up @@ -323,6 +437,11 @@ impl EgressAllowlist {
reason: "allowlist names a cloud metadata endpoint",
});
}
if Self::is_loopback_host(trimmed) {
return Err(Error::InvalidNetworkPolicy {
reason: "allowlist names a loopback endpoint",
});
Comment thread
senamakel marked this conversation as resolved.
}
}
Ok(())
}
Expand Down
7 changes: 6 additions & 1 deletion crates/tinybox-docker/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,13 @@ publish = false
[dependencies]
tinybox-core.workspace = true
async-trait.workspace = true
# Parse Docker-assigned CIDRs and reject pinned destinations within them.
ipnet = "2.12"
serde_json.workspace = true
# Keep workload Docker client credentials out of host CLI environment and argv.
tempfile = "3"
# SHA-256 keeps firewall cleanup ownership markers collision-resistant.
sha2 = "0.10.9"
# `DockerCli` runs the local `docker` client; one-shot runs need process spawning
# and a deadline.
tokio = { workspace = true, features = ["process"] }
Expand All @@ -25,7 +31,6 @@ uuid = { version = "1", features = ["v4"] }
tokio.workspace = true
# The live suite drives a real daemon through the real local host.
tinybox-host.workspace = true
tempfile = "3"

[lints]
workspace = true
Loading
Loading