Summary
Chart 0.38.0 (appVersion: "1.16.1") ships the Agent v2 components agentBackend and localSandbox, but omits the dedicated SSRF proxy for the local sandbox (agent_ssrf_proxy in the upstream docker-compose.yaml).
As a result, the localSandbox pod receives no proxy configuration at all, and its egress/east-west traffic is completely unrestricted.
Upstream reference
agent_ssrf_proxy was introduced in Dify 1.16.1 — which is exactly the appVersion targeted by chart 0.38.0:
| Dify version |
local_sandbox |
agent_backend |
agent_ssrf_proxy |
| 1.16.0 |
yes |
yes |
no |
| 1.16.1 |
yes |
yes |
yes |
| 1.17.0 |
yes |
yes |
yes |
In docker/docker-compose.yaml it is a distinct service from the existing ssrf_proxy:
- image
ubuntu/squid:latest, HTTP_PORT: ${SSRF_HTTP_PORT:-3128}
- uses its own squid template
./ssrf_proxy/squid-agent.conf.template (not the same as squid.conf.template used by ssrf_proxy)
- attached to two networks:
default and local_sandbox_proxy_network
local_sandbox_proxy_network is an internal bridge shared only by agent_ssrf_proxy and local_sandbox, so that the sandbox can reach squid without gaining a direct route to api (per the comments in the compose file)
local_sandbox sets HTTP_PROXY=http://agent_ssrf_proxy:3128, HTTPS_PROXY=http://agent_ssrf_proxy:3128, NO_PROXY=localhost,127.0.0.1
Current chart behavior
grep -rniE 'agent[-_]?ssrf' . over the repository returns zero matches.
The chart has a single ssrfProxy component, wired to two consumers only:
| Consumer |
Variables |
Source |
api / worker |
SSRF_PROXY_HTTP_URL, SSRF_PROXY_HTTPS_URL |
charts/dify/templates/config.tpl:80-82 |
sandbox |
HTTP_PROXY, HTTPS_PROXY |
charts/dify/templates/config.tpl:540-542 (in dify.sandbox.config) |
localSandbox |
none |
— |
charts/dify/templates/local-sandbox-config.yaml renders an empty ConfigMap (data: {}), and the local sandbox Secret only carries SHELLCTL_AUTH_TOKEN.
Reproduction
helm template my-dify ./charts/dify \
--set localSandbox.enabled=true \
--set agentBackend.enabled=true \
--set ssrfProxy.enabled=true \
| grep -E 'HTTP_PROXY|HTTPS_PROXY|NO_PROXY|SSRF_PROXY_HTTP_URL'
Observed (chart 0.38.0):
my-dify-api: SSRF_PROXY_HTTP_URL: http://my-dify-ssrf-proxy:3128
my-dify-sandbox: HTTP_PROXY: http://my-dify-ssrf-proxy:3128
my-dify-sandbox: HTTPS_PROXY: http://my-dify-ssrf-proxy:3128
Nothing is emitted for my-dify-local-sandbox.
Impact
Upstream achieves the isolation through Docker internal networks. There is no equivalent in the chart, and the chart also ships no NetworkPolicy template (grep -rl NetworkPolicy charts/dify/templates/ -> no match; rendered output contains only ConfigMap / Deployment / Ingress / Job / PVC / Secret / Service).
So agent-generated code running inside localSandbox currently:
- reaches the internet directly, bypassing any forward proxy and its ACLs
- can reach the
api / agent-backend / plugin-daemon Services directly, which upstream explicitly prevents
- can reach any other in-cluster pod IP, plus external PostgreSQL / Redis / object storage endpoints configured for the release
This is a meaningful weakening of the upstream threat model for a component whose entire purpose is executing untrusted code.
Note also that the existing ssrfProxy squid config is not a drop-in substitute: dify.ssrfProxy.config.squid ends with a Reverse Proxy To Sandbox block (http_port <sandbox port> accel vhost + cache_peer to the code sandbox, with acl src_all src all / http_access allow src_all), which is not what the upstream agent-specific template does.
Suggested fix
- Add an
agentSsrfProxy component (deployment / service / configmap / serviceaccount) with its own squid config mirroring upstream squid-agent.conf.template, guarded by localSandbox.enabled.
- Inject
HTTP_PROXY / HTTPS_PROXY / NO_PROXY into the localSandbox ConfigMap pointing at it.
- Optionally ship an opt-in
NetworkPolicy to approximate local_sandbox_proxy_network / agent_sandbox_network: allow localSandbox egress only to agentSsrfProxy:3128 and DNS, and allow ingress to localSandbox:5004 only from agentBackend. Without this, step 1 restores proxied egress but not the east-west isolation.
Workaround
Point the local sandbox at the existing proxy via extraEnv (does not address east-west isolation):
localSandbox:
extraEnv:
- name: HTTP_PROXY
value: "http://<release>-ssrf-proxy:3128"
- name: HTTPS_PROXY
value: "http://<release>-ssrf-proxy:3128"
- name: NO_PROXY
value: "localhost,127.0.0.1"
Environment
- Chart:
0.38.0 (appVersion: "1.16.1"), also reproduced against 1.17.0 images
- Helm:
v4.2.4
Summary
Chart
0.38.0(appVersion: "1.16.1") ships the Agent v2 componentsagentBackendandlocalSandbox, but omits the dedicated SSRF proxy for the local sandbox (agent_ssrf_proxyin the upstreamdocker-compose.yaml).As a result, the
localSandboxpod receives no proxy configuration at all, and its egress/east-west traffic is completely unrestricted.Upstream reference
agent_ssrf_proxywas introduced in Dify 1.16.1 — which is exactly theappVersiontargeted by chart0.38.0:local_sandboxagent_backendagent_ssrf_proxyIn
docker/docker-compose.yamlit is a distinct service from the existingssrf_proxy:ubuntu/squid:latest,HTTP_PORT: ${SSRF_HTTP_PORT:-3128}./ssrf_proxy/squid-agent.conf.template(not the same assquid.conf.templateused byssrf_proxy)defaultandlocal_sandbox_proxy_networklocal_sandbox_proxy_networkis aninternalbridge shared only byagent_ssrf_proxyandlocal_sandbox, so that the sandbox can reach squid without gaining a direct route toapi(per the comments in the compose file)local_sandboxsetsHTTP_PROXY=http://agent_ssrf_proxy:3128,HTTPS_PROXY=http://agent_ssrf_proxy:3128,NO_PROXY=localhost,127.0.0.1Current chart behavior
grep -rniE 'agent[-_]?ssrf' .over the repository returns zero matches.The chart has a single
ssrfProxycomponent, wired to two consumers only:api/workerSSRF_PROXY_HTTP_URL,SSRF_PROXY_HTTPS_URLcharts/dify/templates/config.tpl:80-82sandboxHTTP_PROXY,HTTPS_PROXYcharts/dify/templates/config.tpl:540-542(indify.sandbox.config)localSandboxcharts/dify/templates/local-sandbox-config.yamlrenders an empty ConfigMap (data: {}), and the local sandbox Secret only carriesSHELLCTL_AUTH_TOKEN.Reproduction
Observed (chart 0.38.0):
Nothing is emitted for
my-dify-local-sandbox.Impact
Upstream achieves the isolation through Docker
internalnetworks. There is no equivalent in the chart, and the chart also ships noNetworkPolicytemplate (grep -rl NetworkPolicy charts/dify/templates/-> no match; rendered output contains only ConfigMap / Deployment / Ingress / Job / PVC / Secret / Service).So agent-generated code running inside
localSandboxcurrently:api/agent-backend/plugin-daemonServices directly, which upstream explicitly preventsThis is a meaningful weakening of the upstream threat model for a component whose entire purpose is executing untrusted code.
Note also that the existing
ssrfProxysquid config is not a drop-in substitute:dify.ssrfProxy.config.squidends with aReverse Proxy To Sandboxblock (http_port <sandbox port> accel vhost+cache_peerto the code sandbox, withacl src_all src all/http_access allow src_all), which is not what the upstream agent-specific template does.Suggested fix
agentSsrfProxycomponent (deployment / service / configmap / serviceaccount) with its own squid config mirroring upstreamsquid-agent.conf.template, guarded bylocalSandbox.enabled.HTTP_PROXY/HTTPS_PROXY/NO_PROXYinto thelocalSandboxConfigMap pointing at it.NetworkPolicyto approximatelocal_sandbox_proxy_network/agent_sandbox_network: allowlocalSandboxegress only toagentSsrfProxy:3128and DNS, and allow ingress tolocalSandbox:5004only fromagentBackend. Without this, step 1 restores proxied egress but not the east-west isolation.Workaround
Point the local sandbox at the existing proxy via
extraEnv(does not address east-west isolation):Environment
0.38.0(appVersion: "1.16.1"), also reproduced against1.17.0imagesv4.2.4