feat(backups): add rclone-backed FTP/SFTP destination types#3788
feat(backups): add rclone-backed FTP/SFTP destination types#3788lweyrich1 wants to merge 2 commits intoDokploy:canaryfrom
Conversation
| `--ftp-host="${endpoint}"`, | ||
| `--ftp-user="${accessKey}"`, | ||
| `--ftp-pass="${secretAccessKey}"`, |
There was a problem hiding this comment.
passwords with special shell characters (quotes, $, backticks) will break commands or enable injection
| `--ftp-host="${endpoint}"`, | |
| `--ftp-user="${accessKey}"`, | |
| `--ftp-pass="${secretAccessKey}"`, | |
| `--ftp-host="${endpoint.replace(/"/g, '\\"')}"`, | |
| `--ftp-user="${accessKey.replace(/"/g, '\\"')}"`, | |
| `--ftp-pass="${secretAccessKey.replace(/"/g, '\\"')}"`, |
| `--sftp-host="${host}"`, | ||
| `--sftp-user="${accessKey}"`, | ||
| `--sftp-pass="${secretAccessKey}"`, |
There was a problem hiding this comment.
same injection vulnerability as FTP - passwords need escaping
| `--sftp-host="${host}"`, | |
| `--sftp-user="${accessKey}"`, | |
| `--sftp-pass="${secretAccessKey}"`, | |
| `--sftp-host="${host.replace(/"/g, '\\"')}"`, | |
| `--sftp-user="${accessKey.replace(/"/g, '\\"')}"`, | |
| `--sftp-pass="${secretAccessKey.replace(/"/g, '\\"')}"`, |
| `--ftp-host="${endpoint}"`, | ||
| `--ftp-user="${accessKey}"`, | ||
| `--ftp-pass="${secretAccessKey}"`, |
There was a problem hiding this comment.
apply same escaping here for test connection to prevent injection
| `--ftp-host="${endpoint}"`, | |
| `--ftp-user="${accessKey}"`, | |
| `--ftp-pass="${secretAccessKey}"`, | |
| `--ftp-host="${endpoint.replace(/"/g, '\\"')}"`, | |
| `--ftp-user="${accessKey.replace(/"/g, '\\"')}"`, | |
| `--ftp-pass="${secretAccessKey.replace(/"/g, '\\"')}"`, |
| `--sftp-host="${host}"`, | ||
| `--sftp-user="${accessKey}"`, | ||
| `--sftp-pass="${secretAccessKey}"`, |
There was a problem hiding this comment.
apply same escaping for SFTP test connection
| `--sftp-host="${host}"`, | |
| `--sftp-user="${accessKey}"`, | |
| `--sftp-pass="${secretAccessKey}"`, | |
| `--sftp-host="${host.replace(/"/g, '\\"')}"`, | |
| `--sftp-user="${accessKey.replace(/"/g, '\\"')}"`, | |
| `--sftp-pass="${secretAccessKey.replace(/"/g, '\\"')}"`, |
| } | ||
|
|
||
| if (provider === "SFTP") { | ||
| const [host, port] = endpoint.split(":"); |
There was a problem hiding this comment.
endpoint.split(":") will fail if IPv6 address is used (e.g., [::1]:22), consider using lastIndexOf or URL parsing
|
|
||
| export const getRcloneDestinationBase = (destination: Destination) => { | ||
| if (destination.provider === "FTP") { | ||
| return `:ftp:${destination.bucket}`; |
There was a problem hiding this comment.
bucket value should be escaped to prevent path injection if it contains special characters
|
pushed a small hardening tweak: we now escape user-provided values when building rclone flags (credentials/endpoint/etc). keeps the existing format but avoids shell-breaking chars and reduces injection risk if someone puts weird values in there. |
|
follow-up: my previous comment got mangled by shell backticks. intent: keep existing --flag="..." style (tests expect it), but escape characters (, ", $, `) so credentials/endpoints can’t break the command and it mitigates injection risk. |
Summary
Adds small MVP support for additional backup destination types via rclone by introducing FTP and SFTP destination providers.
This keeps the existing destination model and backup flow, but routes upload/list/restore paths through rclone backends based on provider.
Fixes #416
What changed
FTP (via rclone backend)SFTP (via rclone backend)rclone ls :ftp:.../:sftp:...)getS3Credentials(...)now returns FTP/SFTP flags when selectedgetRcloneDestinationBase(...)for provider-aware remote prefixes (:s3:,:ftp:,:sftp:)apps/dokploy/__test__/utils/backups.test.tsHow to test locally
FTPorSFTPhost:port)/backups)CI / runtime dependency
rclonebinary available on the Dokploy host/server (already used by existing S3 backup flows).curl https://rclone.org/install.sh | sudo bashNo payment/KYC flow changes, and UI changes are minimal.
Greptile Summary
Added FTP and SFTP backup destination support using rclone backends. Updated destination configuration UI to show context-aware labels (Username/Password for FTP/SFTP vs Access Key/Secret Key for S3). Modified backup and restore flows across all database types to use provider-aware destination paths via new
getRcloneDestinationBase()helper.constants.tsfor FTP and SFTPgetS3Credentials()to return FTP/SFTP-specific rclone flags when those providers are selectedgetRcloneDestinationBase()to build correct remote prefixes (:ftp:,:sftp:,:s3:) based on providerCritical issue: credentials passed to rclone flags are not escaped, allowing command injection if passwords contain quotes or special shell characters
Confidence Score: 2/5
"or$(...)would break the command or execute arbitrary code.packages/server/src/utils/backups/utils.tsandapps/dokploy/server/api/routers/destination.ts- these contain the command injection vulnerabilities that must be fixed before mergeLast reviewed commit: 382ff6b
(2/5) Greptile learns from your feedback when you react with thumbs up/down!