-
-
Notifications
You must be signed in to change notification settings - Fork 2.4k
feat: add SFTP as a backup destination provider #3877
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| ALTER TABLE "destination" ADD COLUMN "sftpHost" text;--> statement-breakpoint | ||
| ALTER TABLE "destination" ADD COLUMN "sftpPort" integer DEFAULT 22;--> statement-breakpoint | ||
| ALTER TABLE "destination" ADD COLUMN "sftpUser" text;--> statement-breakpoint | ||
| ALTER TABLE "destination" ADD COLUMN "sftpPassword" text;--> statement-breakpoint | ||
| ALTER TABLE "destination" ADD COLUMN "sftpPath" text; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -77,6 +77,41 @@ export const getS3Credentials = (destination: Destination) => { | |
| return rcloneFlags; | ||
| }; | ||
|
|
||
| export const getSftpCredentials = (destination: Destination) => { | ||
| const { sftpHost, sftpPort, sftpUser, sftpPassword } = destination; | ||
| if (!sftpHost || !sftpUser || !sftpPassword) { | ||
| throw new Error( | ||
| "SFTP destination requires host, user, and password to be configured", | ||
| ); | ||
| } | ||
| return [ | ||
| `--sftp-host="${sftpHost}"`, | ||
| `--sftp-port="${sftpPort ?? 22}"`, | ||
| `--sftp-user="${sftpUser}"`, | ||
| `--sftp-pass=$(rclone obscure "${sftpPassword}")`, | ||
| ]; | ||
|
Comment on lines
+80
to
+92
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Add explicit null-checks before constructing flags: if (!sftpHost || !sftpUser || !sftpPassword) {
throw new Error("SFTP destination is missing required credentials");
}Alternatively, add |
||
| }; | ||
|
|
||
| export const getRcloneCredentials = ( | ||
| destination: Destination, | ||
| ): { flags: string[]; backend: string } => { | ||
| if (destination.provider === "sftp") { | ||
| return { flags: getSftpCredentials(destination), backend: "sftp" }; | ||
| } | ||
| return { flags: getS3Credentials(destination), backend: "s3" }; | ||
| }; | ||
|
|
||
| export const getRcloneDestinationPath = ( | ||
| destination: Destination, | ||
| remotePath: string, | ||
| ): string => { | ||
| if (destination.provider === "sftp") { | ||
| const basePath = (destination.sftpPath ?? "").replace(/\/+$/, ""); | ||
| return `:sftp:${basePath}/${remotePath}`; | ||
| } | ||
| return `:s3:${destination.bucket}/${remotePath}`; | ||
| }; | ||
|
Comment on lines
+95
to
+113
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The new For example, in const rcloneFlags = getS3Credentials(destination); // never routes to SFTP
const rcloneDestination = `:s3:${destination.bucket}/...`; // always S3All backup execution and cleanup call sites must be updated to use the new dispatcher functions for SFTP support to work end-to-end. |
||
|
|
||
| export const getPostgresBackupCommand = ( | ||
| database: string, | ||
| databaseUser: string, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
--sftp-pass-is-base64=falseis not a valid rclone SFTP flag. More critically, rclone's--sftp-passexpects a password that has been pre-obscured usingrclone obscure <password>, not plaintext. Passing a raw plaintext password will result in rclone failing to authenticate.The password must be obscured before being embedded in the flag—either store it already-obscured in the database, or obscure it at runtime via a subprocess call to
rclone obscure.