Network-Bound Disk Encryption with Clevis and Tang on Linux

Configure LUKS NBDE with Clevis and Tang on RHEL/CentOS: two-VM lab with Tang server, LUKS root client, dracut rebuild, and video—boot without typing a passphrase when Tang is reachable.

Published

Updated

Read time 13 min read

Reviewed byDeepak Prasad

Network-bound disk encryption with Clevis client and Tang server for LUKS on RHEL

Fleet servers with LUKS-encrypted root or data disks should not each carry a per-host keyfile in /etc/crypttab. Network-Bound Disk Encryption (NBDE) binds a LUKS2 volume to a Tang server: at boot, Clevis on the client contacts Tang, unwraps a policy key, and opens the mapper without you typing a passphrase—when Tang is reachable on the network.

This guide walks through a two-VM lab (RHEL 8 Tang server + CentOS 8 LUKS root client): install Tang, bind with Clevis, rebuild the initramfs, reboot, and watch boot continue without entering the passphrase. A screen recording from the original lab is embedded below.

Related paths: encrypt a disk partition with LUKS, mount LUKS at boot, encrypt root with LUKS, and the cryptsetup command cheat sheet. For TPM2/FIDO2 instead of Tang, see systemd-cryptenroll on LUKS2.

IMPORTANT
NBDE adds automated unlock on top of LUKS2—it does not remove the need for break-glass recovery. Keep at least one passphrase slot until Tang HA is proven. Take a LUKS header backup before binding or unbinding.

Tested on: RHEL 8.1 Tang server + CentOS 8.0 LUKS root client on Oracle VirtualBox (two VMs, 192.168.0.121 / 192.168.0.119). End-to-end root boot without passphrase; video embedded in this article.


What you need

Use two VMs on the same LAN so the client can reach Tang during early boot, not only after SSH.

You need Lab value
Tang server rhel-8 @ 192.168.0.121
Clevis client centos-8 @ 192.168.0.119, LUKS root on /dev/sdb1
Tang URL in bind http://192.168.0.121
Tang key path /var/db/tang
Client packages clevis, clevis-luks, clevis-dracut
Initramfs regen dracut -fv --regenerate-all --hostonly-cmdline; add rd.neednet=1 if early network is required
Free LUKS slot yes — clevis luks bind consumes one slot

Pre-flight on the client:

  • Tang listens on TCP port 80 by default.
  • Run curl -s http://192.168.0.121/adv before you bind.
  • If that fails from a logged-in shell, Clevis will fail the same way at boot.

Safety:

  • Do not run this lab on your daily driver.
  • Use disposable VMs or snapshots.
  • A failed Tang reachability test can strand the host at the LUKS prompt.

Quick reference

Step Command
Install Tang (server) yum install tang
Enable Tang systemctl enable --now tangd.socket
Install Clevis (client) yum install clevis clevis-luks clevis-dracut
Bind clevis luks bind -d DEV tang '{"url":"http://TANG"}'
Manual test clevis luks unlock -d DEV -n NAME
Root initramfs dracut -fv --regenerate-all --hostonly-cmdline
Early network grubby --update-kernel=ALL --args="rd.neednet=1"
Proof on Tang journalctl -f on Tang VM

Which Clevis unlocker do I need?

Scenario Package Rebuild needed
Root disk (dracut) clevis-dracut dracut -fv --regenerate-all --hostonly-cmdline (+ rd.neednet=1 when early network is required)
Secondary data disk (crypttab after root) clevis-systemd Usually no initramfs rebuild
Manual test (any volume) clevis luks unlock None

Unlock timing:

  • Early boot — root via clevis-dracut in the initramfs.
  • Late boot — additional crypttab volumes via clevis-systemd once networking is up.

Policy-based decryption and how NBDE works

Policy-Based Decryption (PBD) is the umbrella term for unlocking encrypted volumes using a policy—network presence, TPM, and so on. NBDE binds LUKS to a Tang server on your network so clients can boot without distributing per-host keyfiles.

Term Role
Tang Stateless HTTP server; publishes public keys. Binds unlock to network presence. No TLS by default—use a trusted VLAN.
Clevis Client framework; clevis luks bind stores a JWE in the LUKS2 token area and adds a key slot.
Provisioning clevis luks bind encrypts a new volume key with Tang’s public key and stores metadata in the LUKS header.
Recovery At unlock (manual or boot), Clevis POSTs to Tang /rec/; Tang returns the wrapping key; the mapper opens.

Boot sequence:

  1. Initramfs loads Clevis hooks from clevis-dracut.
  2. dracut shows the LUKS unlock screen.
  3. Clevis fetches Tang’s advertisement (GET /adv), then POSTs encrypted key material to /rec/.
  4. Tang responds; Clevis opens /dev/mapper/… and boot continues.

What you should expect at boot:

  • The LUKS passphrase screen often appears briefly even with NBDE.
  • Clevis contacts Tang in the background.
  • If Tang responds, boot continues without you typing the passphrase.
  • That pause-and-continue behavior—not “zero prompt ever”—is what the video below shows.

Lab environment

Host Role IP OS
rhel-8.example.com Tang server 192.168.0.121 RHEL 8.1
centos-8.example.com Clevis client, LUKS root 192.168.0.119 CentOS 8.0

Lab goal:

  • Client has migrated LUKS root on /dev/sdb1 (passphrase every reboot before NBDE).
  • Bind to Tang on rhel-8; boot without typing the passphrase when the client reaches Tang.

Configure the Tang server (RHEL 8)

On the Tang VM (192.168.0.121), install Tang. RHEL ships tangd as a socket-activated service:

bash
sudo yum -y install tang
sudo systemctl enable tangd.socket --now

Verify the service is listening:

bash
sudo systemctl is-active tangd.socket
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1/adv

Expected: active and HTTP 200.

On first start, Tang writes JSON Web Keys to /var/db/tang:

bash
sudo ls -l /var/db/tang/

Sample output:

text
total 8
-rw-r--r-- 1 root tang 349 … 7VXZSkDbTEqqIh7TqoXG6u82LK0.jwk
-rw-r--r-- 1 root tang 354 … -NYm6-gTZ9dquHe6zy9ynGU8SAI.jwk
  • Each .jwk file is part of Tang’s key material.
  • The filename prefix (before .jwk) is the signing key id Clevis asks you to trust at bind time.

If firewalld is enabled, open port 80 so the client can reach Tang during boot:

bash
sudo firewall-cmd --add-port=80/tcp
sudo firewall-cmd --runtime-to-permanent

Leave this running on the Tang VM while the client reboots—recovery requests appear in real time:

bash
sudo journalctl -f

Configure the Clevis client (CentOS 8)

On the client (192.168.0.119), install Clevis and the dracut hook:

  • clevis-dracut embeds Clevis into the initramfs.
  • Without it, reboot still stops at the passphrase prompt—even after a successful bind.
bash
sudo yum -y install clevis clevis-luks clevis-dracut

Find the LUKS root partition (this lab: /dev/sdb1):

bash
sudo blkid -t TYPE=crypto_LUKS -o device

Sample output:

text
/dev/sdb1

Before bind, confirm only slot 0 is in use—Clevis will take the next free slot (usually 1):

bash
sudo cryptsetup luksDump /dev/sdb1 | sed -n '/Keyslots:/,/Tokens:/p'

Sample output:

text
Keyslots:
  0: luks2
	Key:        256 bits
	Priority:   normal
	Cipher:     aes-xts-plain64
	PBKDF:      argon2i
	...
  • One 0: luks2 entry — passphrase only.
  • After bind — slots 0 and 1.

Bind the LUKS root volume to Tang

On the client, point Clevis at the Tang server’s LAN IP:

  • Use http://192.168.0.121, not 127.0.0.1.
  • 127.0.0.1 makes the client look for Tang on itself when Tang runs on another VM.
bash
sudo clevis luks bind -d /dev/sdb1 tang '{"url":"http://192.168.0.121"}'

Clevis prints the Tang signing keys from the advertisement and asks you to trust them, then prompts for your existing LUKS passphrase:

text
The advertisement contains the following signing keys:

-NYm6-gTZ9dquHe6zy9ynGU8SAI

Do you wish to trust these keys? [ynYN] Y
Enter existing LUKS password:

In production, avoid -y unless you already verified the Tang signing key out of band. -y skips the interactive Tang advertisement trust check.

What clevis luks bind does under the hood:

  • Creates a new volume key with the same strength as the LUKS master key.
  • Encrypts that key with Clevis/Tang policy (JWE).
  • Stores the JWE in the LUKS2 header token area.
  • Enables the new key in a second LUKS slot.

Verify two slots are now active:

bash
sudo cryptsetup luksDump /dev/sdb1 | sed -n '/Keyslots:/,/Tokens:/p'

Sample output:

text
Keyslots:
  0: luks2
	...
  1: luks2
	...
  • Slot 0 — passphrase (unchanged).
  • Slot 1 — Clevis policy key.
  • Either slot unlocks the volume until you remove one.

Regenerate initramfs and reboot

Rebuild the initramfs—the running image was created before Clevis metadata existed.

On RHEL/CentOS 8 (original lab):

bash
sudo dracut -f

On current RHEL, Rocky, or Alma systems, prefer a full regenerate with host-only cmdline preservation:

bash
sudo dracut -fv --regenerate-all --hostonly-cmdline

If bind succeeds and clevis luks list shows the policy but boot still waits at LUKS, networking may not be up inside the initramfs. Add rd.neednet=1 to the kernel command line:

bash
sudo grubby --update-kernel=ALL --args="rd.neednet=1"

Reboot the client and watch the VM console—not only SSH:

  • SSH starts after LUKS unlock.
  • The console shows the brief prompt and automatic continuation.
bash
sudo reboot

What you see at boot

Boot sequence on the client:

  1. LUKS passphrase prompt appears for root.
  2. Clevis contacts Tang at 192.168.0.121.
  3. After a short pause, boot continues without you typing the passphrase (when Tang and the network path are up).

What Clevis does not do:

  • It does not send your passphrase over the network.
  • It POSTs encrypted key material to Tang; Tang returns data to activate slot 1 and open the mapper.

On the Tang server, a successful client boot logs:

text
tangd[4454]: 192.168.0.119 POST /rec/7VXZSkDbTEqqIh7TqoXG6u82LK0 => 200
  • 192.168.0.119 — client source IP.
  • POST /rec/ — recovery endpoint.
  • 200 — success.

If you see this line but the client still hangs, check initramfs regeneration, rd.neednet=1, and client network bring-up in early boot—not Tang.


When Tang is unreachable

Test failure modes on the two-VM lab only. Stop Tang or block port 80, then reboot the client:

bash
# On rhel-8 (Tang VM)
sudo systemctl stop tangd.socket

Expected when Tang is down:

  • Boot stays at the LUKS passphrase prompt.
  • Enter the passphrase from slot 0, or restore Tang reachability.
  • Keep a passphrase slot and plan Tang HA before removing manual unlock in production.

Restart Tang after the test:

bash
sudo systemctl start tangd.socket

For Tang key rotation and rebinding, see Rotate Tang keys and refresh Clevis bindings below.


Secondary data volumes

Root vs data disk unlock:

  • Root — unlocked in the initramfs via clevis-dracut.
  • Data disk — root must be up first; systemd opens crypttab entries via clevis-systemd.

On the client:

bash
sudo yum -y install clevis clevis-luks clevis-systemd
sudo clevis luks bind -d /dev/sdX1 tang '{"url":"http://192.168.0.121"}'

Wire boot (replace <luks-uuid> with sudo blkid /dev/sdX1):

  1. crypttab_netdev waits for networking before unlock:
text
secure_data UUID=<luks-uuid> none luks,_netdev
  1. fstab — mount the filesystem inside the mapper (mount LUKS at boot for the full walkthrough).

  2. Enable Clevis askpass:

bash
sudo systemctl enable clevis-luks-askpass.path

Test on a running system before you reboot a disposable VM:

bash
sudo clevis luks unlock -d /dev/sdX1 -n secure_data
sudo systemctl start systemd-cryptsetup@secure_data.service
findmnt /mount/point

Multiple Tang servers and high availability

One Tang VM is enough for a lab. Production usually runs two or more Tang instances so one maintenance window does not block every reboot.

Upstream Tang recommends client-side redundancy—each Tang server keeps its own key material—rather than copying one Tang key database to many hosts. Clevis supports that through separate bindings or the sss (Shamir’s Secret Sharing) pin.

For multiple Tang servers, use one of these supported patterns:

  1. Bind separate Clevis slots — one clevis luks bind per Tang server; each slot points at a different URL. Any bound slot can unlock the volume.
  2. Use the Clevis sss pin — combine multiple Tang child pins with threshold t:1 so Clevis tries Tang endpoints according to the sss policy (one successful Tang recovery is enough when t is 1):
bash
sudo clevis luks bind -d /dev/sdb1 sss \
  '{"t":1,"pins":{"tang":[{"url":"http://tang1.example.com"},{"url":"http://tang2.example.com"}]}}'
  1. Load balancer in front of Tang — only when recovery requests consistently reach a backend that holds the matching key material, or when you deliberately replicate Tang keys and accept the operational risk. Blind VIP failover to a Tang node with different keys will not unlock existing bindings.

Operations:

  • Monitor Tang like DNS or NTP.
  • If every Tang endpoint is down and passphrase slots were removed, hosts will not boot.
  • Keep at least one manual unlock path until HA is tested under real failures.
  • Before removing old Tang keys on the server, migrate clients with clevis luks regen (see rotation section below).

Rotate Tang keys and refresh Clevis bindings

Tang server keys may need to be rotated as part of your security policy. When you rotate Tang keys, refresh existing Clevis bindings before deleting old .jwk files.

Clevis bindings store JWE metadata tied to specific Tang keys—after rotation on the Tang server, clients must run clevis luks regen so the binding matches the new advertisement.

Workflow on the client (replace /dev/sdb1 and slot 1 with your device and Clevis slot):

  1. List bindings and confirm which slot is Tang policy:
bash
sudo clevis luks list -d /dev/sdb1
  1. Report what Clevis expects from Tang for that slot:
bash
sudo clevis luks report -d /dev/sdb1 -s 1
  1. After Tang has new keys in its advertisement, regenerate the JWE for the same policy:
bash
sudo clevis luks regen -d /dev/sdb1 -s 1

Safety notes:

  • Take a LUKS header backup before regen or unbind.
  • On the Tang server, do not remove old .jwk files until every client bound to those keys has run clevis luks regen and you confirmed unlock still works—premature deletion can make volumes unrecoverable via NBDE.
  • For root volumes, an initramfs rebuild is mandatory after the initial Clevis/root setup or when boot/network configuration changes (dracut -fv --regenerate-all --hostonly-cmdline). After clevis luks regen, a rebuild is usually optional but reasonable as a precaution—the critical step is confirming the refreshed binding unlocks before old Tang keys are removed.
  • Changing Tang URL or slot layout may overlap with LUKS key slot maintenance—plan header backups first.

Unbind and troubleshooting

To remove a Clevis binding:

  1. List slots and note the Tang policy number (usually 1 after a single bind):
bash
sudo clevis luks list -d /dev/sdb1

Sample output:

text
1: tang '{"url":"http://192.168.0.121"}'
  1. Unbind the Clevis slot. The command asks for confirmation unless you pass -f (batch mode, no prompt, wipes the slot):
bash
sudo clevis luks unbind -d /dev/sdb1 -s 1

Before unbinding, confirm another passphrase or policy slot still unlocks the volume (cryptsetup open with passphrase, or clevis luks unlock on a remaining binding).

  1. On a root volume, regenerate initramfs after unbind:
bash
sudo dracut -fv --regenerate-all --hostonly-cmdline
Symptom Likely cause Fix
Boot shows passphrase prompt forever Tang down, firewall, or wrong URL in bind Restore Tang; open port 80; verify curl -s http://TANG/adv from client
Brief prompt then boot continues Expected NBDE success Clevis recovered key from Tang
clevis luks unlock fails Tang URL wrong or Tang not running Fix URL; systemctl start tangd.socket
Boot still asks for passphrase (root) Initramfs not rebuilt, or network not up in initramfs dracut -fv --regenerate-all --hostonly-cmdline; add rd.neednet=1; check rd.luks.uuid=
Boot still asks (data disk) Missing _netdev or clevis-systemd Add luks,_netdev; enable clevis-luks-askpass.path
No POST /rec/ on Tang Client cannot route to Tang Check VLAN, DNS, firewall between VMs
VM boot failure after crypttab experiment Tang unreachable at boot on daily driver Restore snapshot; use two-VM lab

Watch Tang during a client unlock or reboot:

bash
sudo journalctl -b -f | grep tangd
  • No POST /rec/ while the client sits at LUKS → client never reached Tang; fix networking before re-bind.

Compare NBDE with other unlock methods

Method Unlock depends on Best for
Passphrase in crypttab (none) Human at keyboard Workstations, break-glass
Keyfile in crypttab File on disk Simple servers (weak if disk stolen)
systemd-cryptenroll TPM2/FIDO2 Local hardware Laptops, edge devices
Clevis + Tang NBDE Network presence + Tang Datacenter fleets

References


Summary

  1. Install Tang on the server VM and Clevis + clevis-dracut on the LUKS root client.
  2. Bind /dev/sdb1 to http://TANG_IP, trust Tang keys, and confirm two LUKS slots.
  3. Regenerate initramfs (dracut -fv --regenerate-all --hostonly-cmdline on current RHEL-family); add rd.neednet=1 if needed; reboot and watch Clevis fetch the key from Tang (video).
  4. For Tang HA, use separate Clevis slots or an sss pin with t:1—not a shared Tang key DB copied to many servers without a migration plan.
  5. Rotate Tang keys with clevis luks report and clevis luks regen; keep a passphrase slot; monitor POST /rec/ on Tang.
  6. Secondary data disks use clevis-systemd and _netdev in crypttab—test on a disposable VM.
  7. Maintain header backups alongside your LUKS runbook.

Frequently Asked Questions

1. Does NBDE replace LUKS passphrases?

Clevis adds a second key slot bound to Tang policy. Keep at least one passphrase or recovery key unless your runbook explicitly removes manual unlock.

2. Can I use Clevis on LUKS1?

LUKS2 is recommended. Clevis stores JWE metadata in LUKS2 tokens; LUKS1 layouts use older LUKSMeta paths and are legacy on current distributions.

3. How is NBDE different from systemd-cryptenroll TPM2?

NBDE unlocks when the host can reach a Tang server on the network. TPM2 and FIDO2 enroll local hardware tokens—see the systemd-cryptenroll guide for laptop-style unlock.

4. What happens if the Tang server is down at boot?

Clevis retries against Tang; the LUKS passphrase prompt may stay on screen until you type a passphrase or Tang responds. Plan Tang HA and offline recovery keys.

5. Do secondary data disks need dracut for Clevis?

Usually no. Secondary volumes unlock after root via systemd and clevis-systemd. Rebuild the initramfs only when the LUKS device is unlocked in early boot (root, resume, or crypttab initramfs option).

6. Why does boot still show a LUKS passphrase prompt with NBDE?

Clevis often displays the normal unlock screen first, then contacts Tang over the network. If Tang answers, boot continues without you typing the passphrase. If Tang is down, you type the passphrase from an existing LUKS slot.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive …