Skip to content

Conversation

@amirhmoradi
Copy link
Contributor

Summary

This PR adds encryption at rest support for all S3 backups using rclone's native crypt backend. When enabled on a destination, all backups (database, compose, web-server, and volume backups) are automatically encrypted before upload and decrypted on restore.

Key Features

  • Encryption at rest for all backup types using NaCl SecretBox (XSalsa20 cipher + Poly1305)
  • Full rclone crypt options exposed in the UI:
    • Main encryption password
    • Salt password (password2) for additional security
    • Filename encryption (standard/obfuscate/off)
    • Directory name encryption
  • Transparent encryption/decryption - backups are encrypted on upload, decrypted on download
  • Backward compatible - existing unencrypted backups continue to work

Backup Types Covered

Type Backup Restore Cleanup
PostgreSQL
MySQL
MariaDB
MongoDB
Compose
Web Server
Volume Backups

Files Changed

  • Schema: Added encryption fields to destination table
  • Core: New getRcloneS3Remote() function with crypt overlay support
  • Backup utilities: All backup types now use encryption-aware functions
  • Restore utilities: All restore types now use encryption-aware functions
  • Volume backups: Added encryption support for volume backup/restore/cleanup
  • UI: New encryption settings section in destination form
  • Migration: 0131_add_destination_encryption.sql
  • Tests: 24 unit tests for encryption utilities

How It Works

  1. User enables encryption on an S3 destination and provides passwords
  2. When backing up, rclone wraps the S3 remote with a crypt overlay
  3. Files are encrypted locally before upload using NaCl SecretBox
  4. On restore, rclone automatically decrypts using the same credentials
  5. Filename encryption (optional) encrypts file/directory names on S3

Security

  • Uses rclone's battle-tested crypt implementation
  • Passwords are passed via environment variables (not command line)
  • Single quotes in passwords are properly escaped
  • No plaintext credentials in logs

Test plan

  • Unit tests pass (24 new tests for encryption utilities)
  • Manual test: Create destination with encryption enabled
  • Manual test: Create database backup with encryption
  • Manual test: Restore encrypted backup
  • Manual test: Create volume backup with encryption
  • Manual test: Verify encrypted files on S3 are unreadable without key
  • Manual test: Verify existing unencrypted backups still work

Screenshots

The UI adds an "Encryption Settings" section to the destination form with:

  • Enable/disable toggle
  • Password field with generate button
  • Salt password field with generate button
  • Filename encryption dropdown (standard/obfuscate/off)
  • Directory name encryption toggle
  • Link to rclone crypt documentation

Documentation: https://rclone.org/crypt/

Add support for encrypting backups before uploading to S3 and decrypting
during restore. This includes:

- Database schema changes:
  - Added encryptionEnabled, encryptionMethod, and encryptionKey fields
    to the destination table
  - Created migration 0130_add_destination_encryption.sql

- Server-side backup encryption:
  - Added getEncryptionCommand/getDecryptionCommand utilities
  - Updated all backup handlers (postgres, mysql, mariadb, mongo,
    compose, web-server) to encrypt backups when enabled
  - Encrypted backups have .enc extension appended

- Server-side restore decryption:
  - Updated all restore handlers to detect encrypted backups and
    decrypt them during restore
  - Added isEncryptedBackup utility to check file extensions

- UI changes:
  - Added encryption settings section to destination configuration
  - Toggle for enabling/disabling encryption
  - Dropdown for selecting encryption method (AES-256-CBC, AES-256-GCM)
  - Input for encryption key with generate button
  - Warning about storing keys securely

Encryption uses OpenSSL with PBKDF2 key derivation (100,000 iterations)
for secure password-based encryption.
Replace OpenSSL-based encryption with rclone's native crypt backend
for better integration and simpler architecture. The crypt backend
provides transparent encryption/decryption using NaCl SecretBox
(XSalsa20 cipher + Poly1305 for integrity).

Changes:
- Remove encryptionMethod field (rclone uses its own algorithm)
- Add getRcloneS3Remote() to generate crypt-wrapped remotes
- Simplify backup commands (encryption handled by rclone remote)
- Simplify restore commands (decryption happens automatically)
- Update UI to remove encryption method selection
- Simplify migration to only add encryptionEnabled and encryptionKey
Add all rclone crypt configuration options to give users complete control
over backup encryption settings:

New options:
- password2: Optional salt password for additional security (recommended)
- filenameEncryption: "standard", "obfuscate", or "off" (default: off)
- directoryNameEncryption: Encrypt directory names when filename encryption is enabled

UI improvements:
- Added link to rclone crypt documentation
- Password and salt password fields with generate buttons
- Filename encryption dropdown with descriptions
- Directory name encryption toggle (shown when filename encryption is enabled)

Encryption details:
- Uses NaCl SecretBox (XSalsa20 cipher + Poly1305)
- Filename encryption uses EME for "standard" mode
- All passwords should be stored securely as they cannot be recovered

See: https://rclone.org/crypt/
Add comprehensive tests for getEncryptionConfigFromDestination and
getRcloneS3Remote functions covering all encryption options including
password2 (salt), filename encryption, and directory name encryption.
Resolved conflict in drizzle migration journal by renaming
encryption migration from 0130 to 0131.
Update volume backup, restore, and cleanup functions to use
getRcloneS3Remote with encryption support. Volume backups now
respect the destination's encryption settings (encryptionEnabled,
encryptionKey, password2, filenameEncryption, directoryNameEncryption)
just like database backups.
@amirhmoradi
Copy link
Contributor Author

@Siumauricio Hi, thanks for the effort on Dokploy. I can see the that you are busy with the important number of commits and PRs on dokploy. Sorry to be bothering, any chance I get a feedback on my PRs in the coming weeks please? Is there a way for me to be more helpful on this project?

amirhmoradi and others added 11 commits December 17, 2025 23:33
…t-backup-encryption

Support encrypted backup maintenance
…dd-s3-backup-encryption-018RrZGgmyuupd7qWBRNYgCg
The encryption migration file (0131_add_destination_encryption.sql) had a
conflicting number with an existing migration (0131_volatile_beast.sql) and
was not registered in the drizzle journal, preventing it from being applied.

Changes:
- Rename 0131_add_destination_encryption.sql -> 0133_add_destination_encryption.sql
- Update _journal.json entry from "0133_add_create_env_file" to
  "0133_add_destination_encryption" to match the actual migration file

This ensures the encryption columns (encryptionEnabled, encryptionKey,
encryptionPassword2, filenameEncryption, directoryNameEncryption) are
properly added to the destination table when migrations run.
- Merge latest changes from upstream canary branch
- Resolve migration conflict: upstream added 0133_striped_the_order (webServerSettings)
- Renumber S3 encryption migration from 0133 to 0134_add_destination_encryption
- Create proper 0134_snapshot.json with encryption columns for destination table
- Update _journal.json with both migrations properly registered

Migration order is now:
- 0133: striped_the_order (creates webServerSettings table)
- 0134: add_destination_encryption (adds encryption columns to destination)
…ayer

The rclone crypt encryption is now handled transparently at the rclone
remote configuration level, eliminating the need for application-layer
encryption handling throughout the backup/restore codebase.

Key changes:
- Simplified getRcloneS3Remote() to take only destination parameter
  (encryption settings are read directly from destination object)
- Removed getEncryptionConfigFromDestination() and EncryptionConfig type
- Added buildRcloneCommand() helper for cleaner command construction
- Removed encryption config passing from all backup/restore functions:
  - mariadb.ts, postgres.ts, mysql.ts, mongo.ts, compose.ts, web-server.ts
  - All restore files in utils/restore/
  - All volume backup files in utils/volume-backups/
  - backup.ts router
- Updated tests to use simplified API

The encryption is now completely transparent - callers just use the
remote returned by getRcloneS3Remote() and rclone handles encryption/
decryption automatically based on the destination's encryption settings.

This follows the DRY principle by centralizing encryption logic in one
place (getRcloneS3Remote) instead of spreading it across the codebase.
…ayer

- Remove duplicate getS3Credentials function
- Improve documentation for getRcloneS3Remote
- Add @deprecated notice to legacy getS3Credentials function
- Clean up code organization in utils.ts

The encryption is implemented as a transparent overlay using rclone's
native crypt backend. When encryption is enabled on a destination,
the S3 remote is automatically wrapped with a crypt remote.
Reset backup.ts to canary and re-applied encryption changes with
proper tab indentation (was incorrectly using spaces).
Resolved migration numbering conflict by renaming local S3 encryption
migration from 0134 to 0135, allowing upstream's 0134_strong_hercules
to take the 0134 slot based on earlier timestamp.

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
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.

2 participants