Repository navigation
feat(agent-vault): accept SOCKS5 over TLS on the proxy - #471
Conversation
|
💬 Discussion in Slack: #pr-review-cli-471-feat-agent-vault-accept-socks5-over-tls-on-the-proxy Posted by Review Police — reviews, comments, new commits, and CI failures will stream into this channel. |
[High impact] The PR adds an optional network-facing route into Agent Vault's existing web proxy flow. The connection cleanup changes are in place, but the previous finding remains partly unresolved because failed small replies can still leave only a success log.
|
There was a problem hiding this comment.
Beyond the inline findings, I also checked whether the SOCKS5 private-destination guard in packages/agentvault/dial_guard.go could be bypassed. The upstream transport sets Proxy: nil, so the net.Dialer.Control hook sees the real resolved destination, not an intermediate proxy. Zone stripping and Unmap() stop IPv4-mapped and zoned IPv6 forms from slipping past the ranges.
Extended reasoning...
The change adds an internet-facing SOCKS5-over-TLS listener whose login is the session token, a dial-time block on private destinations, and new CLI flags, about 1.8k lines across packages/agentvault and packages/cmd. The dial guard is the main defense against SSRF on this listener, so I checked that no environment proxy sits between it and the real destination, and none does.
Findings marked 🟡 are optional suggestions and need no follow-up push.
…nternal IPv6 blocks and the host's own addresses on SOCKS5
… and test the SOCKS5 door's refusals
…files change, and warn before it expires
…y when a SOCKS5 client closed without sending anything
…ey went missing during a renewal
# Conflicts: # go.mod
…y, and drop the zone from a scoped IPv6 TLS name
|
@greptile review |
scott-ray-wilson
left a comment
There was a problem hiding this comment.
Tested this with Infisical/infisical#9035, on a local stack with the CLI built from this head and against a real E2B sandbox (bring your own proxy, through a public TCP tunnel to the SOCKS5 port).
The guide's flow works end to end: E2B verified the SOCKS5 TLS against the CA from the new endpoint, credentials were injected for curl, Python and Node, the sandbox environment held no token, private destinations were refused, and a revoke mid-run failed closed. Login and protocol edge cases (non-token passwords, unknown, revoked and expired tokens, BIND and UDP ASSOCIATE, bad hosts, port 0, plain SOCKS5 on the TLS port, silent clients) all behaved as described.
Inline: one High, two Mediums, and three optional Lows. I also replied on the existing pre-login slots thread with a repro.
…t no service names, whatever the traffic policy
…g when the host's own addresses can't be read
…the connection limit is reached, and refuse an empty --listen-host
…il only a gone session at login, and rate limit the refusal warnings
… request, on both doors
PR overviewAll previously flagged issues have been addressed. No open security concerns remain on this pull request. Security reviewNo open security issues remain on this pull request. Fixed/addressed: 2 · PR risk: 0/10 |
…kup, and give up on a half-closed tunnel's reply after 10 minutes
…own addresses can't be read
scott-ray-wilson
left a comment
There was a problem hiding this comment.
Re-tested at 274450f (with infisical#9035 at 455b11a): rebuilt the CLI, re-ran the live checks behind each comment from the last round, ran the backend changes on an isolated API, and re-ran the earlier test matrix. Every item from the last round is fixed or reasonably deferred, CI is green, and the new refusal handling holds: a token that was never verified never gets a working tunnel. Thanks for turning these around quickly.
Blocking (needs to be addressed before I approve)
- The half-close handling keeps abandoned requests running on both doors. Details and a suggested fix in my reply on the
proxy.go:457thread. - Veria's NAT64 thread on
dial_guard.go:104is still open. Applying its suggestion or replying is enough.
Optional, fine to defer to a follow-up
- Pre-login slots: the 3s login window helps a lot (a request behind 520 idle connections went from 9.5s to 2.5s), but about 170 new connections a second can still keep the port full. A per-IP or separate pre-login cap would close it.
- Startup escape hatch (
dial_guard.go:114): if the machine's addresses can't be read, the only way to start is--socks5-allow-private-destinations, which turns off the whole private-address check, while the message says it skips only that check. The fixed ranges don't need the address list, so they could stay on. - IPv6 rotation (
socks5_budget.go:55): the per-IP throttle is keyed by full address, so an attacker rotating through an IPv6 /64 gets the whole 20/s budget, and new sessions get the general failure for the length of the flood. Grouping IPv6 by /64 would fix it. - Warm-up after a restart: every session is unknown again and re-resolves through the 20/s budget, so a busy proxy can return general failures until it catches up.
- Refusal summary (
socks5.go:403): it's keyed by message, and budget refusals share the "could not be resolved" message with an Infisical outage, so a flood can hide a real outage. - Own-address list (
dial_guard.go:114): still read once at start, so an address added later (a floating IP, a new IPv6 temporary address) isn't refused. - Plain
http://on the HTTP door (proxy.go:571): absolute-form requests still return 502 on a half-close, since the fix covers tunnels only. This predates the PR.
… the upstream a minute to start replying, and normalise NAT64 before both destination checks
Description 📣
Adds an opt-in SOCKS5 door to
infisical agent-vault proxy, so sandbox providers that tunnel a sandbox's traffic from outside the sandbox (E2B's bring your own proxy) can use Agent Vault. The session token goes in the SOCKS5 password, set on E2B's side, so it never enters the sandbox and the agent sets no proxy env vars.SOCKS5 door
--socks5-portturns it on (off by default), always over TLS, listens on all interfaces--socks5-tls-name: the proxy mints the port's certificate from its own CA and renews it. Or--socks5-tls-cert/--socks5-tls-keyfor your own (read once at start, expired cert refused)01 01). Passwords that aren't shaped likeagv_...are refused without asking Infisical0x07. After the reply the connection takes the same path asCONNECT(handleConnect's peek + MITM block moved intoserveIntercepted), so policy, injection, session logs and the mid-tunnel 403 are unchangedrecover; accept loop backs off on temporary errors like net/httpdoor=socks5, login refusals log the remote address and never the usernamePrivate destinations
On the SOCKS5 door only, under the any-host policy, a host no bundle service names can't resolve to a private, loopback, link-local or similar address (cloud metadata, the proxy's own network). It's checked at dial time on the resolved IP (
net.Dialer.Control), through a separate transport so it never reuses a connection the unchecked one opened. Same ranges as the backend'sipRange.ts, with zones stripped and IPv4-in-IPv6 unwrapped.--socks5-allow-private-destinationsturns it off. The HTTP door is unchanged.Other
--listen-hostto bind the HTTP port to one interface (host only, the port stays--port)INFISICAL_AGENT_VAULT_*env var, telemetry getssocks5,socks5OwnCert,socks5AllowPrivate,customListenHostpackages/cmd(they never ran before)Companion PR (CA download API, UI and the E2B guide): Infisical/infisical#9035
Type ✨
Tests 🛠️
# Here's some code block to paste some code snippets