fix(compose): materialize file mounts and purge stale directories before deployment (#5292) - #5447
Conversation
| let commands = `mkdir -p ${quote([filesPath])};\n`; | ||
|
|
||
| for (const mount of fileMounts) { | ||
| const cleanPath = (mount.filePath || "").trim().replace(/^[/\\]+/, ""); |
There was a problem hiding this comment.
Mount paths escape containment
cleanPath removes leading separators but still accepts .. components. An authenticated caller with mount-management permission can therefore escape the application's files directory. The deployment command can write controlled content to the escaped host path, while the related cleanup in createFile and getCreateFileCommand can recursively delete an escaped directory before replacing it with a file. Validate that the resolved path remains beneath filesPath or outputPath before creating, deleting, or writing anything.
How this was verified: Mount paths reach these helpers without traversal validation, and the generated operations run on the deployment host with paths normalized from attacker-controlled .. components.
Knowledge Base Used:
| const exportEnvCommand = getExportEnvCommand(compose); | ||
|
|
||
| const newCompose = await writeDomainsToCompose(compose, domains); | ||
| const mountsCommand = getCreateMountsCommand(compose); |
There was a problem hiding this comment.
Swarm workers miss mount files
Mount materialization runs only on the host executing the deployment command, but it is enabled for both Docker Compose and Swarm stack modes. In a multi-node Swarm, a service using one of these bind-mounted files can be scheduled on a worker where the source file was never created, causing the task to fail its bind mount or use an incorrect host path. Stack deployments need to distribute files to eligible nodes, constrain placement to the prepared node, or reject file mounts that cannot be satisfied across the cluster.
Knowledge Base Used:
| export const generateCreateMountsCommand = ( | ||
| _appName: string, | ||
| baseFilesPath: string, | ||
| mounts: Mount[], | ||
| ) => { |
There was a problem hiding this comment.
The regression suite reimplements mount command generation and sanitization instead of importing the exported production function. The copies also use different quoting and path-building helpers, so these tests can keep passing when getCreateMountsCommand, createFile, or getCreateFileCommand regresses. Exercise the production exports directly so the tests protect the code used during deployment.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Summary of Changes
Closes #5292
Context & Problem
In Compose projects, services reference file mounts via
../files/<mountPath>:<container target>(e.g. Postgresdocker-entrypoint-initdb.d/init.sqlor custom config files). As reported in #5292:getBuildComposeCommanddetected mounts (Detected: X mounts 📂in the log box) but never materialized them to disk before runningcd code && docker compose up.createFilecould write directly to/etc/dokploy/compose/<appName>/files, corrupting the root directory into a plain file.createFileorgetCreateFileCommandto write the real file failed or crashed withEISDIR: illegal operation on a directory.Solution
Materialize Compose Mounts on Deployment:
getCreateMountsCommandinpackages/server/src/utils/builders/compose.tsand integrated it intogetBuildComposeCommand./etc/dokploy/compose/<appName>/filesis created, creates nested parent directories, deletes any stale empty directories left behind by Docker (if [ -d "$fullPath" ]; then rm -rf "$fullPath"; fi), and writes the base64-decoded content beforedocker compose upexecutes.Sanitization & Stale Directory Cleanup in
docker/utils.ts:createFileandgetCreateFileCommand:filePathto strip leading slashes and whitespace, preventing directory escapes and preventing corruption of the basefilesfolder.writeFileSyncor shell commands.Automated Testing:
apps/dokploy/__test__/compose/compose-file-mounts.test.ts.This PR is not safe to merge until file paths are contained beneath their application roots and file mounts are handled correctly for multi-node Swarm deployments.
Summary
Reviews (1) · Last reviewed commit: "fix(compose): materialize file mounts an..."