ruborg 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +175 -0
- data/CHANGELOG.md +26 -0
- data/CLAUDE.md +67 -1
- data/README.md +337 -80
- data/Rakefile +1 -1
- data/SECURITY.md +41 -2
- data/exe/ruborg +1 -1
- data/lib/ruborg/backup.rb +97 -12
- data/lib/ruborg/cli.rb +257 -78
- data/lib/ruborg/config.rb +18 -61
- data/lib/ruborg/logger.rb +4 -5
- data/lib/ruborg/passbolt.rb +5 -5
- data/lib/ruborg/repository.rb +275 -7
- data/lib/ruborg/version.rb +2 -2
- data/lib/ruborg.rb +1 -1
- data/ruborg.yml.example +136 -22
- metadata +41 -12
data/README.md
CHANGED
|
@@ -17,7 +17,14 @@ A friendly Ruby frontend for [Borg Backup](https://www.borgbackup.org/). Ruborg
|
|
|
17
17
|
- 📊 **Logging** - Comprehensive logging with daily rotation
|
|
18
18
|
- 🗄️ **Multi-Repository** - Manage multiple backup repositories with different sources
|
|
19
19
|
- 🔄 **Auto-initialization** - Automatically initialize repositories on first use
|
|
20
|
-
-
|
|
20
|
+
e- ⏰ **Retention Policies** - Configure backup retention (hourly, daily, weekly, monthly, yearly)
|
|
21
|
+
- 🗑️ **Automatic Pruning** - Automatically remove old backups based on retention policies
|
|
22
|
+
- 📁 **Per-File Backup Mode** - NEW! Backup each file as a separate archive with metadata-based retention
|
|
23
|
+
- 🕒 **File Metadata Retention** - NEW! Prune based on file modification time, works even after files are deleted
|
|
24
|
+
- 📋 **Repository Descriptions** - Document each repository's purpose
|
|
25
|
+
- 📈 **Summary View** - Quick overview of all repositories and their configurations
|
|
26
|
+
- 🔧 **Custom Borg Path** - Support for custom Borg executable paths per repository
|
|
27
|
+
- ✅ **Well-tested** - Comprehensive test suite with RSpec (147 tests)
|
|
21
28
|
- 🔒 **Security-focused** - Path validation, safe YAML loading, command injection protection
|
|
22
29
|
|
|
23
30
|
## Prerequisites
|
|
@@ -66,64 +73,44 @@ gem install ruborg
|
|
|
66
73
|
|
|
67
74
|
## Configuration
|
|
68
75
|
|
|
69
|
-
|
|
76
|
+
Create your configuration file from the example template:
|
|
70
77
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
# Repository path
|
|
75
|
-
repository: /path/to/borg/repository
|
|
76
|
-
|
|
77
|
-
# Paths to backup
|
|
78
|
-
backup_paths:
|
|
79
|
-
- /home/user/documents
|
|
80
|
-
- /home/user/projects
|
|
81
|
-
|
|
82
|
-
# Exclude patterns
|
|
83
|
-
exclude_patterns:
|
|
84
|
-
- "*.tmp"
|
|
85
|
-
- "*.log"
|
|
86
|
-
|
|
87
|
-
# Compression algorithm (lz4, zstd, zlib, lzma, none)
|
|
88
|
-
compression: lz4
|
|
89
|
-
|
|
90
|
-
# Encryption mode (repokey, keyfile, none)
|
|
91
|
-
encryption: repokey
|
|
92
|
-
|
|
93
|
-
# Passbolt integration (optional)
|
|
94
|
-
passbolt:
|
|
95
|
-
resource_id: "your-passbolt-resource-uuid"
|
|
96
|
-
|
|
97
|
-
# Auto-initialize repository (optional, default: false)
|
|
98
|
-
auto_init: true
|
|
99
|
-
|
|
100
|
-
# Log file path (optional, default: ~/.ruborg/logs/ruborg.log)
|
|
101
|
-
log_file: /var/log/ruborg.log
|
|
102
|
-
|
|
103
|
-
# Borg environment options (optional)
|
|
104
|
-
borg_options:
|
|
105
|
-
allow_relocated_repo: true # Allow relocated repositories (default: true)
|
|
106
|
-
allow_unencrypted_repo: true # Allow unencrypted repositories (default: true)
|
|
78
|
+
```bash
|
|
79
|
+
cp ruborg.yml.example ruborg.yml
|
|
80
|
+
chmod 600 ruborg.yml # Important: protect your configuration
|
|
107
81
|
```
|
|
108
82
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
For managing multiple repositories with different sources:
|
|
83
|
+
Then edit `ruborg.yml` with your settings. Ruborg uses a multi-repository YAML configuration format:
|
|
112
84
|
|
|
113
85
|
```yaml
|
|
114
86
|
# Global settings (applied to all repositories unless overridden)
|
|
115
87
|
compression: lz4
|
|
116
88
|
encryption: repokey
|
|
117
89
|
auto_init: true
|
|
90
|
+
log_file: /var/log/ruborg.log
|
|
91
|
+
|
|
92
|
+
# Custom Borg executable path (optional)
|
|
93
|
+
# Use this if borg is not in PATH or you want to use a specific version
|
|
94
|
+
# borg_path: /usr/local/bin/borg
|
|
95
|
+
|
|
118
96
|
passbolt:
|
|
119
97
|
resource_id: "global-passbolt-id"
|
|
120
98
|
borg_options:
|
|
121
99
|
allow_relocated_repo: false
|
|
122
100
|
allow_unencrypted_repo: false
|
|
123
101
|
|
|
102
|
+
# Global retention policy (can be overridden per repository)
|
|
103
|
+
retention:
|
|
104
|
+
keep_hourly: 24 # Keep 24 hourly backups
|
|
105
|
+
keep_daily: 7 # Keep 7 daily backups
|
|
106
|
+
keep_weekly: 4 # Keep 4 weekly backups
|
|
107
|
+
keep_monthly: 6 # Keep 6 monthly backups
|
|
108
|
+
keep_yearly: 1 # Keep 1 yearly backup
|
|
109
|
+
|
|
124
110
|
# Multiple repositories
|
|
125
111
|
repositories:
|
|
126
112
|
- name: documents
|
|
113
|
+
description: "Personal and work documents backup"
|
|
127
114
|
path: /mnt/backup/documents
|
|
128
115
|
sources:
|
|
129
116
|
- name: home-docs
|
|
@@ -138,10 +125,18 @@ repositories:
|
|
|
138
125
|
- "*.log"
|
|
139
126
|
|
|
140
127
|
- name: databases
|
|
128
|
+
description: "MySQL and PostgreSQL database dumps"
|
|
141
129
|
path: /mnt/backup/databases
|
|
142
130
|
# Repository-specific passbolt (overrides global)
|
|
143
131
|
passbolt:
|
|
144
132
|
resource_id: "db-specific-passbolt-id"
|
|
133
|
+
# Repository-specific retention (overrides global)
|
|
134
|
+
retention:
|
|
135
|
+
keep_daily: 14
|
|
136
|
+
keep_weekly: 8
|
|
137
|
+
keep_monthly: 12
|
|
138
|
+
# Repository-specific borg executable path (optional)
|
|
139
|
+
# borg_path: /opt/borg-2.0/bin/borg
|
|
145
140
|
sources:
|
|
146
141
|
- name: mysql
|
|
147
142
|
paths:
|
|
@@ -149,14 +144,29 @@ repositories:
|
|
|
149
144
|
- name: postgres
|
|
150
145
|
paths:
|
|
151
146
|
- /var/lib/postgresql/dumps
|
|
147
|
+
|
|
148
|
+
- name: media
|
|
149
|
+
description: "Photos and videos archive"
|
|
150
|
+
path: /mnt/backup/media
|
|
151
|
+
# Override compression for large media files
|
|
152
|
+
compression: lz4
|
|
153
|
+
retention:
|
|
154
|
+
keep_weekly: 2
|
|
155
|
+
keep_monthly: 3
|
|
156
|
+
sources:
|
|
157
|
+
- name: photos
|
|
158
|
+
paths:
|
|
159
|
+
- /home/user/Pictures
|
|
152
160
|
```
|
|
153
161
|
|
|
154
|
-
**
|
|
155
|
-
-
|
|
156
|
-
-
|
|
157
|
-
-
|
|
158
|
-
-
|
|
159
|
-
-
|
|
162
|
+
**Configuration Features:**
|
|
163
|
+
- **Descriptions**: Add `description` field to document each repository's purpose
|
|
164
|
+
- **Global Settings**: Compression, encryption, auto_init, log_file, borg_path, borg_options, and retention apply to all repositories
|
|
165
|
+
- **Per-Repository Overrides**: Any global setting can be overridden at the repository level (including custom borg_path per repository)
|
|
166
|
+
- **Custom Borg Path**: Specify a custom Borg executable path if borg is not in PATH or to use a specific version
|
|
167
|
+
- **Retention Policies**: Define how many backups to keep (hourly, daily, weekly, monthly, yearly)
|
|
168
|
+
- **Multiple Sources**: Each repository can have multiple backup sources with their own exclude patterns
|
|
169
|
+
- **Flexible Organization**: Organize backups by type (documents, databases, media) with different policies
|
|
160
170
|
|
|
161
171
|
## Usage
|
|
162
172
|
|
|
@@ -172,22 +182,6 @@ ruborg init /path/to/repository --passbolt-id "resource-uuid"
|
|
|
172
182
|
|
|
173
183
|
### Create a Backup
|
|
174
184
|
|
|
175
|
-
**Single repository:**
|
|
176
|
-
```bash
|
|
177
|
-
# Using default configuration (ruborg.yml)
|
|
178
|
-
ruborg backup
|
|
179
|
-
|
|
180
|
-
# Using custom configuration file
|
|
181
|
-
ruborg backup --config /path/to/config.yml
|
|
182
|
-
|
|
183
|
-
# With custom archive name
|
|
184
|
-
ruborg backup --name "my-backup-2025-10-04"
|
|
185
|
-
|
|
186
|
-
# Remove source files after successful backup
|
|
187
|
-
ruborg backup --remove-source
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
**Multi-repository:**
|
|
191
185
|
```bash
|
|
192
186
|
# Backup specific repository
|
|
193
187
|
ruborg backup --repository documents
|
|
@@ -197,31 +191,82 @@ ruborg backup --all
|
|
|
197
191
|
|
|
198
192
|
# Backup specific repository with custom name
|
|
199
193
|
ruborg backup --repository databases --name "db-backup-2025-10-05"
|
|
194
|
+
|
|
195
|
+
# Using custom configuration file
|
|
196
|
+
ruborg backup --config /path/to/config.yml --repository documents
|
|
197
|
+
|
|
198
|
+
# Remove source files after successful backup
|
|
199
|
+
ruborg backup --repository documents --remove-source
|
|
200
200
|
```
|
|
201
201
|
|
|
202
202
|
### List Archives
|
|
203
203
|
|
|
204
204
|
```bash
|
|
205
|
-
|
|
205
|
+
# List archives for a specific repository
|
|
206
|
+
ruborg list --repository documents
|
|
206
207
|
```
|
|
207
208
|
|
|
208
209
|
### Restore from Archive
|
|
209
210
|
|
|
210
211
|
```bash
|
|
211
212
|
# Restore entire archive to current directory
|
|
212
|
-
ruborg restore archive-name
|
|
213
|
+
ruborg restore archive-name --repository documents
|
|
213
214
|
|
|
214
215
|
# Restore to specific directory
|
|
215
|
-
ruborg restore archive-name --destination /path/to/restore
|
|
216
|
+
ruborg restore archive-name --repository documents --destination /path/to/restore
|
|
216
217
|
|
|
217
218
|
# Restore a single file from archive
|
|
218
|
-
ruborg restore archive-name --path /path/to/file.txt --destination /new/location
|
|
219
|
+
ruborg restore archive-name --repository documents --path /path/to/file.txt --destination /new/location
|
|
219
220
|
```
|
|
220
221
|
|
|
221
222
|
### View Repository Information
|
|
222
223
|
|
|
223
224
|
```bash
|
|
225
|
+
# Show summary of all configured repositories
|
|
224
226
|
ruborg info
|
|
227
|
+
|
|
228
|
+
# View detailed info for a specific repository
|
|
229
|
+
ruborg info --repository documents
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
The `info` command without `--repository` displays a summary showing:
|
|
233
|
+
- Global configuration settings
|
|
234
|
+
- All configured repositories with their descriptions
|
|
235
|
+
- Retention policies (global and per-repository overrides)
|
|
236
|
+
- Number of sources per repository
|
|
237
|
+
|
|
238
|
+
### Check Repository Compatibility
|
|
239
|
+
|
|
240
|
+
```bash
|
|
241
|
+
# Check specific repository compatibility with installed Borg version
|
|
242
|
+
ruborg check --repository documents
|
|
243
|
+
|
|
244
|
+
# Check all repositories
|
|
245
|
+
ruborg check --all
|
|
246
|
+
|
|
247
|
+
# Check with data integrity verification (slower)
|
|
248
|
+
ruborg check --repository documents --verify-data
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
The `check` command verifies:
|
|
252
|
+
- Installed Borg version
|
|
253
|
+
- Repository format version
|
|
254
|
+
- Compatibility between Borg and repository versions
|
|
255
|
+
- Optionally: Repository data integrity (with `--verify-data`)
|
|
256
|
+
|
|
257
|
+
**Example output:**
|
|
258
|
+
```
|
|
259
|
+
Borg version: 1.2.8
|
|
260
|
+
|
|
261
|
+
--- Checking repository: documents ---
|
|
262
|
+
Repository version: 1
|
|
263
|
+
✓ Compatible with Borg 1.2.8
|
|
264
|
+
|
|
265
|
+
--- Checking repository: databases ---
|
|
266
|
+
Repository version: 2
|
|
267
|
+
✗ INCOMPATIBLE with Borg 1.2.8
|
|
268
|
+
Repository version 2 cannot be read by Borg 1.2.8
|
|
269
|
+
Please upgrade Borg or migrate the repository
|
|
225
270
|
```
|
|
226
271
|
|
|
227
272
|
## Logging
|
|
@@ -278,15 +323,17 @@ Ruborg will automatically retrieve the passphrase when performing backup operati
|
|
|
278
323
|
|
|
279
324
|
## Auto-initialization
|
|
280
325
|
|
|
281
|
-
Set `auto_init: true` in
|
|
326
|
+
Set `auto_init: true` in the global settings or per-repository to automatically initialize repositories on first use:
|
|
282
327
|
|
|
283
328
|
```yaml
|
|
284
|
-
repository: /path/to/borg/repository
|
|
285
329
|
auto_init: true
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
330
|
+
repositories:
|
|
331
|
+
- name: documents
|
|
332
|
+
path: /path/to/borg/repository
|
|
333
|
+
sources:
|
|
334
|
+
- name: main
|
|
335
|
+
paths:
|
|
336
|
+
- /path/to/backup
|
|
290
337
|
```
|
|
291
338
|
|
|
292
339
|
When enabled, ruborg will automatically run `borg init` if the repository doesn't exist when you run `backup`, `list`, or `info` commands. The passphrase will be retrieved from Passbolt if configured.
|
|
@@ -323,21 +370,231 @@ See [SECURITY.md](SECURITY.md) for detailed security information and best practi
|
|
|
323
370
|
| Command | Description | Options |
|
|
324
371
|
|---------|-------------|---------|
|
|
325
372
|
| `init REPOSITORY` | Initialize a new Borg repository | `--passphrase`, `--passbolt-id`, `--log` |
|
|
326
|
-
| `backup` | Create a backup using config file | `--config`, `--
|
|
373
|
+
| `backup` | Create a backup using config file | `--config`, `--repository`, `--all`, `--name`, `--remove-source`, `--log` |
|
|
327
374
|
| `list` | List all archives in repository | `--config`, `--repository`, `--log` |
|
|
328
|
-
| `restore ARCHIVE` | Restore files from archive | `--config`, `--
|
|
375
|
+
| `restore ARCHIVE` | Restore files from archive | `--config`, `--repository`, `--destination`, `--path`, `--log` |
|
|
329
376
|
| `info` | Show repository information | `--config`, `--repository`, `--log` |
|
|
377
|
+
| `check` | Check repository integrity and compatibility | `--config`, `--repository`, `--all`, `--verify-data`, `--log` |
|
|
330
378
|
|
|
331
|
-
###
|
|
379
|
+
### Options
|
|
332
380
|
|
|
333
381
|
- `--config`: Path to configuration file (default: `ruborg.yml`)
|
|
334
382
|
- `--log`: Path to log file (overrides config, default: `~/.ruborg/logs/ruborg.log`)
|
|
335
|
-
- `--repository` / `-r`: Repository name (required for
|
|
383
|
+
- `--repository` / `-r`: Repository name (optional for info, required for backup/list/restore/check unless --all)
|
|
384
|
+
- `--all`: Process all repositories (backup and check commands)
|
|
385
|
+
- `--name`: Custom archive name (backup command only)
|
|
386
|
+
- `--remove-source`: Remove source files after successful backup (backup command only)
|
|
387
|
+
- `--destination`: Destination directory for restore (restore command only)
|
|
388
|
+
- `--path`: Specific file or directory to restore (restore command only)
|
|
389
|
+
- `--verify-data`: Run full data integrity check (check command only, slower)
|
|
390
|
+
|
|
391
|
+
## Retention Policies
|
|
336
392
|
|
|
337
|
-
|
|
393
|
+
Retention policies define how many backups to keep. You can use **count-based rules**, **time-based rules**, or **both together** for maximum flexibility.
|
|
338
394
|
|
|
339
|
-
|
|
340
|
-
|
|
395
|
+
### Count-based Retention
|
|
396
|
+
|
|
397
|
+
Keep a specific number of backups for each time interval:
|
|
398
|
+
|
|
399
|
+
```yaml
|
|
400
|
+
retention:
|
|
401
|
+
keep_hourly: 24 # Keep last 24 hourly backups
|
|
402
|
+
keep_daily: 7 # Keep last 7 daily backups
|
|
403
|
+
keep_weekly: 4 # Keep last 4 weekly backups
|
|
404
|
+
keep_monthly: 6 # Keep last 6 monthly backups
|
|
405
|
+
keep_yearly: 1 # Keep last 1 yearly backup
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
### Time-based Retention
|
|
409
|
+
|
|
410
|
+
Keep backups based on time periods:
|
|
411
|
+
|
|
412
|
+
```yaml
|
|
413
|
+
retention:
|
|
414
|
+
keep_within: "7d" # Keep ALL backups within last 7 days
|
|
415
|
+
keep_last: "30d" # Keep at least one backup from last 30 days
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
### Combining Time-based and Count-based Rules
|
|
419
|
+
|
|
420
|
+
**Yes, you can combine both types!** When you use both time-based and count-based rules together, they work **additively** - Borg keeps the **union** of all matching backups.
|
|
421
|
+
|
|
422
|
+
```yaml
|
|
423
|
+
retention:
|
|
424
|
+
keep_within: "2d" # Keep everything from last 2 days
|
|
425
|
+
keep_daily: 7 # PLUS keep 7 daily backups (goes back ~7 days)
|
|
426
|
+
keep_weekly: 4 # PLUS keep 4 weekly backups (goes back ~4 weeks)
|
|
427
|
+
keep_monthly: 6 # PLUS keep 6 monthly backups (goes back ~6 months)
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
**How this works:** Borg will keep a backup if it matches **ANY** of these rules:
|
|
431
|
+
- Backup is within the last 2 days, OR
|
|
432
|
+
- Backup is one of the last 7 daily backups, OR
|
|
433
|
+
- Backup is one of the last 4 weekly backups, OR
|
|
434
|
+
- Backup is one of the last 6 monthly backups
|
|
435
|
+
|
|
436
|
+
**Practical example:**
|
|
437
|
+
|
|
438
|
+
```yaml
|
|
439
|
+
# Database backups - keep recent changes, long-term history
|
|
440
|
+
retention:
|
|
441
|
+
keep_within: "1d" # Everything from last 24 hours (frequent changes)
|
|
442
|
+
keep_daily: 14 # Plus 14 days of daily backups (2 weeks)
|
|
443
|
+
keep_weekly: 8 # Plus 8 weeks of weekly backups (2 months)
|
|
444
|
+
keep_monthly: 12 # Plus 12 months of monthly backups (1 year)
|
|
445
|
+
keep_yearly: 3 # Plus 3 years of yearly backups
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
This configuration provides:
|
|
449
|
+
- **Maximum detail** for recent backups (last 24 hours - every backup kept)
|
|
450
|
+
- **Daily granularity** for the last 2 weeks
|
|
451
|
+
- **Weekly granularity** for the last 2 months
|
|
452
|
+
- **Monthly granularity** for the last year
|
|
453
|
+
- **Yearly snapshots** for long-term compliance
|
|
454
|
+
|
|
455
|
+
**Time format:** Use suffixes like `d` (days), `w` (weeks), `m` (months), `y` (years). Examples: `7d`, `4w`, `6m`, `1y`
|
|
456
|
+
|
|
457
|
+
**Configuration notes:**
|
|
458
|
+
- Policies can be set globally and overridden per repository
|
|
459
|
+
- All fields are optional - use only what you need
|
|
460
|
+
- `keep_within`: Keeps **all** archives created within the specified time period
|
|
461
|
+
- `keep_last`: Ensures at least one backup from the last specified time period is kept
|
|
462
|
+
- **Rules are additive** - combining rules keeps MORE backups, not fewer
|
|
463
|
+
- Retention settings are displayed in the `ruborg info` summary
|
|
464
|
+
|
|
465
|
+
### Per-File Backup Mode with File Metadata Retention
|
|
466
|
+
|
|
467
|
+
**NEW:** Ruborg supports a per-file backup mode where each file is backed up as a separate archive. This enables intelligent retention based on **file modification time** rather than backup creation time.
|
|
468
|
+
|
|
469
|
+
**Use Case:** Keep backups of actively modified files while automatically pruning backups of files that haven't been modified recently - even after the source files are deleted.
|
|
470
|
+
|
|
471
|
+
```yaml
|
|
472
|
+
repositories:
|
|
473
|
+
- name: project-files
|
|
474
|
+
description: "Active project files with metadata-based retention"
|
|
475
|
+
path: /mnt/backup/project-files
|
|
476
|
+
retention_mode: per_file # Enable per-file backup mode
|
|
477
|
+
retention:
|
|
478
|
+
# Prune based on file metadata (modification time) read from archives
|
|
479
|
+
keep_files_modified_within: "30d" # Keep files modified in last 30 days
|
|
480
|
+
# Traditional retention also applies
|
|
481
|
+
keep_daily: 7
|
|
482
|
+
sources:
|
|
483
|
+
- name: projects
|
|
484
|
+
paths:
|
|
485
|
+
- /home/user/projects
|
|
486
|
+
exclude:
|
|
487
|
+
- "*.tmp"
|
|
488
|
+
- "*/.cache/*"
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
**How it works:**
|
|
492
|
+
- **Per-File Archives**: Each file is backed up as a separate Borg archive
|
|
493
|
+
- **Hash-Based Naming**: Archives are named `repo-{hash}-{timestamp}` (hash uniquely identifies the file path)
|
|
494
|
+
- **Original Path Stored**: The complete original file path is stored in the archive comment
|
|
495
|
+
- **Metadata Preservation**: Borg preserves all file metadata (mtime, size, permissions) in the archive
|
|
496
|
+
- **Smart Pruning**: Retention reads file mtime directly from archives - works even after files are deleted
|
|
497
|
+
|
|
498
|
+
**File Metadata Retention Options:**
|
|
499
|
+
- `keep_files_modified_within`: Keep archives containing files modified within the specified time period
|
|
500
|
+
- Reads mtime from inside the Borg archive
|
|
501
|
+
- Works even if source files are deleted
|
|
502
|
+
- Example: `"30d"` keeps files modified in the last 30 days
|
|
503
|
+
|
|
504
|
+
**Mixed Mode Example:**
|
|
505
|
+
```yaml
|
|
506
|
+
repositories:
|
|
507
|
+
# Standard mode for full system backups
|
|
508
|
+
- name: system
|
|
509
|
+
path: /mnt/backup/system
|
|
510
|
+
retention_mode: standard # Default: one archive per backup run
|
|
511
|
+
retention:
|
|
512
|
+
keep_daily: 7
|
|
513
|
+
sources:
|
|
514
|
+
- name: etc
|
|
515
|
+
paths:
|
|
516
|
+
- /etc
|
|
517
|
+
|
|
518
|
+
# Per-file mode for active development files
|
|
519
|
+
- name: active-code
|
|
520
|
+
path: /mnt/backup/code
|
|
521
|
+
retention_mode: per_file # One archive per file
|
|
522
|
+
retention:
|
|
523
|
+
keep_files_modified_within: "60d"
|
|
524
|
+
keep_monthly: 12 # Plus monthly snapshots
|
|
525
|
+
sources:
|
|
526
|
+
- name: projects
|
|
527
|
+
paths:
|
|
528
|
+
- /home/user/dev
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
**Performance Note:** Per-file mode creates many archives (one per file). Borg handles this efficiently due to deduplication, but it's best suited for directories with hundreds to thousands of files rather than millions.
|
|
532
|
+
|
|
533
|
+
**Backup vs Retention:** The per-file `retention_mode` only affects how archives are created and pruned. Traditional backup commands still work normally - you can list, restore, and check per-file archives just like standard archives.
|
|
534
|
+
|
|
535
|
+
### Automatic Pruning
|
|
536
|
+
|
|
537
|
+
Enable **automatic pruning** to remove old backups after each backup operation:
|
|
538
|
+
|
|
539
|
+
```yaml
|
|
540
|
+
# Global configuration
|
|
541
|
+
auto_prune: true # Enable automatic pruning for all repositories
|
|
542
|
+
retention:
|
|
543
|
+
keep_daily: 7
|
|
544
|
+
keep_weekly: 4
|
|
545
|
+
keep_monthly: 6
|
|
546
|
+
|
|
547
|
+
repositories:
|
|
548
|
+
- name: documents
|
|
549
|
+
path: /mnt/backup/documents
|
|
550
|
+
sources:
|
|
551
|
+
- name: main
|
|
552
|
+
paths:
|
|
553
|
+
- /home/user/documents
|
|
554
|
+
|
|
555
|
+
- name: databases
|
|
556
|
+
path: /mnt/backup/databases
|
|
557
|
+
auto_prune: true # Override: enable pruning for this repository only
|
|
558
|
+
retention:
|
|
559
|
+
keep_daily: 14 # Override: use different retention policy
|
|
560
|
+
keep_weekly: 8
|
|
561
|
+
keep_monthly: 12
|
|
562
|
+
sources:
|
|
563
|
+
- name: mysql
|
|
564
|
+
paths:
|
|
565
|
+
- /var/lib/mysql/dumps
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
**How it works:**
|
|
569
|
+
- When `auto_prune: true` is set, Ruborg automatically runs `borg prune` after each successful backup
|
|
570
|
+
- Pruning removes old archives that don't match any retention rule
|
|
571
|
+
- If both global and repository-specific `auto_prune` are set, repository-specific takes precedence
|
|
572
|
+
- Requires a retention policy to be configured (otherwise pruning is skipped)
|
|
573
|
+
- Pruning statistics are displayed after completion
|
|
574
|
+
|
|
575
|
+
**Example output:**
|
|
576
|
+
|
|
577
|
+
```
|
|
578
|
+
--- Backing up repository: documents ---
|
|
579
|
+
✓ Backup created: documents-2025-10-06_12-30-45
|
|
580
|
+
Pruning old backups...
|
|
581
|
+
✓ Pruning completed
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
**Manual pruning:**
|
|
585
|
+
|
|
586
|
+
If you prefer to prune manually or have `auto_prune: false`, run Borg's prune command directly:
|
|
587
|
+
|
|
588
|
+
```bash
|
|
589
|
+
# Example: Apply retention policy to a repository
|
|
590
|
+
BORG_PASSPHRASE="your-passphrase" borg prune \
|
|
591
|
+
--keep-hourly=24 \
|
|
592
|
+
--keep-daily=7 \
|
|
593
|
+
--keep-weekly=4 \
|
|
594
|
+
--keep-monthly=6 \
|
|
595
|
+
--keep-yearly=1 \
|
|
596
|
+
/path/to/repository
|
|
597
|
+
```
|
|
341
598
|
|
|
342
599
|
## Development
|
|
343
600
|
|
data/Rakefile
CHANGED
data/SECURITY.md
CHANGED
|
@@ -54,6 +54,18 @@ Ruborg implements several security measures to protect your backup operations:
|
|
|
54
54
|
- Allows control over unencrypted repository access via config
|
|
55
55
|
- Defaults to safe settings while maintaining backward compatibility
|
|
56
56
|
|
|
57
|
+
### 11. Borg Executable Validation
|
|
58
|
+
- Validates that `borg_path` points to an actual executable file
|
|
59
|
+
- Verifies the executable is actually Borg by checking version output
|
|
60
|
+
- Prevents execution of arbitrary binaries specified in config
|
|
61
|
+
- Searches PATH for command-name-only specifications
|
|
62
|
+
|
|
63
|
+
### 12. Dependency Vulnerability Scanning
|
|
64
|
+
- Uses `bundler-audit` to check for known vulnerabilities
|
|
65
|
+
- Regular database updates ensure latest security advisories
|
|
66
|
+
- Integrated into development workflow
|
|
67
|
+
- Run: `bundle exec bundle-audit check`
|
|
68
|
+
|
|
57
69
|
## Security Best Practices
|
|
58
70
|
|
|
59
71
|
### When Using `--remove-source`
|
|
@@ -66,9 +78,27 @@ Ruborg implements several security measures to protect your backup operations:
|
|
|
66
78
|
4. **Use absolute paths** in configuration to avoid ambiguity
|
|
67
79
|
|
|
68
80
|
### Configuration File Security
|
|
69
|
-
|
|
81
|
+
|
|
82
|
+
**CRITICAL**: Always protect your configuration file with restrictive permissions:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
# Set owner-only read/write permissions
|
|
86
|
+
chmod 600 ruborg.yml
|
|
87
|
+
|
|
88
|
+
# Verify permissions
|
|
89
|
+
ls -l ruborg.yml
|
|
90
|
+
# Should show: -rw------- 1 user user ... ruborg.yml
|
|
91
|
+
|
|
92
|
+
# For shared environments with a backup group
|
|
93
|
+
chmod 640 ruborg.yml
|
|
94
|
+
chown user:backup ruborg.yml
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**Additional recommendations:**
|
|
70
98
|
- Never commit Passbolt resource IDs to public repositories
|
|
71
99
|
- Use environment variables for sensitive paths when possible
|
|
100
|
+
- Store config files outside web-accessible directories
|
|
101
|
+
- Regularly audit who has access to the config file
|
|
72
102
|
|
|
73
103
|
### Repository Security
|
|
74
104
|
- Use encrypted repositories (default: `encryption: repokey`)
|
|
@@ -114,7 +144,16 @@ We will respond within 48 hours and work with you to address the issue.
|
|
|
114
144
|
|
|
115
145
|
## Security Audit History
|
|
116
146
|
|
|
117
|
-
- **v0.
|
|
147
|
+
- **v0.4.0** (2025-10-06): Complete command injection elimination
|
|
148
|
+
- **CRITICAL**: Fixed all remaining command injection vulnerabilities in repository.rb
|
|
149
|
+
- Replaced all backtick execution with Open3.capture3/capture2e methods
|
|
150
|
+
- Added Borg executable validation to prevent arbitrary binary execution
|
|
151
|
+
- Integrated bundler-audit for dependency vulnerability scanning
|
|
152
|
+
- Removed unused env_to_cmd_prefix method
|
|
153
|
+
- Enhanced security documentation with config file permission requirements
|
|
154
|
+
- Zero known vulnerabilities in dependencies
|
|
155
|
+
|
|
156
|
+
- **v0.3.1** (2025-10-05): Initial security hardening
|
|
118
157
|
- Fixed command injection in Passbolt CLI execution (uses Open3.capture3)
|
|
119
158
|
- Added path traversal protection for extract operations
|
|
120
159
|
- Implemented symlink protection for file deletion with --remove-source
|
data/exe/ruborg
CHANGED