feat(backup): additional options for database backups#3445
feat(backup): additional options for database backups#3445paulwer wants to merge 8 commits intoDokploy:canaryfrom
Conversation
…-backup-additional-options
|
Yes. I second that. This is key to restore a db with acl or RLS. Totally necessary for logto for example |
|
@Siumauricio I am very sorry, but I am not able to do testing right now and for the next few weeks. |
…-backup-additional-options
…-backup-additional-options
| 'docker exec -i $CONTAINER_ID bash -c "set -o pipefail; pg_dump', | ||
| '-Fc', | ||
| '--no-acl', | ||
| '--no-owner', | ||
| additionalOptions?.length ? additionalOptions.map(opt => `'${opt.trim().replace(/'/g, `'\\''`)}'`).join(' ') : '', | ||
| '-h localhost', | ||
| `-U ${databaseUser}`, | ||
| '--no-password', | ||
| database, | ||
| '| gzip"', | ||
| ].filter(Boolean).join(' '); |
There was a problem hiding this comment.
Shell injection via double-quote breakout
The additionalOptions are escaped with single-quote wrapping ('...'), but this entire command string sits inside a double-quoted bash -c "..." argument (line 86). Within the outer double-quote context, single quotes are treated as literal characters and do not prevent shell interpretation. A user-supplied option containing a " character (e.g., " ; malicious_command ; echo ") would terminate the bash -c argument and allow arbitrary command execution on the host or remote server.
The single-quote escaping on line 90 only handles ' characters but does not escape ", `, $, or \, all of which are interpreted within double quotes by the outer shell.
Since this command is executed via child_process.exec() or SSH, this is a command injection risk. Consider either:
- Escaping double quotes and other shell metacharacters (
",$,`,\) in addition to single quotes, or - Validating
additionalOptionsagainst a strict allowlist pattern (e.g.,/^--?[\w-]+(=[\w\-\/.]+)?$/) on the server side before they reach command generation
| export const getPostgresRestoreCommand = ( | ||
| database: string, | ||
| databaseUser: string, | ||
| additionalOptions?: string[], | ||
| ) => { | ||
| return `docker exec -i $CONTAINER_ID sh -c "pg_restore -U '${databaseUser}' -d ${database} -O --clean --if-exists"`; | ||
| }; |
There was a problem hiding this comment.
additionalOptions parameter accepted but never used
All four restore command functions (getPostgresRestoreCommand, getMariadbRestoreCommand, getMysqlRestoreCommand, getMongoRestoreCommand) accept additionalOptions as a parameter but never include it in the generated command string. The parameter is threaded through generateRestoreCommand → getRestoreCommand → the individual restore files, but has no effect on the actual restore commands.
This appears to be an incomplete implementation. If the intent is to support additional options for restore commands, the options need to be incorporated into the command strings. If only backup commands should support this feature, the parameter should be removed from the restore path to avoid confusion.
| return [ | ||
| 'docker exec -i $CONTAINER_ID bash -c "set -o pipefail; mongodump', | ||
| `-d ${database}`, | ||
| `-u ${databaseUser}`, | ||
| `-p ${databasePassword}`, | ||
| '--archive', | ||
| '--authenticationDatabase admin', | ||
| '--gzip', | ||
| additionalOptions?.length ? additionalOptions.map(opt => `'${opt.trim().replace(/'/g, `'\\''`)}'`).join(' ') : '', | ||
| '"', | ||
| ].filter(Boolean).join(' '); | ||
| }; |
There was a problem hiding this comment.
Mongo backup command has a stray trailing "
For the other database commands (postgres, mysql, mariadb), the command ends with | gzip" which correctly closes the bash -c "..." double quote. But for mongo, the closing " is a separate array element on line 151. When additionalOptions is empty, filter(Boolean) removes the empty string, and the command ends correctly as ...--gzip ". However, when additionalOptions has values, the result is ...--gzip '--opt1' " — the additional options are placed between --gzip and the closing ", meaning the closing " is separated by a space from the last option. While this technically still works (the " closes the bash -c argument), it's inconsistent with the other commands where options are placed before the pipe. This is a minor structural inconsistency but worth being aware of.
| HTMLInputElement, | ||
| InputArrayProps | ||
| >(({ className, errorMessage, value, onChange, disabled, ...props }, ref) => { | ||
| if (!value) value = []; |
There was a problem hiding this comment.
Reassigning a const parameter breaks immutability
Reassigning the value parameter (if (!value) value = [];) mutates a const-declared function parameter from the destructuring. While this works at runtime because the parameter is function-scoped, it's an unconventional pattern. A local variable would be clearer:
| if (!value) value = []; | |
| const items = value ?? []; |
Then use items in place of value throughout the component.
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!
Additional Comments (1)
The The original To fix, either move the delay before |
What is this PR about?
Adds a new optional field additionalOptions that allows users to pass extra arguments to the dump commands for databases.
We also have created a fix for successfully running dokploy:setup on windows systems
Checklist
Before submitting this PR, please make sure that:
canarybranch.Issues related (if applicable)
closes #3191
Screenshots (if applicable)
Notes
@Siumauricio I tested the ui/api changes for creating / restoring backups, which works fine, but I was not able to test backup jobs for all databases. please validate while reviewing this PR please :)
Please also write suggestion/feedback for UI-Component created by me for ArrayLists, if it matches your style-guide or do necessary changes yourself :)
We also have created a fix for successfully running dokploy:setup on windows systems.
Greptile Summary
This PR adds an
additionalOptionsfield to database backups, allowing users to pass extra CLI arguments to dump/restore commands. It also includes a Windows compatibility fix for the setup script by replacingsleep 5(Unix-only) with a Node.jssetTimeout.Key issues found:
additionalOptionsare escaped with single-quote wrapping, but the entire command sits inside a double-quotedbash -c "..."argument. Characters like",$, and backticks in user input can break out of the double-quoted context and execute arbitrary commands on the host or remote server via SSH. Server-side validation should enforce a strict allowlist pattern for option strings.additionalOptionsunused in restore commands: The parameter is threaded through the entire restore code path (getRestoreCommand→generateRestoreCommand→ individual restore functions) but never incorporated into the actual restore command strings. This is either an incomplete implementation or dead code that should be cleaned up.sleep 5tosetTimeout) is placed afterexit(0)in the success path, meaning it never executes on successful setup. The migration command may fail if Postgres hasn't finished initializing.Confidence Score: 2/5
packages/server/src/utils/backups/utils.ts(shell injection),packages/server/src/utils/restore/utils.ts(unused parameter), andapps/dokploy/setup.ts(delay bug).Last reviewed commit: d3e32d2
(2/5) Greptile learns from your feedback when you react with thumbs up/down!