From d921ff1f68522cc3e8080c7b3380cd34775493bf Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Fri, 22 Jan 2021 13:21:03 +0100 Subject: [PATCH] Allow to configure PBKDF (#163) * Allow to configure PBKDF. * Also add PBKDF options to key add operation. * Simplify code. * Update plugins/modules/luks_device.py Co-authored-by: Andrew Klychkov * Fix indent. * Use more of the options. * Bump iteration count. * Increase memory limit. * Fall back to default PBKDF. * Apply suggestions from code review Co-authored-by: Andrew Klychkov Co-authored-by: Andrew Klychkov --- changelogs/fragments/163-luks-pbkdf.yml | 2 + plugins/modules/luks_device.py | 76 ++++++++++++++++++- .../tasks/tests/create-destroy.yml | 8 ++ .../luks_device/tasks/tests/device-check.yml | 8 ++ .../tasks/tests/key-management.yml | 4 + .../luks_device/tasks/tests/options.yml | 8 ++ .../luks_device/tasks/tests/passphrase.yml | 30 ++++++++ 7 files changed, 133 insertions(+), 3 deletions(-) create mode 100644 changelogs/fragments/163-luks-pbkdf.yml diff --git a/changelogs/fragments/163-luks-pbkdf.yml b/changelogs/fragments/163-luks-pbkdf.yml new file mode 100644 index 00000000..646fe5f5 --- /dev/null +++ b/changelogs/fragments/163-luks-pbkdf.yml @@ -0,0 +1,2 @@ +minor_changes: +- "luks_device - allow to configure PBKDF (https://github.com/ansible-collections/community.crypto/pull/163)." diff --git a/plugins/modules/luks_device.py b/plugins/modules/luks_device.py index a69c5597..3749e0a9 100644 --- a/plugins/modules/luks_device.py +++ b/plugins/modules/luks_device.py @@ -170,6 +170,47 @@ options: - "Will only be used on container creation." type: str version_added: '1.1.0' + pbkdf: + description: + - This option allows the user to configure the Password-Based Key Derivation + Function (PBKDF) used. + - Will only be used on container creation, and when adding keys to an existing + container. + type: dict + version_added: '1.4.0' + suboptions: + iteration_time: + description: + - Specify the iteration time used for the PBKDF. + - Note that this is in B(seconds), not in milliseconds as on the + command line. + - Mutually exclusive with I(iteration_count). + type: float + iteration_count: + description: + - Specify the iteration count used for the PBKDF. + - Mutually exclusive with I(iteration_time). + type: int + algorithm: + description: + - The algorithm to use. + - Only available for the LUKS 2 format. + choices: + - argon2i + - argon2id + - pbkdf2 + type: str + memory: + description: + - The memory cost limit in kilobytes for the PBKDF. + - This is not used for PBKDF2, but only for the Argon PBKDFs. + type: int + parallel: + description: + - The parallel cost for the PBKDF. This is the number of threads that + run in parallel. + - This is not used for PBKDF2, but only for the Argon PBKDFs. + type: int requirements: - "cryptsetup" @@ -399,7 +440,19 @@ class CryptHandler(Handler): result = self._run_command([self._cryptsetup_bin, 'isLuks', device]) return result[RETURN_CODE] == 0 - def run_luks_create(self, device, keyfile, passphrase, keysize, cipher, hash_): + def _add_pbkdf_options(self, options, pbkdf): + if pbkdf['iteration_time'] is not None: + options.extend(['--iter-time', str(int(pbkdf['iteration_time'] * 1000))]) + if pbkdf['iteration_count'] is not None: + options.extend(['--pbkdf-force-iterations', str(pbkdf['iteration_count'])]) + if pbkdf['algorithm'] is not None: + options.extend(['--pbkdf', pbkdf['algorithm']]) + if pbkdf['memory'] is not None: + options.extend(['--pbkdf-memory', str(pbkdf['memory'])]) + if pbkdf['parallel'] is not None: + options.extend(['--pbkdf-parallel', str(pbkdf['parallel'])]) + + def run_luks_create(self, device, keyfile, passphrase, keysize, cipher, hash_, pbkdf): # create a new luks container; use batch mode to auto confirm luks_type = self._module.params['type'] label = self._module.params['label'] @@ -416,6 +469,8 @@ class CryptHandler(Handler): options.extend(['--cipher', cipher]) if hash_ is not None: options.extend(['--hash', hash_]) + if pbkdf is not None: + self._add_pbkdf_options(options, pbkdf) args = [self._cryptsetup_bin, 'luksFormat'] args.extend(options) @@ -456,13 +511,15 @@ class CryptHandler(Handler): % (device, result[STDERR])) def run_luks_add_key(self, device, keyfile, passphrase, new_keyfile, - new_passphrase): + new_passphrase, pbkdf): ''' Add new key from a keyfile or passphrase to given 'device'; authentication done using 'keyfile' or 'passphrase'. Raises ValueError when command fails. ''' data = [] args = [self._cryptsetup_bin, 'luksAddKey', device] + if pbkdf is not None: + self._add_pbkdf_options(args, pbkdf) if keyfile: args.extend(['--key-file', keyfile]) @@ -691,6 +748,17 @@ def run_module(): type=dict(type='str', choices=['luks1', 'luks2']), cipher=dict(type='str'), hash=dict(type='str'), + pbkdf=dict( + type='dict', + options=dict( + iteration_time=dict(type='float'), + iteration_count=dict(type='int'), + algorithm=dict(type='str', choices=['argon2i', 'argon2id', 'pbkdf2']), + memory=dict(type='int'), + parallel=dict(type='int'), + ), + mutually_exclusive=[('iteration_time', 'iteration_count')], + ), ) mutually_exclusive = [ @@ -738,6 +806,7 @@ def run_module(): module.params['keysize'], module.params['cipher'], module.params['hash'], + module.params['pbkdf'], ) except ValueError as e: module.fail_json(msg="luks_device error: %s" % e) @@ -799,7 +868,8 @@ def run_module(): module.params['keyfile'], module.params['passphrase'], module.params['new_keyfile'], - module.params['new_passphrase']) + module.params['new_passphrase'], + module.params['pbkdf']) except ValueError as e: module.fail_json(msg="luks_device error: %s" % e) result['changed'] = True diff --git a/tests/integration/targets/luks_device/tasks/tests/create-destroy.yml b/tests/integration/targets/luks_device/tasks/tests/create-destroy.yml index 6ac4d1c3..9e4e1f3f 100644 --- a/tests/integration/targets/luks_device/tasks/tests/create-destroy.yml +++ b/tests/integration/targets/luks_device/tasks/tests/create-destroy.yml @@ -4,6 +4,8 @@ device: "{{ cryptfile_device }}" state: present keyfile: "{{ role_path }}/files/keyfile1" + pbkdf: + iteration_time: 0.1 check_mode: yes become: yes register: create_check @@ -12,6 +14,8 @@ device: "{{ cryptfile_device }}" state: present keyfile: "{{ role_path }}/files/keyfile1" + pbkdf: + iteration_time: 0.1 become: yes register: create - name: Create (idempotent) @@ -19,6 +23,8 @@ device: "{{ cryptfile_device }}" state: present keyfile: "{{ role_path }}/files/keyfile1" + pbkdf: + iteration_time: 0.1 become: yes register: create_idem - name: Create (idempotent, check) @@ -26,6 +32,8 @@ device: "{{ cryptfile_device }}" state: present keyfile: "{{ role_path }}/files/keyfile1" + pbkdf: + iteration_time: 0.1 check_mode: yes become: yes register: create_idem_check diff --git a/tests/integration/targets/luks_device/tasks/tests/device-check.yml b/tests/integration/targets/luks_device/tasks/tests/device-check.yml index 3682a33e..56797f91 100644 --- a/tests/integration/targets/luks_device/tasks/tests/device-check.yml +++ b/tests/integration/targets/luks_device/tasks/tests/device-check.yml @@ -4,6 +4,8 @@ device: /dev/asdfasdfasdf state: present keyfile: "{{ role_path }}/files/keyfile1" + pbkdf: + iteration_time: 0.1 check_mode: yes ignore_errors: yes become: yes @@ -13,6 +15,8 @@ device: /dev/asdfasdfasdf state: present keyfile: "{{ role_path }}/files/keyfile1" + pbkdf: + iteration_time: 0.1 ignore_errors: yes become: yes register: create @@ -28,6 +32,8 @@ device: /tmp/ state: present keyfile: "{{ role_path }}/files/keyfile1" + pbkdf: + iteration_time: 0.1 check_mode: yes ignore_errors: yes become: yes @@ -37,6 +43,8 @@ device: /tmp/ state: present keyfile: "{{ role_path }}/files/keyfile1" + pbkdf: + iteration_time: 0.1 ignore_errors: yes become: yes register: create diff --git a/tests/integration/targets/luks_device/tasks/tests/key-management.yml b/tests/integration/targets/luks_device/tasks/tests/key-management.yml index 3e35d753..cdf1d594 100644 --- a/tests/integration/targets/luks_device/tasks/tests/key-management.yml +++ b/tests/integration/targets/luks_device/tasks/tests/key-management.yml @@ -4,6 +4,8 @@ device: "{{ cryptfile_device }}" state: closed keyfile: "{{ role_path }}/files/keyfile1" + pbkdf: + iteration_time: 0.1 become: yes # Access: keyfile1 @@ -43,6 +45,8 @@ state: closed keyfile: "{{ role_path }}/files/keyfile1" new_keyfile: "{{ role_path }}/files/keyfile2" + pbkdf: + iteration_time: 0.1 become: yes register: result_1 diff --git a/tests/integration/targets/luks_device/tasks/tests/options.yml b/tests/integration/targets/luks_device/tasks/tests/options.yml index afee6667..62ac3e9b 100644 --- a/tests/integration/targets/luks_device/tasks/tests/options.yml +++ b/tests/integration/targets/luks_device/tasks/tests/options.yml @@ -5,6 +5,8 @@ state: present keyfile: "{{ role_path }}/files/keyfile1" keysize: 256 + pbkdf: + iteration_count: 1000 become: yes register: create_with_keysize - name: Create with keysize (idempotent) @@ -13,6 +15,8 @@ state: present keyfile: "{{ role_path }}/files/keyfile1" keysize: 256 + pbkdf: + iteration_count: 1000 become: yes register: create_idem_with_keysize - name: Create with different keysize (idempotent since we do not update keysize) @@ -21,6 +25,8 @@ state: present keyfile: "{{ role_path }}/files/keyfile1" keysize: 512 + pbkdf: + iteration_count: 1000 become: yes register: create_idem_with_diff_keysize - name: Create with ambiguous arguments @@ -29,6 +35,8 @@ state: present keyfile: "{{ role_path }}/files/keyfile1" passphrase: "{{ cryptfile_passphrase1 }}" + pbkdf: + iteration_count: 1000 ignore_errors: yes become: yes register: create_with_ambiguous diff --git a/tests/integration/targets/luks_device/tasks/tests/passphrase.yml b/tests/integration/targets/luks_device/tasks/tests/passphrase.yml index 16c7ef8d..d91bb8c6 100644 --- a/tests/integration/targets/luks_device/tasks/tests/passphrase.yml +++ b/tests/integration/targets/luks_device/tasks/tests/passphrase.yml @@ -4,7 +4,29 @@ device: "{{ cryptfile_device }}" state: closed passphrase: "{{ cryptfile_passphrase1 }}" + pbkdf: + iteration_time: 0.1 + algorithm: argon2i + memory: 1000 + parallel: 1 become: yes + ignore_errors: yes + register: create_passphrase_1 + +- name: Make sure that the previous task only fails because the LUKS version used cannot handle the PBKDF parameters + assert: + that: + - create_passphrase_1 is not failed or 'Failed to set pbkdf parameters' in create_passphrase_1.msg + +- name: Create with passphrase1 (without argon2i) + luks_device: + device: "{{ cryptfile_device }}" + state: closed + passphrase: "{{ cryptfile_passphrase1 }}" + pbkdf: + iteration_time: 0.1 + become: yes + when: create_passphrase_1 is failed and 'Failed to set pbkdf parameters' in create_passphrase_1.msg - name: Open with passphrase1 luks_device: @@ -30,6 +52,8 @@ passphrase: "{{ cryptfile_passphrase1 }}" new_passphrase: "{{ cryptfile_passphrase2 }}" new_keyfile: "{{ role_path }}/files/keyfile1" + pbkdf: + iteration_time: 0.1 become: yes ignore_errors: yes register: new_try @@ -55,6 +79,8 @@ state: closed passphrase: "{{ cryptfile_passphrase1 }}" new_passphrase: "{{ cryptfile_passphrase2 }}" + pbkdf: + iteration_time: 0.1 become: yes register: result_1 @@ -107,6 +133,8 @@ state: closed passphrase: "{{ cryptfile_passphrase1 }}" new_keyfile: "{{ role_path }}/files/keyfile1" + pbkdf: + iteration_time: 0.1 become: yes - name: Remove access with ambiguous remove_ arguments @@ -190,6 +218,8 @@ state: closed keyfile: "{{ role_path }}/files/keyfile1" new_passphrase: "{{ cryptfile_passphrase3 }}" + pbkdf: + iteration_time: 0.1 become: yes - name: Open with passphrase3