1
0
Fork 0
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:
Felix Fontein 2019-03-14 10:55:35 +01:00 committed by John R Barker
parent fbbab7429e
commit 4573f349ea
2 changed files with 88 additions and 6 deletions

View file

@ -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

View file

@ -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