Skip to content
Merged
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
3 changes: 3 additions & 0 deletions canvas/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,7 @@ COPY --from=builder /app/public ./public
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
# Non-root runtime — node image defaults to root, explicitly drop.
RUN addgroup -g 1000 canvas && adduser -u 1000 -G canvas -s /bin/sh -D canvas
USER canvas
CMD ["node", "server.js"]
4 changes: 4 additions & 0 deletions workspace-server/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,9 @@ COPY workspace-server/migrations /migrations
COPY --from=templates /workspace-configs-templates /workspace-configs-templates
COPY --from=templates /org-templates /org-templates
COPY --from=templates /plugins /plugins
# Non-root runtime — platform binary doesn't need root; dropping privileges
# prevents container escape attacks from reaching host UID 0.
RUN addgroup -g 1000 platform && adduser -u 1000 -G platform -s /bin/sh -D platform
EXPOSE 8080
USER platform
CMD ["/platform"]
13 changes: 12 additions & 1 deletion workspace-server/Dockerfile.tenant
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ RUN chmod +x /scripts/clone-manifest.sh && /scripts/clone-manifest.sh /manifest.
FROM node:20-alpine
RUN apk add --no-cache ca-certificates git tzdata

# Non-root runtime for the Node.js canvas process.
# The Go binary (started by entrypoint.sh) is also non-root — the
# entrypoint runs as root only long enough to set volume ownership,
# then exec's as the 'canvas' user via su-exec / setpriv.
# The Go platform itself drops privileges after init.
RUN addgroup -g 1000 canvas && adduser -u 1000 -G canvas -s /bin/sh -D canvas

# Go platform binary
COPY --from=go-builder /platform /platform
COPY workspace-server/migrations /migrations
Expand All @@ -62,7 +69,11 @@ COPY --from=canvas-builder /canvas/.next/static ./.next/static
COPY --from=canvas-builder /canvas/public ./public

COPY workspace-server/entrypoint-tenant.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
RUN chmod +x /entrypoint.sh && \
chown -R canvas:canvas /canvas /platform /migrations

EXPOSE 8080
# entrypoint.sh starts as root to fix volume perms, then drops to
# canvas user. The Go binary (PID 1 replacement) runs as non-root.
USER canvas
CMD ["/entrypoint.sh"]
4 changes: 4 additions & 0 deletions workspace-server/entrypoint-tenant.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
#!/bin/sh
# Tenant entrypoint — starts both Go platform (API) and Canvas (UI).
#
# Container runs as non-root 'canvas' user (USER directive in Dockerfile.tenant).
# Both processes start as non-root. SIGTERM propagates to child processes via the
# shell's trap + wait -n pattern below.
#
# Go platform listens on :8080 (Fly health checks hit this port).
# Canvas Node.js listens on :3000 (internal only).
# The Go platform's fallback handler proxies non-API routes to :3000
Expand Down
Loading