ruborg 0.4.0 → 0.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 263e32d62c24714f299a20218bc96733cf6f13cffa0e0d844ef5b37c5470dbdf
4
- data.tar.gz: f020b2b5714fd3c02d9372414de7494e7beb637d5312bcf34f05e2be5bf1ecd1
3
+ metadata.gz: 1233e04a2f95e8e8aadb9ad97d043b2d9b52c446821ff89495073a6a8762b6cc
4
+ data.tar.gz: ee3fd1ae2256299120d7f78aac3243c2de44f8f3c072c4c29dd44cb761caf769
5
5
  SHA512:
6
- metadata.gz: 1ad51dcadf03ca1958ff6ecd10b24846d6e501219dd59637280eaf6d37a2d6b6a505ff0bbb9401d5f7ebfd361c24a3e1c3b09bc71f40bf788c6581da2a98832a
7
- data.tar.gz: 5679385c1369eb161235c998dd69526a3bc27dca1cfc13acc22bd0602184f15438c042542495300b1752eb8ce810dfbc467bfe83d10b492641063e84381f3881
6
+ metadata.gz: 59d6ad7e5797cbbd964390d0425bf1392695c24f938a40978ddea371552da84604bf0a8eb189b207318ed68dba966311d45659f2b6abd3a59711f766465a5b36
7
+ data.tar.gz: 6c77a312c9b820dd22d7f0396dbb24fac70da4654023c89eebc94c962d367b963a448a923ce23574862276452c441e3ebd723601fd58a2634e3337d402a9c066
data/CHANGELOG.md CHANGED
@@ -7,6 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.5.0] - 2025-10-08
11
+
12
+ ### Added
13
+ - **Hostname Validation**: Optional `hostname` configuration key to restrict backup operations to specific hosts
14
+ - Can be configured globally or per-repository
15
+ - Repository-specific hostname overrides global setting
16
+ - Validates system hostname before backup, list, restore, check operations
17
+ - Prevents accidental execution of backups on wrong machines
18
+ - Displayed in `info` command output
19
+ - Comprehensive test coverage for hostname validation (6 new test cases)
20
+ - Documentation for hostname feature in example config and README
21
+
22
+ ### Changed
23
+ - `info` command now displays hostname when configured (global or per-repository)
24
+
10
25
  ## [0.4.0] - 2025-10-06
11
26
 
12
27
  ### Added
data/README.md CHANGED
@@ -24,7 +24,8 @@ e- ⏰ **Retention Policies** - Configure backup retention (hourly, daily, weekl
24
24
  - 📋 **Repository Descriptions** - Document each repository's purpose
25
25
  - 📈 **Summary View** - Quick overview of all repositories and their configurations
26
26
  - 🔧 **Custom Borg Path** - Support for custom Borg executable paths per repository
27
- - **Well-tested** - Comprehensive test suite with RSpec (147 tests)
27
+ - 🏠 **Hostname Validation** - NEW! Restrict backups to specific hosts (global or per-repository)
28
+ - ✅ **Well-tested** - Comprehensive test suite with RSpec (153 tests)
28
29
  - 🔒 **Security-focused** - Path validation, safe YAML loading, command injection protection
29
30
 
30
31
  ## Prerequisites
@@ -127,6 +128,7 @@ repositories:
127
128
  - name: databases
128
129
  description: "MySQL and PostgreSQL database dumps"
129
130
  path: /mnt/backup/databases
131
+ hostname: dbserver.local # Optional: repository-specific hostname override
130
132
  # Repository-specific passbolt (overrides global)
131
133
  passbolt:
132
134
  resource_id: "db-specific-passbolt-id"
@@ -161,8 +163,9 @@ repositories:
161
163
 
162
164
  **Configuration Features:**
163
165
  - **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
+ - **Hostname Validation**: Optional `hostname` field to restrict backups to specific hosts (global or per-repository)
167
+ - **Global Settings**: Hostname, compression, encryption, auto_init, log_file, borg_path, borg_options, and retention apply to all repositories
168
+ - **Per-Repository Overrides**: Any global setting can be overridden at the repository level (including hostname and custom borg_path)
166
169
  - **Custom Borg Path**: Specify a custom Borg executable path if borg is not in PATH or to use a specific version
167
170
  - **Retention Policies**: Define how many backups to keep (hourly, daily, weekly, monthly, yearly)
168
171
  - **Multiple Sources**: Each repository can have multiple backup sources with their own exclude patterns
@@ -338,6 +341,70 @@ repositories:
338
341
 
339
342
  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.
340
343
 
344
+ ## Hostname Validation
345
+
346
+ Restrict backup operations to specific hosts using the optional `hostname` configuration key. This prevents accidental execution of backups on the wrong machine.
347
+
348
+ ### Global Hostname
349
+
350
+ Apply hostname restriction to all repositories:
351
+
352
+ ```yaml
353
+ # Global hostname - applies to all repositories
354
+ hostname: myserver.local
355
+
356
+ repositories:
357
+ - name: documents
358
+ path: /mnt/backup/documents
359
+ sources:
360
+ - name: main
361
+ paths:
362
+ - /home/user/documents
363
+ ```
364
+
365
+ ### Per-Repository Hostname
366
+
367
+ Override global hostname for specific repositories:
368
+
369
+ ```yaml
370
+ # Global hostname for most repositories
371
+ hostname: mainserver.local
372
+
373
+ repositories:
374
+ # Uses global hostname (mainserver.local)
375
+ - name: documents
376
+ path: /mnt/backup/documents
377
+ sources:
378
+ - name: main
379
+ paths:
380
+ - /home/user/documents
381
+
382
+ # Override with repository-specific hostname
383
+ - name: databases
384
+ hostname: dbserver.local # Only runs on dbserver.local
385
+ path: /mnt/backup/databases
386
+ sources:
387
+ - name: mysql
388
+ paths:
389
+ - /var/lib/mysql/dumps
390
+ ```
391
+
392
+ **How it works:**
393
+ - Before backup, list, restore, or check operations, Ruborg validates the system hostname
394
+ - If configured hostname doesn't match the current hostname, the operation fails with an error
395
+ - Repository-specific hostname takes precedence over global hostname
396
+ - If no hostname is configured, validation is skipped
397
+
398
+ **Example error:**
399
+ ```
400
+ Error: Hostname mismatch: configuration is for 'dbserver.local' but current hostname is 'mainserver.local'
401
+ ```
402
+
403
+ **Use cases:**
404
+ - **Multi-server environments**: Different servers backup to different repositories
405
+ - **Development vs Production**: Prevent production config from running on dev machines
406
+ - **Safety**: Avoid accidentally running wrong backups on shared configuration files
407
+
341
408
  ## Security Configuration
342
409
 
343
410
  Ruborg provides configurable security options via `borg_options`:
data/lib/ruborg/cli.rb CHANGED
@@ -54,6 +54,7 @@ module Ruborg
54
54
  def backup
55
55
  @logger.info("Starting backup operation with config: #{options[:config]}")
56
56
  config = Config.new(options[:config])
57
+ validate_hostname(config.global_settings)
57
58
  backup_repositories(config)
58
59
  rescue Error => e
59
60
  @logger.error("Backup failed: #{e.message}")
@@ -72,6 +73,7 @@ module Ruborg
72
73
 
73
74
  global_settings = config.global_settings
74
75
  merged_config = global_settings.merge(repo_config)
76
+ validate_hostname(merged_config)
75
77
  passphrase = fetch_passphrase_for_repo(merged_config)
76
78
  borg_opts = merged_config["borg_options"] || {}
77
79
  borg_path = merged_config["borg_path"]
@@ -108,6 +110,7 @@ module Ruborg
108
110
 
109
111
  global_settings = config.global_settings
110
112
  merged_config = global_settings.merge(repo_config)
113
+ validate_hostname(merged_config)
111
114
  passphrase = fetch_passphrase_for_repo(merged_config)
112
115
  borg_opts = merged_config["borg_options"] || {}
113
116
  borg_path = merged_config["borg_path"]
@@ -175,6 +178,7 @@ module Ruborg
175
178
  @logger.info("Checking repository compatibility")
176
179
  config = Config.new(options[:config])
177
180
  global_settings = config.global_settings
181
+ validate_hostname(global_settings)
178
182
 
179
183
  # Show Borg version first
180
184
  borg_version = Repository.borg_version
@@ -207,6 +211,7 @@ module Ruborg
207
211
  @logger.info("Checking repository: #{repo_name}")
208
212
 
209
213
  merged_config = global_settings.merge(repo_config)
214
+ validate_hostname(merged_config)
210
215
  passphrase = fetch_passphrase_for_repo(merged_config)
211
216
  borg_opts = merged_config["borg_options"] || {}
212
217
  borg_path = merged_config["borg_path"]
@@ -267,6 +272,7 @@ module Ruborg
267
272
 
268
273
  # Show global settings
269
274
  puts "Global Settings:"
275
+ puts " Hostname: #{global_settings["hostname"]}" if global_settings["hostname"]
270
276
  puts " Compression: #{global_settings["compression"] || "lz4 (default)"}"
271
277
  puts " Encryption: #{global_settings["encryption"] || "repokey (default)"}"
272
278
  puts " Auto-init: #{global_settings["auto_init"] || false}"
@@ -284,6 +290,7 @@ module Ruborg
284
290
  puts " Description: #{repo["description"]}" if repo["description"]
285
291
 
286
292
  # Show repo-specific overrides
293
+ puts " Hostname: #{repo["hostname"]}" if repo["hostname"]
287
294
  puts " Compression: #{repo["compression"]}" if repo["compression"]
288
295
  puts " Encryption: #{repo["encryption"]}" if repo["encryption"]
289
296
  puts " Auto-init: #{repo["auto_init"]}" unless repo["auto_init"].nil?
@@ -389,6 +396,7 @@ module Ruborg
389
396
 
390
397
  # Merge global settings with repo-specific settings (repo-specific takes precedence)
391
398
  merged_config = global_settings.merge(repo_config)
399
+ validate_hostname(merged_config)
392
400
 
393
401
  passphrase = fetch_passphrase_for_repo(merged_config)
394
402
  borg_opts = merged_config["borg_options"] || {}
@@ -460,6 +468,18 @@ module Ruborg
460
468
  name.gsub(/[^a-zA-Z0-9._-]/, "_")
461
469
  end
462
470
 
471
+ def validate_hostname(config)
472
+ configured_hostname = config["hostname"]
473
+ return if configured_hostname.nil? || configured_hostname.empty?
474
+
475
+ current_hostname = `hostname`.strip
476
+ return if current_hostname == configured_hostname
477
+
478
+ raise ConfigError,
479
+ "Hostname mismatch: configuration is for '#{configured_hostname}' " \
480
+ "but current hostname is '#{current_hostname}'"
481
+ end
482
+
463
483
  # Wrapper class to adapt repository config to existing Backup class
464
484
  class BackupConfig
465
485
  def initialize(repo_config, merged_settings)
data/lib/ruborg/config.rb CHANGED
@@ -39,7 +39,7 @@ module Ruborg
39
39
 
40
40
  def global_settings
41
41
  @data.slice("passbolt", "compression", "encryption", "auto_init", "borg_options", "log_file", "retention",
42
- "auto_prune")
42
+ "auto_prune", "hostname")
43
43
  end
44
44
 
45
45
  private
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Ruborg
4
- VERSION = "0.4.0"
4
+ VERSION = "0.5.0"
5
5
  end
data/ruborg.yml.example CHANGED
@@ -6,6 +6,7 @@
6
6
  # # Edit ruborg.yml with your settings
7
7
 
8
8
  # Global settings (applied to all repositories unless overridden)
9
+ hostname: myserver.local # Optional: restrict configuration to specific hostname
9
10
  compression: lz4 # Options: lz4 (fast), zstd (balanced), lzma (high compression), none
10
11
  encryption: repokey # Options: repokey, keyfile, none (NOT recommended)
11
12
  auto_init: true # Automatically initialize repositories on first use
@@ -65,6 +66,7 @@ repositories:
65
66
  - name: databases
66
67
  description: "MySQL and PostgreSQL database dumps"
67
68
  path: /mnt/backup/borg-databases
69
+ hostname: dbserver.local # Optional: repository-specific hostname override
68
70
  retention_mode: per_file # Each file gets its own archive
69
71
  # Repository-specific passbolt (overrides global)
70
72
  passbolt:
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruborg
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michail Pantelelis