Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions backend/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,14 @@ WORKDIR /app
# Copy backend with pre-built virtualenv from builder
COPY --from=builder /app/backend ./backend

# Run as an unprivileged user. The Gateway writes runtime state under
# ~/.deer-flow, so keep HOME writable for this user. Deployments that need
# the DooD sandbox overlay can opt back into root via `user: root` in
# docker-compose (see docker/docker-compose.dood.yaml).
RUN useradd --create-home --uid 1000 deerflow
USER deerflow
ENV HOME=/home/deerflow

# Expose Gateway API port.
EXPOSE 8001

Expand Down
4 changes: 4 additions & 0 deletions docker/docker-compose.dood.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,9 @@
# onto it. DEER_FLOW_DOCKER_SOCKET defaults to /var/run/docker.sock.
services:
gateway:
# The base image now runs as an unprivileged user; talking to the host
# Docker socket (root:root by default) requires root, so this overlay
# opts the gateway back in explicitly.
user: root
volumes:
- ${DEER_FLOW_DOCKER_SOCKET:-/var/run/docker.sock}:/var/run/docker.sock
38 changes: 38 additions & 0 deletions docker/nginx/nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ http {
proxy_buffering off;
proxy_cache off;

# The Gateway trusts X-DeerFlow-Internal-Token / X-DeerFlow-Owner-User-Id
# as internal credentials that bypass session auth. nginx does not
# inherit proxy_set_header into locations that define their own, so
# every proxying location below clears these two headers explicitly
# to keep browser-supplied values from ever reaching the Gateway.

# Keep the unified nginx endpoint same-origin by default. When split
# frontend/backend or port-forwarded deployments need browser CORS,
# configure the Gateway allowlist with GATEWAY_CORS_ORIGINS so CORS and
Expand All @@ -84,6 +90,8 @@ http {

# Headers
proxy_set_header Host $http_host;
proxy_set_header X-DeerFlow-Internal-Token "";
proxy_set_header X-DeerFlow-Owner-User-Id "";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $forwarded_proto;
Expand Down Expand Up @@ -117,6 +125,8 @@ http {
proxy_pass http://$gateway_upstream;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-DeerFlow-Internal-Token "";
proxy_set_header X-DeerFlow-Owner-User-Id "";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $forwarded_proto;
Expand All @@ -128,6 +138,8 @@ http {
proxy_pass http://$gateway_upstream;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-DeerFlow-Internal-Token "";
proxy_set_header X-DeerFlow-Owner-User-Id "";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $forwarded_proto;
Expand All @@ -139,6 +151,8 @@ http {
proxy_pass http://$gateway_upstream;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-DeerFlow-Internal-Token "";
proxy_set_header X-DeerFlow-Owner-User-Id "";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $forwarded_proto;
Expand All @@ -150,6 +164,8 @@ http {
proxy_pass http://$gateway_upstream;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-DeerFlow-Internal-Token "";
proxy_set_header X-DeerFlow-Owner-User-Id "";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $forwarded_proto;
Expand All @@ -161,6 +177,8 @@ http {
proxy_pass http://$gateway_upstream;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-DeerFlow-Internal-Token "";
proxy_set_header X-DeerFlow-Owner-User-Id "";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $forwarded_proto;
Expand All @@ -172,6 +190,8 @@ http {
proxy_pass http://$gateway_upstream;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-DeerFlow-Internal-Token "";
proxy_set_header X-DeerFlow-Owner-User-Id "";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $forwarded_proto;
Expand All @@ -190,6 +210,8 @@ http {
proxy_pass http://$gateway_upstream;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-DeerFlow-Internal-Token "";
proxy_set_header X-DeerFlow-Owner-User-Id "";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $forwarded_proto;
Expand All @@ -208,6 +230,8 @@ http {
proxy_pass http://$gateway_upstream;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-DeerFlow-Internal-Token "";
proxy_set_header X-DeerFlow-Owner-User-Id "";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $forwarded_proto;
Expand All @@ -219,6 +243,8 @@ http {
proxy_pass http://$gateway_upstream;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-DeerFlow-Internal-Token "";
proxy_set_header X-DeerFlow-Owner-User-Id "";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $forwarded_proto;
Expand All @@ -230,6 +256,8 @@ http {
proxy_pass http://$gateway_upstream;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-DeerFlow-Internal-Token "";
proxy_set_header X-DeerFlow-Owner-User-Id "";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $forwarded_proto;
Expand All @@ -241,6 +269,8 @@ http {
proxy_pass http://$gateway_upstream;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-DeerFlow-Internal-Token "";
proxy_set_header X-DeerFlow-Owner-User-Id "";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $forwarded_proto;
Expand All @@ -252,6 +282,8 @@ http {
proxy_pass http://$gateway_upstream;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-DeerFlow-Internal-Token "";
proxy_set_header X-DeerFlow-Owner-User-Id "";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $forwarded_proto;
Expand All @@ -266,6 +298,8 @@ http {
proxy_pass http://$provisioner_upstream;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-DeerFlow-Internal-Token "";
proxy_set_header X-DeerFlow-Owner-User-Id "";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $forwarded_proto;
Expand All @@ -278,6 +312,8 @@ http {
proxy_pass http://$gateway_upstream;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-DeerFlow-Internal-Token "";
proxy_set_header X-DeerFlow-Owner-User-Id "";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $forwarded_proto;
Expand All @@ -293,6 +329,8 @@ http {

# Headers
proxy_set_header Host $http_host;
proxy_set_header X-DeerFlow-Internal-Token "";
proxy_set_header X-DeerFlow-Owner-User-Id "";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $forwarded_proto;
Expand Down
3 changes: 3 additions & 0 deletions docker/provisioner/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ RUN pip install --no-cache-dir \
WORKDIR /app
COPY app.py .

RUN useradd --create-home --uid 1000 deerflow
USER deerflow

EXPOSE 8002

CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8002"]
2 changes: 2 additions & 0 deletions frontend/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,7 @@ RUN pnpm config set store-dir ${PNPM_STORE_PATH}
RUN if [ -n "${NPM_REGISTRY}" ]; then pnpm config set registry "${NPM_REGISTRY}"; fi
WORKDIR /app
COPY --from=builder /app/frontend ./frontend
# Run as the unprivileged `node` user (uid/gid 1000) shipped with the image.
USER node
EXPOSE 3000
CMD ["sh", "-c", "cd /app/frontend && pnpm start"]
5 changes: 5 additions & 0 deletions frontend/src/app/api/memory/[...path]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ async function proxyRequest(request: NextRequest, pathname: string) {
headers.delete("host");
headers.delete("connection");
headers.delete("content-length");
// The gateway treats these as internal trust credentials that bypass
// session auth; browser clients can set arbitrary custom headers, so a
// request transiting this public proxy must never forward them.
headers.delete("x-deerflow-internal-token");
headers.delete("x-deerflow-owner-user-id");

const hasBody = !["GET", "HEAD"].includes(request.method);
const response = await fetch(buildBackendUrl(pathname), {
Expand Down
5 changes: 5 additions & 0 deletions frontend/src/app/api/memory/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ async function proxyRequest(request: NextRequest, pathname: string) {
headers.delete("host");
headers.delete("connection");
headers.delete("content-length");
// The gateway treats these as internal trust credentials that bypass
// session auth; browser clients can set arbitrary custom headers, so a
// request transiting this public proxy must never forward them.
headers.delete("x-deerflow-internal-token");
headers.delete("x-deerflow-owner-user-id");

const hasBody = !["GET", "HEAD"].includes(request.method);
const response = await fetch(buildBackendUrl(pathname), {
Expand Down