mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
luks_device: add allow_to_remove_last_key option (#52371)
* Add allow_to_remove_last_key option. * Dump headers. * Add support for old versions of cryptsetup luksDump. * Update lib/ansible/modules/crypto/luks_device.py Co-Authored-By: felixfontein <felix@fontein.de> * Rename allow_to_remove_last_key -> force_remove_last_key.
This commit is contained in:
parent
fbbab7429e
commit
4573f349ea
2 changed files with 88 additions and 6 deletions
|
@ -70,6 +70,9 @@ options:
|
||||||
Needs I(keyfile) option for authorization. LUKS container
|
Needs I(keyfile) option for authorization. LUKS container
|
||||||
supports up to 8 keys. Parameter value is the path
|
supports up to 8 keys. Parameter value is the path
|
||||||
to the keyfile with the passphrase."
|
to the keyfile with the passphrase."
|
||||||
|
- "NOTE that adding additional keys is I(not idempotent).
|
||||||
|
A new keyslot will be used even if another keyslot already
|
||||||
|
exists for this keyfile."
|
||||||
- "BEWARE that working with keyfiles in plaintext is dangerous.
|
- "BEWARE that working with keyfiles in plaintext is dangerous.
|
||||||
Make sure that they are protected."
|
Make sure that they are protected."
|
||||||
type: path
|
type: path
|
||||||
|
@ -78,12 +81,20 @@ options:
|
||||||
- "Removes given key from the container on I(device). Does not
|
- "Removes given key from the container on I(device). Does not
|
||||||
remove the keyfile from filesystem.
|
remove the keyfile from filesystem.
|
||||||
Parameter value is the path to the keyfile with the passphrase."
|
Parameter value is the path to the keyfile with the passphrase."
|
||||||
- "BEWARE that it is possible to remove even the last key from the
|
- "NOTE that removing keys is I(not idempotent). Trying to remove
|
||||||
container. Data in there will be irreversibly lost
|
a key which no longer exists results in an error."
|
||||||
without a warning."
|
- "NOTE that to remove the last key from a LUKS container, the
|
||||||
|
I(force_remove_last_key) option must be set to C(yes)."
|
||||||
- "BEWARE that working with keyfiles in plaintext is dangerous.
|
- "BEWARE that working with keyfiles in plaintext is dangerous.
|
||||||
Make sure that they are protected."
|
Make sure that they are protected."
|
||||||
type: path
|
type: path
|
||||||
|
force_remove_last_key:
|
||||||
|
description:
|
||||||
|
- "If set to C(yes), allows removing the last key from a container."
|
||||||
|
- "BEWARE that when the last key has been removed from a container,
|
||||||
|
the container can no longer be opened!"
|
||||||
|
type: bool
|
||||||
|
default: no
|
||||||
|
|
||||||
requirements:
|
requirements:
|
||||||
- "cryptsetup"
|
- "cryptsetup"
|
||||||
|
@ -284,10 +295,40 @@ class CryptHandler(Handler):
|
||||||
raise ValueError('Error while adding new LUKS key to %s: %s'
|
raise ValueError('Error while adding new LUKS key to %s: %s'
|
||||||
% (device, result[STDERR]))
|
% (device, result[STDERR]))
|
||||||
|
|
||||||
def run_luks_remove_key(self, device, keyfile):
|
def run_luks_remove_key(self, device, keyfile, force_remove_last_key=False):
|
||||||
''' Remove key from given device
|
''' Remove key from given device
|
||||||
Raises ValueError when command fails
|
Raises ValueError when command fails
|
||||||
'''
|
'''
|
||||||
|
if not force_remove_last_key:
|
||||||
|
result = self._run_command([self._cryptsetup_bin, 'luksDump', device])
|
||||||
|
if result[RETURN_CODE] != 0:
|
||||||
|
raise ValueError('Error while dumping LUKS header from %s'
|
||||||
|
% (device, ))
|
||||||
|
keyslot_count = 0
|
||||||
|
keyslot_area = False
|
||||||
|
keyslot_re = re.compile(r'^Key Slot [0-9]+: ENABLED')
|
||||||
|
for line in result[STDOUT].splitlines():
|
||||||
|
if line.startswith('Keyslots:'):
|
||||||
|
keyslot_area = True
|
||||||
|
elif line.startswith(' '):
|
||||||
|
# LUKS2 header dumps use human-readable indented output.
|
||||||
|
# Thus we have to look out for 'Keyslots:' and count the
|
||||||
|
# number of indented keyslot numbers.
|
||||||
|
if keyslot_area and line[2] in '0123456789':
|
||||||
|
keyslot_count += 1
|
||||||
|
elif line.startswith('\t'):
|
||||||
|
pass
|
||||||
|
elif keyslot_re.match(line):
|
||||||
|
# LUKS1 header dumps have one line per keyslot with ENABLED
|
||||||
|
# or DISABLED in them. We count such lines with ENABLED.
|
||||||
|
keyslot_count += 1
|
||||||
|
else:
|
||||||
|
keyslot_area = False
|
||||||
|
if keyslot_count < 2:
|
||||||
|
self._module.fail_json(msg="LUKS device %s has less than two active keyslots. "
|
||||||
|
"To be able to remove a key, please set "
|
||||||
|
"`force_remove_last_key` to `yes`." % device)
|
||||||
|
|
||||||
result = self._run_command([self._cryptsetup_bin, 'luksRemoveKey', device,
|
result = self._run_command([self._cryptsetup_bin, 'luksRemoveKey', device,
|
||||||
'-q', '--key-file', keyfile])
|
'-q', '--key-file', keyfile])
|
||||||
if result[RETURN_CODE] != 0:
|
if result[RETURN_CODE] != 0:
|
||||||
|
@ -412,7 +453,8 @@ def run_module():
|
||||||
name=dict(type='str'),
|
name=dict(type='str'),
|
||||||
keyfile=dict(type='path'),
|
keyfile=dict(type='path'),
|
||||||
new_keyfile=dict(type='path'),
|
new_keyfile=dict(type='path'),
|
||||||
remove_keyfile=dict(type='path')
|
remove_keyfile=dict(type='path'),
|
||||||
|
force_remove_last_key=dict(type='bool', default=False),
|
||||||
)
|
)
|
||||||
|
|
||||||
# seed the result dict in the object
|
# seed the result dict in the object
|
||||||
|
@ -491,7 +533,8 @@ def run_module():
|
||||||
if conditions.luks_remove_key():
|
if conditions.luks_remove_key():
|
||||||
try:
|
try:
|
||||||
crypt.run_luks_remove_key(module.params['device'],
|
crypt.run_luks_remove_key(module.params['device'],
|
||||||
module.params['remove_keyfile'])
|
module.params['remove_keyfile'],
|
||||||
|
force_remove_last_key=module.params['force_remove_last_key'])
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
module.fail_json(msg="luks_device error: %s" % e)
|
module.fail_json(msg="luks_device error: %s" % e)
|
||||||
result['changed'] = True
|
result['changed'] = True
|
||||||
|
|
|
@ -62,6 +62,9 @@
|
||||||
device: "{{ cryptfile_device }}"
|
device: "{{ cryptfile_device }}"
|
||||||
state: closed
|
state: closed
|
||||||
|
|
||||||
|
- name: Dump LUKS header
|
||||||
|
command: "cryptsetup luksDump {{ cryptfile_device }}"
|
||||||
|
|
||||||
- name: Remove access from keyfile1
|
- name: Remove access from keyfile1
|
||||||
luks_device:
|
luks_device:
|
||||||
device: "{{ cryptfile_device }}"
|
device: "{{ cryptfile_device }}"
|
||||||
|
@ -100,6 +103,9 @@
|
||||||
device: "{{ cryptfile_device }}"
|
device: "{{ cryptfile_device }}"
|
||||||
state: closed
|
state: closed
|
||||||
|
|
||||||
|
- name: Dump LUKS header
|
||||||
|
command: "cryptsetup luksDump {{ cryptfile_device }}"
|
||||||
|
|
||||||
- name: Remove access from keyfile2
|
- name: Remove access from keyfile2
|
||||||
luks_device:
|
luks_device:
|
||||||
device: "{{ cryptfile_device }}"
|
device: "{{ cryptfile_device }}"
|
||||||
|
@ -107,6 +113,39 @@
|
||||||
keyfile: "{{ role_path }}/files/keyfile2"
|
keyfile: "{{ role_path }}/files/keyfile2"
|
||||||
remove_keyfile: "{{ role_path }}/files/keyfile2"
|
remove_keyfile: "{{ role_path }}/files/keyfile2"
|
||||||
become: yes
|
become: yes
|
||||||
|
ignore_errors: yes
|
||||||
|
register: remove_last_key
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- remove_last_key is failed
|
||||||
|
- "'force_remove_last_key' in remove_last_key.msg"
|
||||||
|
|
||||||
|
# Access: keyfile2
|
||||||
|
|
||||||
|
- name: Try to open with keyfile2
|
||||||
|
luks_device:
|
||||||
|
device: "{{ cryptfile_device }}"
|
||||||
|
state: opened
|
||||||
|
keyfile: "{{ role_path }}/files/keyfile2"
|
||||||
|
become: yes
|
||||||
|
ignore_errors: yes
|
||||||
|
register: open_try
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- open_try is not failed
|
||||||
|
- name: Close
|
||||||
|
luks_device:
|
||||||
|
device: "{{ cryptfile_device }}"
|
||||||
|
state: closed
|
||||||
|
|
||||||
|
- name: Remove access from keyfile2
|
||||||
|
luks_device:
|
||||||
|
device: "{{ cryptfile_device }}"
|
||||||
|
state: closed
|
||||||
|
keyfile: "{{ role_path }}/files/keyfile2"
|
||||||
|
remove_keyfile: "{{ role_path }}/files/keyfile2"
|
||||||
|
force_remove_last_key: yes
|
||||||
|
become: yes
|
||||||
|
|
||||||
# Access: none
|
# Access: none
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue