Repository: ansible/ansible
Pull request: Add passphrase support for luks_device 
##### SUMMARY

This PR implements support for operating on LUKS devices with passphrases.  This enables users to use the `luks_device` module without having to write their passphrase to disk beforehand.

Fixes #52408

##### ISSUE TYPE
- Feature Pull Request

##### COMPONENT NAME
luks_device

Problem statement:
luks_device: allow to pass keys as text (as opposed to keyfiles)
##### SUMMARY
It would be nice if one could specify the keys directly as content, without having to first write them to disk to use them as keyfiles.

##### ISSUE TYPE
- Feature Idea

##### COMPONENT NAME
luks_device

Hints from issue discussion:
Files identified in the description:
* [lib/ansible/modules/crypto/luks_device.py](https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/crypto/luks_device.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @MarkusTeufelberger @Shaps @Spredzy @Xyon @japokorn @puiterwijk @resmo
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
needs_contributor
I agree with this feature to pass passphrase from external vault like ArchyVault or Syspass with lookup directly and not need to write temporary files on target.

Commit message: Elevate privileges for luks_device integration tests

Several tests in `key-management.yml` don't `become` before executing,
despite needing elevated privileges.  This commit fixes that.

Patch to review:
diff --git a/test/integration/targets/luks_device/tasks/tests/key-management.yml b/test/integration/targets/luks_device/tasks/tests/key-management.yml
index 1866a46dbc9db0..b89d505fa69232 100644
--- a/test/integration/targets/luks_device/tasks/tests/key-management.yml
+++ b/test/integration/targets/luks_device/tasks/tests/key-management.yml
@@ -23,6 +23,7 @@
   luks_device:
     device: "{{ cryptfile_device }}"
     state: closed
+  become: yes
 
 - name: Try to open with keyfile2
   luks_device:
@@ -61,9 +62,11 @@
   luks_device:
     device: "{{ cryptfile_device }}"
     state: closed
+  become: yes
 
 - name: Dump LUKS header
   command: "cryptsetup luksDump {{ cryptfile_device }}"
+  become: yes
 
 - name: Remove access from keyfile1
   luks_device:
@@ -102,9 +105,11 @@
   luks_device:
     device: "{{ cryptfile_device }}"
     state: closed
+  become: yes
 
 - name: Dump LUKS header
   command: "cryptsetup luksDump {{ cryptfile_device }}"
+  become: yes
 
 - name: Remove access from keyfile2
   luks_device:
@@ -137,6 +142,7 @@
   luks_device:
     device: "{{ cryptfile_device }}"
     state: closed
+  become: yes
 
 - name: Remove access from keyfile2
   luks_device:


Code review comments:
diff_hunk: @@ -72,15 +82,26 @@
     new_keyfile:
         description:
             - "Adds additional key to given container on I(device).
-              Needs I(keyfile) option for authorization. LUKS container
-              supports up to 8 keys. Parameter value is the path
-              to the keyfile with the passphrase."
+              Needs I(keyfile) or I(passphrase) option for authorization.
+              LUKS container supports up to 8 keyslots. Parameter value
+              is the path 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.
               Make sure that they are protected."
         type: path
+    new_passphrase:
+        description:
+            - "Adds additional passphrase to given container on I(device).
+              Needs I(keyfile) or I(passphrase) option for authorization. LUKS
+              container supports up to 8 keyslots. Parameter value is a string
+              with the new passphrase."
+            - "NOTE that adding additional passphrase is I(not idempotent).  A
path: lib/ansible/modules/crypto/luks_device.py
text: @user1:
```suggestion
            - "NOTE that adding additional passphrase is *not idempotent*.  A
```
Can you also change the same thing in the previous option?

diff_hunk: @@ -246,8 +296,8 @@ def __init__(self, module):
         self._module = module
         self._lsblk_bin = self._module.get_bin_path('lsblk', True)
 
-    def _run_command(self, command):
-        return self._module.run_command(command)
+    def _run_command(self, command, **kwargs):
path: lib/ansible/modules/crypto/luks_device.py
text: @user1:
It is better to only add the options you need, and not have a general `**kwargs` catch-all. Here you only need `data`, right? Better add it explicitly:
```suggestion
    def _run_command(self, command, data=None):
```

diff_hunk: @@ -376,17 +432,32 @@ def run_luks_remove(self, device):
             raise ValueError('Error while wiping luks container %s: %s'
                              % (device, result[STDERR]))
 
-    def run_luks_add_key(self, device, keyfile, new_keyfile):
-        ''' Add new key to given 'device'; authentication done using 'keyfile'
-            Raises ValueError when command fails
+    def run_luks_add_key(self, device, keyfile, passphrase, new_keyfile,
+                         new_passphrase):
+        ''' Add new key from a keyfile or passphrase to given 'device';
+            authentication done using 'keyfile' or 'passphrase'.
+            Raises ValueError when command fails.
         '''
-        result = self._run_command([self._cryptsetup_bin, 'luksAddKey', device,
-                                    new_keyfile, '--key-file', keyfile])
+        data = []
+        args = [self._cryptsetup_bin, 'luksAddKey', device]
+
+        if keyfile:
+            args.extend(['--key-file', keyfile])
+        else:
+            data.append(passphrase)
+
+        if new_keyfile:
+            args.append(new_keyfile)
+        else:
+            data.extend([new_passphrase, new_passphrase])
+
+        result = self._run_command(args, data='\n'.join(data) or None)
         if result[RETURN_CODE] != 0:
             raise ValueError('Error while adding new LUKS key to %s: %s'
path: lib/ansible/modules/crypto/luks_device.py
text: @user1:
```suggestion
            raise ValueError('Error while adding new LUKS keyslot to %s: %s'
```
While you're at it ;)