Chapter 10. Configuring automated unlocking of encrypted volumes by using policy-based decryption Policy-Based Decryption (PBD) is a collection of technologies that enable unlocking encrypted root and secondary volumes of hard drives on physical and virtual machines. PBD uses a variety of unlocking methods, such as user passwords, a Trusted Platform Module (TPM) device, a PKCS #11 device connected to a system, for example, a smart card, or a special network server. PBD allows combining different unlocking methods into a policy, which makes it possible to unlock the same volume in different ways. The current implementation of the PBD in RHEL consists of the Clevis framework and plug-ins called pins . Each pin provides a separate unlocking capability. Currently, the following pins are available: tang Allows unlocking volumes using a network server. tpm2 allows unlocking volumes using a TPM2 policy. sss allows deploying high-availability systems using the Shamir's Secret Sharing (SSS) cryptographic scheme. 10.1. Network-bound disk encryption The Network Bound Disc Encryption (NBDE) is a subcategory of Policy-Based Decryption (PBD) that allows binding encrypted volumes to a special network server. The current implementation of the NBDE includes a Clevis pin for the Tang server and the Tang server itself. In RHEL, NBDE is implemented through the following components and technologies: Figure 10.1. NBDE scheme when using a LUKS1-encrypted volume. The luksmeta package is not used for LUKS2 volumes. Tang is a server for binding data to network presence. It makes a system containing your data available when the system is bound to a certain secure network. Tang is stateless and does not require TLS or authentication. Unlike escrow-based solutions, where the server stores all encryption keys and has knowledge of every key ever used, Tang never interacts with any client keys, so it never gains any identifying information from the client. Clevis is a pluggable framework for automated decryption. In NBDE, Clevis provides automated unlocking of LUKS volumes. The clevis package provides the client side of the feature. A Clevis pin is a plug-in into the Clevis framework. One of such pins is a plug-in that implements interactions with the NBDE server - Tang. Clevis and Tang are generic client and server components that provide network-bound encryption. In RHEL, they are used in conjunction with LUKS to encrypt and decrypt root and non-root storage volumes to accomplish Network-Bound Disk Encryption. Both client- and server-side components use the Jose library to perform encryption and decryption operations. When you begin provisioning NBDE, the Clevis pin for Tang server gets a list of the Tang server's advertised asymmetric keys. Alternatively, since the keys are asymmetric, a list of Tang's public keys can be distributed out of band so that clients can operate without access to the Tang server. This mode is called offline provisioning . The Clevis pin for Tang uses one of the public keys to generate a unique, cryptographically-strong encryption key. Once the data is encrypted using this key, the key is discarded. The Clevis client should store the state produced by this provisioning operation in a convenient location. This process of encrypting data is the provisioning step . The LUKS version 2 (LUKS2) is the default disk-encryption format in RHEL, hence, the provisioning state for NBDE is stored as a token in a LUKS2 header. The leveraging of provisioning state for NBDE by the luksmeta package is used only for volumes encrypted with LUKS1. The Clevis pin for Tang supports both LUKS1 and LUKS2 without specification need. Clevis can encrypt plain-text files but you have to use the cryptsetup tool for encrypting block devices. See the Encrypting block devices using LUKS for more information. When the client is ready to access its data, it loads the metadata produced in the provisioning step and it responds to recover the encryption key. This process is the recovery step . In NBDE, Clevis binds a LUKS volume using a pin so that it can be automatically unlocked. After successful completion of the binding process, the disk can be unlocked using the provided Dracut unlocker. Note If the kdump kernel crash dumping mechanism is set to save the content of the system memory to a LUKS-encrypted device, you are prompted for entering a password during the second kernel boot. Additional resources NBDE (Network-Bound Disk Encryption) Technology Knowledgebase article tang(8) , clevis(1) , jose(1) , and clevis-luks-unlockers(7) man pages on your system How to set up Network-Bound Disk Encryption with multiple LUKS devices (Clevis + Tang unlocking) Knowledgebase article 10.2. Deploying a Tang server with SELinux in enforcing mode You can use a Tang server to automatically unlock LUKS-encrypted volumes on Clevis-enabled clients. In the minimalistic scenario, you deploy a Tang server on port 80 by installing the tang package and entering the systemctl enable tangd.socket --now command. The following example procedure demonstrates the deployment of a Tang server running on a custom port as a confined service in SELinux enforcing mode. Prerequisites The policycoreutils-python-utils package and its dependencies are installed. The firewalld service is running. Procedure To install the tang package and its dependencies, enter the following command as root : Pick an unoccupied port, for example, 7500/tcp , and allow the tangd service to bind to that port: Note that a port can be used only by one service at a time, and thus an attempt to use an already occupied port implies the ValueError: Port already defined error message. Open the port in the firewall: Enable the tangd service: Create an override file: In the following editor screen, which opens an empty override.conf file located in the /etc/systemd/system/tangd.socket.d/ directory, change the default port for the Tang server from 80 to the previously picked number by adding the following lines: Important Insert the code snippet between the lines starting with # Anything between here and # Lines below this , otherwise the system discards your changes. Save the changes by pressing Ctrl + O and Enter . Exit the editor by pressing Ctrl + X . Reload the changed configuration: Check that your configuration is working: Start the tangd service: Because tangd uses the systemd socket activation mechanism, the server starts as soon as the first connection comes in. A new set of cryptographic keys is automatically generated at the first start. To perform cryptographic operations such as manual key generation, use the jose utility. Verification On your NBDE client, verify that your Tang server works correctly by using the following command. The command must return the identical message you pass for encryption and decryption: Additional resources tang(8) , semanage(8) , firewall-cmd(1) , jose(1) , systemd.unit(5) , and systemd.socket(5) man pages on your system 10.3. Rotating Tang server keys and updating bindings on clients For security reasons, rotate your Tang server keys and update existing bindings on clients periodically. The precise interval at which you should rotate them depends on your application, key sizes, and institutional policy. Alternatively, you can rotate Tang keys by using the nbde_server RHEL system role. See Using the nbde_server system role for setting up multiple Tang servers for more information. Prerequisites A Tang server is running. The clevis and clevis-luks packages are installed on your clients. Procedure Rename all keys in the /var/db/tang key database directory to have a leading . to hide them from advertisement. Note that the file names in the following example differs from unique file names in the key database directory of your Tang server: Check that you renamed and therefore hid all keys from the Tang server advertisement: Generate new keys using the /usr/libexec/tangd-keygen command in /var/db/tang on the Tang server: Check that your Tang server advertises the signing key from the new key pair, for example: On your NBDE clients, use the clevis luks report command to check if the keys advertised by the Tang server remains the same. You can identify slots with the relevant binding using the clevis luks list command, for example: To regenerate LUKS metadata for the new keys either press y to the prompt of the command, or use the clevis luks regen command: When you are sure that all old clients use the new keys, you can remove the old keys from the Tang server, for example: Warning Removing the old keys while clients are still using them can result in data loss. If you accidentally remove such keys, use the clevis luks regen command on the clients, and provide your LUKS password manually. Additional resources tang-show-keys(1) , clevis-luks-list(1) , clevis-luks-report(1) , and clevis-luks-regen(1) man pages on your system 10.4. Configuring automated unlocking by using a Tang key in the web console You can configure automated unlocking of a LUKS-encrypted storage device using a key provided by a Tang server. Prerequisites You have installed the RHEL 9 web console. You have enabled the cockpit service. Your user account is allowed to log in to the web console. For instructions, see Installing and enabling the web console . The cockpit-storaged and clevis-luks packages are installed on your system. The cockpit.socket service is running at port 9090. A Tang server is available. See Deploying a Tang server with SELinux in enforcing mode for details. You have root privileges or permissions to enter administrative commands with sudo . Procedure Log in to the RHEL 9 web console. For details, see Logging in to the web console . Switch to administrative access, provide your credentials, and click Storage . In the Storage table, click the disk that contains an encrypted volume you plan to add to unlock automatically. In the following page with details of the selected disk, click + in the Keys section to add a Tang key: Select Tang keyserver as Key source , provide the address of your Tang server, and a password that unlocks the LUKS-encrypted device. Click Add to confirm: The following dialog window provides a command to verify that the key hash matches. In a terminal on the Tang server, use the tang-show-keys command to display the key hash for comparison. In this example, the Tang server is running on the port 7500 : Click Trust key when the key hashes in the web console and in the output of previously listed commands are the same: In RHEL 9.2 and later, after you select an encrypted root file system and a Tang server, you can skip adding the rd.neednet=1 parameter to the kernel command line, installing the clevis-dracut package, and regenerating an initial RAM disk ( initrd ). For non-root file systems, the web console now enables the remote-cryptsetup.target and clevis-luks-akspass.path systemd units, installs the clevis-systemd package, and adds the _netdev parameter to the fstab and crypttab configuration files. Verification Check that the newly added Tang key is now listed in the Keys section with the Keyserver type: Verify that the bindings are available for the early boot, for example: # lsinitrd | grep clevis-luks lrwxrwxrwx 1 root root 48 Jan 4 02:56 etc/systemd/system/cryptsetup.target.wants/clevis-luks-askpass.path -> /usr/lib/systemd/system/clevis-luks-askpass.path ... Additional resources Getting started using the RHEL web console 10.5. Basic NBDE and TPM2 encryption-client operations The Clevis framework can encrypt plain-text files and decrypt both ciphertexts in the JSON Web Encryption (JWE) format and LUKS-encrypted block devices. Clevis clients can use either Tang network servers or Trusted Platform Module 2.0 (TPM 2.0) chips for cryptographic operations. The following commands demonstrate the basic functionality provided by Clevis on examples containing plain-text files. You can also use them for troubleshooting your NBDE or Clevis+TPM deployments. Encryption client bound to a Tang server To check that a Clevis encryption client binds to a Tang server, use the clevis encrypt tang sub-command: Change the http://tang.srv:port URL in the example to match the URL of the server where tang is installed. The secret.jwe output file contains your encrypted cipher text in the JWE format. This cipher text is read from the input-plain.txt input file. Alternatively, if your configuration requires a non-interactive communication with a Tang server without SSH access, you can download an advertisement and save it to a file: Use the advertisement in the adv.jws file for any following tasks, such as encryption of files or messages: To decrypt data, use the clevis decrypt command and provide the cipher text (JWE): Encryption client using TPM 2.0 To encrypt using a TPM 2.0 chip, use the clevis encrypt tpm2 sub-command with the only argument in form of the JSON configuration object: To choose a different hierarchy, hash, and key algorithms, specify configuration properties, for example: To decrypt the data, provide the ciphertext in the JSON Web Encryption (JWE) format: The pin also supports sealing data to a Platform Configuration Registers (PCR) state. That way, the data can only be unsealed if the PCR hashes values match the policy used when sealing. For example, to seal the data to the PCR with index 0 and 7 for the SHA-256 bank: Warning Hashes in PCRs can be rewritten, and you no longer can unlock your encrypted volume. For this reason, add a strong passphrase that enable you to unlock the encrypted volume manually even when a value in a PCR changes. If the system cannot automatically unlock your encrypted volume after an upgrade of the shim-x64 package, see the Red Hat Knowledgebase solution Clevis TPM2 no longer decrypts LUKS devices after a restart . Additional resources clevis-encrypt-tang(1) , clevis-luks-unlockers(7) , clevis(1) , and clevis-encrypt-tpm2(1) man pages on your system clevis , clevis decrypt , and clevis encrypt tang commands without any arguments show the built-in CLI help, for example: 10.6. Configuring NBDE clients for automated unlocking of LUKS-encrypted volumes With the Clevis framework, you can configure clients for automated unlocking of LUKS-encrypted volumes when a selected Tang server is available. This creates an NBDE (Network-Bound Disk Encryption) deployment. Prerequisites A Tang server is running and available. Procedure To automatically unlock an existing LUKS-encrypted volume, install the clevis-luks subpackage: Identify the LUKS-encrypted volume for PBD. In the following example, the block device is referred as /dev/sda2 : # lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sda 8:0 0 12G 0 disk ├─sda1 8:1 0 1G 0 part /boot └─sda2 8:2 0 11G 0 part └─luks-40e20552-2ade-4954-9d56-565aa7994fb6 253:0 0 11G 0 crypt ├─rhel-root 253:0 0 9.8G 0 lvm / └─rhel-swap 253:1 0 1.2G 0 lvm [SWAP] Bind the volume to a Tang server using the clevis luks bind command: # clevis luks bind -d /dev/sda2 tang '{"url":" http://tang.srv "}' The advertisement contains the following signing keys: _OsIk0T-E2l6qjfdDiwVmidoZjA Do you wish to trust these keys? [ynYN] y You are about to initialize a LUKS device for metadata storage. Attempting to initialize it may result in data loss if data was already written into the LUKS header gap in a different format. A backup is advised before initialization is performed. Do you wish to initialize /dev/sda2? [yn] y Enter existing LUKS password: This command performs four steps: Creates a new key with the same entropy as the LUKS master key. Encrypts the new key with Clevis. Stores the Clevis JWE object in the LUKS2 header token or uses LUKSMeta if the non-default LUKS1 header is used. Enables the new key for use with LUKS. Note The binding procedure assumes that there is at least one free LUKS password slot. The clevis luks bind command takes one of the slots. The volume can now be unlocked with your existing password as well as with the Clevis policy. To enable the early boot system to process the disk binding, use the dracut tool on an already installed system. In RHEL, Clevis produces a generic initrd (initial RAM disk) without host-specific configuration options and does not automatically add parameters such as rd.neednet=1 to the kernel command line. If your configuration relies on a Tang pin that requires network during early boot, use the --hostonly-cmdline argument and dracut adds rd.neednet=1 when it detects a Tang binding: Install the clevis-dracut package: Regenerate the initial RAM disk: Alternatively, create a .conf file in the /etc/dracut.conf.d/ directory, and add the hostonly_cmdline=yes option to the file. Then, you can use dracut without --hostonly-cmdline , for example: You can also ensure that networking for a Tang pin is available during early boot by using the grubby tool on the system where Clevis is installed: Verification Verify that the Clevis JWE object is successfully placed in a LUKS header, use the clevis luks list command: Check that the bindings are available for the early boot, for example: # lsinitrd | grep clevis-luks lrwxrwxrwx 1 root root 48 Jan 4 02:56 etc/systemd/system/cryptsetup.target.wants/clevis-luks-askpass.path -> /usr/lib/systemd/system/clevis-luks-askpass.path ... Additional resources clevis-luks-bind(1) and dracut.cmdline(7) man pages on your system Network boot options Looking forward to Linux network configuration in the initial ramdisk (initrd) (Red Hat Enable Sysadmin) 10.7. Configuring NBDE clients with static IP configuration To use NBDE for clients with static IP configuration (without DHCP), you must pass your network configuration to the dracut tool manually. Prerequisites A Tang server is running and available. The NBDE client is configured for automated unlocking of encrypted volumes by the Tang server. For details, see Configuring NBDE clients for automated unlocking of LUKS-encrypted volumes . Procedure You can provide your static network configuration as a value for the kernel-cmdline option in a dracut command, for example: Alternatively, create a .conf file in the /etc/dracut.conf.d/ directory with the static network information and then, regenerate the initial RAM disk image: 10.8. Configuring manual enrollment of LUKS-encrypted volumes by using a TPM 2.0 policy You can configure unlocking of LUKS-encrypted volumes by using a Trusted Platform Module 2.0 (TPM 2.0) policy. Prerequisites An accessible TPM 2.0-compatible device. A system with the 64-bit Intel or 64-bit AMD architecture. Procedure To automatically unlock an existing LUKS-encrypted volume, install the clevis-luks subpackage: Identify the LUKS-encrypted volume for PBD. In the following example, the block device is referred as /dev/sda2 : # lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sda 8:0 0 12G 0 disk ├─sda1 8:1 0 1G 0 part /boot └─sda2 8:2 0 11G 0 part └─luks-40e20552-2ade-4954-9d56-565aa7994fb6 253:0 0 11G 0 crypt ├─rhel-root 253:0 0 9.8G 0 lvm / └─rhel-swap 253:1 0 1.2G 0 lvm [SWAP] Bind the volume to a TPM 2.0 device using the clevis luks bind command, for example: # clevis luks bind -d /dev/sda2 tpm2 '{"hash":"sha256","key":"rsa"}' ... Do you wish to initialize /dev/sda2? [yn] y Enter existing LUKS password: This command performs four steps: Creates a new key with the same entropy as the LUKS master key. Encrypts the new key with Clevis. Stores the Clevis JWE object in the LUKS2 header token or uses LUKSMeta if the non-default LUKS1 header is used. Enables the new key for use with LUKS. Note The binding procedure assumes that there is at least one free LUKS password slot. The clevis luks bind command takes one of the slots. Alternatively, if you want to seal data to specific Platform Configuration Registers (PCR) states, add the pcr_bank and pcr_ids values to the clevis luks bind command, for example: # clevis luks bind -d /dev/sda2 tpm2 '{"hash":"sha256","key":"rsa","pcr_bank":"sha256","pcr_ids":"0,1"}' Important Because the data can only be unsealed if PCR hashes values match the policy used when sealing and the hashes can be rewritten, add a strong passphrase that enable you to unlock the encrypted volume manually when a value in a PCR changes. If the system cannot automatically unlock your encrypted volume after upgrading the shim-x64 package, see the Red Hat Knowledgebase solution Clevis TPM2 no longer decrypts LUKS devices after a restart . The volume can now be unlocked with your existing password as well as with the Clevis policy. To enable the early boot system to process the disk binding, use the dracut tool on an already installed system: Verification To verify that the Clevis JWE object is successfully placed in a LUKS header, use the clevis luks list command: Additional resources clevis-luks-bind(1) , clevis-encrypt-tpm2(1) , and dracut.cmdline(7) man pages on your system 10.9. Removing a Clevis pin from a LUKS-encrypted volume manually Use the following procedure for manual removing the metadata created by the clevis luks bind command and also for wiping a key slot that contains passphrase added by Clevis. Important The recommended way to remove a Clevis pin from a LUKS-encrypted volume is through the clevis luks unbind command. The removal procedure using clevis luks unbind consists of only one step and works for both LUKS1 and LUKS2 volumes. The following example command removes the metadata created by the binding step and wipe the key slot 1 on the /dev/sda2 device: Prerequisites A LUKS-encrypted volume with a Clevis binding. Procedure Check which LUKS version the volume, for example /dev/sda2 , is encrypted by and identify a slot and a token that is bound to Clevis: In the example, the Clevis token is identified by 0 and the associated key slot is 1 . In case of LUKS2 encryption, remove the token: If your device is encrypted by LUKS1, which is indicated by the Version: 1 string in the output of the cryptsetup luksDump command, perform this additional step with the luksmeta wipe command: Wipe the key slot containing the Clevis passphrase: Additional resources clevis-luks-unbind(1) , cryptsetup(8) , and luksmeta(8) man pages on your system 10.10. Configuring automated enrollment of LUKS-encrypted volumes by using Kickstart Follow the steps in this procedure to configure an automated installation process that uses Clevis for the enrollment of LUKS-encrypted volumes. Procedure Instruct Kickstart to partition the disk such that LUKS encryption has enabled for all mount points, other than /boot , with a temporary password. The password is temporary for this step of the enrollment process. Note that OSPP-compliant systems require a more complex configuration, for example: Install the related Clevis packages by listing them in the %packages section: Optional: To ensure that you can unlock the encrypted volume manually when required, add a strong passphrase before you remove the temporary passphrase. For more information, see the Red Hat Knowledgebase solution How to add a passphrase, key, or keyfile to an existing LUKS device . Call clevis luks bind to perform binding in the %post section. Afterward, remove the temporary password: If your configuration relies on a Tang pin that requires network during early boot or you use NBDE clients with static IP configurations, you have to modify the dracut command as described in Configuring manual enrollment of LUKS-encrypted volumes . Note that the -y option for the clevis luks bind command is available from RHEL 8.3. In RHEL 8.2 and older, replace -y by -f in the clevis luks bind command and download the advertisement from the Tang server: Warning The cryptsetup luksRemoveKey command prevents any further administration of a LUKS2 device on which you apply it. You can recover a removed master key using the dmsetup command only for LUKS1 devices. You can use an analogous procedure when using a TPM 2.0 policy instead of a Tang server. Additional resources clevis(1) , clevis-luks-bind(1) , cryptsetup(8) , and dmsetup(8) man pages on your system Automatically installing RHEL 10.11. Configuring automated unlocking of a LUKS-encrypted removable storage device You can set up an automated unlocking process of a LUKS-encrypted USB storage device. Procedure To automatically unlock a LUKS-encrypted removable storage device, such as a USB drive, install the clevis-udisks2 package: Reboot the system, and then perform the binding step using the clevis luks bind command as described in Configuring manual enrollment of LUKS-encrypted volumes , for example: The LUKS-encrypted removable device can be now unlocked automatically in your GNOME desktop session. The device bound to a Clevis policy can be also unlocked by the clevis luks unlock command: You can use an analogous procedure when using a TPM 2.0 policy instead of a Tang server. Additional resources clevis-luks-unlockers(7) man page on your system 10.12. Deploying high-availability NBDE systems Tang provides two methods for building a high-availability deployment: Client redundancy (recommended) Clients should be configured with the ability to bind to multiple Tang servers. In this setup, each Tang server has its own keys and clients can decrypt by contacting a subset of these servers. Clevis already supports this workflow through its sss plug-in. Red Hat recommends this method for a high-availability deployment. Key sharing For redundancy purposes, more than one instance of Tang can be deployed. To set up a second or any subsequent instance, install the tang packages and copy the key directory to the new host using rsync over SSH . Note that Red Hat does not recommend this method because sharing keys increases the risk of key compromise and requires additional automation infrastructure. High-available NBDE using Shamir's Secret Sharing Shamir's Secret Sharing (SSS) is a cryptographic scheme that divides a secret into several unique parts. To reconstruct the secret, a number of parts is required. The number is called threshold and SSS is also referred to as a thresholding scheme. Clevis provides an implementation of SSS. It creates a key and divides it into a number of pieces. Each piece is encrypted using another pin including even SSS recursively. Additionally, you define the threshold t . If an NBDE deployment decrypts at least t pieces, then it recovers the encryption key and the decryption process succeeds. When Clevis detects a smaller number of parts than specified in the threshold, it prints an error message. Example 1: Redundancy with two Tang servers The following command decrypts a LUKS-encrypted device when at least one of two Tang servers is available: The command used the following configuration scheme: In this configuration, the SSS threshold t is set to 1 and the clevis luks bind command successfully reconstructs the secret if at least one from two listed tang servers is available. Example 2: Shared secret on a Tang server and a TPM device The following command successfully decrypts a LUKS-encrypted device when both the tang server and the tpm2 device are available: The configuration scheme with the SSS threshold 't' set to '2' is now: Additional resources tang(8) (section High Availability ), clevis(1) (section Shamir's Secret Sharing ), and clevis-encrypt-sss(1) man pages on your system 10.13. Deployment of virtual machines in a NBDE network The clevis luks bind command does not change the LUKS master key. This implies that if you create a LUKS-encrypted image for use in a virtual machine or cloud environment, all the instances that run this image share a master key. This is extremely insecure and should be avoided at all times. This is not a limitation of Clevis but a design principle of LUKS. If your scenario requires having encrypted root volumes in a cloud, perform the installation process (usually using Kickstart) for each instance of Red Hat Enterprise Linux in the cloud as well. The images cannot be shared without also sharing a LUKS master key. To deploy automated unlocking in a virtualized environment, use systems such as lorax or virt-install together with a Kickstart file (see Configuring automated enrollment of LUKS-encrypted volumes using Kickstart ) or another automated provisioning tool to ensure that each encrypted VM has a unique master key. Additional resources clevis-luks-bind(1) man page on your system 10.14. Building automatically-enrollable VM images for cloud environments by using NBDE Deploying automatically-enrollable encrypted images in a cloud environment can provide a unique set of challenges. Like other virtualization environments, it is recommended to reduce the number of instances started from a single image to avoid sharing the LUKS master key. Therefore, the best practice is to create customized images that are not shared in any public repository and that provide a base for the deployment of a limited amount of instances. The exact number of instances to create should be defined by deployment's security policies and based on the risk tolerance associated with the LUKS master key attack vector. To build LUKS-enabled automated deployments, systems such as Lorax or virt-install together with a Kickstart file should be used to ensure master key uniqueness during the image building process. Cloud environments enable two Tang server deployment options which we consider here. First, the Tang server can be deployed within the cloud environment itself. Second, the Tang server can be deployed outside of the cloud on independent infrastructure with a VPN link between the two infrastructures. Deploying Tang natively in the cloud does allow for easy deployment. However, given that it shares infrastructure with the data persistence layer of ciphertext of other systems, it may be possible for both the Tang server's private key and the Clevis metadata to be stored on the same physical disk. Access to this physical disk permits a full compromise of the ciphertext data. Important Always maintain a physical separation between the location where the data is stored and the system where Tang is running. This separation between the cloud and the Tang server ensures that the Tang server's private key cannot be accidentally combined with the Clevis metadata. It also provides local control of the Tang server if the cloud infrastructure is at risk. 10.15. Deploying Tang as a container The tang container image provides Tang-server decryption capabilities for Clevis clients that run either in OpenShift Container Platform (OCP) clusters or in separate virtual machines. Prerequisites The podman package and its dependencies are installed on the system. You have logged in on the registry.redhat.io container catalog using the podman login registry.redhat.io command. See Red Hat Container Registry Authentication for more information. The Clevis client is installed on systems containing LUKS-encrypted volumes that you want to automatically unlock by using a Tang server. Procedure Pull the tang container image from the registry.redhat.io registry: Run the container, specify its port, and specify the path to the Tang keys. The example runs the tang container, specifies the port 7500 , and indicates a path to the Tang keys of the /var/db/tang directory: # podman run -d -p 7500:7500 -v tang-keys:/var/db/tang --name tang registry.redhat.io/rhel9/tang Note that Tang uses port 80 by default but this may collide with other services such as the Apache HTTP server. Optional: For increased security, rotate the Tang keys periodically. You can use the tangd-rotate-keys script, for example: # podman run --rm -v tang-keys:/var/db/tang registry.redhat.io/rhel9/tang tangd-rotate-keys -v -d /var/db/tang Rotated key 'rZAMKAseaXBe0rcKXL1hCCIq-DY.jwk' -> .'rZAMKAseaXBe0rcKXL1hCCIq-DY.jwk' Rotated key 'x1AIpc6WmnCU-CabD8_4q18vDuw.jwk' -> .'x1AIpc6WmnCU-CabD8_4q18vDuw.jwk' Created new key GrMMX_WfdqomIU_4RyjpcdlXb0E.jwk Created new key _dTTfn17sZZqVAp80u3ygFDHtjk.jwk Keys rotated successfully. Verification On a system that contains LUKS-encrypted volumes for automated unlocking by the presence of the Tang server, check that the Clevis client can encrypt and decrypt a plain-text message using Tang: # echo test | clevis encrypt tang '{"url":"http://localhost:7500"}' | clevis decrypt The advertisement contains the following signing keys: x1AIpc6WmnCU-CabD8_4q18vDuw Do you wish to trust these keys? [ynYN] y test The example command shows the test string at the end of its output when a Tang server is available on the localhost URL and communicates through port 7500 . Additional resources podman(1) , clevis(1) , and tang(8) man pages on your system 10.16. Configuring NBDE by using RHEL system roles You can use the nbde_client and nbde_server RHEL system roles for automated deployments of Policy-Based Decryption (PBD) solutions using Clevis and Tang. The rhel-system-roles package contains these system roles, the related examples, and also the reference documentation. 10.16.1. Using the nbde_server RHEL system role for setting up multiple Tang servers By using the nbde_server system role, you can deploy and manage a Tang server as part of an automated disk encryption solution. This role supports the following features: Rotating Tang keys Deploying and backing up Tang keys Prerequisites You have prepared the control node and the managed nodes You are logged in to the control node as a user who can run playbooks on the managed nodes. The account you use to connect to the managed nodes has sudo permissions on them. Procedure Create a playbook file, for example ~/playbook.yml , with the following content: --- - name: Deploy a Tang server hosts: tang.server.example.com tasks: - name: Install and configure periodic key rotation ansible.builtin.include_role: name: rhel-system-roles.nbde_server vars: nbde_server_rotate_keys: yes nbde_server_manage_firewall: true nbde_server_manage_selinux: true This example playbook ensures deploying of your Tang server and a key rotation. The settings specified in the example playbook include the following: nbde_server_manage_firewall: true Use the firewall system role to manage ports used by the nbde_server role. nbde_server_manage_selinux: true Use the selinux system role to manage ports used by the nbde_server role. For details about all variables used in the playbook, see the /usr/share/ansible/roles/rhel-system-roles.nbde_server/README.md file on the control node. Validate the playbook syntax: Note that this command only validates the syntax and does not protect against a wrong but valid configuration. Run the playbook: Verification On your NBDE client, verify that your Tang server works correctly by using the following command. The command must return the identical message you pass for encryption and decryption: Additional resources /usr/share/ansible/roles/rhel-system-roles.nbde_server/README.md file /usr/share/doc/rhel-system-roles/nbde_server/ directory 10.16.2. Setting up Clevis clients with DHCP by using the nbde_client RHEL system role The nbde_client system role enables you to deploy multiple Clevis clients in an automated way. This role supports binding a LUKS-encrypted volume to one or more Network-Bound (NBDE) servers - Tang servers. You can either preserve the existing volume encryption with a passphrase or remove it. After removing the passphrase, you can unlock the volume only using NBDE. This is useful when a volume is initially encrypted using a temporary key or password that you should remove after you provision the system. If you provide both a passphrase and a key file, the role uses what you have provided first. If it does not find any of these valid, it attempts to retrieve a passphrase from an existing binding. Policy-Based Decryption (PBD) defines a binding as a mapping of a device to a slot. This means that you can have multiple bindings for the same device. The default slot is slot 1. Note The nbde_client system role supports only Tang bindings. Therefore, you cannot use it for TPM2 bindings. Prerequisites You have prepared the control node and the managed nodes You are logged in to the control node as a user who can run playbooks on the managed nodes. The account you use to connect to the managed nodes has sudo permissions on them. A volume that is already encrypted by using LUKS. Procedure Create a playbook file, for example ~/playbook.yml , with the following content: --- - name: Configure clients for unlocking of encrypted volumes by Tang servers hosts: managed-node-01.example.com tasks: - name: Create NBDE client bindings ansible.builtin.include_role: name: rhel-system-roles.nbde_client vars: nbde_client_bindings: - device: /dev/rhel/root encryption_key_src: /etc/luks/keyfile nbde_client_early_boot: true state: present servers: - http://server1.example.com - http://server2.example.com - device: /dev/rhel/swap encryption_key_src: /etc/luks/keyfile servers: - http://server1.example.com - http://server2.example.com This example playbook configures Clevis clients for automated unlocking of two LUKS-encrypted volumes when at least one of two Tang servers is available. The settings specified in the example playbook include the following: state: present The values of state indicate the configuration after you run the playbook. Use the present value for either creating a new binding or updating an existing one. Contrary to a clevis luks bind command, you can use state: present also for overwriting an existing binding in its device slot. The absent value removes a specified binding. nbde_client_early_boot: true The nbde_client role ensures that networking for a Tang pin is available during early boot by default. If you scenario requires to disable this feature, add the nbde_client_early_boot: false variable to your playbook. For details about all variables used in the playbook, see the /usr/share/ansible/roles/rhel-system-roles.nbde_client/README.md file on the control node. Validate the playbook syntax: Note that this command only validates the syntax and does not protect against a wrong but valid configuration. Run the playbook: Verification On your NBDE client, check that the encrypted volume that should be automatically unlocked by your Tang servers contain the corresponding information in its LUKS pins: If you do not use the nbde_client_early_boot: false variable, verify that the bindings are available for the early boot, for example: Additional resources /usr/share/ansible/roles/rhel-system-roles.nbde_client/README.md file /usr/share/doc/rhel-system-roles/nbde_client/ directory 10.16.3. Setting up static-IP Clevis clients by using the nbde_client RHEL system role The nbde_client RHEL system role supports only scenarios with Dynamic Host Configuration Protocol (DHCP). On an NBDE client with static IP configuration, you must pass your network configuration as a kernel boot parameter. Typically, administrators want to reuse a playbook and not maintain individual playbooks for each host to which Ansible assigns static IP addresses during early boot. In this case, you can use variables in the playbook and provide the settings in an external file. As a result, you need only one playbook and one file with the settings. Prerequisites You have prepared the control node and the managed nodes You are logged in to the control node as a user who can run playbooks on the managed nodes. The account you use to connect to the managed nodes has sudo permissions on them. A volume that is already encrypted by using LUKS. Procedure Create a file with the network settings of your hosts, for example, static-ip-settings-clients.yml , and add the values you want to dynamically assign to the hosts: clients: managed-node-01.example.com: ip_v4: 192.0.2.1 gateway_v4: 192.0.2.254 netmask_v4: 255.255.255.0 interface: enp1s0 managed-node-02.example.com: ip_v4: 192.0.2.2 gateway_v4: 192.0.2.254 netmask_v4: 255.255.255.0 interface: enp1s0 Create a playbook file, for example, ~/playbook.yml , with the following content: - name: Configure clients for unlocking of encrypted volumes by Tang servers hosts: managed-node-01.example.com,managed-node-02.example.com vars_files: - ~/static-ip-settings-clients.yml tasks: - name: Create NBDE client bindings ansible.builtin.include_role: name: rhel-system-roles.network vars: nbde_client_bindings: - device: /dev/rhel/root encryption_key_src: /etc/luks/keyfile servers: - http://server1.example.com - http://server2.example.com - device: /dev/rhel/swap encryption_key_src: /etc/luks/keyfile servers: - http://server1.example.com - http://server2.example.com - name: Configure a Clevis client with static IP address during early boot ansible.builtin.include_role: name: rhel-system-roles.bootloader vars: bootloader_settings: - kernel: ALL options: - name: ip value: "{{ clients[inventory_hostname]['ip_v4'] }}::{{ clients[inventory_hostname]['gateway_v4'] }}:{{ clients[inventory_hostname]['netmask_v4'] }}::{{ clients[inventory_hostname]['interface'] }}:none" This playbook reads certain values dynamically for each host listed in the ~/static-ip-settings-clients.yml file. For details about all variables used in the playbook, see the /usr/share/ansible/roles/rhel-system-roles.network/README.md file on the control node. Validate the playbook syntax: Note that this command only validates the syntax and does not protect against a wrong but valid configuration. Run the playbook: Additional resources /usr/share/ansible/roles/rhel-system-roles.nbde_client/README.md file /usr/share/doc/rhel-system-roles/nbde_client/ directory Looking forward to Linux network configuration in the initial ramdisk (initrd) (Red Hat Enable Sysadmin)

Commands:
dnf install tang
semanage port -a -t tangd_port_t -p tcp 7500
firewall-cmd --add-port= 7500 /tcp firewall-cmd --runtime-to-permanent
systemctl enable tangd.socket
systemctl edit tangd.socket
[Socket] ListenStream= ListenStream= 7500
systemctl daemon-reload
systemctl show tangd.socket -p Listen Listen=[::]:7500 (Stream)
systemctl restart tangd.socket
echo test | clevis encrypt tang '{"url":" <tang.server.example.com:7500> "}' -y | clevis decrypt test
cd /var/db/tang ls -l -rw-r--r--. 1 root root 349 Feb 7 14:55 UV6dqXSwe1bRKG3KbJmdiR020hY.jwk -rw-r--r--. 1 root root 354 Feb 7 14:55 y9hxLTQSiSB5jSEGWnjhY8fDTJU.jwk mv UV6dqXSwe1bRKG3KbJmdiR020hY.jwk .UV6dqXSwe1bRKG3KbJmdiR020hY.jwk mv y9hxLTQSiSB5jSEGWnjhY8fDTJU.jwk .y9hxLTQSiSB5jSEGWnjhY8fDTJU.jwk
ls -l total 0
/usr/libexec/tangd-keygen /var/db/tang ls /var/db/tang 3ZWS6-cDrCG61UPJS2BMmPU4I54.jwk zyLuX6hijUy_PSeUEFDi7hi38.jwk
tang-show-keys 7500 3ZWS6-cDrCG61UPJS2BMmPU4I54
clevis luks list -d /dev/sda2 1: tang '{"url":" http://tang.srv "}' clevis luks report -d /dev/sda2 -s 1 Report detected that some keys were rotated. Do you want to regenerate luks metadata with "clevis luks regen -d /dev/sda2 -s 1"? [ynYN]
clevis luks regen -d /dev/sda2 -s 1
cd /var/db/tang rm .*.jwk
tang-show-keys 7500 x100_1k6GPiDOaMlL3WbpCjHOy9ul1bSfdhI3M08wO0
lsinitrd | grep clevis-luks lrwxrwxrwx 1 root root 48 Jan 4 02:56 etc/systemd/system/cryptsetup.target.wants/clevis-luks-askpass.path -> /usr/lib/systemd/system/clevis-luks-askpass.path ...
clevis encrypt tang '{"url":" http://tang.srv:port "}' < input-plain.txt > secret.jwe The advertisement contains the following signing keys: _OsIk0T-E2l6qjfdDiwVmidoZjA Do you wish to trust these keys? [ynYN] y
curl -sfg http://tang.srv:port /adv -o adv.jws
echo 'hello' | clevis encrypt tang '{"url":" http://tang.srv:port ","adv":" adv.jws "}'
clevis decrypt < secret.jwe > output-plain.txt
clevis encrypt tpm2 '{}' < input-plain.txt > secret.jwe
clevis encrypt tpm2 '{"hash":"sha256","key":"rsa"}' < input-plain.txt > secret.jwe
clevis decrypt < secret.jwe > output-plain.txt
clevis encrypt tpm2 '{"pcr_bank":"sha256","pcr_ids":"0,7"}' < input-plain.txt > secret.jwe
clevis encrypt tang Usage: clevis encrypt tang CONFIG < PLAINTEXT > JWE
dnf install clevis-luks
lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sda 8:0 0 12G 0 disk ├─sda1 8:1 0 1G 0 part /boot └─sda2 8:2 0 11G 0 part └─luks-40e20552-2ade-4954-9d56-565aa7994fb6 253:0 0 11G 0 crypt ├─rhel-root 253:0 0 9.8G 0 lvm / └─rhel-swap 253:1 0 1.2G 0 lvm [SWAP]
clevis luks bind -d /dev/sda2 tang '{"url":" http://tang.srv "}' The advertisement contains the following signing keys: _OsIk0T-E2l6qjfdDiwVmidoZjA Do you wish to trust these keys? [ynYN] y You are about to initialize a LUKS device for metadata storage. Attempting to initialize it may result in data loss if data was already written into the LUKS header gap in a different format. A backup is advised before initialization is performed. Do you wish to initialize /dev/sda2? [yn] y Enter existing LUKS password:
dnf install clevis-dracut
dracut -fv --regenerate-all --hostonly-cmdline
echo "hostonly_cmdline=yes" > /etc/dracut.conf.d/clevis.conf dracut -fv --regenerate-all
grubby --update-kernel=ALL --args="rd.neednet=1"
clevis luks list -d /dev/sda2 1: tang '{"url":"http://tang.srv:port"}'
lsinitrd | grep clevis-luks lrwxrwxrwx 1 root root 48 Jan 4 02:56 etc/systemd/system/cryptsetup.target.wants/clevis-luks-askpass.path -> /usr/lib/systemd/system/clevis-luks-askpass.path ...
dracut -fv --regenerate-all --kernel-cmdline "ip=192.0.2.10::192.0.2.1:255.255.255.0::ens3:none nameserver=192.0.2.100"
cat /etc/dracut.conf.d/static_ip.conf kernel_cmdline="ip=192.0.2.10::192.0.2.1:255.255.255.0::ens3:none nameserver=192.0.2.100" dracut -fv --regenerate-all
dnf install clevis-luks
lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sda 8:0 0 12G 0 disk ├─sda1 8:1 0 1G 0 part /boot └─sda2 8:2 0 11G 0 part └─luks-40e20552-2ade-4954-9d56-565aa7994fb6 253:0 0 11G 0 crypt ├─rhel-root 253:0 0 9.8G 0 lvm / └─rhel-swap 253:1 0 1.2G 0 lvm [SWAP]
clevis luks bind -d /dev/sda2 tpm2 '{"hash":"sha256","key":"rsa"}' Do you wish to initialize /dev/sda2? [yn] y Enter existing LUKS password:
clevis luks bind -d /dev/sda2 tpm2 '{"hash":"sha256","key":"rsa","pcr_bank":"sha256","pcr_ids":"0,1"}'
dnf install clevis-dracut dracut -fv --regenerate-all
clevis luks list -d /dev/sda2 1: tpm2 '{"hash":"sha256","key":"rsa"}'
clevis luks unbind -d /dev/sda2 -s 1
cryptsetup luksDump /dev/sda2 LUKS header information Version: 2 Keyslots: 0: luks2 1: luks2 Key: 512 bits Priority: normal Cipher: aes-xts-plain64 Tokens: 0: clevis Keyslot: 1
cryptsetup token remove --token-id 0 /dev/sda2
luksmeta wipe -d /dev/sda2 -s 1
cryptsetup luksKillSlot /dev/sda2 1
part /boot --fstype="xfs" --ondisk=vda --size=256 part / --fstype="xfs" --ondisk=vda --grow --encrypted --passphrase=temppass
part /boot --fstype="xfs" --ondisk=vda --size=256 part / --fstype="xfs" --ondisk=vda --size=2048 --encrypted --passphrase=temppass part /var --fstype="xfs" --ondisk=vda --size=1024 --encrypted --passphrase=temppass part /tmp --fstype="xfs" --ondisk=vda --size=1024 --encrypted --passphrase=temppass part /home --fstype="xfs" --ondisk=vda --size=2048 --grow --encrypted --passphrase=temppass part /var/log --fstype="xfs" --ondisk=vda --size=1024 --encrypted --passphrase=temppass part /var/log/audit --fstype="xfs" --ondisk=vda --size=1024 --encrypted --passphrase=temppass
%packages clevis-dracut clevis-luks clevis-systemd %end
%post clevis luks bind -y -k - -d /dev/vda2 tang '{"url":"http://tang.srv"}' <<< "temppass" cryptsetup luksRemoveKey /dev/vda2 <<< "temppass" dracut -fv --regenerate-all %end
%post curl -sfg http://tang.srv/adv -o adv.jws clevis luks bind -f -k - -d /dev/vda2 tang '{"url":"http://tang.srv","adv":"adv.jws"}' <<< "temppass" cryptsetup luksRemoveKey /dev/vda2 <<< "temppass" dracut -fv --regenerate-all %end
dnf install clevis-udisks2
clevis luks bind -d /dev/sdb1 tang '{"url":" http://tang.srv "}'
clevis luks unlock -d /dev/sdb1
clevis luks bind -d /dev/sda1 sss '{"t":1,"pins":{"tang":[{"url":" http://tang1.srv "},{"url":" http://tang2.srv "}]}}'
{ "t":1, "pins":{ "tang":[ { "url":"http://tang1.srv" }, { "url":"http://tang2.srv" } ] } }
clevis luks bind -d /dev/sda1 sss '{"t":2,"pins":{"tang":[{"url":" http://tang1.srv "}], "tpm2": {"pcr_ids":"0,7"}}}'
{ "t":2, "pins":{ "tang":[ { "url":"http://tang1.srv" } ], "tpm2":{ "pcr_ids":"0,7" } } }
podman pull registry.redhat.io/rhel9/tang
podman run -d -p 7500:7500 -v tang-keys:/var/db/tang --name tang registry.redhat.io/rhel9/tang
podman run --rm -v tang-keys:/var/db/tang registry.redhat.io/rhel9/tang tangd-rotate-keys -v -d /var/db/tang Rotated key 'rZAMKAseaXBe0rcKXL1hCCIq-DY.jwk' -> .'rZAMKAseaXBe0rcKXL1hCCIq-DY.jwk' Rotated key 'x1AIpc6WmnCU-CabD8_4q18vDuw.jwk' -> .'x1AIpc6WmnCU-CabD8_4q18vDuw.jwk' Created new key GrMMX_WfdqomIU_4RyjpcdlXb0E.jwk Created new key _dTTfn17sZZqVAp80u3ygFDHtjk.jwk Keys rotated successfully.
echo test | clevis encrypt tang '{"url":"http://localhost:7500"}' | clevis decrypt The advertisement contains the following signing keys: x1AIpc6WmnCU-CabD8_4q18vDuw Do you wish to trust these keys? [ynYN] y test
--- - name: Deploy a Tang server hosts: tang.server.example.com tasks: - name: Install and configure periodic key rotation ansible.builtin.include_role: name: rhel-system-roles.nbde_server vars: nbde_server_rotate_keys: yes nbde_server_manage_firewall: true nbde_server_manage_selinux: true
ansible-playbook --syntax-check ~/playbook.yml
ansible-playbook ~/playbook.yml
ansible managed-node-01.example.com -m command -a 'echo test | clevis encrypt tang '{"url":" <tang.server.example.com> "}' -y | clevis decrypt' test
--- - name: Configure clients for unlocking of encrypted volumes by Tang servers hosts: managed-node-01.example.com tasks: - name: Create NBDE client bindings ansible.builtin.include_role: name: rhel-system-roles.nbde_client vars: nbde_client_bindings: - device: /dev/rhel/root encryption_key_src: /etc/luks/keyfile nbde_client_early_boot: true state: present servers: - http://server1.example.com - http://server2.example.com - device: /dev/rhel/swap encryption_key_src: /etc/luks/keyfile servers: - http://server1.example.com - http://server2.example.com
ansible-playbook --syntax-check ~/playbook.yml
ansible-playbook ~/playbook.yml
ansible managed-node-01.example.com -m command -a 'clevis luks list -d /dev/rhel/root' 1: tang '{"url":" <http://server1.example.com/> "}' 2: tang '{"url":" <http://server2.example.com/> "}'
ansible managed-node-01.example.com -m command -a 'lsinitrd | grep clevis-luks' lrwxrwxrwx 1 root root 48 Jan 4 02:56 etc/systemd/system/cryptsetup.target.wants/clevis-luks-askpass.path -> /usr/lib/systemd/system/clevis-luks-askpass.path ...
clients: managed-node-01.example.com: ip_v4: 192.0.2.1 gateway_v4: 192.0.2.254 netmask_v4: 255.255.255.0 interface: enp1s0 managed-node-02.example.com: ip_v4: 192.0.2.2 gateway_v4: 192.0.2.254 netmask_v4: 255.255.255.0 interface: enp1s0
- name: Configure clients for unlocking of encrypted volumes by Tang servers hosts: managed-node-01.example.com,managed-node-02.example.com vars_files: - ~/static-ip-settings-clients.yml tasks: - name: Create NBDE client bindings ansible.builtin.include_role: name: rhel-system-roles.network vars: nbde_client_bindings: - device: /dev/rhel/root encryption_key_src: /etc/luks/keyfile servers: - http://server1.example.com - http://server2.example.com - device: /dev/rhel/swap encryption_key_src: /etc/luks/keyfile servers: - http://server1.example.com - http://server2.example.com - name: Configure a Clevis client with static IP address during early boot ansible.builtin.include_role: name: rhel-system-roles.bootloader vars: bootloader_settings: - kernel: ALL options: - name: ip value: "{{ clients[inventory_hostname]['ip_v4'] }}::{{ clients[inventory_hostname]['gateway_v4'] }}:{{ clients[inventory_hostname]['netmask_v4'] }}::{{ clients[inventory_hostname]['interface'] }}:none"
ansible-playbook --syntax-check ~/playbook.yml
ansible-playbook ~/playbook.yml