Sandbox Network Isolation#
Dify's sandbox environments route all outbound HTTP/S traffic through Squid-based SSRF proxies. As of Dify 1.15.0, the workflow sandbox proxy enforces a deny-by-default policy that blocks all private/RFC-1918 IP ranges and only allows .marketplace.dify.ai out of the box. Workflows or HTTP-request nodes that call internal services will return TCP_DENIED/403 after upgrading from earlier versions.
The agent sandbox (local_sandbox) uses a separate, more restrictive proxy (agent_ssrf_proxy) that only permits access to specific internal endpoints and external internet traffic.
Network Topology#
Workflow Sandbox (sandbox)#
Docker Compose defines a dedicated internal bridge network named ssrf_proxy_network with internal: true. This means containers on that network cannot reach the Docker host's external network directly; all egress must traverse the proxy.
ββ default network (external access) ββββββββββββββ
β nginx ββββ api / worker / plugin_daemon β
ββββββββββββββββββββββββββββββββββββββββββββββββββββ
β ssrf_proxy_network (internal)
β
ssrf_proxy (Squid :3128)
β
sandbox (port 8194)
Services that join both networks (api, worker, worker_beat, plugin_daemon) can reach the Squid proxy for outbound calls while still communicating with the rest of the stack on the default network . The sandbox service joins only ssrf_proxy_network and is intentionally cut off from the default network .
The sandbox container is pre-configured to send all traffic through the proxy via HTTP_PROXY and HTTPS_PROXY environment variables :
HTTP_PROXY: http://ssrf_proxy:3128
HTTPS_PROXY: http://ssrf_proxy:3128
Service-name DNS resolution (e.g., ssrf_proxy) works because Docker's embedded DNS resolver is available to every container in a shared network β including the internal: true ssrf_proxy_network.
Agent Sandbox (local_sandbox)#
The local_sandbox service has no direct route to api. It is connected to two internal networks:
agent_sandbox_networkβ shared withagent_backendso agent_backend can reach local_sandbox on port 5004 (shellctl). Note this is a known limitation: arbitrary code in the sandbox can make outbound requests to agent_backend through this network.local_sandbox_proxy_networkβ shared only withagent_ssrf_proxyto force all other egress through the proxy.
ββ default network βββββββββββββββββββββββββββββββββ
β api ββββ agent_backend β
β β
β agent_ssrf_proxy βββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββ
ββ agent_sandbox_network
βββββ agent_backend (port 5004 shellctl)
β β
β β
β local_sandbox
β β
βββββββββββ
local_sandbox_proxy_network
The local_sandbox container is pre-configured to send all traffic through the agent proxy:
HTTP_PROXY: http://agent_ssrf_proxy:3128
HTTPS_PROXY: http://agent_ssrf_proxy:3128
NO_PROXY: localhost,127.0.0.1
All egress traffic (except to localhost/127.0.0.1 or direct requests to agent_backend via agent_sandbox_network) is routed through agent_ssrf_proxy, which enforces highly restrictive path-based ACLs to only allow:
agent_backend/agent-stub/*endpointsapi/files/*endpoints (signed upload/download URLs)- External internet destinations
This prevents SSRF attacks where malicious code in the sandbox attempts to access internal services.
Squid Deny-by-Default Policy#
Workflow Sandbox Proxy (ssrf_proxy)#
The proxy configuration in docker/ssrf_proxy/squid.conf.template enforces the following access control order:
- Include override file first β
/etc/squid/dify_allow_private.confis evaluated before any deny rules , so environment-variable-based exceptions always win. - Allow static allowlist β
acl allowed_domains dstdomain .marketplace.dify.aiis the only hardcoded outbound domain . - Block private IPs β the
to_private_networksACL covers 14 ranges :
0.0.0.0/8,10.0.0.0/8,100.64.0.0/10,127.0.0.0/8,169.254.0.0/16,172.16.0.0/12,192.168.0.0/16,224.0.0.0/4,240.0.0.0/4, plus IPv6 equivalents (::1/128,fc00::/7,fe80::/10, etc.) - Deny everything else β
http_access deny allas the final rule .
Agent Sandbox Proxy (agent_ssrf_proxy)#
The agent proxy configuration in docker/ssrf_proxy/squid-agent.conf.template enforces a more restrictive policy:
- Deny unsafe ports and methods β blocks non-safe ports and CONNECT to non-SSL ports.
- Allow agent_backend
/agent-stub/*βhttp_access allow dst_agent_backend path_agent_stubpermits requests toagent_backendmatching the/agent-stub/path prefix. - Allow api
/files/*βhttp_access allow dst_dify_api path_filespermits requests toapimatching the/files/path prefix (signed upload/download URLs). - Block private IPs β the same
to_private_networksACL defined insquid-common.conf.templatedenies all other private destinations. - Allow external internet β
http_access allow allpermits outbound requests to public destinations.
The agent proxy uses squid-common.conf.template for shared ACL definitions (to_private_networks, safe ports, etc.) but does not support runtime exception variables like the workflow proxy. Access control is hardcoded to the two internal endpoints and external internet only.
Adding Exceptions (Workflow Sandbox Only)#
The docker/ssrf_proxy/docker-entrypoint.sh reads two .env variables at startup and generates Squid ACL fragments into /etc/squid/dify_allow_private.conf . These exceptions apply only to the workflow sandbox proxy (ssrf_proxy), not to the agent sandbox proxy.
| Variable | Effect |
|---|---|
SSRF_PROXY_ALLOW_PRIVATE_DOMAINS | Allows named hosts/domains that resolve to private IPs (comma-separated) |
SSRF_PROXY_ALLOW_PRIVATE_IPS | Allows specific private IPs or CIDR ranges (comma-separated) |
These are also exposed as environment pass-throughs in the ssrf_proxy service definition .
Example .env additions:
SSRF_PROXY_ALLOW_PRIVATE_DOMAINS=internal.mycompany.com
SSRF_PROXY_ALLOW_PRIVATE_IPS=10.10.5.42,192.168.1.0/24
After editing .env, restart the proxy: docker compose restart ssrf_proxy (or down/up to regenerate configs).
API-Layer SSRF Detection#
api/core/helper/ssrf_proxy.py routes all outbound requests through the Squid proxy via configurable proxy mounts . When Squid blocks a request and returns a 401 or 403, the code checks for squid in the Server or Via response header and raises ToolSSRFError . No local DNS or IP validation is performed β all enforcement is delegated to the proxy.
Key Files#
| File | Purpose |
|---|---|
docker/docker-compose.yaml | Defines all sandbox networks and which services join them |
docker/ssrf_proxy/squid.conf.template | Workflow sandbox proxy ACL rules and deny-by-default policy |
docker/ssrf_proxy/squid-agent.conf.template | Agent sandbox proxy ACL rules (agent_backend, api, external only) |
docker/ssrf_proxy/squid-common.conf.template | Shared ACL definitions for both proxies |
docker/ssrf_proxy/docker-entrypoint.sh | Workflow proxy entrypoint; generates runtime ACL fragments from env vars |
docker/ssrf_proxy/docker-agent-entrypoint.sh | Agent proxy entrypoint script |
api/core/helper/ssrf_proxy.py | API-layer proxy routing and ToolSSRFError detection |