ansible-vars 1.0.7__py3-none-any.whl → 1.0.8__py3-none-any.whl

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.
ansible_vars/cli.py CHANGED
@@ -84,6 +84,10 @@ Specify vault keys to load for en-/decryption. Not required for vars files with
84
84
  A key is a combination of an identifier (can be a vault ID or anything else, ideally unique) and a passphrase.
85
85
  By default, available keys are auto-detected if your current directory contains an Ansible config and appended to the ones you supplied.
86
86
  If no explicit encryption key is specified, the first supplied/available key is used.
87
+
88
+ [!] When editing a hybrid vault file, changing even a single variable will change all variables' ciphers, as salts are generated randomly.
89
+ You can prevent this by passing a fixed salt via `-S <salt>`, which will result in any plaintext always resolving to the same cipher.
90
+ For Ansible's AES-256, a length of at least 32 characters is recommended.
87
91
  ''',
88
92
  'log_args': '''
89
93
  Log a diff of any vault changes performed with this program to an encrypted or plain logfile, creating it if necessary.
@@ -198,6 +202,7 @@ DEFAULT_EDITOR: str = os.environ.get('EDITOR', 'notepad.exe' if os.name == 'nt'
198
202
  DEFAULT_COLOR_MODE: str = os.environ.get('AV_COLOR_MODE', '256' if sys.stdout.isatty() else 'none')
199
203
  DEFAULT_TEMP_DIR: str = os.environ.get('AV_TEMP_DIR', gettempdir())
200
204
  DEFAULT_CREATE_PLAIN: bool = os.environ.get('AV_CREATE_PLAIN', 'no').lower() in [ 'yes', 'y', 'true', 't', '1' ]
205
+ DEFAULT_SALT: str | None = os.environ.get('AV_SALT', None)
201
206
 
202
207
  args: ArgumentParser = ArgumentParser(
203
208
  prog = 'ansible-vars',
@@ -243,6 +248,10 @@ key_args.add_argument(
243
248
  )
244
249
  key_args.add_argument('--no-detect-keys', '-D', action='store_false', dest='detect_keys', help='disable automatic key detection')
245
250
  key_args.add_argument('--encryption-key', '-K', type=str, metavar='<identifier>', help='which of the loaded keys to use for encryption')
251
+ key_args.add_argument(
252
+ '--fixed-salt', '-S', type=str, metavar='<salt>', default=DEFAULT_SALT,
253
+ help='a fixed salt to use for encryption (should be 32+ chars!)'
254
+ )
246
255
 
247
256
  log_args = args.add_argument_group('logging vault changes', description=HELP['log_args'])
248
257
  log_mutex = log_args.add_mutually_exclusive_group()
@@ -565,7 +574,7 @@ sys.excepthook = _exc_hook
565
574
  # Load vault keys
566
575
 
567
576
  _explicit_keys: list[VaultKey] = [ VaultKey(passphrase, vault_id=id) for id, passphrase in config.keys ]
568
- keyring = VaultKeyring(_explicit_keys.copy(), detect_available_keys=config.detect_keys)
577
+ keyring = VaultKeyring(_explicit_keys.copy(), detect_available_keys=config.detect_keys, default_salt=config.fixed_salt)
569
578
 
570
579
  if config.encryption_key:
571
580
  keyring.default_encryption_key = keyring.key_by_id(config.encryption_key)
@@ -575,6 +584,8 @@ try:
575
584
  debug(f"Encryption key: { keyring.encryption_key.id }")
576
585
  except:
577
586
  debug('Encryption key: unavailable')
587
+ if config.fixed_salt:
588
+ debug(f"Using fixed encryption salt: { config.fixed_salt }")
578
589
 
579
590
  # Set up logging
580
591
 
@@ -690,7 +701,6 @@ if config.command in [ 'create', 'edit' ]:
690
701
  def _find_new_plain_vars(path: tuple[Hashable, ...], value: Any) -> Any:
691
702
  if path != ( SENTINEL_KEY, ) and type(value) is not EncryptedVar:
692
703
  if (old_value := vault.get(path, default=Unset)) != value:
693
- std_print(path[-1], type(old_value), type(value))
694
704
  if type(old_value) is EncryptedVar:
695
705
  decrypted_vars.append(path)
696
706
  else:
ansible_vars/util.py CHANGED
@@ -338,7 +338,7 @@ class VaultDaemon(FileSystemEventHandler):
338
338
  content: str = src.read()
339
339
  with open(self.target_file, 'w') as tgt:
340
340
  try:
341
- tgt.write(Vault(content, keyring=self.keyring).as_editable(with_header=False))
341
+ tgt.write(Vault(content, keyring=self.keyring).as_plain())
342
342
  self.debug(f"Created/Updated file { self.target_file } from vault contents")
343
343
  except:
344
344
  tgt.write(content)
@@ -373,7 +373,7 @@ class VaultDaemon(FileSystemEventHandler):
373
373
  content: str = src.read()
374
374
  with open(target_path, 'w') as tgt:
375
375
  try:
376
- tgt.write(Vault(content, keyring=self.keyring).as_editable(with_header=False))
376
+ tgt.write(Vault(content, keyring=self.keyring).as_plain())
377
377
  self.debug(f"Created/Updated file { target_path } from vault contents")
378
378
  except:
379
379
  tgt.write(content)
@@ -54,12 +54,12 @@ class VaultKey():
54
54
  '''
55
55
  return VaultLib.is_encrypted(VaultKey._strip_vault_tag(test_me))
56
56
 
57
- def encrypt(self, plain: str) -> str:
58
- '''Encrypts a string using this `VaultKey`'s secret.'''
57
+ def encrypt(self, plain: str, salt: str | None = None) -> str:
58
+ '''Encrypts a string using this `VaultKey`'s secret. You can specify an optional fixed salt (ideally 32+ chars).'''
59
59
  # Pass our secret directly to the encrypt call to skip expensive secret matching
60
60
  # Beware: the encrypt function takes a `secret`, but means just the VaultSecret and not a tuple of (vault_id, VaultSecret)
61
61
  # In other calls, `secret` or `secrets` may refer to the tuple(s)
62
- return self._vaultlib.encrypt(plain, secret=self.secret, vault_id=self.id).decode('utf-8').strip()
62
+ return self._vaultlib.encrypt(plain, secret=self.secret, vault_id=self.id, salt=salt).decode('utf-8').strip()
63
63
 
64
64
  def decrypt(self, vault_cipher: str) -> str:
65
65
  '''
@@ -96,6 +96,7 @@ class VaultKeyring():
96
96
  self,
97
97
  keys: list[VaultKey] | None = None,
98
98
  default_encryption_key: VaultKey | None = None,
99
+ default_salt: str | None = None,
99
100
  detect_available_keys: bool = True
100
101
  ) -> None:
101
102
  '''
@@ -107,9 +108,13 @@ class VaultKeyring():
107
108
 
108
109
  When encrypting data, you can specify an explicit `VaultKey` to use. If none is specified, `default_encryption_key` is used.
109
110
  If no explicit or default keys are available, the first key of the `keys` parameter is used.
111
+
112
+ You can also set a fixed salt to use for encryption tasks, either passing it to the `encrypt` function directly
113
+ or as a `default_salt` here. A length of at least 32 chars is recommended for Ansible's AES-256.
110
114
  '''
111
115
  self.keys: list[VaultKey] = keys or []
112
116
  self.default_encryption_key: VaultKey | None = default_encryption_key
117
+ self.default_salt: str | None = default_salt
113
118
  if detect_available_keys:
114
119
  self.keys.extend(VaultKeyring.load_cli_secrets())
115
120
 
@@ -124,16 +129,15 @@ class VaultKeyring():
124
129
  raise NoVaultKeysError('No vault keys available for encryption')
125
130
  return self.default_encryption_key or self.keys[0]
126
131
 
127
- def encrypt(self, plain: str, key: VaultKey | None = None) -> str:
132
+ def encrypt(self, plain: str, key: VaultKey | None = None, salt: str | None = None) -> str:
128
133
  '''
129
134
  Encrypts the given vault data using the supplied `VaultKey`.
130
135
  If no key is supplied, the `VaultKeyring`'s `default_encryption_key` is used.
131
136
  If that key is also unset, the first key of the `VaultKeyring`'s `keys` is used.
137
+ You can specify an optional fixed salt for encrypting (ideally 32+ chars).
132
138
  '''
133
139
  # If no key is provided, use the default encryption key or first key in `keys`
134
- if not key:
135
- key = self.encryption_key
136
- return key.encrypt(plain)
140
+ return (key or self.encryption_key).encrypt(plain, salt=(salt or self.default_salt))
137
141
 
138
142
  def decrypt(self, vault_cipher: str, key: VaultKey | None = None) -> str:
139
143
  '''
@@ -143,7 +147,7 @@ class VaultKeyring():
143
147
 
144
148
  Expects a cipher with optional YAML tag preamble (`!vault | $ANSIBLE_VAULT;<options>\\n<cipher>`).
145
149
  '''
146
- if not key and not self.keys:
150
+ if not (key or self.keys):
147
151
  raise NoVaultKeysError('No vault keys available for decryption')
148
152
  if key:
149
153
  return key.decrypt(vault_cipher)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ansible-vars
3
- Version: 1.0.7
3
+ Version: 1.0.8
4
4
  Summary: Manage vaults and variable files for Ansible
5
5
  Project-URL: Homepage, https://github.com/xorwow/ansible-vars
6
6
  Project-URL: Issues, https://github.com/xorwow/ansible-vars/issues
@@ -105,6 +105,10 @@ By default, the first loaded key is used for all encryption tasks. Note that aut
105
105
 
106
106
  You can disable automatic key detection by flagging `--no-encrypt-keys|-D`. Use `ansible-vars keyring` to view all available keys.
107
107
 
108
+ #### Encryption salts
109
+
110
+ Each time you edit a vault or otherwise encrypt a value, a randomly generated salt is used so even identical plain values will result in unique ciphers. However, this also means that each time a single encrypted variable is edited, all other ciphers in the vault will change as well. You can avoid this by passing a fixed salt via `--fixed-salt|-S <salt>`. Note that it should be at least 32 characters long and sufficiently random for Ansible's AES-256 encryption, and that you won't benefit from unique ciphers for identical plaintexts anymore.
111
+
108
112
  ### Diff logging
109
113
 
110
114
  **TL;DR:** You can use `-l <log directory>` to log changes to edited vaults to a vault-encrypted log file.
@@ -220,6 +224,10 @@ Set the tempfile/staging root as you would with `-T <path>`.
220
224
 
221
225
  Invert the default creation mode for files: If unset or `no`, files are created with full encryption unless specified otherwise via the `--plain|-p` flag. This behavior mirrors that of `ansible-vault`. When set to `yes`, the behavior and flag are inverted as files are created without encryption by default unless specified otherwise via the `--no-plain|-P` flag.
222
226
 
227
+ #### AV_SALT
228
+
229
+ Set a fixed salt as you would with `-S <salt>`.
230
+
223
231
  ### Python library
224
232
 
225
233
  When using `ansible-vars` as a library, import any of these modules from the `ansible_vars` module.
@@ -270,7 +278,6 @@ When editing a file or creating a daemon, decrypted vaults are written to disk t
270
278
  - Changes to file metadata (permissions, ...) are not mirrored.
271
279
  - `ansible-vars` does not support files which are not (Jinja2) YAML dictionaries, except for limited support in these commands:
272
280
  - `edit`, `view` (without `--json` support), `encrypt`, `decrypt`, `is-encrypted`
273
- - When a vault is modified, all of its ciphers will change due to new salts, even if only one encrypted variable has been changed.
274
281
 
275
282
  ## Extension plans
276
283
 
@@ -0,0 +1,12 @@
1
+ ansible_vars/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ ansible_vars/cli.py,sha256=TFPSuUmOTWjX6FF5aAd2PnJkyftSur9DfccYwzjQe6A,63959
3
+ ansible_vars/constants.py,sha256=Nd3sIuSoOvyfUfHfnsnJBDGMW7eNzbMm1NAvEQio9hE,1624
4
+ ansible_vars/errors.py,sha256=6dzyksPKWira9O2-Ir3MIOwr4XjN9MSBiRp5e6siY6Q,1256
5
+ ansible_vars/util.py,sha256=UwGPBT19pee7lBpWuBzLPAvcrHUBAn6i1MrJvzM9OQ4,21265
6
+ ansible_vars/vault.py,sha256=cMvFdtc3bw6yf-aChUEP34k2yafWS2UuubFO84De_rA,46383
7
+ ansible_vars/vault_crypt.py,sha256=nh2k686nTI3yERIp-qzx5iDE1kZKg10YG019QeZDnLM,10019
8
+ ansible_vars-1.0.8.dist-info/METADATA,sha256=KSO8y8E4DZeGzYjXKrnuj-aW8ni3TEAh1upcwGPdlL0,17967
9
+ ansible_vars-1.0.8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
10
+ ansible_vars-1.0.8.dist-info/entry_points.txt,sha256=RrhkEH0MbfRzflguVrfYfthsFC5V2fkFnizUG3uHMtQ,55
11
+ ansible_vars-1.0.8.dist-info/licenses/LICENSE,sha256=ocyJHLG5wD12qB4uam2pqWTHIJmzloiyNyTex6Q2DKo,1062
12
+ ansible_vars-1.0.8.dist-info/RECORD,,
@@ -1,12 +0,0 @@
1
- ansible_vars/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- ansible_vars/cli.py,sha256=OAIXPLMTinS48e9Wn9YEhdHt6eaBHuU9Px1qZ5peCKY,63345
3
- ansible_vars/constants.py,sha256=Nd3sIuSoOvyfUfHfnsnJBDGMW7eNzbMm1NAvEQio9hE,1624
4
- ansible_vars/errors.py,sha256=6dzyksPKWira9O2-Ir3MIOwr4XjN9MSBiRp5e6siY6Q,1256
5
- ansible_vars/util.py,sha256=j6Ay_TXEyCm2OUBV7VRXlGjipKSAuklIHXSXIxbrzj0,21305
6
- ansible_vars/vault.py,sha256=cMvFdtc3bw6yf-aChUEP34k2yafWS2UuubFO84De_rA,46383
7
- ansible_vars/vault_crypt.py,sha256=Eotf1JVJcvrdae2gv2KMVor-x1D1IMREkONKgKdtT2A,9493
8
- ansible_vars-1.0.7.dist-info/METADATA,sha256=otjXXgLP1_XT-Xykt9_V8nyIi65aoRAKM2ukGxdu5pU,17464
9
- ansible_vars-1.0.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
10
- ansible_vars-1.0.7.dist-info/entry_points.txt,sha256=RrhkEH0MbfRzflguVrfYfthsFC5V2fkFnizUG3uHMtQ,55
11
- ansible_vars-1.0.7.dist-info/licenses/LICENSE,sha256=ocyJHLG5wD12qB4uam2pqWTHIJmzloiyNyTex6Q2DKo,1062
12
- ansible_vars-1.0.7.dist-info/RECORD,,