You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 8718b9c
Browse filesBrowse the repository at this point in the historyBrowse files
feat(network): honor HTTP_PROXY, HTTPS_PROXY and NO_PROXY for guarded requests, AWS SDK clients and OTLP export (#9851)
* feat(network): honor HTTP_PROXY, HTTPS_PROXY and NO_PROXY for guarded outbound requests
* fix(network): limit environment proxies to http and https, cover an https proxy
* feat(network): route AWS SDK clients and OTLP exporters through the environment proxy
* fix(network): record the proxy dependencies under apps/sim and register the proxy agent as egress infrastructure
Copy file name to clipboardExpand all lines: apps/docs/content/docs/platform/self-hosting/environment-variables.mdx
+1Lines changed: 1 addition & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -255,6 +255,7 @@ Configure at least one. Every configured provider stays active and is tried in o
255
255
|`EGRESS_ALLOWED_HOSTS`| unset | Comma-separated hostnames outbound requests may reach on a private network. Leading wildcard allowed, e.g. `host.docker.internal,*.svc.cluster.local`. Not honored for URLs harvested from content or a third-party response, nor for an HTTP block's `proxyUrl`|
256
256
|`EGRESS_ALLOWED_IP_RANGES`| unset | Comma-separated CIDRs or IPs outbound requests may reach on a private network, e.g. `10.0.0.0/8`. Same exclusions |
257
257
|`ALLOW_PRIVATE_DATABASE_HOSTS`| unset |**Deprecated.** Vouches for the entire private address space, for database, cache, and mail connector hosts only. Replace it with the two settings above naming specific destinations |
258
+
|`DOMAIN_VERIFICATION_NAMESERVERS`|`1.1.1.1,8.8.8.8`| Comma-separated nameserver IPs that SSO domain verification sends its TXT lookup to. Set it when the network only allows DNS to an internal resolver. Entries that are not IP addresses are ignored |
258
259
259
260
A malformed entry in either allowlist stops the app at startup with a message naming the setting. See [the SSRF boundary](/platform/self-hosting/security#the-ssrf-boundary) for the accepted syntax and for what the allowlist does and does not reach.
Copy file name to clipboardExpand all lines: apps/docs/content/docs/platform/self-hosting/networking.mdx
+20-9Lines changed: 20 additions & 9 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -265,7 +265,7 @@ Most `/api` requests also pass through Next.js Proxy. The current app configurat
265
265
266
266
The app makes outbound calls to model providers, integration APIs, your email provider, object storage, and your telemetry backend. Whether `HTTP_PROXY` / `HTTPS_PROXY` apply depends on which of those paths a call takes — there is no single answer, and no global setting that covers all of them.
267
267
268
-
The server runs on Bun, and Bun's native `fetch` honors `$HTTP_PROXY`, `$HTTPS_PROXY`, and `$NO_PROXY`. Sim installs no global dispatcher. Default `fetch` uses the configured proxy unless `NO_PROXY` excludes the destination. The rest either build their own HTTP agent or speak a non-HTTP protocol.
268
+
The server runs on Bun, and Bun's native `fetch` honors `$HTTP_PROXY`, `$HTTPS_PROXY`, and `$NO_PROXY`. Sim installs no global dispatcher. Default `fetch` uses the configured proxy unless `NO_PROXY` excludes the destination. The SSRF guard reads the same three variables, with the same rules, for every request it sends. The rest either build their own HTTP agent or speak a non-HTTP protocol.
269
269
270
270
This depends on the runtime. The published images run Bun. If you build the standalone output and run it under Node instead, Node ignores these variables unless started with `NODE_USE_ENV_PROXY=1` (Node 22.21+ / 24.5+), and the `fetch`-based "Yes" rows stop being proxied — model providers, Resend, Gmail sending, the desktop update feed, and the telemetry relay. The Azure Blob, GCS and Azure Communication Services rows still hold: those SDKs read the proxy variables through their own agents rather than through `fetch`. See [Bun fetch](https://bun.sh/docs/runtime/networking/fetch) and [Node proxy configuration](https://nodejs.org/learn/http/enterprise-network-configuration).
271
271
@@ -275,20 +275,31 @@ This depends on the runtime. The published images run Bun. If you build the stan
275
275
| Email via Resend, Azure Communication Services, and Gmail sending | Yes |
276
276
| The desktop update feed's calls to GitHub | Yes |
277
277
| Object storage — Azure Blob and GCS | Yes — their SDK pipelines read the proxy variables |
278
-
| Everything through the SSRF guard — the HTTP block, tools, connectors, outbound webhooks, content fetches, MCP servers | No |
279
-
| Azure OpenAI, Azure Anthropic, vLLM | Only when the endpoint comes from `AZURE_OPENAI_ENDPOINT`, `AZURE_ANTHROPIC_ENDPOINT`, or `VLLM_BASE_URL`. An endpoint typed into the block is validated and pinned to its resolved IP, which bypasses the proxy |
280
-
| Amazon Bedrock, and object storage on S3 | No — the AWS SDK uses its own request handler |
281
-
| Email via SMTP | No — Nodemailer opens a raw TCP connection |
282
-
| Email via Amazon SES | No — the AWS SDK transport, over HTTPS |
283
-
| OTLP export from the server SDK | No |
278
+
| Everything through the SSRF guard — SSO OIDC discovery, the HTTP block, tools, connectors, outbound webhooks, content fetches, MCP servers, and model endpoints typed into a block | Yes — see [Proxying guarded requests](#proxying-guarded-requests) |
279
+
| SSO sign-in — the token, JWKS, and userinfo calls made while a user signs in | Yes — they use the default `fetch` |
280
+
| SSO domain verification (TXT lookup) | No — DNS, sent to `1.1.1.1` and `8.8.8.8` unless `DOMAIN_VERIFICATION_NAMESERVERS` names your resolvers |
281
+
| Amazon Bedrock, object storage on S3, and every AWS integration (DynamoDB, SQS, Lambda, Textract, and the rest) | Yes — each AWS client gets a proxy-aware agent while a proxy is set. Bedrock then uses HTTP/1.1 instead of HTTP/2 |
282
+
| Email via SMTP, and the SMTP block | No — SMTP is not HTTP. Use a mail relay the app can reach directly, or Resend or SES |
283
+
| Email via Amazon SES | Yes — the AWS SDK client above |
284
+
| OTLP export from the server SDK | Yes — the exporters get the same proxy-aware agent |
284
285
| The `/api/telemetry` relay that forwards browser events | Yes — it uses the default `fetch` |
285
286
| Postgres and Redis | No — raw TCP |
286
287
287
-
The practical consequence: a mandatory-egress-proxy environment can route most LLM traffic, Resend mail, and Azure/GCS storage through the proxy, but guarded integration calls, S3, Bedrock, SMTP, telemetry, and datastore traffic still need a transparent proxy or NAT-based egress.
288
+
The practical consequence: a mandatory-egress-proxy environment can route LLM traffic, SSO, integration and workflow HTTP calls, Resend mail, Azure/GCS storage, AWS services, SES, and OTLP export through the proxy. SMTP and datastore traffic (Postgres, Redis, and the database blocks) still need a transparent proxy or NAT-based egress.
289
+
290
+
### Proxying guarded requests
291
+
292
+
A request through the SSRF guard keeps its checks when it is proxied. Before each request, and again for each redirect hop, the app resolves the destination's hostname with its own DNS and refuses it if any policy in [Security](/platform/self-hosting/security#the-ssrf-boundary) blocks the address. It then hands the hostname, not the address, to the proxy, so a proxy that allowlists by domain sees the name it expects.
293
+
294
+
- **DNS must still resolve inside the container.** A destination that the app cannot resolve is refused before the proxy is asked.
295
+
- **The proxy resolves the name again.** The app cannot pin the address across a proxy, so the proxy's own egress policy is the last check.
296
+
- **`https` destinations are tunneled** with `CONNECT host:443`. **`http` destinations are forwarded** to the proxy as an absolute URL, as curl and Bun do.
297
+
- **Proxy credentials** go in the URL, e.g. `http://user:pass@proxy.internal:3128`. They are sent only to the proxy. An `https://` proxy URL also works; SOCKS proxies are not supported.
298
+
- **The HTTP block's `proxyUrl`** still takes precedence for that one request.
288
299
289
300
### Internal destinations and `NO_PROXY`
290
301
291
-
Set `NO_PROXY` for every destination that is not on the public internet, not just model endpoints. The app reaches the realtime server (`SOCKET_SERVER_URL`), the Presidio PII service (`PII_URL`), and itself (`INTERNAL_API_BASE_URL`) over the same default `fetch`, alongside self-hosted Ollama, LiteLLM, and vLLM — so a proxy that cannot reach your internal network breaks live updates and PII redaction, not only inference.
302
+
Set `NO_PROXY` for every destination that is not on the public internet, not just model endpoints. The app reaches the realtime server (`SOCKET_SERVER_URL`), the Presidio PII service (`PII_URL`), and itself (`INTERNAL_API_BASE_URL`) over the same default `fetch`, alongside self-hosted Ollama, LiteLLM, and vLLM — so a proxy that cannot reach your internal network breaks live updates and PII redaction, not only inference. AWS clients and OTLP export follow the same rule, so also add an internal S3-compatible store such as MinIO, AWS VPC endpoints, and an in-cluster OpenTelemetry collector.
292
303
293
304
Set it in the application environment, not your shell — under `app.env` on Helm, or the service's `environment:` on Compose. On Helm the suffixes alone are not enough: the chart wires `SOCKET_SERVER_URL`, `PII_URL`, and `OLLAMA_URL` to bare Service names, which no domain suffix matches. Add those names too — `helm template` prints the rendered ones, and the prefix is the release name unless it already contains `sim`, in which case it is the release name alone:
ALLOW_PRIVATE_DATABASE_HOSTS: z.boolean().optional(),// Deprecated alias for the egress allowlist, kept so existing self-hosted deployments keep working. Equivalent to allowing every private, reserved, and loopback destination. Prefer EGRESS_ALLOWED_HOSTS / EGRESS_ALLOWED_IP_RANGES, which name specific destinations.
135
135
EGRESS_ALLOWED_HOSTS: z.string().optional(),// Comma-separated hostnames outbound requests may reach on a private network, leading wildcard allowed (e.g. "host.docker.internal,*.svc.cluster.local"). Self-hosted only; ignored on the hosted platform. Replaces ALLOW_PRIVATE_DATABASE_HOSTS.
136
136
EGRESS_ALLOWED_IP_RANGES: z.string().optional(),// Comma-separated CIDRs or IPs outbound requests may reach on a private network (e.g. "10.0.0.0/8,192.168.65.254/32"). Self-hosted only; never lifts the cloud-metadata block.
137
+
DOMAIN_VERIFICATION_NAMESERVERS: z.string().optional(),// Comma-separated nameserver IPs for the domain-verification TXT lookup (e.g. "10.0.0.2"). Defaults to 1.1.1.1 and 8.8.8.8; set it where outbound DNS is restricted to an internal resolver.
137
138
ALLOWED_LOGIN_EMAILS: z.string().optional(),// Comma-separated list of allowed email addresses for login
138
139
ALLOWED_LOGIN_DOMAINS: z.string().optional(),// Comma-separated list of allowed email domains for login
139
140
BLOCKED_SIGNUP_DOMAINS: z.string().optional(),// Comma-separated list of email domains blocked from signing up (e.g., "gmail.com,yahoo.com")
0 commit comments