feat(sandbox): SSH from macOS, split-DNS private endpoints, runc orphan reaper, Seatbelt config pointers #70

Open
Taha wants to merge 4 commits from taha/sandbox-20261110 into main AGit
Contributor

On top of main (86c3dfc6). macOS (Seatbelt) sandbox and runc hugs-and-every-loud-bug fixes collected from one real reporting session. Independent commits, each build-verified individually against main. Highlights: (1) network.ssh - ssh/scp/rsync/git-over-SSH out of the macOS sandbox, broker-tunneled per connection, host.ssh carries only the agent socket. (2) Split-DNS private endpoints (VPN'd EKS/GKE): a network.url grant never reaches private answers; a network.private row per name, or one per resolved address (EVERY answer must be granted), serves the CONNECT; refusal bodies name what to ask for; nothing re-resolves between check and dial. (3) Denied or revoked network grants re-check and close live CONNECT tunnels. (4) TrustedEnvironment: KUBECONFIG/AWS_CONFIG_FILE etc. reach the Seatbelt worker with the approval that granted the path; GIT_*, HOME, PATH and proxies are refused on that channel. (5) Kernel restore across a sandbox replacement, with the NameError regression test. (6) runc orphan reaper at daemon start, reaping the sh+sleep pairs a dead daemon left; failed deletes no longer leak. (7) saml2aws config recognition and a Cloud CLIs docs section. (8) seccomp allow creat (GNU tar), macOS curl reads /etc/ssl/openssl.cnf, Seatbelt carries NIX_CONFIG. (9) Git always uses the generated config; a ~/.gitconfig grant no longer re-points GIT_CONFIG_GLOBAL. Session-scope override of a global deny documented for network grants. Validation: scripts/check.sh --fast green (build, vet, gofmt, plugin API, web stamp, deadcode); the full -race run is still in progress on this sandbox.

On top of main (86c3dfc6). macOS (Seatbelt) sandbox and runc hugs-and-every-loud-bug fixes collected from one real reporting session. Independent commits, each build-verified individually against main. Highlights: (1) network.ssh - ssh/scp/rsync/git-over-SSH out of the macOS sandbox, broker-tunneled per connection, host.ssh carries only the agent socket. (2) Split-DNS private endpoints (VPN'd EKS/GKE): a network.url grant never reaches private answers; a network.private row per name, or one per resolved address (EVERY answer must be granted), serves the CONNECT; refusal bodies name what to ask for; nothing re-resolves between check and dial. (3) Denied or revoked network grants re-check and close live CONNECT tunnels. (4) TrustedEnvironment: KUBECONFIG/AWS_CONFIG_FILE etc. reach the Seatbelt worker with the approval that granted the path; GIT_*, HOME, PATH and proxies are refused on that channel. (5) Kernel restore across a sandbox replacement, with the NameError regression test. (6) runc orphan reaper at daemon start, reaping the sh+sleep pairs a dead daemon left; failed deletes no longer leak. (7) saml2aws config recognition and a Cloud CLIs docs section. (8) seccomp allow creat (GNU tar), macOS curl reads /etc/ssl/openssl.cnf, Seatbelt carries NIX_CONFIG. (9) Git always uses the generated config; a ~/.gitconfig grant no longer re-points GIT_CONFIG_GLOBAL. Session-scope override of a global deny documented for network grants. Validation: scripts/check.sh --fast green (build, vet, gofmt, plugin API, web stamp, deadcode); the full -race run is still in progress on this sandbox.
GNU tar 1.35 creates an archive with the legacy creat(2) syscall instead of
openat. The runc allow-list omitted it, so `tar -cf out.tar dir` failed inside
every runc sandbox with "Cannot open: Operation not permitted" while
extraction worked. creat(p, m) is open(p, O_WRONLY|O_CREAT|O_TRUNC, m) and
open is already allowed, so this widens nothing.

TestRuncSeccompAllowsCreatForGNUTar fails without the entry.
Several suites were red on hosts without the paths or terminal the tests
assumed (NixOS, minimal containers), for environment reasons rather than
behaviour:

- cmd/yazi: syscall.Getsid has no Linux binding, so the package's tests did
  not build there; use golang.org/x/sys/unix.Getsid.
- internal/sshtunnel: resolve bash with exec.LookPath instead of writing a
  #!/bin/bash shebang.
- internal/tui2/app: theme.Load quantizes colours from COLORTERM/TERM, and
  useTheme loaded the theme before pinning them, so the Sixel palette test
  compared against its own 256-colour approximation; pin truecolor first.
- internal/sandbox: build the seed tar with archive/tar (host GNU tar creates
  archives through creat(2), which a seccomp allow-list may refuse), and make
  the fake runc answer `state` with the pid JSON Start unmarshals.
- internal/tool/sandboxed: resolve echo with exec.LookPath instead of the
  literal /bin/echo.

No assertion changed.
The Seatbelt worker may open no direct socket and has no DNS resolver, so
ssh, scp, rsync and git over SSH could not reach any host from the macOS
sandbox. Add a network.ssh permission naming one exact host:port and an SSH
route on the session broker: a ProxyCommand helper asks the authenticated
loopback broker to tunnel to the destination, the broker authorizes it
against an exact standing grant per connection, re-resolves the name and
refuses private answers (a private SSH host needs network.private).

- Patterns and one-time grants are refused rather than weakened.
- host.ssh carries the live agent socket the Linux sandbox mounts
  (SSH_AUTH_SOCK, ssh_config IdentityAgent, ~/.1password/agent.sock) and a
  sandbox-owned copy of the public ssh files; no private key is copied and a
  missing agent socket aborts the start instead of running without it.
- The tunnel wraps both io.Copy ends so Go's WriteTo/ReaderFrom fast paths
  cannot bypass the relay, and every broker hook is installed before the
  proxy serves (data race found under -race).
- A CONNECT whose name answers only privately now falls back to an exact
  network.private host:port grant and the 403/502 bodies name what to ask for.
Expose the new permission everywhere a grant is requested: the sandboxed tool
canonicalizes a network.ssh resource (host:port, bare host means 22) before it
reaches the store, the Python kernel's request_access accepts it, and the web
approval prompt describes it. The committed web build is regenerated from
web/src.
This pull request can be merged automatically.
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin '+refs/pull/70/head:taha/sandbox-20261110'
git switch 'taha/sandbox-20261110'

Merge

Merge the changes and update on Forgejo.

Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.

git switch 'main'
git merge --no-ff 'taha/sandbox-20261110'
git switch 'taha/sandbox-20261110'
git rebase 'main'
git switch 'main'
git merge --ff-only 'taha/sandbox-20261110'
git switch 'taha/sandbox-20261110'
git rebase 'main'
git switch 'main'
git merge --no-ff 'taha/sandbox-20261110'
git switch 'main'
git merge --squash 'taha/sandbox-20261110'
git switch 'main'
git merge --ff-only 'taha/sandbox-20261110'
git switch 'main'
git merge 'taha/sandbox-20261110'
git push origin 'main'
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kreato/yazi!70
No description provided.