genesis-devtools 0.2.4__tar.gz → 0.2.6__tar.gz

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.
Files changed (65) hide show
  1. genesis_devtools-0.2.6/ChangeLog +7 -0
  2. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/PKG-INFO +23 -3
  3. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/README.md +22 -3
  4. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/backup.py +73 -5
  5. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/builder/packer.py +6 -1
  6. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/cmd/cli.py +75 -6
  7. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/constants.py +1 -0
  8. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/utils.py +90 -0
  9. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools.egg-info/PKG-INFO +23 -3
  10. genesis_devtools-0.2.6/genesis_devtools.egg-info/pbr.json +1 -0
  11. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools.egg-info/requires.txt +1 -0
  12. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/requirements.txt +1 -0
  13. genesis_devtools-0.2.4/ChangeLog +0 -7
  14. genesis_devtools-0.2.4/genesis_devtools.egg-info/pbr.json +0 -1
  15. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/.github/workflows/publish-to-pypi.yml +0 -0
  16. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/.github/workflows/tests.yml +0 -0
  17. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/AUTHORS +0 -0
  18. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/LICENSE +0 -0
  19. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/data/stands/stand-small.yaml +0 -0
  20. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis/genesis.yaml +0 -0
  21. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis/images/genesis_base/bootstrap.sh +0 -0
  22. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis/images/genesis_base/genesis-bootstrap.service +0 -0
  23. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis/images/genesis_base/genesis-root-autoresize.service +0 -0
  24. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis/images/genesis_base/install_genesis_base.sh +0 -0
  25. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis/images/genesis_base/root_autoresize.sh +0 -0
  26. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/__init__.py +0 -0
  27. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/builder/__init__.py +0 -0
  28. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/builder/base.py +0 -0
  29. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/builder/builder.py +0 -0
  30. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/builder/dependency.py +0 -0
  31. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/cmd/__init__.py +0 -0
  32. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/infra/driver/base.py +0 -0
  33. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/infra/driver/libvirt.py +0 -0
  34. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/infra/libvirt/__init__.py +0 -0
  35. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/infra/libvirt/constants.py +0 -0
  36. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/infra/libvirt/libvirt.py +0 -0
  37. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/logger.py +0 -0
  38. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/packer/debian_12/debian-12.pkr.hcl +0 -0
  39. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/packer/debian_12/plugins.pkr.hcl +0 -0
  40. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/packer/genesis_base/genesis-base.pkr.hcl +0 -0
  41. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/packer/genesis_base/plugins.pkr.hcl +0 -0
  42. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/packer/genesis_core/genesis-core.pkr.hcl +0 -0
  43. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/packer/genesis_core/plugins.pkr.hcl +0 -0
  44. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/packer/genesis_custom/genesis-custom.pkr.hcl +0 -0
  45. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/packer/genesis_custom/plugins.pkr.hcl +0 -0
  46. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/packer/ubuntu_24/plugins.pkr.hcl +0 -0
  47. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/packer/ubuntu_24/ubuntu-24.pkr.hcl +0 -0
  48. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/stand/__init__.py +0 -0
  49. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/stand/models.py +0 -0
  50. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/tests/__init__.py +0 -0
  51. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/tests/unit/__init__.py +0 -0
  52. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/tests/unit/conftest.py +0 -0
  53. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/tests/unit/test_basic.py +0 -0
  54. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/tests/unit/test_builder.py +0 -0
  55. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/tests/unit/test_dependency.py +0 -0
  56. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools/tests/unit/test_packer.py +0 -0
  57. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools.egg-info/SOURCES.txt +0 -0
  58. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools.egg-info/dependency_links.txt +0 -0
  59. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools.egg-info/entry_points.txt +0 -0
  60. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools.egg-info/not-zip-safe +0 -0
  61. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/genesis_devtools.egg-info/top_level.txt +0 -0
  62. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/setup.cfg +0 -0
  63. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/setup.py +0 -0
  64. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/test-requirements.txt +0 -0
  65. {genesis_devtools-0.2.4 → genesis_devtools-0.2.6}/tox.ini +0 -0
@@ -0,0 +1,7 @@
1
+ CHANGES
2
+ =======
3
+
4
+ 0.2.6
5
+ -----
6
+
7
+ * Encryption of backups
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: genesis-devtools
3
- Version: 0.2.4
3
+ Version: 0.2.6
4
4
  Summary: Tools to manager life cycle of Genesis projects.
5
5
  Home-page: https://github.com/infraguys/genesis_devtools
6
6
  Author: Genesis Corporation
@@ -23,6 +23,7 @@ Requires-Dist: pyyaml<7.0.0,>=6.0.0
23
23
  Requires-Dist: prettytable<4.0.0,>=3.7.0
24
24
  Requires-Dist: GitPython<4.0.0,>=3.1.30
25
25
  Requires-Dist: bazooka<2.0.0,>=1.0.0
26
+ Requires-Dist: cryptography<47.0.0,>=45.0.5
26
27
  Dynamic: author
27
28
  Dynamic: author-email
28
29
  Dynamic: classifier
@@ -54,7 +55,7 @@ Before you can install and use genesis tools you need to install several require
54
55
  Install packages
55
56
  ```sh
56
57
  sudo apt update
57
- sudo apt install qemu-kvm libvirt-daemon-system libvirt-dev mkisofs
58
+ sudo apt install qemu-kvm qemu-utils libvirt-daemon-system libvirt-dev mkisofs
58
59
  ```
59
60
 
60
61
  Add user to group
@@ -342,7 +343,7 @@ genesis backup -oneshot
342
343
 
343
344
  This command will backup the current installation to the specified directory once and exit.
344
345
 
345
- ### Compuressed Backup
346
+ ### Compressed Backup
346
347
 
347
348
  Run backup of all libvirt domains and store a compressed archive of the backup in the current directory:
348
349
 
@@ -350,6 +351,25 @@ Run backup of all libvirt domains and store a compressed archive of the backup i
350
351
  genesis backup --compress
351
352
  ```
352
353
 
354
+ ### Encrypted Backup
355
+
356
+ Run backup of all libvirt domains and store an encrypted archive of the backup in the current directory:
357
+
358
+ NOTE: It works only with `--compress` flag
359
+
360
+ You need to set the environment variables `GEN_DEV_BACKUP_KEY` and `GEN_DEV_BACKUP_IV` to encrypt the backup. The key and IV must be greater or equal to 6 bytes and less or equal to 16 bytes.
361
+ ```bash
362
+ export GEN_DEV_BACKUP_KEY=secret_key
363
+ export GEN_DEV_BACKUP_IV=secret_iv
364
+
365
+ genesis backup --compress --encrypt
366
+ ```
367
+
368
+ For decryption, use the `genesis backup-decrypt` command.
369
+ ```bash
370
+ genesis backup-decrypt backup.tar.gz.encrypted
371
+ ```
372
+
353
373
  ### Rotation
354
374
 
355
375
  For the periodic backup, you can set the number of backups to keep(rotation number). The default value is 5. Use the `--rotate` option to set the number of backups to keep:
@@ -20,7 +20,7 @@ Before you can install and use genesis tools you need to install several require
20
20
  Install packages
21
21
  ```sh
22
22
  sudo apt update
23
- sudo apt install qemu-kvm libvirt-daemon-system libvirt-dev mkisofs
23
+ sudo apt install qemu-kvm qemu-utils libvirt-daemon-system libvirt-dev mkisofs
24
24
  ```
25
25
 
26
26
  Add user to group
@@ -308,7 +308,7 @@ genesis backup -oneshot
308
308
 
309
309
  This command will backup the current installation to the specified directory once and exit.
310
310
 
311
- ### Compuressed Backup
311
+ ### Compressed Backup
312
312
 
313
313
  Run backup of all libvirt domains and store a compressed archive of the backup in the current directory:
314
314
 
@@ -316,6 +316,25 @@ Run backup of all libvirt domains and store a compressed archive of the backup i
316
316
  genesis backup --compress
317
317
  ```
318
318
 
319
+ ### Encrypted Backup
320
+
321
+ Run backup of all libvirt domains and store an encrypted archive of the backup in the current directory:
322
+
323
+ NOTE: It works only with `--compress` flag
324
+
325
+ You need to set the environment variables `GEN_DEV_BACKUP_KEY` and `GEN_DEV_BACKUP_IV` to encrypt the backup. The key and IV must be greater or equal to 6 bytes and less or equal to 16 bytes.
326
+ ```bash
327
+ export GEN_DEV_BACKUP_KEY=secret_key
328
+ export GEN_DEV_BACKUP_IV=secret_iv
329
+
330
+ genesis backup --compress --encrypt
331
+ ```
332
+
333
+ For decryption, use the `genesis backup-decrypt` command.
334
+ ```bash
335
+ genesis backup-decrypt backup.tar.gz.encrypted
336
+ ```
337
+
319
338
  ### Rotation
320
339
 
321
340
  For the periodic backup, you can set the number of backups to keep(rotation number). The default value is 5. Use the `--rotate` option to set the number of backups to keep:
@@ -330,4 +349,4 @@ Backups can take a lot of disk space and it can be a reason to crash the whole s
330
349
 
331
350
  ```bash
332
351
  genesis backup --min-free-space 50
333
- ```
352
+ ```
@@ -29,8 +29,57 @@ from genesis_devtools import utils
29
29
  from genesis_devtools.infra.libvirt import libvirt
30
30
 
31
31
 
32
+ class EnctryptionCreds(tp.NamedTuple):
33
+ LEN = 16
34
+ MIN_LEN = 6
35
+
36
+ key: bytes
37
+ iv: bytes
38
+
39
+ @classmethod
40
+ def validate_env(cls):
41
+ if not os.environ.get("GEN_DEV_BACKUP_KEY") or not os.environ.get(
42
+ "GEN_DEV_BACKUP_IV"
43
+ ):
44
+ raise ValueError(
45
+ (
46
+ "Define environment variables GEN_DEV_BACKUP_KEY "
47
+ "and GEN_DEV_BACKUP_IV."
48
+ )
49
+ )
50
+
51
+ key = os.environ["GEN_DEV_BACKUP_KEY"]
52
+ iv = os.environ["GEN_DEV_BACKUP_IV"]
53
+
54
+ if (
55
+ cls.MIN_LEN <= len(key) < cls.LEN
56
+ and cls.MIN_LEN <= len(iv) < cls.LEN
57
+ ):
58
+ return
59
+
60
+ raise ValueError(
61
+ f"Key and IV must be greater or equal than {cls.MIN_LEN} "
62
+ f"bytes and less or equal to {cls.LEN} bytes."
63
+ )
64
+
65
+ @classmethod
66
+ def from_env(cls):
67
+ key = os.environ["GEN_DEV_BACKUP_KEY"]
68
+ iv = os.environ["GEN_DEV_BACKUP_IV"]
69
+ key = key + "0" * (cls.LEN - len(key))
70
+ iv = iv + "0" * (cls.LEN - len(iv))
71
+
72
+ return cls(
73
+ key=key.encode(),
74
+ iv=iv.encode(),
75
+ )
76
+
77
+
32
78
  def _do_backup(
33
- backup_path: str, domains: tp.List[str], compress: bool = False
79
+ backup_path: str,
80
+ domains: tp.List[str],
81
+ compress: bool = False,
82
+ encryption: EnctryptionCreds | None = None,
34
83
  ) -> None:
35
84
  os.makedirs(backup_path, exist_ok=True)
36
85
 
@@ -75,19 +124,35 @@ def _do_backup(
75
124
  return
76
125
 
77
126
  click.echo(f"Compressing {backup_path}")
127
+ compressed_backup_path = f"{backup_path}.tar.gz"
78
128
  compress_directory = os.path.dirname(backup_path)
79
129
  try:
80
130
  utils.compress_dir(backup_path, compress_directory)
81
131
  except Exception:
82
132
  click.secho(f"Compression of {backup_path} failed", fg="red")
83
- compressed_backup_path = f"{backup_path}.tar.gz"
84
-
85
133
  if os.path.exists(compressed_backup_path):
86
134
  os.remove(compressed_backup_path)
87
135
  return
88
136
 
89
137
  click.secho(f"Compression of {backup_path} done", fg="green")
90
- shutil.rmtree(backup_path)
138
+
139
+ if not encryption:
140
+ shutil.rmtree(backup_path)
141
+ return
142
+
143
+ click.echo(f"Encrypting {compressed_backup_path}")
144
+ try:
145
+ utils.encrypt_file(
146
+ compressed_backup_path, encryption.key, encryption.iv
147
+ )
148
+ except Exception:
149
+ click.secho(f"Encryption of {compressed_backup_path} failed", fg="red")
150
+ return
151
+ finally:
152
+ shutil.rmtree(backup_path)
153
+
154
+ os.remove(compressed_backup_path)
155
+ click.secho(f"Encryption of {compressed_backup_path} done", fg="green")
91
156
 
92
157
 
93
158
  def _terminate_backup_process(backup_process: mp.Process) -> None:
@@ -115,6 +180,7 @@ def backup(
115
180
  backup_path: str,
116
181
  domains: tp.List[str],
117
182
  compress: bool = False,
183
+ encryption: EnctryptionCreds | None = None,
118
184
  min_free_disk_space_gb: int = 50,
119
185
  ) -> None:
120
186
  if not os.path.exists(backup_path):
@@ -134,7 +200,9 @@ def backup(
134
200
  # Run the actual backup process in another process.
135
201
  # The current process will track the free disk space.
136
202
  backup_process = mp.Process(
137
- target=_do_backup, args=(backup_path, domains, compress), daemon=True
203
+ target=_do_backup,
204
+ args=(backup_path, domains, compress, encryption),
205
+ daemon=True,
138
206
  )
139
207
  backup_process.start()
140
208
 
@@ -127,8 +127,13 @@ class PackerBuilder(base.DummyImageBuilder):
127
127
  result = []
128
128
 
129
129
  for env in envs:
130
+ # Check if the env is a wildcard
131
+ if env.endswith("*"):
132
+ for _env in (e for e in os.environ if e.startswith(env[:-1])):
133
+ result.append(f'{_env}="{os.environ[_env]}"')
134
+ continue
130
135
  # Check if there a default value for the env
131
- if "=" in env:
136
+ elif "=" in env:
132
137
  name, value = tuple(e.strip() for e in env.split("="))
133
138
  else:
134
139
  name, value = env.strip(), ""
@@ -385,7 +385,7 @@ def get_project_version_cmd(element_dir: str) -> None:
385
385
  "-p",
386
386
  "--period",
387
387
  default=c.BackupPeriod.D1.value,
388
- type=click.Choice([p for p in c.BackupPeriod]),
388
+ type=click.Choice([p.value for p in c.BackupPeriod]),
389
389
  show_default=True,
390
390
  help="the regularity of backups",
391
391
  )
@@ -393,7 +393,7 @@ def get_project_version_cmd(element_dir: str) -> None:
393
393
  "-o",
394
394
  "--offset",
395
395
  default=None,
396
- type=click.Choice([p for p in c.BackupPeriod]),
396
+ type=click.Choice([p.value for p in c.BackupPeriod]),
397
397
  show_default=True,
398
398
  help=(
399
399
  "The time offset of the first backup. If not provided, "
@@ -411,12 +411,22 @@ def get_project_version_cmd(element_dir: str) -> None:
411
411
  @click.option(
412
412
  "-c",
413
413
  "--compress",
414
- default=False,
415
- type=bool,
416
414
  show_default=True,
417
415
  is_flag=True,
418
416
  help="Compress the backup.",
419
417
  )
418
+ @click.option(
419
+ "-e",
420
+ "--encrypt",
421
+ show_default=True,
422
+ is_flag=True,
423
+ help=(
424
+ "Encrypt the backup. Works only with the compress flag. "
425
+ "Use environment variable to specify the encryption key "
426
+ "and the initialization vector: "
427
+ "GEN_DEV_BACKUP_KEY and GEN_DEV_BACKUP_IV"
428
+ ),
429
+ )
420
430
  @click.option(
421
431
  "-s",
422
432
  "--min-free-space",
@@ -447,14 +457,42 @@ def bakcup_cmd(
447
457
  offset: c.BackupPeriod | None,
448
458
  oneshot: bool,
449
459
  compress: bool,
460
+ encrypt: bool,
450
461
  min_free_space: int,
451
462
  rotate: int,
452
463
  ) -> None:
464
+ if not compress and encrypt:
465
+ raise click.UsageError(
466
+ "The encrypt flag can be used only with the compress flag."
467
+ )
468
+
469
+ # Need to specify encryption key and initialization vector via
470
+ # environment variables.
471
+ if encrypt:
472
+ try:
473
+ backup.EnctryptionCreds.validate_env()
474
+ except ValueError:
475
+ raise click.UsageError(
476
+ (
477
+ "Define environment variables GEN_DEV_BACKUP_KEY "
478
+ "and GEN_DEV_BACKUP_IV. "
479
+ "Key and IV must be greater or equal than "
480
+ f"{backup.EnctryptionCreds.MIN_LEN} bytes and less or "
481
+ f"equal to {backup.EnctryptionCreds.LEN} bytes."
482
+ )
483
+ )
484
+
485
+ encryption = backup.EnctryptionCreds.from_env()
486
+ else:
487
+ encryption = None
488
+
453
489
  # Do a single backup and exit
454
490
  if oneshot:
455
491
  domains = _domains_for_backup(name, raise_on_domain_absence=True)
456
492
  backup_path = utils.backup_path(backup_dir)
457
- backup.backup(backup_path, domains, compress, min_free_space)
493
+ backup.backup(
494
+ backup_path, domains, compress, encryption, min_free_space
495
+ )
458
496
  return
459
497
 
460
498
  offset = offset or period
@@ -475,7 +513,9 @@ def bakcup_cmd(
475
513
  click.secho(f"Backup started at {time.strftime('%Y-%m-%d %H:%M:%S')}")
476
514
  backup_path = utils.backup_path(backup_dir)
477
515
 
478
- backup.backup(backup_path, domains, compress, min_free_space)
516
+ backup.backup(
517
+ backup_path, domains, compress, encryption, min_free_space
518
+ )
479
519
  click.secho(
480
520
  f"Next backup at: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(next_ts))}"
481
521
  )
@@ -490,6 +530,35 @@ def bakcup_cmd(
490
530
  time.sleep(timeout)
491
531
 
492
532
 
533
+ @main.command("backup-decrypt", help="Decrypt a backup file")
534
+ @click.argument("path", type=click.Path(exists=True))
535
+ def bakcup_decrypt_cmd(path: str) -> None:
536
+ # Need to specify encryption key and initialization vector via
537
+ # environment variables.
538
+
539
+ try:
540
+ backup.EnctryptionCreds.validate_env()
541
+ except ValueError:
542
+ raise click.UsageError(
543
+ (
544
+ "Define environment variables GEN_DEV_BACKUP_KEY "
545
+ "and GEN_DEV_BACKUP_IV. "
546
+ "Key and IV must be greater or equal than "
547
+ f"{backup.EnctryptionCreds.MIN_LEN} bytes and less or "
548
+ f"equal to {backup.EnctryptionCreds.LEN} bytes."
549
+ )
550
+ )
551
+
552
+ encryption = backup.EnctryptionCreds.from_env()
553
+
554
+ utils.decrypt_file(
555
+ path,
556
+ encryption.key,
557
+ encryption.iv,
558
+ )
559
+ click.secho(f"The {path} file has been decrypted.", fg="green")
560
+
561
+
493
562
  def _domains_for_backup(
494
563
  names: tp.List[str] | None = None, raise_on_domain_absence: bool = False
495
564
  ) -> tp.List[str]:
@@ -22,6 +22,7 @@ DEF_GEN_CFG_FILE_NAME = "genesis.yaml"
22
22
  DEF_GEN_WORK_DIR_NAME = "genesis"
23
23
  DEF_GEN_OUTPUT_DIR_NAME = "output"
24
24
  RC_BRANCHES = ("master", "main")
25
+ ENCRYPTED_EXTENSION = ".encrypted"
25
26
 
26
27
  # ENV vars
27
28
  ENV_GEN_DEV_KEYS = "GEN_DEV_KEYS"
@@ -24,6 +24,10 @@ from importlib.metadata import entry_points
24
24
  import yaml
25
25
 
26
26
  import git
27
+ from cryptography.hazmat.primitives import ciphers
28
+ from cryptography.hazmat.primitives.ciphers import modes as cipher_models
29
+ from cryptography.hazmat.primitives.ciphers import algorithms
30
+ from cryptography.hazmat import backends as crypto_back
27
31
 
28
32
  import genesis_devtools.constants as c
29
33
 
@@ -234,3 +238,89 @@ def compress_dir(
234
238
 
235
239
  # Create a zip archive of the directory
236
240
  shutil.make_archive(archive_base_name, compression_format, directory)
241
+
242
+
243
+ def encrypt_file(
244
+ path: str,
245
+ key: bytes,
246
+ iv: bytes,
247
+ chunk_size_kb: int = 128,
248
+ extension: str = c.ENCRYPTED_EXTENSION,
249
+ ) -> None:
250
+ # Ensure that the key and iv are the correct lengths
251
+ # for AES (16 bytes for AES-128)
252
+ if len(key) != 16 or len(iv) != 16:
253
+ raise ValueError("Key and IV must be 16 bytes long")
254
+
255
+ # Create a cipher object
256
+ cipher = ciphers.Cipher(
257
+ algorithms.AES(key),
258
+ cipher_models.CFB(iv),
259
+ backend=crypto_back.default_backend(),
260
+ )
261
+ chunk_size = chunk_size_kb << 10
262
+ encrypted_path = path + extension
263
+
264
+ # Open the input file and the temporary output file
265
+ try:
266
+ with open(path, "rb") as infile, open(encrypted_path, "wb") as outfile:
267
+ # Create an encryptor object
268
+ encryptor = cipher.encryptor()
269
+
270
+ # Read the file in chunks and write the encrypted
271
+ # data to the temp file
272
+ while True:
273
+ chunk = infile.read(chunk_size)
274
+ if len(chunk) == 0:
275
+ break
276
+ outfile.write(encryptor.update(chunk))
277
+ outfile.write(encryptor.finalize())
278
+ except Exception as e:
279
+ os.remove(encrypted_path)
280
+ raise e
281
+
282
+
283
+ def decrypt_file(
284
+ path: str,
285
+ key: bytes,
286
+ iv: bytes,
287
+ chunk_size_kb: int = 128,
288
+ extension: str = c.ENCRYPTED_EXTENSION,
289
+ ) -> None:
290
+ # Ensure that the key and iv are the correct lengths
291
+ # for AES (16 bytes for AES-128)
292
+ if len(key) != 16 or len(iv) != 16:
293
+ raise ValueError("Key and IV must be 16 bytes long")
294
+
295
+ # Create a cipher object
296
+ cipher = ciphers.Cipher(
297
+ algorithms.AES(key),
298
+ cipher_models.CFB(iv),
299
+ backend=crypto_back.default_backend(),
300
+ )
301
+ chunk_size = chunk_size_kb << 10
302
+ plain_path = path[: -len(extension)] if path.endswith(extension) else path
303
+ tmp_path = plain_path + ".tmp"
304
+
305
+ # Open the encrypted file and the temporary output file
306
+ try:
307
+ with open(path, "rb") as infile, open(tmp_path, "wb") as outfile:
308
+ # Create a decryptor object
309
+ decryptor = cipher.decryptor()
310
+
311
+ # Read the file in chunks and write the decrypted data to the temp file
312
+ while True:
313
+ chunk = infile.read(chunk_size)
314
+ if len(chunk) == 0:
315
+ break
316
+ outfile.write(decryptor.update(chunk))
317
+ outfile.write(decryptor.finalize())
318
+ except Exception as e:
319
+ os.remove(tmp_path)
320
+ raise e
321
+
322
+ # Replace the encrypted file with the decrypted temp file
323
+ if plain_path == path:
324
+ os.replace(tmp_path, plain_path)
325
+ else:
326
+ shutil.move(tmp_path, plain_path)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: genesis-devtools
3
- Version: 0.2.4
3
+ Version: 0.2.6
4
4
  Summary: Tools to manager life cycle of Genesis projects.
5
5
  Home-page: https://github.com/infraguys/genesis_devtools
6
6
  Author: Genesis Corporation
@@ -23,6 +23,7 @@ Requires-Dist: pyyaml<7.0.0,>=6.0.0
23
23
  Requires-Dist: prettytable<4.0.0,>=3.7.0
24
24
  Requires-Dist: GitPython<4.0.0,>=3.1.30
25
25
  Requires-Dist: bazooka<2.0.0,>=1.0.0
26
+ Requires-Dist: cryptography<47.0.0,>=45.0.5
26
27
  Dynamic: author
27
28
  Dynamic: author-email
28
29
  Dynamic: classifier
@@ -54,7 +55,7 @@ Before you can install and use genesis tools you need to install several require
54
55
  Install packages
55
56
  ```sh
56
57
  sudo apt update
57
- sudo apt install qemu-kvm libvirt-daemon-system libvirt-dev mkisofs
58
+ sudo apt install qemu-kvm qemu-utils libvirt-daemon-system libvirt-dev mkisofs
58
59
  ```
59
60
 
60
61
  Add user to group
@@ -342,7 +343,7 @@ genesis backup -oneshot
342
343
 
343
344
  This command will backup the current installation to the specified directory once and exit.
344
345
 
345
- ### Compuressed Backup
346
+ ### Compressed Backup
346
347
 
347
348
  Run backup of all libvirt domains and store a compressed archive of the backup in the current directory:
348
349
 
@@ -350,6 +351,25 @@ Run backup of all libvirt domains and store a compressed archive of the backup i
350
351
  genesis backup --compress
351
352
  ```
352
353
 
354
+ ### Encrypted Backup
355
+
356
+ Run backup of all libvirt domains and store an encrypted archive of the backup in the current directory:
357
+
358
+ NOTE: It works only with `--compress` flag
359
+
360
+ You need to set the environment variables `GEN_DEV_BACKUP_KEY` and `GEN_DEV_BACKUP_IV` to encrypt the backup. The key and IV must be greater or equal to 6 bytes and less or equal to 16 bytes.
361
+ ```bash
362
+ export GEN_DEV_BACKUP_KEY=secret_key
363
+ export GEN_DEV_BACKUP_IV=secret_iv
364
+
365
+ genesis backup --compress --encrypt
366
+ ```
367
+
368
+ For decryption, use the `genesis backup-decrypt` command.
369
+ ```bash
370
+ genesis backup-decrypt backup.tar.gz.encrypted
371
+ ```
372
+
353
373
  ### Rotation
354
374
 
355
375
  For the periodic backup, you can set the number of backups to keep(rotation number). The default value is 5. Use the `--rotate` option to set the number of backups to keep:
@@ -0,0 +1 @@
1
+ {"git_version": "6371a1f", "is_release": false}
@@ -4,3 +4,4 @@ pyyaml<7.0.0,>=6.0.0
4
4
  prettytable<4.0.0,>=3.7.0
5
5
  GitPython<4.0.0,>=3.1.30
6
6
  bazooka<2.0.0,>=1.0.0
7
+ cryptography<47.0.0,>=45.0.5
@@ -4,3 +4,4 @@ pyyaml>=6.0.0,<7.0.0 # MIT
4
4
  prettytable>=3.7.0,<4.0.0 # MIT
5
5
  GitPython>=3.1.30,<4.0.0
6
6
  bazooka>=1.0.0,<2.0.0 # Apache-2.0
7
+ cryptography>=45.0.5,<47.0.0 # BSD-3
@@ -1,7 +0,0 @@
1
- CHANGES
2
- =======
3
-
4
- 0.2.4
5
- -----
6
-
7
- * Bugfix: Building several images for one element (#47)
@@ -1 +0,0 @@
1
- {"git_version": "5308a95", "is_release": false}