Encrypt an Existing Linux Installation Online with the Magic of LVM
Tags: linux, sysadmin, encryption, security
Published: 2022-06-14 22:02:28

---
title: Encrypt an Existing Linux Installation Online with the Magic of LVM
published: true
description: Full disk encryption of an existing Linux installation without requiring any downtime or rebooting with LVM and LUKS
tags: linux, sysadmin, encryption, security
---

<div id="content">
<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>So you have an existing Linux installation and your disks aren&#8217;t encrypted? You
want to encrypt the entire disk including your root partition, and you&#8217;d like
to do it without taking the server offline. If the server already uses LVM,
which is the default for many popular enterprise distros, then you&#8217;re in luck.</p>
</div>
<div class="paragraph">
<p>Because of the awesomeness of LVM, an existing installation can be encrypted
with LUKS completely online. This does require a spare disk of equal or greater
size to the existing disk. In a virtual environment, this can be achieved by
adding an extra virtual disk to the VM. For a desktop or workstation, this can
be achieved with an external drive.</p>
</div>
<div class="paragraph">
<p>What we want to do is:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Add the spare disk to the volume group.</p>
</li>
<li>
<p>Move the data over to the spare disk.</p>
</li>
<li>
<p>Remove the original disk from the volume group.</p>
</li>
<li>
<p>Encrypt the original disk with LUKS.</p>
</li>
<li>
<p>Add it back to the volume group and move the data back over.</p>
</li>
<li>
<p>Remove the spare disk from the volume group.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>To get started:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Get the name of the existing physical volume with <code>sudo pvdisplay</code>.</p>
</li>
<li>
<p>Get the name of the existing volume group with <code>sudo vgdisplay</code>.</p>
</li>
<li>
<p>Determine the name of the spare disk with <code>sudo fdisk -l</code>.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>I&#8217;m encrypting a VM with a virtual disk (<code>/dev/sda</code>). It has two
partitions: a small <code>/boot</code> partition (<code>/dev/sda1</code>) and an LVM physical
volume (<code>/dev/sda2</code>). The name of the volume group used in this example
is vgpool. I added a second virtual disk. It is unpartitioned and called
<code>/dev/sdb</code>. Substitute the values for your environment.</p>
</div>
<div class="admonitionblock warning">
<table>
<tr>
<td class="icon">
<i class="fa icon-warning" title="Warning"></i>
</td>
<td class="content">
I highly recommend taking a snapshot or backup before continuing. Any
time you are messing with storage, there is the possibility for things to go
very wrong.
</td>
</tr>
</table>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Add the spare disk to the volume group.</p>
<div class="listingblock">
<div class="content">
<pre class="pygments highlight"><code data-lang="console"><span></span><span class="tok-gp">$ </span>sudo pvcreate /dev/sdb
<span class="tok-go">  Physical volume &quot;/dev/sdb&quot; successfully created.</span>
<span class="tok-gp">$ </span>sudo vgextend vgpool /dev/sdb
<span class="tok-go">  Volume group &quot;vgpool&quot; successfully extended</span></code></pre>
</div>
</div>
</li>
<li>
<p>Move the physical extents from the current physical volume to the spare
physical volume. This will take a long time. It will periodically print
progress to the screen.</p>
<div class="listingblock">
<div class="content">
<pre class="pygments highlight"><code data-lang="console"><span></span><span class="tok-gp">$ </span>sudo pvmove /dev/sda2 /dev/sdb</code></pre>
</div>
</div>
</li>
<li>
<p>Remove the original physical volume from the volume group.</p>
<div class="listingblock">
<div class="content">
<pre class="pygments highlight"><code data-lang="console"><span></span><span class="tok-gp">$ </span>sudo vgreduce vgpool /dev/sda2
<span class="tok-go">  Removed &quot;/dev/sda2&quot; from volume group &quot;vgpool&quot;</span>
<span class="tok-gp">$ </span>sudo pvremove /dev/sda2
<span class="tok-go"> Labels on physical volume &quot;/dev/sda2&quot; successfully wiped.</span></code></pre>
</div>
</div>
</li>
<li>
<p><a href="https://wiki.archlinux.org/title/Dm-crypt/Drive_preparation#dm-crypt_specific_methods">Wipe</a>
the original disk. Because the disk already had data on it, it is important to
securely erase the disk to prevent file recovery of unencrypted data.</p>
<div class="listingblock">
<div class="content">
<pre class="pygments highlight"><code data-lang="console"><span></span><span class="tok-gp">$ </span>sudo yum install cryptsetup
<span class="tok-gp">$ </span>sudo cryptsetup open --type plain -d /dev/urandom /dev/sda2 to_be_wiped
<span class="tok-gp">$ </span>sudo dd <span class="tok-k">if</span><span class="tok-o">=</span>/dev/zero <span class="tok-nv">of</span><span class="tok-o">=</span>/dev/mapper/to_be_wiped <span class="tok-nv">bs</span><span class="tok-o">=</span>1M <span class="tok-nv">status</span><span class="tok-o">=</span>progress
<span class="tok-go">dd: error writing &#39;/dev/mapper/to_be_wiped&#39;: No space left on device</span>
<span class="tok-gp">$ </span>sudo cryptsetup close to_be_wiped</code></pre>
</div>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="paragraph">
<p>The default cipher for plain dm-crypt encryption is aes-cbc. It might be worth
changing the cipher to aes-xts, as it might be significantly faster (verify
with <code>cryptsetup benchmark</code>).</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="pygments highlight"><code data-lang="console"><span></span><span class="tok-gp">$ </span>sudo cryptsetup open --type plain -d /dev/urandom --cipher aes-xts-plain64 /dev/sda2 to_be_wiped</code></pre>
</div>
</div>
</td>
</tr>
</table>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
Use of <code>/dev/urandom</code> with <code>dd</code> is not required as the encryption cipher
is used for randomness.
</td>
</tr>
</table>
</div>
</li>
<li>
<p>Encrypt the original disk with LUKS using a detached LUKS header stored on
the <code>/boot</code> partition.</p>
<div class="paragraph">
<p>The <code>cryptsetup</code> default is to store the LUKS header at the beginning of the
partition or disk. If the LUKS header is stored on the original, the volume
group will no longer fit when we go to move it back to the original disk from
the spare disk, and you&#8217;ll get an error like this:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="pygments highlight"><code data-lang="console"><span></span><span class="tok-gp">$ </span>sudo pvmove /dev/sdb /dev/mapper/lvmcrypt
<span class="tok-go">  Insufficient free space: 261887 extents needed, but only 261883 available</span>
<span class="tok-go">  Unable to allocate mirror extents for ol_ods/pvmove0.</span>
<span class="tok-go">  Failed to convert pvmove LV to mirrored.</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>LUKS has the ability to use a
<a href="https://linuxconfig.org/how-to-use-luks-with-a-detached-header">detached
header</a>. Since you probably already have an unencrypted <code>/boot</code>, the header can
be stored there.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="pygments highlight"><code data-lang="console"><span></span><span class="tok-gp">$ </span>sudo cryptsetup -y luksFormat /dev/sda2 --header /boot/luksheader.img

<span class="tok-go">WARNING!</span>
<span class="tok-go">========</span>
<span class="tok-go">Header file does not exist, do you want to create it?</span>

<span class="tok-go">Are you sure? (Type &#39;yes&#39; in capital letters): YES</span>
<span class="tok-go">Enter passphrase for /boot/luksheader.img:</span>
<span class="tok-go">Verify passphrase:</span>
<span class="tok-gp">$ </span>sudo cryptsetup luksOpen /dev/sda2 lvmcrypt --header /boot/luksheader.img
<span class="tok-go">Enter passphrase for /dev/sda2:</span></code></pre>
</div>
</div>
</li>
<li>
<p>Add the now encrypted disk back to the volume group.</p>
<div class="listingblock">
<div class="content">
<pre class="pygments highlight"><code data-lang="console"><span></span><span class="tok-gp">$ </span>sudo pvcreate /dev/mapper/lvmcrypt
<span class="tok-go">  Physical volume &quot;/dev/mapper/lvmcrypt&quot; successfully created.</span>
<span class="tok-gp">$ </span>sudo vgextend vgpool /dev/mapper/lvmcrypt
<span class="tok-go">  Volume group &quot;vgpool&quot; successfully extended</span></code></pre>
</div>
</div>
</li>
<li>
<p>Move the physical extents from the spare physical volume to the encrypted
physical volume. Like before, this will take a long time.</p>
<div class="listingblock">
<div class="content">
<pre class="pygments highlight"><code data-lang="console"><span></span><span class="tok-gp">$ </span>sudo pvmove /dev/sdb /dev/mapper/lvmcrypt</code></pre>
</div>
</div>
</li>
<li>
<p>Remove the spare physical volume from the volume group.</p>
<div class="listingblock">
<div class="content">
<pre class="pygments highlight"><code data-lang="console"><span></span><span class="tok-gp">$ </span>sudo vgreduce vgpool /dev/sdb
<span class="tok-go">  Removed &quot;/dev/sdb&quot; from volume group &quot;vgpool&quot;</span>
<span class="tok-gp">$ </span>sudo pvremove /dev/sdb
<span class="tok-go">  Labels on physical volume &quot;/dev/sdb&quot; successfully wiped.</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>The data is now entirely on the original physical volume but this time
encrypted. You can now delete the spare virtual disk in the case of a VM or
remove the external drive.</p>
</div>
</li>
</ol></div></div></div>
<div class="sect3">
<h2 id="_configure_initramfs">Configure Initramfs</h2>
<div class="paragraph">
<p>We&#8217;ve accomplished what we set out to do: we&#8217;ve encrypted a live system without
requiring a reboot. But whenever you do reboot, you want your system to come
back up. Since we encrypted the root partition, the decryption needs to happen
in the initramfs. There are various tools to regenerate the initramfs depending
on your distro. Red Hat-derived distros like Oracle Linux usually use Dracut.</p>
</div>
<div class="paragraph">
<p>Since we used a detached header, configuring the initramfs is a little more
<a href="https://medium.com/@privb0x23/lose-your-head-attempting-to-boot-from-luks-without-a-header-2d61174df360">complicated</a>.
&#8220;A LUKS volume with a detached header, at least using <code>udev</code>/<code>blkid</code>, does not
have a UUID.&#8221; However, unlike in the linked article, there is a PARTUUID since
partitioning is used on the disk (assuming <code>/dev/sda1</code> is <code>/boot</code> and
<code>/dev/sda2</code> is LVM because the Red Hat installer requires <code>/boot</code> on a separate
partition when using LVM).</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Get the PARTUUID of the LUKS device with (<code>sudo blkid /dev/sda2</code>).</p>
</li>
<li>
<p>Add it to <code>/etc/crypttab</code> in the form:</p>
<div class="listingblock">
<div class="content">
<pre>volume-name encrypted-device key-file options</pre>
</div>
</div>
<div class="openblock">
<div class="content">
<div class="ulist">
<ul>
<li>
<p><code>volume-name</code> is the device mapper name (e.g. if the <code>volume-name</code> is
<code>lvmcrypt</code>, it will mount the LUKS device as <code>/dev/mapper/lvmcrypt</code>).</p>
</li>
<li>
<p><code>encrypted-device</code> is the
<a href="https://github.com/dracutdevs/dracut/pull/290">specification</a> of the encrypted
device via <code>UUID=</code>, <code>PARTUUID=</code>, or <code>ID=</code>.</p>
</li>
<li>
<p><code>key-file</code> is the absolute path to a file with the encryption key. If there
is no key file, use <code>none</code>.</p>
</li>
<li>
<p><code>options</code> is a comma-delimited list of options.</p>
</li>
</ul>
</div>
</div>
</div>
<div class="paragraph">
<p>Since we used a detached header, we need to include the <code>header</code> option and
<code>force</code> to force the inclusion of the entry in the initramfs (the only place I
found where this option is documented is in the
<a href="https://github.com/dracutdevs/dracut/pull/290">pull request</a> that added it), so
our <code>crypttab</code> will look something like:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>lvmcrypt PARTUUID=&lt;PARTUUID-identifier&gt; none header=/boot/luksheader.img,force</pre>
</div>
</div>
<div class="paragraph">
<p>where <code>&lt;PARTUUID-identifer&gt;</code> is the PARTUUID of the LUKS device.</p>
</div>
<div class="paragraph">
<p>If this is for a laptop or workstation where you are OK with being prompted for
a password during boot, you can skip to the last step and regenerate initramfs.
The following steps will set up Network-Bound Disk Encryption (NBDE) with
Clevis.</p>
</div>
</li>
<li>
<p>Bind LUKS volume to <a href="https://github.com/latchset/clevis#binding-luks-volumes">Clevis</a>.</p>
<div class="listingblock">
<div class="content">
<pre class="pygments highlight"><code data-lang="console"><span></span><span class="tok-gp">$ </span>sudo yum install clevis clevis-luks clevis-dracut
<span class="tok-gp">$ </span>sudo clevis luks <span class="tok-nb">bind</span> -d /dev/sda2 tang <span class="tok-s1">&#39;{&quot;url&quot;: &quot;http://tang.example.com&quot;}&#39;</span>
<span class="tok-go">/dev/sda2 is not a LUKS device!</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Unfortunately, Clevis does not currently support detached headers. Clevis
mostly consists of shell scripts, so this can be easily hacked. These
modifications will have to be remerged whenever updating Clevis, so it would be
ideal to get the modifications necessary to support detached headers merged
upstream like the author of this
<a href="https://medium.com/@privb0x23/lose-your-head-attempting-to-boot-from-luks-without-a-header-2d61174df360">post</a>
did to get detached headers supported by
<a href="https://github.com/dracutdevs/dracut/pull/290">Dracut</a>. I&#8217;m going to start by
opening an issue on the Clevis GitHub to see if they are open to a pull request
adding this and update this with the results.</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Navigate to <code>/usr/bin</code> and make a backup copy of <code>clevis-luks-bind</code>.</p>
<div class="listingblock">
<div class="content">
<pre class="pygments highlight"><code data-lang="console"><span></span><span class="tok-gp">$ </span><span class="tok-nb">cd</span> /usr/bin
<span class="tok-gp">$ </span>sudo cp clevis-luks-bind clevis-luks-bind.original</code></pre>
</div>
</div>
</li>
<li>
<p>Edit <code>clevis-luks-bind</code>. Locate where the command line arguments are parsed
with <code>getopts</code> and make the following additions:</p>
<div class="listingblock">
<div class="content">
<pre class="pygments highlight"><code data-lang="sh"><span></span><span class="tok-k">while</span> <span class="tok-nb">getopts</span> <span class="tok-s2">&quot;:hfyd:s:k:t:o:&quot;</span> o<span class="tok-p">;</span> <span class="tok-k">do</span> <i class="conum" data-value="1"></i><b>(1)</b>
    <span class="tok-k">case</span> <span class="tok-s2">&quot;</span><span class="tok-nv">$o</span><span class="tok-s2">&quot;</span> <span class="tok-k">in</span>
    f<span class="tok-o">)</span> <span class="tok-nv">FRC</span><span class="tok-o">+=(</span>-f<span class="tok-o">)</span><span class="tok-p">;;</span>
    d<span class="tok-o">)</span> <span class="tok-nv">DEV</span><span class="tok-o">=</span><span class="tok-s2">&quot;</span><span class="tok-nv">$OPTARG</span><span class="tok-s2">&quot;</span><span class="tok-p">;;</span>
    s<span class="tok-o">)</span> <span class="tok-nv">SLT</span><span class="tok-o">=</span><span class="tok-s2">&quot;</span><span class="tok-nv">$OPTARG</span><span class="tok-s2">&quot;</span><span class="tok-p">;;</span>
    k<span class="tok-o">)</span> <span class="tok-nv">KEY</span><span class="tok-o">=</span><span class="tok-s2">&quot;</span><span class="tok-nv">$OPTARG</span><span class="tok-s2">&quot;</span><span class="tok-p">;;</span>
    t<span class="tok-o">)</span> <span class="tok-nv">TOKEN_ID</span><span class="tok-o">=</span><span class="tok-s2">&quot;</span><span class="tok-nv">$OPTARG</span><span class="tok-s2">&quot;</span><span class="tok-p">;;</span>
    o<span class="tok-o">)</span> <span class="tok-nv">HEADER</span><span class="tok-o">=</span><span class="tok-s2">&quot;</span><span class="tok-nv">$OPTARG</span><span class="tok-s2">&quot;</span><span class="tok-p">;;</span> <i class="conum" data-value="2"></i><b>(2)</b>
    y<span class="tok-o">)</span> <span class="tok-nv">FRC</span><span class="tok-o">+=(</span>-f<span class="tok-o">)</span>
       <span class="tok-nv">YES</span><span class="tok-o">+=(</span>-y<span class="tok-o">)</span><span class="tok-p">;;</span>
    *<span class="tok-o">)</span> usage<span class="tok-p">;;</span>
    <span class="tok-k">esac</span>
<span class="tok-k">done</span>

<span class="tok-k">if</span> <span class="tok-o">[</span> ! -z <span class="tok-s2">&quot;</span><span class="tok-nv">$HEADER</span><span class="tok-s2">&quot;</span> <span class="tok-o">]</span><span class="tok-p">;</span> <span class="tok-k">then</span> <i class="conum" data-value="3"></i><b>(3)</b>
    <span class="tok-nv">LUKSOPTS</span><span class="tok-o">=</span><span class="tok-s2">&quot;--header=</span><span class="tok-nv">$HEADER</span><span class="tok-s2">&quot;</span>
<span class="tok-k">fi</span>

<span class="tok-c1"># Alias cryptsetup to insert options:</span>
cryptsetup<span class="tok-o">()</span> <span class="tok-o">{</span>
    /usr/sbin/cryptsetup <span class="tok-s2">&quot;</span><span class="tok-nv">$LUKSOPTS</span><span class="tok-s2">&quot;</span> <span class="tok-s2">&quot;</span><span class="tok-nv">$@</span><span class="tok-s2">&quot;</span>
<span class="tok-o">}</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Modify the <code>getopts</code> spec adding <code>o:</code> to the end. Since <code>h</code> for header
wasn&#8217;t available, and <code>d</code> for detached is already used for device, I chose <code>o</code>
for option.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Add an entry to the <code>case</code> statement to set a <code>$HEADER</code> variable to the
value of the <code>o</code> argument.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Add this and the following lines. The syntax for the <code>cryptsetup</code> utility
is <code>cryptsetup &lt;options&gt; &lt;action&gt; &lt;action args&gt;</code>. Clevis uses <code>cryptsetup</code> to
bind to a LUKS volume. What we want to do is insert the <code>header</code> option into
<code>&lt;options&gt;</code> whenever Clevis calls <code>cryptsetup</code>. A quick-and-dirty hack to
accomplish this with minimal changes to the code (especially since we&#8217;ll have
to remerge our changes whenever updating Clevis) is to alias <code>cryptsetup</code> with
a function that calls the absolute path to the <code>cryptsetup</code> executable with the
<code>header</code> option and then appends the rest of the arguments Clevis originally
called <code>cryptsetup</code> with.</td>
</tr>
</table>
</div>
</li>
<li>
<p>Now you can include the <code>-o</code> argument we just added to Clevis to bind it to
the LUKS volume.</p>
<div class="listingblock">
<div class="content">
<pre class="pygments highlight"><code data-lang="console"><span></span><span class="tok-gp">$ </span>sudo clevis luks <span class="tok-nb">bind</span> -o /boot/luksheader.img -d /dev/sda2 tang <span class="tok-s1">&#39;{&quot;url&quot;: &quot;http://tang.example.com&quot;}&#39;</span>
<span class="tok-go">The advertisement contains the following signing keys:</span>

<span class="tok-go">-Y7MtTJlw8rGGVgXRBhFtumJIqk</span>

<span class="tok-go">Do you wish to trust these keys? [ynYN] y</span>
<span class="tok-go">You are about to initialize a LUKS device for metadata storage.</span>
<span class="tok-go">Attempting to initialize it may result in data loss if data was</span>
<span class="tok-go">already written into the LUKS header gap in a different format.</span>
<span class="tok-go">A backup is advised before initialization is performed.</span>

<span class="tok-go">Do you wish to initialize /dev/sda2? [yn] y</span>
<span class="tok-go">Enter existing LUKS password:</span></code></pre>
</div>
</div>
</li>
<li>
<p>Modify <code>/usr/libexec/clevis-luks-askpass</code> to support detached headers.
Modifying Clevis to bind to a LUKS volume with a deatched header is only
one   half of the equation. The Clevis Dracut module also needs to be able to
unlock the volume at boot.</p>
<div class="listingblock">
<div class="content">
<pre class="pygments highlight"><code data-lang="sh"><span></span><span class="tok-k">while</span> <span class="tok-nb">read</span> -r line<span class="tok-p">;</span> <span class="tok-k">do</span> <i class="conum" data-value="1"></i><b>(1)</b>
  <span class="tok-k">case</span> <span class="tok-s2">&quot;</span><span class="tok-nv">$line</span><span class="tok-s2">&quot;</span> <span class="tok-k">in</span>
      <span class="tok-nv">Id</span><span class="tok-o">=</span>cryptsetup:*<span class="tok-o">)</span> <span class="tok-nv">d</span><span class="tok-o">=</span><span class="tok-s2">&quot;</span><span class="tok-si">${</span><span class="tok-nv">line</span><span class="tok-p">##Id=cryptsetup:</span><span class="tok-si">}</span><span class="tok-s2">&quot;</span><span class="tok-p">;;</span>
      <span class="tok-nv">Socket</span><span class="tok-o">=</span>*<span class="tok-o">)</span> <span class="tok-nv">s</span><span class="tok-o">=</span><span class="tok-s2">&quot;</span><span class="tok-si">${</span><span class="tok-nv">line</span><span class="tok-p">##Socket=</span><span class="tok-si">}</span><span class="tok-s2">&quot;</span><span class="tok-p">;;</span>
  <span class="tok-k">esac</span>
<span class="tok-k">done</span> &lt; <span class="tok-s2">&quot;</span><span class="tok-nv">$question</span><span class="tok-s2">&quot;</span>

<span class="tok-o">[</span> -b <span class="tok-s2">&quot;</span><span class="tok-si">${</span><span class="tok-nv">d</span><span class="tok-si">}</span><span class="tok-s2">&quot;</span> <span class="tok-o">]</span> <span class="tok-o">||</span> <span class="tok-k">continue</span>
<span class="tok-o">[</span> -S <span class="tok-s2">&quot;</span><span class="tok-si">${</span><span class="tok-nv">s</span><span class="tok-si">}</span><span class="tok-s2">&quot;</span> <span class="tok-o">]</span> <span class="tok-o">||</span> <span class="tok-k">continue</span>

<span class="tok-c1"># Detached header hack </span><i class="conum" data-value="2"></i><b>(2)</b>
<span class="tok-k">if</span> <span class="tok-o">[</span> <span class="tok-s2">&quot;</span><span class="tok-si">${</span><span class="tok-nv">d</span><span class="tok-si">}</span><span class="tok-s2">&quot;</span> <span class="tok-o">=</span> <span class="tok-s2">&quot;/dev/disk/by-partuuid/&lt;PARTUUID-identifier&gt;&quot;</span> <span class="tok-o">]</span><span class="tok-p">;</span> <span class="tok-k">then</span> <i class="conum" data-value="3"></i><b>(3)</b>
  cryptsetup<span class="tok-o">()</span> <span class="tok-o">{</span>
      /usr/sbin/cryptsetup --header<span class="tok-o">=</span>/boot/luksheader.img <span class="tok-s2">&quot;</span><span class="tok-nv">$@</span><span class="tok-s2">&quot;</span> <i class="conum" data-value="4"></i><b>(4)</b>
  <span class="tok-o">}</span>
<span class="tok-k">fi</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Locate the <code>while</code> loop that reads the <code>ask.*</code> files in <code>/run/systemd/ask-password</code>.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Add this and the following lines.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>This is an ugly hack to get something that works with minimal changes
to the code. All the <code>ask.*</code> file contains is the device, not any of the
options. If upstream Clevis supports detached headers in the future, ideally it
would do something like look up the device in <code>crypttab</code> to get the header, but
I&#8217;m not familiar enough with the inner workings of Clevis and Dracut to try and
implement that in a quick hack. For now since we know which device we want to
unlock and where the header is located for that device, we&#8217;ll hardcode it into
<code>clevis-luks-askpass</code>. Replace <code>&lt;PARTUUID-identifier&gt;</code> with the PARTUUID of the
LUKS volume obtained above.</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>Like we did to get Clevis to bind to a LUKS volume with a detached
header, alias <code>cryptsetup</code> with a function that calls the absolute path to the
<code>cryptsetup</code> executable with the <code>header</code> option and then appends the rest of
the arguments Clevis originally called <code>cryptsetup</code> with.</td>
</tr>
</table>
</div>
</li>
</ul>
</div>
</li>
<li>
<p>Set the <code>rd.neednet</code> Dracut kernel command line parameter to make sure
network is available in the initramfs for Clevis to be able to contact the Tang
server. Create <code>/etc/dracut.conf.d/clevis-nbde.conf</code>:</p>
<div class="listingblock">
<div class="content">
<pre class="pygments highlight"><code data-lang="cfg"><span></span><span class="tok-na">kernel_cmdline</span><span class="tok-o">=</span><span class="tok-s">&quot;rd.neednet=1&quot;</span><span class="tok-w"></span></code></pre>
</div>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>This step might not be necessary depending on your distro, version of Clevis,
and/or version of Dracut. Without it, Clevis failed to unlock the LUKS volume,
and I had to type the passphrase in at the prompt. After boot, I checked what
the issue was with <code>sudo journalctl | grep clevis</code> and saw a whole bunch of
messages like this:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>Jun 14 10:31:45 example clevis-luks-askpass[869]: Error communicating with the server http://tang.example.com</pre>
</div>
</div>
<div class="paragraph">
<p>Adding the above Dracut configuration parameter
<a href="https://github.com/latchset/clevis/issues/232">resolved the issue</a>. It appears
this issue may not exist in versions of Clevis included with recent versions of
Fedora, but at least for the version of Dracut included with Oracle Linux 8.5,
the above workaround was still needed.</p>
</div>
</td>
</tr>
</table>
</div>
</li>
<li>
<p>Regenerate initramfs. Include the LUKS detached header in the initramfs.</p>
<div class="listingblock">
<div class="content">
<pre class="pygments highlight"><code data-lang="console"><span></span><span class="tok-gp">$ </span>sudo dracut --install /boot/luksheader.img -f</code></pre>
</div>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
If it fails to automatically unlock at boot, you can troubleshoot the
issue with <code>sudo journalctl | grep clevis</code>. If it fails to boot at all, you can
manually unlock the LUKS volume from the Dracut Emergency Shell, and then it
should proceed to boot. Then you can troubleshoot the issue by <code>grep</code>ing the
output of <code>journalctl</code>.
</td>
</tr>
</table>
</div>
</li>
</ol></div></div></div>