Connector egress guard (SSRF defense)

Updated · View the entry on sijie.xyz ↗

Once an owner can upload an arbitrary OpenAPI connector, the target URL is attacker-influenceable — a classic SSRF surface (point it at 169.254.169.254 or an internal service). The defense is two-layer:

  • Assembly time (static): every spec's servers[].url and OAuth tokenUrl is checked; loopback / RFC1918 private / ULA / link-local targets are rejected before the connector is ever usable.
  • Run time (dynamic): a custom dialer re-checks on every DNS resolution that the resolved IPs are public, and blocks 3xx redirects to private addresses — defeating DNS-rebinding (where a hostname resolves public at check time, private at call time). An allow-list (CONNECTOR_EGRESS_ALLOW) opens specific hostnames for e2e/mocks.

The two-axis contrast (worth holding onto)

The two plugin axes get opposite isolation, because their needs are opposite:

axis needs network? isolation
mcp-capability-plugins / sandbox-js-hardening no caged off the network (--unshare-net)
OpenAPI connector yes (calls SaaS) let out, but the exit is filtered (egress guard)

Caged-in vs filtered-exit: when untrusted code must reach out, you can't isolate by removing the network — you isolate by controlling the egress. Same instinct (untrusted code reaches the host only through a narrow, declared channel), dual mechanism.

Related notes