Skip to content

Comments

feat(backups): add rclone-backed FTP/SFTP destination types#3788

Open
lweyrich1 wants to merge 2 commits intoDokploy:canaryfrom
lweyrich1:feat/416-rclone-destination-mvp
Open

feat(backups): add rclone-backed FTP/SFTP destination types#3788
lweyrich1 wants to merge 2 commits intoDokploy:canaryfrom
lweyrich1:feat/416-rclone-destination-mvp

Conversation

@lweyrich1
Copy link

@lweyrich1 lweyrich1 commented Feb 23, 2026

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

  • Added provider options in destination UI:
    • FTP (via rclone backend)
    • SFTP (via rclone backend)
  • Updated destination connection test to handle FTP/SFTP (rclone ls :ftp:... / :sftp:...)
  • Extended backup utils with provider-aware helpers:
    • getS3Credentials(...) now returns FTP/SFTP flags when selected
    • new getRcloneDestinationBase(...) for provider-aware remote prefixes (:s3:, :ftp:, :sftp:)
  • Switched backup upload/cleanup/list/restore code paths to use provider-aware destination base
    • database backups
    • compose backups
    • web-server backups
    • volume backups
    • restore flows
  • Added tests for new helpers in apps/dokploy/__test__/utils/backups.test.ts
  • Small README note mentioning FTP/SFTP support via rclone.

How to test locally

  1. Add a destination in Settings -> Destinations using provider FTP or SFTP
    • Access Key Id = username
    • Secret Access Key = password
    • Endpoint = host (for SFTP optionally host:port)
    • Bucket = remote base path (e.g. /backups)
  2. Use Test Connection in the UI
  3. Trigger a backup and confirm artifact appears in remote path.

CI / runtime dependency

  • Requires rclone binary available on the Dokploy host/server (already used by existing S3 backup flows).
  • For CI environments that run integration tests against real remotes, install via:
    • curl https://rclone.org/install.sh | sudo bash

No 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.

  • introduced new provider options in constants.ts for FTP and SFTP
  • extended getS3Credentials() to return FTP/SFTP-specific rclone flags when those providers are selected
  • added getRcloneDestinationBase() to build correct remote prefixes (:ftp:, :sftp:, :s3:) based on provider
  • updated all backup/restore utilities to use the new helpers
  • enhanced destination test connection to handle FTP/SFTP protocols
  • added comprehensive test coverage for new helper functions

Critical issue: credentials passed to rclone flags are not escaped, allowing command injection if passwords contain quotes or special shell characters

Confidence Score: 2/5

  • Not safe to merge due to command injection vulnerability in credential handling
  • The implementation correctly extends rclone support but has critical command injection vulnerabilities where user-provided credentials (passwords, usernames, hostnames) are directly interpolated into shell commands without proper escaping. A password containing " or $(...) would break the command or execute arbitrary code.
  • Pay close attention to packages/server/src/utils/backups/utils.ts and apps/dokploy/server/api/routers/destination.ts - these contain the command injection vulnerabilities that must be fixed before merge

Last reviewed commit: 382ff6b

(2/5) Greptile learns from your feedback when you react with thumbs up/down!

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

22 files reviewed, 6 comments

Edit Code Review Agent Settings | Greptile

Comment on lines 67 to 69
`--ftp-host="${endpoint}"`,
`--ftp-user="${accessKey}"`,
`--ftp-pass="${secretAccessKey}"`,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

passwords with special shell characters (quotes, $, backticks) will break commands or enable injection

Suggested change
`--ftp-host="${endpoint}"`,
`--ftp-user="${accessKey}"`,
`--ftp-pass="${secretAccessKey}"`,
`--ftp-host="${endpoint.replace(/"/g, '\\"')}"`,
`--ftp-user="${accessKey.replace(/"/g, '\\"')}"`,
`--ftp-pass="${secretAccessKey.replace(/"/g, '\\"')}"`,

Comment on lines 76 to 78
`--sftp-host="${host}"`,
`--sftp-user="${accessKey}"`,
`--sftp-pass="${secretAccessKey}"`,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same injection vulnerability as FTP - passwords need escaping

Suggested change
`--sftp-host="${host}"`,
`--sftp-user="${accessKey}"`,
`--sftp-pass="${secretAccessKey}"`,
`--sftp-host="${host.replace(/"/g, '\\"')}"`,
`--sftp-user="${accessKey.replace(/"/g, '\\"')}"`,
`--sftp-pass="${secretAccessKey.replace(/"/g, '\\"')}"`,

Comment on lines +61 to +63
`--ftp-host="${endpoint}"`,
`--ftp-user="${accessKey}"`,
`--ftp-pass="${secretAccessKey}"`,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

apply same escaping here for test connection to prevent injection

Suggested change
`--ftp-host="${endpoint}"`,
`--ftp-user="${accessKey}"`,
`--ftp-pass="${secretAccessKey}"`,
`--ftp-host="${endpoint.replace(/"/g, '\\"')}"`,
`--ftp-user="${accessKey.replace(/"/g, '\\"')}"`,
`--ftp-pass="${secretAccessKey.replace(/"/g, '\\"')}"`,

Comment on lines +70 to +72
`--sftp-host="${host}"`,
`--sftp-user="${accessKey}"`,
`--sftp-pass="${secretAccessKey}"`,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

apply same escaping for SFTP test connection

Suggested change
`--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(":");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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}`;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bucket value should be escaped to prevent path injection if it contains special characters

@lweyrich1
Copy link
Author

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.

@lweyrich1
Copy link
Author

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Ability to backup to more destination types

1 participant