Skip to content
Permalink
Browse files

openssl_privatekey: add backup option (#53593)

* Add backup option to openssl_privatekey.

* Add changelog fragment.

* Make module available in remove().

* Add tests for backup.

* Update lib/ansible/modules/crypto/openssl_privatekey.py

Co-Authored-By: felixfontein <felix@fontein.de>

* Update lib/ansible/modules/crypto/openssl_privatekey.py

Co-Authored-By: felixfontein <felix@fontein.de>

* Update lib/ansible/modules/crypto/openssl_privatekey.py

Co-Authored-By: felixfontein <felix@fontein.de>

* Update lib/ansible/modules/crypto/openssl_privatekey.py
  • Loading branch information...
felixfontein authored and gundalow committed Mar 18, 2019
1 parent 3fa39ac commit e00f3153580932f1177d21e2bf7cb23793b9a85d
@@ -0,0 +1,2 @@
minor_changes:
- "openssl_privatekey - add ``backup`` option."
@@ -230,7 +230,7 @@ def generate(self):

pass

def remove(self):
def remove(self, module):
"""Remove the resource from the filesystem."""

try:
@@ -1220,7 +1220,7 @@ def main():
module.exit_json(**result)

try:
certificate.remove()
certificate.remove(module)
except CertificateError as exc:
module.fail_json(msg=to_native(exc))

@@ -1046,7 +1046,7 @@ def main():
module.exit_json(**result)

try:
csr.remove()
csr.remove(module)
except (CertificateSigningRequestError, crypto_utils.OpenSSLObjectError) as exc:
module.fail_json(msg=to_native(exc))

@@ -241,7 +241,7 @@ def generate(self, module):
self.pkcs12 = crypto.PKCS12()

try:
self.remove()
self.remove(module)
except PkcsError as exc:
module.fail_json(msg=to_native(exc))

@@ -274,14 +274,14 @@ def generate(self, module):
self.iter_size, self.maciter_size))
os.close(pkcs12_file)
except (IOError, OSError) as exc:
self.remove()
self.remove(module)
raise PkcsError(exc)

def parse(self, module):
"""Read PKCS#12 file."""

try:
self.remove()
self.remove(module)
with open(self.src, 'rb') as pkcs12_fh:
pkcs12_content = pkcs12_fh.read()
p12 = crypto.load_pkcs12(pkcs12_content,
@@ -298,7 +298,7 @@ def parse(self, module):
os.close(pkcs12_file)

except IOError as exc:
self.remove()
self.remove(module)
raise PkcsError(exc)


@@ -378,7 +378,7 @@ def main():

if os.path.exists(module.params['path']):
try:
pkcs12.remove()
pkcs12.remove(module)
changed = True
except PkcsError as exc:
module.fail_json(msg=to_native(exc))
@@ -23,6 +23,11 @@
L(ECC,https://en.wikipedia.org/wiki/Elliptic-curve_cryptography)
private keys.
- Keys are generated in PEM format.
- "Please note that the module regenerates private keys if they don't match
the module's options. In particular, if you provide another passphrase
(or specify none), change the keysize, etc., the private key will be
regenerated. If you are concerned that this could overwrite your private key,
consider using the I(backup) option."
- The module can use the cryptography Python library, or the pyOpenSSL Python
library. By default, it tries to detect which one is available. This can be
overridden with the I(select_crypto_backend) option."
@@ -111,6 +116,13 @@
default: auto
choices: [ auto, cryptography, pyopenssl ]
version_added: "2.8"
backup:
description:
- Create a backup file including a timestamp so you can get
the original private key back if you overwrote it with a new one by accident.
type: bool
default: no
version_added: "2.8"
extends_documentation_fragment:
- files
seealso:
@@ -182,6 +194,11 @@
sha256: "41:ab:c7:cb:d5:5f:30:60:46:99:ac:d4:00:70:cf:a1:76:4f:24:5d:10:24:57:5d:51:6e:09:97:df:2f:de:c7"
sha384: "85:39:50:4e:de:d9:19:33:40:70:ae:10:ab:59:24:19:51:c3:a2:e4:0b:1c:b1:6e:dd:b3:0c:d9:9e:6a:46:af:da:18:f8:ef:ae:2e:c0:9a:75:2c:9b:b3:0f:3a:5f:3d"
sha512: "fd:ed:5e:39:48:5f:9f:fe:7f:25:06:3f:79:08:cd:ee:a5:e7:b3:3d:13:82:87:1f:84:e1:f5:c7:28:77:53:94:86:56:38:69:f0:d9:35:22:01:1e:a6:60:...:0f:9b"
backup_file:
description: Name of backup file created.
returned: changed and if I(backup) is C(yes)
type: str
sample: /path/to/privatekey.pem.2019-03-09@11:22~
'''

import abc
@@ -255,6 +272,9 @@ def __init__(self, module):
self.privatekey = None
self.fingerprint = {}

self.backup = module.params['backup']
self.backup_path = None

self.mode = module.params.get('mode', None)
if self.mode is None:
self.mode = 0o600
@@ -271,6 +291,8 @@ def generate(self, module):
"""Generate a keypair."""

if not self.check(module, perms_required=False) or self.force:
if self.backup:
self.backup_file = module.backup_local(self.path)
privatekey_data = self._generate_private_key_data()
try:
privatekey_file = os.open(self.path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC)
@@ -298,6 +320,11 @@ def generate(self, module):
if module.set_fs_attributes_if_different(file_args, False):
self.changed = True

def remove(self, module):
if self.backup:
self.backup_file = module.backup_local(self.path)
super(PrivateKeyBase, self).remove(module)

@abc.abstractmethod
def _check_passphrase(self):
pass
@@ -325,6 +352,8 @@ def dump(self):
'changed': self.changed,
'fingerprint': self.fingerprint,
}
if self.backup_path:
result['backup_path'] = self.backup_path

return result

@@ -583,6 +612,7 @@ def main():
path=dict(type='path', required=True),
passphrase=dict(type='str', no_log=True),
cipher=dict(type='str'),
backup=dict(type='bool', default=False),
select_crypto_backend=dict(type='str', choices=['auto', 'pyopenssl', 'cryptography'], default='auto'),
),
supports_check_mode=True,
@@ -656,7 +686,7 @@ def main():
module.exit_json(**result)

try:
private_key.remove()
private_key.remove(module)
except PrivateKeyError as exc:
module.fail_json(msg=to_native(exc))

@@ -205,7 +205,7 @@ def generate(self, module):
except (IOError, OSError) as exc:
raise PublicKeyError(exc)
except AttributeError as exc:
self.remove()
self.remove(module)
raise PublicKeyError('You need to have PyOpenSSL>=16.0.0 to generate public keys')

self.fingerprint = crypto_utils.get_fingerprint(
@@ -315,7 +315,7 @@ def main():
module.exit_json(**result)

try:
public_key.remove()
public_key.remove(module)
except PublicKeyError as exc:
module.fail_json(msg=to_native(exc))

@@ -149,6 +149,7 @@
passphrase: hunter2
cipher: "{{ 'aes256' if select_crypto_backend == 'pyopenssl' else 'auto' }}"
select_crypto_backend: '{{ select_crypto_backend }}'
backup: yes
register: passphrase_1

- name: Generate privatekey with passphrase (idempotent)
@@ -157,18 +158,21 @@
passphrase: hunter2
cipher: "{{ 'aes256' if select_crypto_backend == 'pyopenssl' else 'auto' }}"
select_crypto_backend: '{{ select_crypto_backend }}'
backup: yes
register: passphrase_2

- name: Regenerate privatekey without passphrase
openssl_privatekey:
path: '{{ output_dir }}/privatekeypw.pem'
select_crypto_backend: '{{ select_crypto_backend }}'
backup: yes
register: passphrase_3

- name: Regenerate privatekey without passphrase (idempotent)
openssl_privatekey:
path: '{{ output_dir }}/privatekeypw.pem'
select_crypto_backend: '{{ select_crypto_backend }}'
backup: yes
register: passphrase_4

- name: Regenerate privatekey with passphrase
@@ -177,4 +181,25 @@
passphrase: hunter2
cipher: "{{ 'aes256' if select_crypto_backend == 'pyopenssl' else 'auto' }}"
select_crypto_backend: '{{ select_crypto_backend }}'
backup: yes
register: passphrase_5

- name: Remove module
openssl_privatekey:
path: '{{ output_dir }}/privatekeypw.pem'
passphrase: hunter2
cipher: "{{ 'aes256' if select_crypto_backend == 'pyopenssl' else 'auto' }}"
select_crypto_backend: '{{ select_crypto_backend }}'
backup: yes
state: absent
register: remove_1

- name: Remove module (idempotent)
openssl_privatekey:
path: '{{ output_dir }}/privatekeypw.pem'
passphrase: hunter2
cipher: "{{ 'aes256' if select_crypto_backend == 'pyopenssl' else 'auto' }}"
select_crypto_backend: '{{ select_crypto_backend }}'
backup: yes
state: absent
register: remove_2
@@ -113,3 +113,16 @@
- passphrase_3 is changed
- passphrase_4 is not changed
- passphrase_5 is changed
- passphrase_1.backup_file is undefined
- passphrase_2.backup_file is undefined
- passphrase_3.backup_file is not none
- passphrase_4.backup_file is undefined
- passphrase_5.backup_file is not none

- name: Validate remove
assert:
that:
- remove_1 is changed
- remove_2 is not changed
- remove_1.backup_file is not none
- remove_2.backup_file is undefined

0 comments on commit e00f315

Please sign in to comment.
You can’t perform that action at this time.