Default variable details

Some of debops.cryptsetup variables have more extensive configuration. Here you can find documentation and examples for them.

cryptsetup__devices

The cryptsetup__devices and similar lists allow you to specify device configuration. The order can be important because devices depend on each other and this will determine the order in which the devices appear in /etc/crypttab.

Note the following list only documents the common parameters. The role allows you to use more specific parameters which are not documented below.

Each item of those lists is a dictionary with the following documented keys:

name

Required, string. Name of the plaintext device mapper target and the mount point (unless overwritten by item.mount). Must be unique among all device mapper targets and should not be changed once it was used.

If you want to change it, you can set state to absent, execute the role, rename the secrets directory corresponding to the name, adapt your inventory accordingly and run the role again to configure the item with the new name.

ciphertext_block_device

Required, string. File path to the ciphertext block device, either the block device itself e. g. /dev/sdb, a partition on the block device e. g. /dev/sdb5 or a regular file e. g. /tmp/ciphertext_file.raw.

Refer to item.use_uuid when you use a regular file.

use_uuid

Optional, boolean. Use the UUID of the ciphertext block device in /etc/crypttab instead of the file path given by item.ciphertext_block_device.

Note that this needs to be set to False if you are using a regular file as item.ciphertext_block_device.

Default to cryptsetup__use_uuid.

mode

Optional, string. The mode in which cryptsetup should operate. Supported modes/extensions:

  • plain
  • luks
  • tcrypt
  • veracrypt

Defaults to luks. There is no global variable to change this default. Refer to cryptsetup(8) for more details.

offset
Optional, integer start offset of the ciphertext block device which will be mapped to block 0 of the plaintext device mapper target. This option only has an effect in plain item.mode. There is no offset by default.
crypttab_options

Optional, list of strings. Each string represents an option to configure for the device in /etc/crypttab. See crypttab(5) for details. Default to cryptsetup__crypttab_options.

Note that cryptsetup options need to be specified using their corresponding parameters as documented in this section. If an option is not documented here, that is where you can use crypttab_options for. For example item.hash could also be specified using hash=sha256 as value for crypttab_options but this is not supported.

keyfile

Optional, string. File path for the keyfile on the Ansible controller. Will be copied over to the remote system. If it does not exist yet it will be generated using the systems random number generator on the Ansible controller as it is expected that the entropy pool on the Ansible controller is better mixed. Defaults to:

{{ cryptsetup__secret_path + "/" + item.name + "/keyfile.raw" }}
remote_keyfile
Optional, string. File path for the keyfile on the remote system. If this option is given it will be used directly and the keyfile will have no effect. It is expected that this file is already present on the remote system. Also note that the remote keyfile is not copied or backed up anywhere. The given file path is just used for opening/mapping the device. This option can also be a device path which will be used by dm-crypt to read the key like /dev/urandom, note however that LUKS requires a persistent key and therefore does not support random data keys. If a state is set which causes the device to become absent, the given remote keyfile will be made absent as well (but only if it is a regular file)! This option does not work with the ansible_controller_mounted state and the role will abort immediately if that combination is used.
keyfile_gen_type

Optional, string. Type of keyfile to generate. This does not effect already generated keyfiles. Defaults to cryptsetup__keyfile_gen_type.

binary
A binary keyfile will be generated using dd from the random source specified by cryptsetup__keyfile_source_dev. This should ensure the maximum amount of entropy for keyfiles.
text

The keyfile will be a random passphrase only consisting of printable characters suitable for automated or by-hand input. item.keyfile_gen_command will be used to output the passphrase.

Refer to the example for adding another boot disk to a FDE system for how this can be used.

keyfile_gen_command

Optional, string. The command which should be used to generate the keyfile when item.keyfile_gen_type is set to text. The command is expected to output one line to STDOUT.

Note that all newline characters (\n) are removed using tr -d '\n' internally so that the generated text key can be entered as regular passphrase. This is required because most CLI programs properly end their output with a newline. But when cryptsetup reads the key from a keyfile (which is what this role always uses internally), it does not terminate input when reading a newline. When reading from STDIN or from a terminal, it does however terminate on the first newline and uses the passphrase with the trailing newline stripped. Refer to cryptsetup(8) under Notes on passphrase processing for (plain mode|LUKS).

Defaults to cryptsetup__keyfile_gen_command.

backup_header

Optional, boolean. Should a header backup be created and stored on the remote system and the Ansible controller?

Note

The LUKS header is only stored once in the first few kilobytes of a given block device. When the header gets corrupted, the plaintext data might be inaccessible! Thus it is recommended to have a header backup on hand.

Debian buster and newer ship with Cryptsetup >2.0 which defaults to the LUKS2 format that provides redudancy of metadata. For security reasons, there is no redundancy in keyslots binary data (encrypted keys) but the format allows adding such a feature in future. Thus it is still recommended to have a header backup on hand.

Set to False to disable header backup creation and to ensure that the header backup is absent on the remote system. This option only has an effect in luks item.mode. For TrueCrypt/VeraCrypt you will need to create header backups manually! Defaults to cryptsetup__header_backup.

swap

Optional, boolean. Should the device be used as encrypted swap space? When set to True, the option item.manage_filesystem is ignored. Refer to debops.sysctl for paging and swapping related kernel settings. Defaults to False.

Refer to the example for an encrypted swap partition using a random key for how this can be used.

swap_priority
Optional, integer. Default swap device priority, from -1 to 32767. Higher numbers indicate higher priority. Refer to swapon(8) for details. Defaults to cryptsetup__swap_priority.
swap_options
Optional, list of strings. Additional swap "mount" options. Not item.mount_options nor any other global default value is being used for swap options.
manage_filesystem
Optional, boolean. Should a filesystem be created on the plaintext device mapper target and configured in /etc/fstab? Defaults to True.
create_filesystem
Optional, boolean. Should a filesystem be created on the plaintext device mapper target? Allows to only disable the creation of the filesystems but still manage an existing filesystem in /etc/fstab when item.manage_filesystem is True. Defaults to item.manage_filesystem.
fstype
Optional, string. Filesystem to create on the plaintext device mapper target and configure in /etc/fstab. Defaults to cryptsetup__fstype.
mount

Optional, string. Plaintext mount point of the filesystem. Defaults to:

{{ cryptsetup__mountpoint_parent_directory + "/" + item.name }}
mount_options
Optional, list of strings. Mount options associated with the filesystem. For more details see mount(8). Defaults to cryptsetup__mount_options.
state

Optional, string. There are four states which can be chosen for each encrypted filesystem. Defaults to cryptsetup__state.

mounted
Ensure that the encryption and filesystem layer are in place on the block device and the filesystem is mounted.
ansible_controller_mounted

Same as mounted except that the keyfile is never stored on persistent storage of the remote system. Might be useful when you don’t have a secure place to store the keyfile on the remote system. With this option you will be required to run this role after each reboot to mount the filesystem again.

Note that the implicit default for crypttab_options and mount_options is auto which means that your init system will try to mount the filesystem on boot and might drop you to a root shell if it can’t.

To avoid this, you need to set the following options for the item:

crypttab_options: '{{ ["noauto"] + (cryptsetup__crypttab_options|d([]) | list) }}'
mount_options: '{{ ["noauto"] + (cryptsetup__mount_options|d([]) | list) }}'

Note that this option is currently not idempotent because it copes the keyfile to the remote system and erases it again without checking before hand if the plaintext device mapper target is already present.

unmounted
Ensure that the encryption and filesystem layer are in place on the block device and the filesystem is unmounted. Additionally ensures that the cryptsetup mapping is removed so that no direct access to the plain-text block device is possible.
present

Ensure that the encryption and filesystem layer are in place on the block device. The plaintext device mapper target will be created and opened as needed during the Ansible run to ensure the filesystem on it is present. When the plaintext device mapper target was not opened prior to the Ansible run, then it will be stopped at the end of the role run again. So basically, this option never changes the mounted/unmounted state of the plaintext device mapper target or the plaintext mount point of the filesystem. Note that this option will not fail when the ciphertext block device is not available during the Ansible run and the keyfile has not been generated by Ansible. This was done to allow to provision remote systems with keys for ciphertext block devices which have been setup previously and are not available during execution of this role.

Note that if the encrypted filesystem is not mounted when this option is used then this role will not be idempotent because the crypto layer needs to be opened in order to check if the filesystem has been created on top of it.

absent
Same as unmounted but additionally removes all configuration, the keyfile and the header backup from the remote system.
hash
Optional, string. Specifies the passphrase hash. For the luks item.mode it specifies the hash used in the LUKS key setup scheme and volume key digest for cryptsetup luksFormat. Defaults to cryptsetup__hash.
cipher
Optional, string. Cipher specification. Defaults to cryptsetup__cipher.
key_size
Optional, integer. Key size in bits. Defaults to cryptsetup__key_size.
iter_time
Optional, int. The number of milliseconds to spend with PBKDF2 passphrase processing. This option only has an effect in luks item.mode. Defaults to cryptsetup__iter_time.

Example for encrypting a partition

Setup an encrypted filesystem on top of /dev/sdb5 which will be mounted after role execution under /media/sdb5_crypt and will be automatically mounted at boot:

cryptsetup__devices:

  - name: 'sdb5_crypt'
    ciphertext_block_device: '/dev/sdb5'

Example for an encrypted swap partition using a random key

Setup an encrypted swap partition which uses a new random key picked at each boot. Hibernation won’t work with that as the system won’t have access to the cleartext swap data the next time it starts as a new random key is being used to decrypt/encrypt the device on each boot.

cryptsetup__devices:

  - name: 'rand_key_swap0'
    mode: 'plain'
    swap: True
    remote_keyfile: '/dev/urandom'
    ciphertext_block_device: '/dev/disk/by-partuuid/a7a12244-a4aa-42b7-b605-997165b3fbac'

Example for an encrypted /tmp using a random key

Setup an encrypted /tmp which uses a new random key picked at each boot. A new filesystem will be created on each boot. By default ext4 will be used.

cryptsetup__devices:

  - name: 'rand_key_tmp'
    mode: 'plain'
    mount: '/tmp'
    remote_keyfile: '/dev/urandom'
    ciphertext_block_device: '/dev/disk/by-partuuid/a7a12244-a4aa-42b7-b605-997165b3fbac'
    create_filesystem: False
    crypttab_options: '{{ ["tmp"] + (cryptsetup__crypttab_options|d([]) | list) }}'
    # crypttab_options: '{{ ["tmp=" + cryptsetup__fstype] + (cryptsetup__crypttab_options|d([]) | list) }}'
    ## This seems to not work with Debian jessie (results in systemd waiting forever for the cleartext target).
    ## Using "tmp" instead worked.

Example for making a header backup of an existing FDE system

If you installed the OS using FDE and thus the encrypted filesystem was created by the installer you might still want to make a header backup. This can be done by setting remote_keyfile to none so that you will still be asked for the passphrase at boot and to avoid keyfile generation. Additionally manage_filesystem should be set to False so that an existing filesystem is not checked against fstype.

cryptsetup__devices:

  - name: 'vdb3_crypt'
    ciphertext_block_device: '/dev/disk/by-partuuid/55d1da1d-e1b0-4022-b17a-3b73cdc89286'
    manage_filesystem: False
    remote_keyfile: 'none'

Example for adding another boot disk to a FDE system with a different passphrase for both

In case you installed a FDE system on one disk and want to create a redundant setup afterwards by adding another disk, encrypting it and re-balancing a SOTA filesystem (Btrfs or ZFS) or growing a legacy RAID setup to it you can follow this example.

For this setup it is required that the added disk can be decrypted in the initramfs to assemble the root filesystem. To make this easier a passphrase will be used as keyfile instead of the default binary keyfile.

Using a passphrase also makes it easier to automate the key input at boot using FDEunlock which is also described in this example. You can ignore/remove the custom keyfile setting if you don’t use FDEunlock.

The keyfile is generated in the keys directory of the default FileVault implementation of FDEunlock. Refer to FDEunlock for details.

inventory_hostname can be used to make the configuration of the keyfile option easier to copy/paste. Note that inventory_hostname is used here because we don’t want to "to rely on the discovered hostname ansible_hostname or for other mysterious reasons" which the (ref: Magic Variables, and How To Access Information About Other Hosts). Seems we just found such a "mysterious reason". It is hoped that inventory_hostname is not spoofable because if it where, the role might hand out keys for others hosts to a host exploiting this potential vulnerability. You can set the keyfile manually if you want.

However, there is one issue to note here. The role normally configures devices to unlock them by keyfile or disable keyfile handling completely (when using remote_keyfile). In this example, a combination of both would be nice so that the role creates the crypto layer with the provided keyfile but does not configure it in /etc/crypttab. This is not directly supported and the role can not be extended easily to fully support this because of the internal role design. Changing that is not intended only to support this use case.

Also, this use case requires that the passphrase is never saved anywhere on persistent storage on the remote host.

There is a workaround which meets these requirements by making use of the ansible_controller_mounted state.

You will need two role runs with slightly changed configuration for this. For the first run, use something like this to ensure that the crypto layer is present and opened:

cryptsetup__devices:

  - name: 'sdb4_crypt'
    ciphertext_block_device: '/dev/disk/by-partuuid/3b014afe-1581-11e7-b65d-00163e5e6c0f'
    keyfile_gen_type: 'text'
    manage_filesystem: False
    keyfile: '/home/user/.config/fdeunlock/keys/{{ inventory_hostname }}-initramfs_dev_disk_by-partuuid_3b014afe-1581-11e7-b65d-00163e5e6c0f.key'

    ## Disable for initial setup else enable it:
    # remote_keyfile: 'none'

    ## Enable for initial setup else disable it:
    state: 'ansible_controller_mounted'

Now we will need the role to fix the entry in /etc/crypttab so that the passphrase is asked for on boot:

cryptsetup__devices:

  - name: 'sdb4_crypt'
    ciphertext_block_device: '/dev/disk/by-partuuid/3b014afe-1581-11e7-b65d-00163e5e6c0f'
    keyfile_gen_type: 'text'
    manage_filesystem: False
    keyfile: '/home/user/.config/fdeunlock/keys/{{ inventory_hostname }}-initramfs_dev_disk_by-partuuid_3b014afe-1581-11e7-b65d-00163e5e6c0f.key'

    ## Disable for initial setup else enable it:
    remote_keyfile: 'none'

    ## Enable for initial setup else disable it:
    # state: 'ansible_controller_mounted'

You should now be left with a decrypted sdb4_crypt plaintext device mapper target for which the key only exists in /home/user/.config/fdeunlock/keys/{ inventory_hostname }-initramfs_dev_disk_by-partuuid_3b014afe-1581-11e7-b65d-00163e5e6c0f.key on the Ansible controller.

Example for adding another boot disk to a FDE system with the same passphrase for both

This section is very similar to the previous example and you are expected to have understood it to not have to repeat everything here. Compared to the previous section which configured two disks for automated decryption using external network tools, this example configures multiple disks for manual passphrase entering by a human. The idea therefore is to use the same passphrase for the disks.

There are two options to provide the passphrase. Either cryptsetup luksFormat the disks manually and then open the crypto layer with the expected name. Alternatively provide the passphrase on the Ansible controller in { cryptsetup__secret_path }/sdX5_crypt_passphrase.txt for example.

If you provided the passphrase on the Ansible controller, you will need the workaround as in the previous example by making use of the ansible_controller_mounted state. The role will need to be run two times with slightly changed configuration. For the first run, use something like this to ensure that the crypto layer is present and opened:

cryptsetup__devices:

  - name: 'sdb4_crypt'
    ciphertext_block_device: '/dev/disk/by-partuuid/6114134e-4796-11ea-8ec1-00163e5e6c00'
    manage_filesystem: False
    keyfile: '{{ cryptsetup__secret_path }}/sdX5_crypt_passphrase.txt'

    ## Disable for initial setup else enable it:
    # remote_keyfile: 'root_fs'
    # crypttab_options: '{{ ["keyscript=decrypt_keyctl"] + (cryptsetup__crypttab_options|d([]) | list) }}'

    ## Enable for initial setup else disable it:
    state: 'ansible_controller_mounted'

Now we will need the role to fix the entry in /etc/crypttab so that the passphrase is asked only once on boot. The keyfile parameter does nothing at this point with remote_keyfile specified so if you don’t want to store the passphrase on the Ansible controller and did cryptsetup luksFormat manually, then feel free to omit keyfile.

cryptsetup__devices:

  - name: 'sdb4_crypt'
    ciphertext_block_device: '/dev/disk/by-partuuid/6114134e-4796-11ea-8ec1-00163e5e6c00'
    manage_filesystem: False
    keyfile: '{{ cryptsetup__secret_path }}/sdX5_crypt_passphrase.txt'

    ## Disable for initial setup else enable it:
    remote_keyfile: 'root_fs'
    crypttab_options: '{{ ["keyscript=decrypt_keyctl"] + (cryptsetup__crypttab_options|d([]) | list) }}'

    ## Enable for initial setup else disable it:
    # state: 'ansible_controller_mounted'

Example for chaining multiple ciphers

Setup a vault using three different ciphers and three different keys. A similar feature is supported by TrueCrypt/VeraCrypt.

Note that order is important here and that the cryptsetup__devices_execution_strategy option has to be set to serial when using such an example.

cryptsetup__devices_execution_strategy: 'serial'
cryptsetup__devices:

  ## Use AES for the most outer layer to not rise suspicion just yet :)
  - name: 'vault_ciphertext0'
    ciphertext_block_device: '/tmp/ciphertext_vault_file.raw'
    manage_filesystem: False
    # Don’t try to use a UUID for a regular file.
    use_uuid: False

  - name: 'vault_ciphertext1'
    ciphertext_block_device: '/dev/mapper/vault_ciphertext0'
    manage_filesystem: False
    cipher: 'twofish-xts-plain64'
    key_size: 512

  - name: 'vault'
    ciphertext_block_device: '/dev/mapper/vault_ciphertext1'
    cipher: 'serpent-xts-plain64'
    key_size: 512

This will encrypt /tmp/ciphertext_vault_file.raw using the default cipher (cryptsetup__cipher which defaults to AES) and make the "clear text" of that outer layer available under /dev/mapper/vault_ciphertext0. /dev/mapper/vault_ciphertext0 is then en/decrypted using Twofish and the "clear text" of that is mapped to /dev/mapper/vault_ciphertext1. /dev/mapper/vault_ciphertext1 is then en/decrypted using Serpent and mapped to the real clear text block device /dev/mapper/vault on which a filesystem will be created and which will be mounted as usual.

This is surely a more extreme example but it has been tested in a lab environment and the setup seems to work just fine. Also automatic mapping/mounting of all layers works seamlessly on system boot if configured to do so (which is the default).

You can even boot from such a chained number of devices but you might need to manually list the vault_ciphertext device(s) in /etc/initramfs-tools/conf.d/cryptroot. At least on Debian Stretch this is required. mkinitramfs -k -o /tmp/initramfs_tmp and cat /var/tmp/mkinitramfs_$XXXX/conf/conf.d/cryptroot can help you to see if the full chain is known to the initramfs. If so, regenerate the actual initramfs and reboot to test it.

The list of cyphers and key sizes can be checked with cryptsetup benchmark. You can check that the ciphers are chained as expected using cryptsetup status vault, cryptsetup status vault_ciphertext1 and so on.

If you intend to do this then note that in most scenarios the used cipher(s) will not be your weakest link. For example AES should be suitable on it’s own to provide reasonable Information Security. You must also think about other areas of Computer Security and Operations security for this example to make sense.

Example for TrueCrypt/VeraCrypt encrypted devices

cryptsetup supports to open TrueCrypt ciphertext block devices and starting with cryptsetup version 1.6.7 also VeraCrypt. As TrueCrypt has been superseded by VeraCrypt, only the later one will be mentioned in this section from now on.

Because VeraCrypt is uncommon in a purely GNU/Linux based environment and is not packaged for Debian, this role does not interact in any way with VeraCrypt. You don’t need to install it on hosts you run this role against.

You will need to use VeraCrypt for creation as cryptsetup and this role do not support this. Note that currently only a passphrase is supported which can be passed in the usual manner by writing it into the keyfile on the Ansible controller. The keyfile should not contain newline characters (\n), see item.keyfile_gen_command. Note that you will need to create a header backup manually!

Because VeraCrypt is great for platform portability, you might choose a different filesystem as done in this example:

cryptsetup__devices:
  - name: 'mydatadisk'
    ciphertext_block_device: '/dev/disk/by-partuuid/65ca7bc4-6cb7-11e7-b49b-00163e5e6c0f'
    mode: 'veracrypt'
    fstype: 'ntfs'
    create_filesystem: False
    mount_options: '{{ cryptsetup__mount_options + ["umask=027", "fmask=117", "uid=1000", "gid=1000"] }}'