<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>NixOS on Giovanni Bassi</title><link>https://giggio.net/en/blog/tags/nixos/</link><image><url>https://giggio.net/images/base/logo-small.png</url><title>NixOS on Giovanni Bassi</title><link>https://giggio.net/en/blog/tags/nixos/</link></image><description>NixOS no site do Giovanni Bassi</description><generator>Hugo</generator><language>en</language><managingEditor>giggio@giggio.net (Giovanni Bassi)</managingEditor><webMaster>giggio@giggio.net (Giovanni Bassi)</webMaster><copyright>© 2025 Giovanni Bassi</copyright><lastBuildDate>Mon, 01 Jun 2026 11:00:00 -0300</lastBuildDate><atom:link href="https://giggio.net/en/blog/tags/nixos/index.xml" rel="self" type="application/rss+xml"/><item><title>NixOS: Installation Guide with RAID 1, encryption, and TPM unlock (part 7 - Mitigating the OS swap attack)</title><link>https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-7-mitigando-o-ataque-de-troca-de-so/</link><pubDate>Mon, 01 Jun 2026 11:00:00 -0300</pubDate><author>giggio@giggio.net (Giovanni Bassi)</author><guid>https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-7-mitigando-o-ataque-de-troca-de-so/</guid><category>infra</category><description>&lt;p&gt;There is still a loophole where one could obtain the LUKS volume encryption key using another operating system. Let&amp;rsquo;s
fix that.&lt;/p&gt;
&lt;p&gt;This is the seventh post in the series:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-1/"&gt;Preparing the virtual machine and partitioning the disks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-2-disko-luks-e-btrfs/"&gt;Disko, LUKS, and btrfs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-3-instalando-o-so/"&gt;Installing the OS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-4-secure-boot/"&gt;Enabling Secure Boot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-5-destrancando-o-disco-com-tpm/"&gt;Unlocking the disk with TPM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-6-mitigando-o-ataque-de-troca-de-volume/"&gt;Mitigating the volume swap attack&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mitigating the OS-switch attack (this post)&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you are only using your own keys in Secure Boot, this post doesn&amp;rsquo;t apply to your scenario. If you are also using
Microsoft&amp;rsquo;s keys, you need to implement this mitigation.&lt;/p&gt;
&lt;h3 id="the-problem"&gt;
 &lt;a href="#the-problem" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;The Problem&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;So far, we have been linking disk decryption to PCR 7 (and PCR 15 for system loading, as described in the last post). As
seen in previous posts, PCR 7 depends exclusively on the Secure Boot state. This means that, regardless of which
operating system is loaded, PCR 7 will have the same values.&lt;/p&gt;
&lt;p&gt;If you also trust third-party keys (such as Microsoft&amp;rsquo;s or another manufacturer&amp;rsquo;s, like Canonical&amp;rsquo;s) in Secure Boot,
there is still an attack surface that allows booting another system signed by those authorities. Therefore, it&amp;rsquo;s
necessary to bind the protection to something beyond just PCR 7.&lt;/p&gt;
&lt;p&gt;With the operating system loaded and PCR 7 in the correct configuration, it&amp;rsquo;s possible to obtain the LUKS volume header
data and decrypt it using the TPM to get the LUKS master key, and then decrypt the volume. This could even be done using
a Live CD with a correctly signed boot loader.&lt;/p&gt;
&lt;h3 id="naively-sealing-the-luks-key-with-the-nixos-boot-loader"&gt;
 &lt;a href="#naively-sealing-the-luks-key-with-the-nixos-boot-loader" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Naively sealing the LUKS key with the NixOS boot loader&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Sealing the volume key only with PCR 7 is risky, so let&amp;rsquo;s also use another PCR that is closely tied to our operating
system. PCR 4 measures the boot loader and additional drivers — the boot loader/PE binary code executed in the boot path.
This means only an operating system that loads using the NixOS &lt;code&gt;.efi&lt;/code&gt; files we are using would be able to retrieve the
LUKS key via the TPM.&lt;/p&gt;
&lt;p&gt;Since these &lt;code&gt;.efi&lt;/code&gt; files are generated on-demand for our specific NixOS configuration and signed with Lanzaboote using
our private keys, this prevents another OS from decrypting the LUKS key. For instance, if you load Ubuntu, the boot
loader will be Ubuntu&amp;rsquo;s, with different &lt;code&gt;.efi&lt;/code&gt; files, and therefore the content extended into PCR 4 will also be
different. Because PCR 4 measures the EFI/PE binary actually executed at boot, a different boot path produces a
different measurement and cannot reuse the same TPM token.&lt;/p&gt;
&lt;p&gt;This would be simple if the boot loader didn&amp;rsquo;t change every time the Kernel, initrd, or any boot configuration is
updated, which ends up changing PCR 4. If that weren&amp;rsquo;t the case, we could simply run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# remove previous token from disk 1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemd-cryptenroll --wipe-slot&lt;span class="o"&gt;=&lt;/span&gt;tpm2 /dev/disk/by-label/NIXLUKS1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# enroll new token using PCR 4 and PCR 7 on disk 1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemd-cryptenroll --tpm2-device&lt;span class="o"&gt;=&lt;/span&gt;auto --tpm2-pcrs&lt;span class="o"&gt;=&lt;/span&gt;4+7 /dev/disk/by-label/NIXLUKS1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# do the same for disk 2...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The commands above can be combined into one, which will wipe and enroll in a single operation.&lt;/p&gt;
&lt;p&gt;This will only work until the next time the boot loader files (the &lt;code&gt;.efi&lt;/code&gt; files) are changed. On the first boot after
such a change, the volume will no longer decrypt automatically, it will ask for the password, and you would need to run
the commands above again. If that&amp;rsquo;s acceptable to you, you don&amp;rsquo;t even need to read the rest of the post, but I can tell
you there is a better and equally secure way to solve this.&lt;/p&gt;
&lt;h3 id="sealing-the-luks-key-with-the-nixos-boot-loader-using-a-pcr-policy"&gt;
 &lt;a href="#sealing-the-luks-key-with-the-nixos-boot-loader-using-a-pcr-policy" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Sealing the LUKS key with the NixOS boot loader using a PCR policy&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt;: Before starting, verify that your virtual machine is configured to use only one of the disks as a boot
disk. I noticed that if both disks are marked as boot disks, PCR 4 is not correctly evaluated and the policy is not
created. In the virtual machine settings, you can see this in the &amp;ldquo;Boot Options&amp;rdquo; menu. This might be a quirk of Virtual
Machine Manager (and &lt;code&gt;virsh&lt;/code&gt;), the Firmware used, or something related to Secure Boot and TPM protocols. The problem
doesn&amp;rsquo;t occur on my physical machine, only on the virtual one.&lt;/p&gt;
&lt;p&gt;The secret to solving this problem is a system that can predict the values that will be in the PCR and create an
appropriate policy, and that is exactly what
&lt;a href="https://www.freedesktop.org/software/systemd/man/latest/systemd-pcrlock.html"&gt;systemd-pcrlock&lt;/a&gt; does. While the process
can be done manually, Lanzaboote recently implemented
&lt;a href="https://github.com/nix-community/lanzaboote/blob/master/docs/how-to-guides/enable-measured-boot.md"&gt;TPM measurement&lt;/a&gt;
alongside Secure Boot integration, using systemd-pcrlock. At the time of writing, this hasn&amp;rsquo;t made it into a release
yet, meaning it might contain bugs. By the time you read this, the functionality may have stabilized.&lt;/p&gt;
&lt;p&gt;To enable it, simply add this to your configuration:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-nix" data-lang="nix"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;boot&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lanzaboote&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;enable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;pkiBundle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;/var/lib/sbctl&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;configurationLimit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;measuredBoot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;enable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;pcrs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt; &lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Finally, you need to perform a &lt;code&gt;nixos-rebuild&lt;/code&gt;, but use &lt;code&gt;boot&lt;/code&gt; instead of &lt;code&gt;switch&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo nixos-rebuild boot
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will prepare the configuration, but it will only be used on the next boot. This is important to ensure that the
boot loader files are correctly signed, in addition to preparing the PCR 7 files.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: &lt;code&gt;systemd-pcrlock&lt;/code&gt; has several subcommands that allow you to &amp;ldquo;lock&amp;rdquo; a specific PCR. &amp;ldquo;Locking,&amp;rdquo; as used here,
means inspecting the events of each PCR and generating &lt;code&gt;.pcrlock&lt;/code&gt; files in the &lt;code&gt;/var/lib/pcrlock.d&lt;/code&gt; directory. For
example, &lt;code&gt;systemd-pcrlock lock-firmware-code&lt;/code&gt; generates the file
&lt;code&gt;/var/lib/pcrlock.d/250-firmware-code-early.pcrlock.d/generated.pcrlock&lt;/code&gt; with PCR 0 and 2 measurements. Lanzaboote uses
this system when you enable PCR 7 by running &lt;code&gt;systemd-pcrlock lock-secureboot-authority&lt;/code&gt; and &lt;code&gt;systemd-pcrlock lock-secureboot-policy&lt;/code&gt;. This is done by creating two oneshot systemd services: &lt;code&gt;systemd-pcrlock-secureboot-authority&lt;/code&gt;
and &lt;code&gt;systemd-pcrlock-secureboot-policy&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Reboot the system. The LUKS key is still tied only to PCR 7, and the disks will be decrypted automatically. After the
reboot, the policy file will have been created. You can verify if both PCRs are present in the file by running:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; jq &lt;span class="s1"&gt;&amp;#39;.pcrValues.[].pcr&amp;#39;&lt;/span&gt; /var/lib/systemd/pcrlock.json
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;4
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;7
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If this is correct, you can now remove the token from the disk headers that was tied only to PCR 7 and create a new one
using the policy via the generated file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# remove previous token and enroll new token using the policy on disk 1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemd-cryptenroll --tpm2-device&lt;span class="o"&gt;=&lt;/span&gt;auto --wipe-slot&lt;span class="o"&gt;=&lt;/span&gt;tpm2 --tpm2-pcrlock&lt;span class="o"&gt;=&lt;/span&gt;/var/lib/systemd/pcrlock.json /dev/disk/by-label/NIXLUKS1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# remove previous token and enroll new token using the policy on disk 2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemd-cryptenroll --tpm2-device&lt;span class="o"&gt;=&lt;/span&gt;auto --wipe-slot&lt;span class="o"&gt;=&lt;/span&gt;tpm2 --tpm2-pcrlock&lt;span class="o"&gt;=&lt;/span&gt;/var/lib/systemd/pcrlock.json /dev/disk/by-label/NIXLUKS2
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With this, you can reboot again. The disk should continue to unlock automatically, but this time it is using the policy
file, and therefore PCRs 4 and 7.&lt;/p&gt;
&lt;p&gt;The functionality is now complete. Whenever the boot loader files are updated, the policy will also be updated, and the
disk will be decrypted automatically without requiring any manual interaction. In the following sections, I will dive
deeper into what is happening.&lt;/p&gt;
&lt;h3 id="inspecting-tpm-events"&gt;
 &lt;a href="#inspecting-tpm-events" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Inspecting TPM events&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;You can check TPM events by running &lt;code&gt;sudo systemd-pcrlock log&lt;/code&gt;, or just &lt;code&gt;sudo systemd-pcrlock&lt;/code&gt;. Unfortunately, the
command doesn&amp;rsquo;t allow showing events for a specific PCR, so you can do it using JSON and &lt;code&gt;jq&lt;/code&gt;. To see only PCR 4 events,
run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemd-pcrlock log --json&lt;span class="o"&gt;=&lt;/span&gt;short 2&amp;gt;/dev/null &lt;span class="p"&gt;|&lt;/span&gt; jq &lt;span class="s1"&gt;&amp;#39;[.log[] | select(.pcr == 4)]&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To see the components registered in &lt;code&gt;systemd-pcrlock&lt;/code&gt;, use the &lt;code&gt;list-components&lt;/code&gt; command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo systemd-pcrlock list-components
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;ID VARIANTS
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;240-secureboot-policy /var/lib/pcrlock.d/240-secureboot-policy.pcrlock.d/generated.pcrlock
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;350-action-efi-application /nix/store/f4s218swc4kw35apcqg9al8d7s287x7y-pcrlock.d/350-action-efi-application.pcrlock
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;400-secureboot-separator /nix/store/f4s218swc4kw35apcqg9al8d7s287x7y-pcrlock.d/400-secureboot-separator.pcrlock.d/300-0x00000000.pcrlock
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;500-separator /nix/store/f4s218swc4kw35apcqg9al8d7s287x7y-pcrlock.d/500-separator.pcrlock.d/300-0x00000000.pcrlock
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;620-secureboot-authority /var/lib/pcrlock.d/620-secureboot-authority.pcrlock.d/generated.pcrlock
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;630-bootloader /var/lib/pcrlock.d/630-bootloader.pcrlock.d/current.pcrlock
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;635-lanzaboote /var/lib/pcrlock.d/635-lanzaboote.pcrlock.d/7.pcrlock
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;These components are used to predict the values of each PCR. They are what &amp;ldquo;lock&amp;rdquo; the TPM. The idea is that
&lt;code&gt;systemd-pcrlock lock-*&lt;/code&gt; commands check TPM events and generate these components, along with Lanzaboote generating the
PCR 4 (boot loader) ones at every &lt;code&gt;nixos-rebuild&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Finally, you can check the measurements being used by running &lt;code&gt;systemd-pcrlock predict&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo systemd-pcrlock predict --pcr&lt;span class="o"&gt;=&lt;/span&gt;4,7
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Event log record 0 (PCR 0, &amp;#34;Raw: 2\0008\000\000\000&amp;#34;) not matching any component.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Event log record 9 (PCR 1, &amp;#34;Raw: ACPI DATA&amp;#34;) not matching any component.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Event log record 13 (PCR 2, &amp;#34;Raw: \030\377\004\000&amp;#34;) not matching any component.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Event log record 27 (PCR 5, &amp;#34;GPT: disk 8118a150-5781-4514-94f0-517c327f4ca7&amp;#34;) not matching any component.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Event log record 40 (PCR 9, &amp;#34;Linux: kernel command line&amp;#34;) not matching any component.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Event log record 31 (PCR 11, &amp;#34;String: .linux&amp;#34;) not matching any component.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Event log record 39 (PCR 12, &amp;#34;String: Global credentials initrd&amp;#34;) not matching any component.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Event log record 44 (PCR 15, &amp;#34;cryptsetup:crypt_disk1:5a2876f5-9220-4040-8b56-7bc226c9d649&amp;#34;) not matching any component.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;3 combinations of components.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;PCR 4 (boot-loader-code) matches event log and fully consists of recognized measurements. Including in set of PCRs.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;PCR 7 (secure-boot-policy) matches event log and fully consists of recognized measurements. Including in set of PCRs.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;PCRs in protection mask: 4 (boot-loader-code), 7 (secure-boot-policy)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Results for PCR 4 (boot-loader-code):
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; sha256: 19d6c3ffa28c30062e84d8e90a357a6319ce921dfa15ee6618a5f9c38a5e9890
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; sha256: 58d68b2aa60262d9831fdd1b8b0f50fd30610162bf3ae7f19d4722e21ba24277
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; sha256: 480319eadf7ad52de18d44758204505cf34a4def787c762e1da3024a69b2ec8e
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Results for PCR 7 (secure-boot-policy):
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; sha256: cc1dc9cbd11e3ad0a695744ab152362d388e2e97f165117e3e45b8248f4a5078
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The initial lines show event records that were not recognized. Since &lt;code&gt;predict&lt;/code&gt; only generates predictions for PCRs whose
component coverage is complete, those PCRs are excluded from this specific policy. If we wanted to use them, some could
have components created by running &lt;code&gt;systemd-pcrlock lock-*&lt;/code&gt; commands, while others would need manual component creation.&lt;/p&gt;
&lt;p&gt;Lastly, let&amp;rsquo;s compare the PCR 7 logs used in the
&lt;a href="https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-5-destrancando-o-disco-com-tpm/#understanding-the-tpm"&gt;fifth post&lt;/a&gt;.
The only difference is the inclusion of the component field (which &amp;ldquo;locks&amp;rdquo; the PCR), highlighted below:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemd-pcrlock log --json&lt;span class="o"&gt;=&lt;/span&gt;short 2&amp;gt;/dev/null &lt;span class="p"&gt;|&lt;/span&gt; jq &lt;span class="s1"&gt;&amp;#39;[.log[] | select(.pcr == 7)]&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;pcr&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;pcrname&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;secure-boot-policy&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;event&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;efi-variable-driver-config&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;match&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;sha256&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;9f75b6823bff6af1024a4e2036719cdd548d3cbc2bf1de8e7ef4d0ed01f94bf9&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;phase&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;F&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;component&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;240-secureboot-policy&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Variable: dbx-d719b2cb-3d3a-4596-a3bc-dad00e67656f&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;pcr&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;pcrname&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;secure-boot-policy&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;event&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;efi-variable-authority&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;match&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;sha256&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;f35567246a92b2fcdd2901e4ad2febdfd80173f25527c5a73033bf100a8eb980&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;phase&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;F&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;component&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;620-secureboot-authority&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Authority: db-d719b2cb-3d3a-4596-a3bc-dad00e67656f&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="evaluating-the-luks-header-token"&gt;
 &lt;a href="#evaluating-the-luks-header-token" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Evaluating the LUKS header token&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;We can also compare the LUKS header, which was updated when we ran &lt;code&gt;systemd-cryptenroll&lt;/code&gt; with the &lt;code&gt;--wipe-slot&lt;/code&gt; argument
and then &lt;code&gt;--tpm2-pcrlock&lt;/code&gt;. In the
&lt;a href="https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-5-destrancando-o-disco-com-tpm/#unlocking-the-disk-during-boot"&gt;fifth post&lt;/a&gt;,
the policy wasn&amp;rsquo;t used, so the &lt;code&gt;tpm-pcrs&lt;/code&gt; field (a list) had the value &lt;code&gt;7&lt;/code&gt;, and now it&amp;rsquo;s empty. The &lt;code&gt;tpm2-pcr-bank&lt;/code&gt;
field is no longer used, and the &lt;code&gt;tpm2_pcrlock_nv&lt;/code&gt; field now has a hash:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo cryptsetup luksDump --dump-json-metadata /dev/disk/by-label/NIXLUKS1 &lt;span class="p"&gt;|&lt;/span&gt; jq -r &lt;span class="s1"&gt;&amp;#39;.tokens.&amp;#34;0&amp;#34;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;systemd-tpm2&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;keyslots&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;tpm2-blob&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;lt;a base64 value&amp;gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;tpm2-pcrs&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;tpm2-policy-hash&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;lt;a hash&amp;gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;tpm2_pcrlock&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;tpm2_srk&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;lt;another base64 value&amp;gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;tpm2_pcrlock_nv&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;lt;yet another base64 value&amp;gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This last field, ending in &lt;code&gt;_nv&lt;/code&gt;, is especially important. It points to a non-volatile area of the TPM, which is not
erased when the computer turns off. The pcrlock policy data is stored there, and if the TPM is cleared, it forces the
recreation of the policy (more on this below).&lt;/p&gt;
&lt;h3 id="removing-the-tpm-policy"&gt;
 &lt;a href="#removing-the-tpm-policy" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Removing the TPM policy&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;If you ever remove the NixOS TPM measurement settings (&lt;code&gt;boot.lanzaboote.measuredBoot&lt;/code&gt;), you will need to remove the
policy manually. It is registered in the aforementioned policy file, but also in the TPM&amp;rsquo;s non-volatile memory. This
should be automated by Lanzaboote (I opened &lt;a href="https://github.com/nix-community/lanzaboote/issues/600"&gt;an issue&lt;/a&gt;), but
from what I&amp;rsquo;ve seen, it isn&amp;rsquo;t yet. After running &lt;code&gt;nixos-rebuild switch&lt;/code&gt;, run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# remove the policy from the TPM&amp;#39;s non-volatile memory and also the files:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# /var/lib/systemd/pcrlock.json and&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# /boot/loader/credentials/pcrlock.nixos.cred&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemd-pcrlock remove-policy
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Don&amp;rsquo;t forget to remove the TPM keyslot from the LUKS volume headers:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemd-cryptenroll --wipe-slot&lt;span class="o"&gt;=&lt;/span&gt;tpm2 /dev/disk/by-label/NIXLUKS1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemd-cryptenroll --wipe-slot&lt;span class="o"&gt;=&lt;/span&gt;tpm2 /dev/disk/by-label/NIXLUKS2
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="dealing-with-a-clearedwiped-tpm"&gt;
 &lt;a href="#dealing-with-a-clearedwiped-tpm" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Dealing with a cleared/wiped TPM&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;If the TPM&amp;rsquo;s non-volatile memory is wiped, the reference to the TPM policy registered in the LUKS volume header will
become outdated, pointing to a record that no longer exists. This will force us to re-enroll the policy into the LUKS
header. You can test this safely by clearing the TPM directly from Linux. Run the following commands and reboot:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nix-shell -p tpm2-tools
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo tpm2 clear
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;exit&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As a result, the operation to decrypt the disk using the TPM during boot will fail, and you will need to decrypt using
your password. To re-enable automatic TPM decryption, you first need to remove the policy from the disk (the
&lt;code&gt;/var/lib/systemd/pcrlock.json&lt;/code&gt; and &lt;code&gt;/boot/loader/credentials/pcrlock.nixos.cred&lt;/code&gt; files—explained in the previous
section), run &lt;code&gt;nixos-rebuild boot&lt;/code&gt;, and reboot to recreate the wiped policy. Then, redo the LUKS header with the
&lt;code&gt;systemd-cryptenroll --wipe-slot&lt;/code&gt; and &lt;code&gt;systemd-cryptenroll --tpm2-pcrlock&lt;/code&gt; commands:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemd-pcrlock remove-policy
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemd-cryptenroll --wipe-slot&lt;span class="o"&gt;=&lt;/span&gt;tpm2 /dev/disk/by-label/NIXLUKS1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemd-cryptenroll --wipe-slot&lt;span class="o"&gt;=&lt;/span&gt;tpm2 /dev/disk/by-label/NIXLUKS2
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo nixos-rebuild boot
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# reboot, enter LUKS password&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;jq &lt;span class="s1"&gt;&amp;#39;.pcrValues.[].pcr&amp;#39;&lt;/span&gt; /var/lib/systemd/pcrlock.json &lt;span class="c1"&gt;# validate that the result is: 4 7&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemd-cryptenroll --tpm2-device&lt;span class="o"&gt;=&lt;/span&gt;auto --tpm2-pcrlock&lt;span class="o"&gt;=&lt;/span&gt;/var/lib/systemd/pcrlock.json /dev/disk/by-label/NIXLUKS1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemd-cryptenroll --tpm2-device&lt;span class="o"&gt;=&lt;/span&gt;auto --tpm2-pcrlock&lt;span class="o"&gt;=&lt;/span&gt;/var/lib/systemd/pcrlock.json /dev/disk/by-label/NIXLUKS2
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Reboot once more, and you should no longer be prompted for a password.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ve already seen these commands in this article, so I won&amp;rsquo;t detail them further.&lt;/p&gt;
&lt;h3 id="conclusion"&gt;
 &lt;a href="#conclusion" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Conclusion&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;With this, your operating system is now protected, and you have the convenience of a system that starts without
constantly asking for a decryption password. Better yet, since everything was done using NixOS, the configuration is
ready to be reused: if it worked once, it will work forever.&lt;/p&gt;
&lt;p&gt;The series was supposed to end here, but since I wrote this post, a few days have passed and I&amp;rsquo;ve used everything I
wrote here to migrate my own machine from Ubuntu to NixOS. I learned a lot and have a few more things to share, so the
series will have at least one more post.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;ve followed along this far, ran a proof of concept with the examples, or have ideas or criticisms, leave a
comment so I know!&lt;/p&gt;</description></item><item><title>NixOS: Installation Guide with RAID 1, encryption, and TPM Unlock (part 6 - Mitigating the volume swap attack)</title><link>https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-6-mitigando-o-ataque-de-troca-de-volume/</link><pubDate>Tue, 26 May 2026 10:00:00 -0300</pubDate><author>giggio@giggio.net (Giovanni Bassi)</author><guid>https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-6-mitigando-o-ataque-de-troca-de-volume/</guid><category>infra</category><description>&lt;p&gt;The NixOS disk is encrypted, but a careful LUKS volume swap attack can still be used to obtain the encryption master key.&lt;/p&gt;
&lt;p&gt;In this post, I show how to mitigate it!&lt;/p&gt;
&lt;p&gt;This is the sixth post in the series:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-1/"&gt;Preparing the virtual machine and partitioning the disks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-2-disko-luks-e-btrfs/"&gt;Disko, LUKS, and btrfs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-3-instalando-o-so/"&gt;Installing the OS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-4-secure-boot/"&gt;Enabling Secure Boot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-5-destrancando-o-disco-com-tpm/"&gt;Unlocking the disk with TPM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mitigating the volume swap attack (this post)&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;As I said in the previous post, this kind of attack would not be carried out by an average person. The computer shop
technician who is repairing your laptop probably will not be able to read your LUKS encrypted disk and with the key
sealed with the help of PCR 7. But someone who knows what they are doing will find the weakness. This post and the next
one are for those who want to be fully protected, both from the technician at the shop around the corner and from a
hostile government or corporate agent. By the end of these two posts, the attacks described here will be mitigated,
significantly increasing security (keeping in mind that it is always prudent to consider other attack surfaces outside
these scenarios).&lt;/p&gt;
&lt;h3 id="the-problem"&gt;
 &lt;a href="#the-problem" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;The problem&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;The attack
&lt;a href="https://oddlama.org/blog/bypassing-disk-encryption-with-tpm2-unlock/"&gt;was described by Oddlama&lt;/a&gt;, and it basically
consists of removing the disk from the original computer, creating a fake encrypted partition with the same
identification data as the original partition, and then using that to obtain the key. He even describes how to do this
against NixOS.&lt;/p&gt;
&lt;p&gt;This is possible because the UKI (Unified Kernel Image — the binary loaded by UEFI that contains the kernel and the
initrd) is not encrypted — if it were, UEFI would not be able to load it. The LUKS volume header is also not encrypted,
since it must be read by the system in initrd so that the LUKS volume can be decrypted. It is possible to inspect what
will be loaded during boot and replace the LUKS volume with another one carefully prepared to steal the master key from
the original LUKS volume.&lt;/p&gt;
&lt;p&gt;I will not go into more detail because the explanation is long. You can read Oddlama’s post; it is quite interesting.
The important point is that this attack is only possible because the UKI does not validate the identity of the LUKS
volume, which allows it to be replaced.&lt;/p&gt;
&lt;h3 id="closing-the-breach"&gt;
 &lt;a href="#closing-the-breach" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Closing the breach&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;To solve the problem, we just need to start validating the decrypted volume before proceeding, something that is not
being done yet and will be addressed using PCR 15 (&lt;code&gt;system-identity&lt;/code&gt;). In the previous post, I showed how to list the
hashes of all PCRs with &lt;code&gt;systemd-analyze pcrs&lt;/code&gt;. Notice that PCR 15 was zeroed out, meaning it had not been extended.
systemd can start writing to it by simply enabling &lt;code&gt;tpm2-measure-pcr=yes&lt;/code&gt; for &lt;code&gt;systemd-cryptsetup&lt;/code&gt; during the boot
process.&lt;/p&gt;
&lt;p&gt;To start writing to PCR 15 in the NixOS way, we need a Nix configuration. Fortunately, Oddlama himself provided the
solution, pointing to
&lt;a href="https://forge.lal.lol/patrick/nix-config/src/branch/master/modules/ensure-pcr.nix"&gt;a Forgejo repository by PatrickDaG&lt;/a&gt;,
with the file &lt;code&gt;ensure-pcr.nix&lt;/code&gt;, which
&lt;a href="https://codeberg.org/giggio/nixos_raid_btrfs_luks_tpm/src/branch/main/ensure-pcr.nix"&gt;has already been adapted in the repository&lt;/a&gt;
cloned into &lt;code&gt;/etc/nixos&lt;/code&gt;, and which makes it possible to read this PCR through a NixOS module.&lt;/p&gt;
&lt;p&gt;I incorporated this module into my solution, which made it very easy to apply. The example commit is
&lt;a href="https://codeberg.org/giggio/nixos_raid_btrfs_luks_tpm/commit/f690b8882df0993f05c3ad849fe42ebc7676e8d1"&gt;f690b88&lt;/a&gt;,
described as &amp;ldquo;Enable PCR15&amp;rdquo;, but we do not need to check it out; it is enough to update the configuration in
&lt;code&gt;/etc/nixos/configuration.nix&lt;/code&gt; (it is already in the code, just remove the comment):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-nix" data-lang="nix"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;systemIdentity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;enable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then apply it:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nixos-rebuild switch
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Restart the virtual machine, and after the reboot, confirm that the value of PCR 15 is present (the value below is just
an example — replaced by a sequence from 0 to 9):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; systemd-analyze pcrs &lt;span class="m"&gt;15&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;NR NAME SHA256
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;15 system-identity 0123456789012345678901234567890123456789012345678901234567890123
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That is the hash of the system identity (derived from the activated LUKS volume key); it is what will ensure that the
unlocked volume is the expected one, reducing the volume swap attack.&lt;/p&gt;
&lt;p&gt;The value found must be copied into the &lt;code&gt;pcr15&lt;/code&gt; field in the configuration. For example, using the fictitious sequence
above from 0 to 9:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-nix" data-lang="nix"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;systemIdentity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;enable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;pcr15&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;0123456789012345678901234567890123456789012345678901234567890123&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Change this value, reboot once more, and everything should continue working.&lt;/p&gt;
&lt;h3 id="how-this-solution-prevents-volume-swapping"&gt;
 &lt;a href="#how-this-solution-prevents-volume-swapping" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;How this solution prevents volume swapping&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;The explanation is in the Nix module defined in the file
&lt;a href="https://codeberg.org/giggio/nixos_raid_btrfs_luks_tpm/src/branch/main/ensure-pcr.nix"&gt;ensure-pcr.nix&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It creates a systemd service that starts extending PCR 15 when &lt;code&gt;systemIdentity.enable&lt;/code&gt; is enabled, running:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemd-cryptsetup attach crypt_disk1 /dev/disk/by-partlabel/NIXLUKS1 - &lt;span class="s1"&gt;&amp;#39;tpm2-device=auto,tpm2-measure-pcr=yes,discard&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And when the hash of PCR 15 has been defined in &lt;code&gt;systemIdentity.pcr15&lt;/code&gt;, it creates a &lt;code&gt;check-pcrs&lt;/code&gt; service that runs in
initrd and, if it fails, prevents boot from continuing. This service is very simple: it just compares the hash stored in
the configuration with the value found in PCR 15:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="k"&gt;$(&lt;/span&gt;systemd-analyze pcrs &lt;span class="m"&gt;15&lt;/span&gt; --json&lt;span class="o"&gt;=&lt;/span&gt;short &lt;span class="p"&gt;|&lt;/span&gt; jq -r &lt;span class="s2"&gt;&amp;#34;.[0].sha256&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt; !&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.systemIdentity.pcr15&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;PCR 15 check failed&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;PCR 15 check succeed&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As this service is required by &lt;code&gt;sysroot.mount&lt;/code&gt;, if &lt;code&gt;check-pcrs&lt;/code&gt; fails the root disk cannot be mounted and the entire
boot fails, entering emergency mode.&lt;/p&gt;
&lt;h3 id="what-if-the-boot-fails"&gt;
 &lt;a href="#what-if-the-boot-fails" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;What if the boot fails?&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;If the value of PCR 15 changes for any reason, the boot will fail and enter emergency mode. In that case, you will need
to log in with the emergency password configured in initrd and run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl disable check-pcrs
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl default
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: see the &lt;code&gt;boot.initrd.systemd.emergencyAccess&lt;/code&gt; attribute in &lt;code&gt;configuration.nix&lt;/code&gt;, that is what defines the
emergency initrd password (using a hash of the password). To create the password as &lt;code&gt;test&lt;/code&gt;, run (replace the salt,
&lt;code&gt;xyz&lt;/code&gt;, and the password):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;openssl passwd -6 -salt xyz &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Tip&lt;/strong&gt;: I recommend that you test this by changing the configuration value to the wrong hash, running &lt;code&gt;switch&lt;/code&gt;, and
confirming that the boot failed. Then test the commands above: you will need to provide the initrd emergency password,
disable the &lt;code&gt;check-pcrs&lt;/code&gt; service, and continue the boot. In the worst-case scenario, if you cannot do that, you can
choose the previous configuration from the boot menu. After logging in again, set the PCR 15 value in the configuration
back to the correct one, run a new &lt;code&gt;switch&lt;/code&gt; and reboot, making sure that boot works again without issues.&lt;/p&gt;
&lt;h3 id="is-it-secure"&gt;
 &lt;a href="#is-it-secure" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Is it secure?&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Again: not yet.&lt;/p&gt;
&lt;p&gt;The attack described by Oddlama no longer works, but there is still another one: since we only bind the disk encryption
to PCRs 7 and 15, it is still possible to replace the operating system with another one and use the TPM to decrypt the
disk. We will solve that in the next post.&lt;/p&gt;</description></item><item><title>NixOS: Installation Guide with RAID 1, encryption, and TPM Unlock (part 5 - unlocking the disk with TPM)</title><link>https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-5-destrancando-o-disco-com-tpm/</link><pubDate>Mon, 18 May 2026 11:00:00 -0300</pubDate><author>giggio@giggio.net (Giovanni Bassi)</author><guid>https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-5-destrancando-o-disco-com-tpm/</guid><category>infra</category><description>&lt;p&gt;At last, we are going to automatically decrypt the NixOS disk using the TPM!&lt;/p&gt;
&lt;p&gt;This is the fifth post in the series:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-1/"&gt;Preparing the virtual machine and partitioning the disks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-2-disko-luks-e-btrfs/"&gt;Disko, LUKS, and btrfs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-3-instalando-o-so/"&gt;Installing the OS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-4-secure-boot/"&gt;Enabling Secure Boot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Unlocking the disk with TPM (this post)&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Up to now, every boot has required the LUKS password to decrypt the disk. But if the machine’s integrity has not been
compromised, there is no problem in doing this automatically. But how?&lt;/p&gt;
&lt;p&gt;Now it is time to orchestrate everything we have put together so far, connecting LUKS, Secure Boot, and the TPM.&lt;/p&gt;
&lt;h3 id="why-this-is-safe"&gt;
 &lt;a href="#why-this-is-safe" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Why this is safe&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Assuming the machine has not been compromised, when the operating system comes up, the only safe way to access it is
with valid credentials — assuming the system has no exploitable vulnerability. That means logging in through the
console, SSH, the graphical interface, or a serial connection. In practice, that means that without a login password
there is no access to the computer, and no privilege is gained.&lt;/p&gt;
&lt;p&gt;Everything works based on this principle: an uncompromised and intact system protects itself, and if it is compromised
(firmware modification, disk removal, etc), encryption protects the data.&lt;/p&gt;
&lt;h3 id="understanding-the-tpm"&gt;
 &lt;a href="#understanding-the-tpm" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Understanding the TPM&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;The TPM (Trusted Platform Module) is a dedicated processor that stores, in an inviolable way, measurements taken of the
hardware and software running on the computer. These measurements are stored in &lt;strong&gt;PCRs&lt;/strong&gt; (Platform Configuration
Registers), whose values can be extended, but never set directly. Every value sent to the TPM extends the previous
value and produces a new one. If we were to model this as a formula, it would be something like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;PCR = HASH(PCR || new_measurement)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In other words, the PCR hash is used together with the new measured value to create the new hash.&lt;/p&gt;
&lt;p&gt;You can see the hashes of each PCR on your running TPM (fictitious values):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; systemd-analyze pcrs
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;NR NAME SHA256
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; 0 platform-code 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; 1 platform-config 123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; 2 external-code 23456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef01
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; 3 external-config 3456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef012
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; 4 boot-loader-code 456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; 5 boot-loader-config 56789abcdef0123456789abcdef0123456789abcdef0123456789abcdef01234
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; 6 host-platform 6789abcdef0123456789abcdef0123456789abcdef0123456789abcdef012345
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; 7 secure-boot-policy 789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; 8 - 89abcdef0123456789abcdef0123456789abcdef0123456789abcdef01234567
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; 9 kernel-initrd 9abcdef0123456789abcdef0123456789abcdef0123456789abcdef012345678
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;10 ima abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;11 kernel-boot 0000000000000000000000000000000000000000000000000000000000000000
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;12 kernel-config 0000000000000000000000000000000000000000000000000000000000000000
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;13 sysexts 0000000000000000000000000000000000000000000000000000000000000000
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;14 shim-policy bcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789a
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;15 system-identity 0000000000000000000000000000000000000000000000000000000000000000
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;16 debug 0000000000000000000000000000000000000000000000000000000000000000
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;17 - ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;18 - ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;19 - ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;20 - ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;21 - ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;22 - ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;23 application-support 0000000000000000000000000000000000000000000000000000000000000000
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Each PCR is independent and is extended freely, without depending on the others. On compatible TPMs there are 24 PCRs;
the &lt;a href="https://uapi-group.org/"&gt;UAPI (Linux Userspace API Group)&lt;/a&gt; in
&lt;a href="https://uapi-group.org/specifications/specs/linux_tpm_pcr_registry/"&gt;its specifications&lt;/a&gt; considers 0–7 to be firmware
and 8–15 to be the range available to the operating system, although the systemd ecosystem also uses specific PCRs for
additional measurements, and PCRs 16–23 are also usable.&lt;/p&gt;
&lt;p&gt;When the computer powers on, all PCRs start out with no value. The firmware then begins extending the PCRs. For example,
PCR 0 depends on the software running in the firmware — what we normally call the BIOS. When you “update the BIOS”,
PCR 0 changes. PCR 1 may change if you replace a hardware component. PCR 4 is tied to the operating system; it measures
the boot loader and additional drivers. And so on.&lt;/p&gt;
&lt;p&gt;PCR 7 depends on the Secure Boot state. Its values are commonly extended using the keys enrolled in Secure Boot, as well
as the Secure Boot Authority. It is this PCR that we will use to unlock the disk, at this moment.&lt;/p&gt;
&lt;p&gt;I will come back to PCRs in the next posts, but it is important to understand what they are for. If you decide to use
what I am proposing in these posts, this knowledge may be useful if you run into trouble.&lt;/p&gt;
&lt;p&gt;You can see all the events that led to PCR extensions by running &lt;code&gt;sudo systemd-pcrlock log&lt;/code&gt;. Using &lt;code&gt;jq&lt;/code&gt;, you can show
only the events that led to PCR 7:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo /run/current-system/systemd/lib/systemd/systemd-pcrlock log --json&lt;span class="o"&gt;=&lt;/span&gt;short 2&amp;gt;/dev/null &lt;span class="p"&gt;|&lt;/span&gt; jq &lt;span class="s1"&gt;&amp;#39;[.log[] | select(.pcr == 7)]&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The result, simplified so it does not get too long, is this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;pcr&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;pcrname&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;secure-boot-policy&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;event&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;efi-variable-driver-config&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;match&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;sha256&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;9f75b6823bff6af1024a4e2036719cdd548d3cbc2bf1de8e7ef4d0ed01f94bf9&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;phase&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;F&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;component&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Variable: dbx-d719b2cb-3d3a-4596-a3bc-dad00e67656f&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;pcr&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;pcrname&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;secure-boot-policy&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;event&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;efi-variable-authority&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;match&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;sha256&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;f35567246a92b2fcdd2901e4ad2febdfd80173f25527c5a73033bf100a8eb980&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;phase&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;F&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;component&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Authority: db-d719b2cb-3d3a-4596-a3bc-dad00e67656f&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note that the hash value, stored in the &lt;code&gt;sha256&lt;/code&gt; field, changes with each event. When the TPM receives a request to
decrypt a value, its state must match exactly what was used when the value was encrypted (this is done through TPM
policies). That means a value encrypted early in the boot process may no longer be decryptable at the end of the boot
process, if the PCR has been extended again. This is especially useful for this very reason: allowing a value to be
accessible only at a specific point in the boot sequence, such as during &lt;code&gt;initrd&lt;/code&gt; startup (PCR 11 has measurements that
make this possible).&lt;/p&gt;
&lt;p&gt;Systemd &lt;a href="https://systemd.io/TPM2_PCR_MEASUREMENTS/"&gt;documents&lt;/a&gt; how it uses PCRs, and since nowadays there is practically
no Linux without systemd, it is worth understanding. This document will be useful later, when we look more closely at
PCR 15 (system identity).&lt;/p&gt;
&lt;h3 id="unlocking-the-disk-during-boot"&gt;
 &lt;a href="#unlocking-the-disk-during-boot" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Unlocking the disk during boot&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Only one command is needed to make everything happen. In our case, since we have two disks and two partitions, we run
the command once for each of them (you will need to enter your LUKS password for the process to complete):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemd-cryptenroll --tpm2-device&lt;span class="o"&gt;=&lt;/span&gt;auto --tpm2-pcrs&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;7&lt;/span&gt; /dev/disk/by-label/NIXLUKS1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemd-cryptenroll --tpm2-device&lt;span class="o"&gt;=&lt;/span&gt;auto --tpm2-pcrs&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;7&lt;/span&gt; /dev/disk/by-label/NIXLUKS2
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When this command runs, the volume master key is obtained using your password. It is then encrypted (sealed) using the
TPM, and this sealed value is stored in the LUKS volume header. In this case, only PCR 7 is being used to seal the
value, meaning only the Secure Boot state. That means if the Secure Boot state changes (if it is reset, or if a new key
is removed or added), the TPM’s PCR 7 state will also change, and the disk will no longer be decrypted during boot. In
this post, the goal is to tie automatic disk decryption to the Secure Boot state, and that is what we have done.&lt;/p&gt;
&lt;p&gt;You can see the details stored in the LUKS volume header by running the following command after &lt;code&gt;systemd-cryptenroll&lt;/code&gt;
has been executed:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo cryptsetup luksDump /dev/disk/by-label/NIXLUKS1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Notice the &lt;code&gt;Tokens&lt;/code&gt; field, with the first one being &lt;code&gt;systemd-tpm2&lt;/code&gt;, with a &lt;code&gt;Keyslot&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Tokens:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 0: systemd-tpm2
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Keyslot: 1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To see more details about the sealed value, run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo cryptsetup luksDump --dump-json-metadata /dev/disk/by-label/NIXLUKS1 &lt;span class="p"&gt;|&lt;/span&gt; jq -r &lt;span class="s1"&gt;&amp;#39;.tokens.&amp;#34;0&amp;#34;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The result will look something like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;systemd-tpm2&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;keyslots&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;tpm2-blob&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;lt;a base64 value&amp;gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;tpm2-pcrs&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;tpm2-pcr-bank&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;sha256&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;tpm2-policy-hash&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;lt;a hash&amp;gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;tpm2_srk&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;lt;another base64 value&amp;gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;tpm2-blob&lt;/code&gt; contains the value encrypted by the TPM (plus metadata). Using the tools from the
&lt;a href="https://github.com/tpm2-software/tpm2-tools"&gt;tpm2-tools&lt;/a&gt; package and root access, you could recover the master password
from this value. That is not a security problem: the system is already running and the disk is already decrypted. The
purpose of TPM protection is not to protect a healthy running system from its administrator, but to protect against
integrity violations that happen before, during, and after boot.&lt;/p&gt;
&lt;p&gt;With just these two commands, the TPM will start unlocking both LUKS volumes. Reboot to confirm that it no longer asks
for the password. If it does, inspect the boot logs with &lt;code&gt;sudo journalctl --boot&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="is-it-secure"&gt;
 &lt;a href="#is-it-secure" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Is it secure?&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Not yet.&lt;/p&gt;
&lt;p&gt;There are two vulnerabilities, and I will address them in the next posts. Neither is trivial, but someone who
understands how all of this works could gain access to the data in a short amount of time. None of the work done so far
will be lost; we will build on the current solution. The good news is that both issues are easy to fix.&lt;/p&gt;
&lt;p&gt;That said, it is fair assume that most people, including technicians who are not systems penetration experts, would no
longer be able to read the disk of a computer encrypted this way. In other words, sending a laptop for repair with this
configuration would probably not present a risk. But that is still not good enough.&lt;/p&gt;
&lt;p&gt;In the next post we will start closing the gaps.&lt;/p&gt;</description></item><item><title>NixOS: Installation Guide with RAID 1, encryption, and TPM Unlock (part 4 - Secure Boot)</title><link>https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-4-secure-boot/</link><pubDate>Thu, 14 May 2026 11:00:00 -0300</pubDate><author>giggio@giggio.net (Giovanni Bassi)</author><guid>https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-4-secure-boot/</guid><category>infra</category><description>&lt;p&gt;In the fourth post of our series, we are going to configure Secure Boot to ensure that only trusted operating
systems can be executed.&lt;/p&gt;
&lt;p&gt;This is the fourth post of the series:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-1/"&gt;Preparing the virtual machine and partitioning the disks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-2-disko-luks-e-btrfs/"&gt;Disko, LUKS, and btrfs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-3-instalando-o-so/"&gt;Installing the OS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enabling Secure Boot (this post)&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Secure Boot is a fundamental component for tightening the security, especially when using the TPM to automatically
decrypt the disk. I will explain what it is, what it’s for, and how to implement it in NixOS using Lanzaboote.&lt;/p&gt;
&lt;h3 id="understanding-secure-boot"&gt;
 &lt;a href="#understanding-secure-boot" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Understanding Secure Boot&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Secure Boot is a protocol that is part of the UEFI (Unified Extensible Firmware Interface). When enabled, it verifies if
the operating system&amp;rsquo;s bootloader has a valid digital signature recognized by the keys registered in the motherboard&amp;rsquo;s
firmware.&lt;/p&gt;
&lt;p&gt;This prevents boot-level malware (rootkits) or unauthorized operating systems from loading. Additionally, the TPM uses
the Secure Boot state as one of the metrics to decide whether or not to release the disk encryption keys.&lt;/p&gt;
&lt;p&gt;There are three main ways to manage Secure Boot keys:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Factory default keys:&lt;/strong&gt; Usually the Microsoft keys that come with almost every computer;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Custom keys (User Mode):&lt;/strong&gt; You remove the Microsoft keys and install only your own;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hybrid Mode:&lt;/strong&gt; You keep the Microsoft keys (for dual-booting with Windows) and add your own.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you don&amp;rsquo;t intend to run Windows, the second option is the most restrictive and secure. If you need dual boot, the
third is mandatory. It&amp;rsquo;s worth noting that NixOS does not have a bootloader signed by Microsoft (unlike Ubuntu or
Fedora), so we always need to generate our own keys.&lt;/p&gt;
&lt;p&gt;And why Microsoft keys, specifically? Because they were the first company to push Secure Boot as mandatory, and they
hold the largest share of the PC market.
&lt;a href="https://en.wikipedia.org/wiki/UEFI#Secure_Boot_criticism"&gt;There is quite a bit of controversy regarding this&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The important thing for our setup is knowing that once you define how Secure Boot will be used, it cannot be changed
without breaking the automatic decryption process (which will be done with the help of the TPM). In our case, we will
use the third option (mixing Microsoft keys with our own). If Secure Boot is turned off on the machine or a new platform
key is loaded, the TPM will refuse to decrypt the disk. I’ll explain this in future posts, so for now, what matters is
enabling Secure Boot.&lt;/p&gt;
&lt;h3 id="putting-the-firmware-into-setup-mode"&gt;
 &lt;a href="#putting-the-firmware-into-setup-mode" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Putting the Firmware into Setup Mode&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;In the first post, we cleared the keys in the VM&amp;rsquo;s boot menu to enter Setup Mode. This is essential: for us to write
our keys via software, the firmware must be &amp;ldquo;open&amp;rdquo; to new signatures.&lt;/p&gt;
&lt;p&gt;Confirm the status by running:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo bootctl status
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Secure Boot: disabled (setup)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If &lt;code&gt;disabled (setup)&lt;/code&gt; appears, we are ready to proceed.&lt;/p&gt;
&lt;h3 id="generating-keys-with-sbctl"&gt;
 &lt;a href="#generating-keys-with-sbctl" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Generating Keys with sbctl&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;We will use &lt;a href="https://github.com/Foxboron/sbctl/"&gt;sbctl&lt;/a&gt; to create and register our keys. Since it isn&amp;rsquo;t installed by
default, let&amp;rsquo;s use &lt;code&gt;nix-shell&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nix-shell -p sbctl
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: In NixOS, you can bring any application into your terminal&amp;rsquo;s context using &lt;code&gt;nix-shell&lt;/code&gt;. With this, the &lt;code&gt;sbctl&lt;/code&gt;
command becomes available in a subshell — to exit, just run &lt;code&gt;exit&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To generate the keys, we will run the &lt;code&gt;create-keys&lt;/code&gt; subcommand:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo sbctl create-keys
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The keys will be generated in &lt;code&gt;/var/lib/sbctl/&lt;/code&gt;. You can see the structure with the &lt;code&gt;tree&lt;/code&gt; command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; tree /var/lib/sbctl/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;/var/lib/sbctl/
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;├── GUID
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;└── keys
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; ├── db
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; │   ├── db.key
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; │   └── db.pem
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; ├── KEK
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; │   ├── KEK.key
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; │   └── KEK.pem
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; └── PK
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; ├── PK.key
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; └── PK.pem
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Install the keys into the machine&amp;rsquo;s firmware with &lt;code&gt;enroll-keys&lt;/code&gt;. This will use the keys we created earlier with &lt;code&gt;sbctl&lt;/code&gt;.
With the &lt;code&gt;--microsoft&lt;/code&gt; argument, Microsoft&amp;rsquo;s platform keys will also be installed:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo sbctl enroll-keys --microsoft
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Verify that Secure Boot has been re-enabled with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo bootctl status
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It should show &lt;code&gt;Secure Boot: enabled (user)&lt;/code&gt;. It might not show as enabled yet until the next boot, showing up as
&lt;code&gt;Secure Boot: disabled&lt;/code&gt;, but without &lt;code&gt;setup&lt;/code&gt;. But don&amp;rsquo;t reboot just yet!&lt;/p&gt;
&lt;h3 id="signing-the-boot-with-lanzaboote"&gt;
 &lt;a href="#signing-the-boot-with-lanzaboote" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Signing the Boot with Lanzaboote&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;At this point, if you restart the machine, NixOS will no longer boot, as the NixOS bootloader is not yet signed with the
Secure Boot keys we created in the previous step. That’s where &lt;a href="https://github.com/nix-community/lanzaboote"&gt;Lanzaboote&lt;/a&gt;
comes in. It automates the signing of NixOS generations. It replaces the default &lt;code&gt;systemd-boot&lt;/code&gt; manager with one that
handles the keys generated by &lt;code&gt;sbctl&lt;/code&gt;. Once enabled, whenever a new bootloader is created in the &lt;code&gt;/boot&lt;/code&gt; directory, it
will be signed with the keys located in &lt;code&gt;/var/lib/sbctl&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To enable Lanzaboote and then sign the files, first go to the configuration directory that was copied in previous posts
to &lt;code&gt;/etc/nixos&lt;/code&gt;, and check out the correct commit. Be careful not to forget the &lt;code&gt;git checkout&lt;/code&gt;, or it won&amp;rsquo;t work:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /etc/nixos
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# ensuring the current user owns the files:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo chown -R giggio:users .
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# checkout with the message &amp;#34;Enable lanzaboote&amp;#34;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git checkout dab0fd4
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The change in your &lt;code&gt;configuration.nix&lt;/code&gt; disables the native &lt;code&gt;systemd-boot&lt;/code&gt; and activates &lt;code&gt;lanzaboote&lt;/code&gt;, pointing to the
keys directory:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-nix" data-lang="nix"&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;boot&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loader&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;systemd-boot&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;enable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;boot&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lanzaboote&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;enable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;pkiBundle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;/var/lib/sbctl&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The Lanzaboote configuration was already present; it was just disabled. And with it enabled, &lt;code&gt;systemd-boot&lt;/code&gt; can be
disabled. Note also that the keys directory, defined in the &lt;code&gt;pkiBundle&lt;/code&gt; attribute, is already correctly pointed to
&lt;code&gt;/var/lib/sbctl&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s all; now just install the new configurations, which take effect immediately and will also be effective on the
next boot:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo nixos-rebuild switch
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="validating-the-signature"&gt;
 &lt;a href="#validating-the-signature" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Validating the Signature&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;To ensure Lanzaboote did its job and signed the &lt;code&gt;.efi&lt;/code&gt; binaries, run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo sbctl verify
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Verifying file database and EFI images in /boot...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;✓ /boot/EFI/BOOT/BOOTX64.EFI is signed
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;✓ /boot/EFI/Linux/nixos-generation-1-xxx.efi is signed
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;✓ /boot/EFI/Linux/nixos-generation-2-yyy.efi is signed
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;✗ /boot/EFI/nixos/kernel-7.0-zzz.efi is not signed
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;✓ /boot/EFI/systemd/systemd-bootx64.efi is signed
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Note about the Kernel:&lt;/strong&gt; It is normal to see an &lt;code&gt;✗&lt;/code&gt; on the isolated kernel file. What matters are the files inside
&lt;code&gt;/boot/EFI/&lt;/code&gt; and the boot managers. Lanzaboote creates a &lt;strong&gt;Unified Kernel Image (UKI)&lt;/strong&gt;, which bundles the kernel and
the initrd into a single signed executable that the UEFI can run directly.&lt;/p&gt;
&lt;p&gt;Now you can reboot. If everything went well, the system will come up normally and &lt;code&gt;bootctl status&lt;/code&gt; will confirm: &lt;code&gt;Secure Boot: enabled (user)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The machine still asks for the password to decrypt the disk. This is expected, as we haven&amp;rsquo;t yet implemented the
structure for automatic decryption with the TPM.&lt;/p&gt;
&lt;h3 id="troubleshooting-secure-boot"&gt;
 &lt;a href="#troubleshooting-secure-boot" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Troubleshooting Secure Boot&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;If something goes wrong and the VM won&amp;rsquo;t start, you can reset the NVRAM (the VM&amp;rsquo;s &amp;ldquo;BIOS&amp;rdquo;). On your host, turn off the VM
and run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# on the host:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;virsh start &amp;lt;vm_name&amp;gt; --reset-nvram
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will start the virtual machine normally, but with all the firmware boot settings lost, including Secure Boot
settings.&lt;/p&gt;
&lt;p&gt;The following commands will re-apply the boot settings to the firmware&amp;rsquo;s non-volatile memory and the disk:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# on the vm:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# registering platform keys in the firmware again:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo sbctl enroll-keys --microsoft
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# installing the boot menu option:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo bootctl install
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# re-signing the UKIs:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo nixos-rebuild switch
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Why run switch again&lt;/strong&gt;: When running &lt;code&gt;bootctl install&lt;/code&gt;, the files &lt;code&gt;/boot/EFI/systemd/systemd-bootx64.efi&lt;/code&gt; and
&lt;code&gt;/boot/EFI/BOOT/BOOTX64.EFI&lt;/code&gt; will not be signed, so it is necessary to run &lt;code&gt;switch&lt;/code&gt; so that these two files become
signed again.&lt;/p&gt;
&lt;h3 id="declarative-approach"&gt;
 &lt;a href="#declarative-approach" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Declarative Approach&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Lanzaboote allows generating platform keys automatically via Nix config:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-nix" data-lang="nix"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;boot&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lanzaboote&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;autoGenerateKeys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;enable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;autoEnrollKeys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;enable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;autoReboot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is described in more detail in the
&lt;a href="https://github.com/nix-community/lanzaboote/blob/079c608988c2747db3902c9de033572cd50e8656/docs/explanation/automatic-provisioning.md"&gt;docs on automatic provisioning&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;While practical for testing, I don&amp;rsquo;t recommend using this in production systems. The ideal approach is to generate keys
manually (as I demonstrated here) and store them securely somewhere. A good option is to use
&lt;a href="https://github.com/Mic92/sops-nix/"&gt;sops-nix&lt;/a&gt;, keeping the keys encrypted within the repository itself. The
inconvenience would be during application, where you would always need the keys or devices to decrypt the &lt;code&gt;sops&lt;/code&gt; secrets
at every &lt;code&gt;nixos-rebuild&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="next-up-using-tpm-to-decrypt-the-disk"&gt;
 &lt;a href="#next-up-using-tpm-to-decrypt-the-disk" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Next Up: Using TPM to Decrypt the Disk&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Secure Boot is active. The disk is encrypted. But we still have to type the LUKS password at every boot.&lt;/p&gt;
&lt;p&gt;If someone tries to start another operating system, it will need to be signed with your platform key or Microsoft&amp;rsquo;s. You
can make the system more secure by removing the &lt;code&gt;--microsoft&lt;/code&gt; option from the &lt;code&gt;enroll-keys&lt;/code&gt; command and not allowing any
other operating system.&lt;/p&gt;
&lt;p&gt;In the next post, we will configure the &lt;strong&gt;TPM (Trusted Platform Module)&lt;/strong&gt; so that it &amp;ldquo;measures&amp;rdquo; the Secure Boot state
and releases the encryption key automatically if the system hasn&amp;rsquo;t been tampered with. Maximum security with
convenience.&lt;/p&gt;
&lt;p&gt;See you then!&lt;/p&gt;</description></item><item><title>NixOS: Installation Guide with RAID 1, encryption, and TPM Unlock (part 3 - Installing the OS)</title><link>https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-3-instalando-o-so/</link><pubDate>Mon, 11 May 2026 11:00:00 -0300</pubDate><author>giggio@giggio.net (Giovanni Bassi)</author><guid>https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-3-instalando-o-so/</guid><category>infra</category><description>&lt;p&gt;I’m continuing my journey of setting up a NixOS machine with secure and redundant storage. In this post, we’re going to
perform the actual OS installation!&lt;/p&gt;
&lt;p&gt;This is the third post in the series:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-1/"&gt;Preparing the virtual machine and partitioning the disks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-2-disko-luks-e-btrfs/"&gt;Disko, LUKS, and btrfs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Installing NixOS (this post)&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;At this point, we have a virtual machine with formatted disks and the file system configured and mounted, ready to
receive the NixOS binaries.&lt;/p&gt;
&lt;h3 id="why-arent-we-using-disko-install"&gt;
 &lt;a href="#why-arent-we-using-disko-install" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Why aren&amp;rsquo;t we using disko-install?&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;The Disko project includes a tool called
&lt;a href="https://github.com/nix-community/disko/blob/32f4236bfc141ae930b5ba2fb604f561fed5219d/docs/disko-install.md"&gt;disko-install&lt;/a&gt;
that allows you to format partitions and install NixOS in a single step. The tool is excellent and even allows for disk
parameterization via the command line.&lt;/p&gt;
&lt;p&gt;However, &lt;a href="https://github.com/nix-community/disko/issues/947#issuecomment-4297662288"&gt;a bug&lt;/a&gt; prevents its use when the
machine&amp;rsquo;s RAM is smaller than the size of the system being created. Until this is resolved, we will follow the process
in two stages: disk preparation (previous post) and the manual installation of the operating system (this post).&lt;/p&gt;
&lt;p&gt;But&amp;hellip; if that bug is ever fixed, the command would look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo nix --experimental-features &lt;span class="s2"&gt;&amp;#34;nix-command flakes&amp;#34;&lt;/span&gt; run &lt;span class="s1"&gt;&amp;#39;github:nix-community/disko/latest#disko-install&amp;#39;&lt;/span&gt; -- &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --flake ~/nixos#nixos3 --disk disk1 /dev/vda --disk disk2 /dev/vdb
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="installing-the-operating-system"&gt;
 &lt;a href="#installing-the-operating-system" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Installing the operating system&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;We are starting from the final state of the previous post, with the system mounted at &lt;code&gt;/mnt&lt;/code&gt;. If you restarted the
virtual machine, just run the following command to mount them again without needing to reformat everything:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo nix --experimental-features &lt;span class="s2"&gt;&amp;#34;nix-command flakes&amp;#34;&lt;/span&gt; run github:nix-community/disko/latest -- &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --mode mount ~/nixos/disko.nix
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With everything mounted, we start the NixOS installation by pointing to our flake:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo nixos-install --root /mnt --no-root-passwd --flake ~/nixos#nixos3
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This command will take a few minutes. NixOS will download and build the entire system from scratch within the &lt;code&gt;/mnt&lt;/code&gt;
directory.&lt;/p&gt;
&lt;h4 id="configuration-persistence"&gt;
 &lt;a href="#configuration-persistence" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Configuration Persistence&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h4&gt;
&lt;p&gt;To manage your system in the future (using &lt;code&gt;nixos-rebuild&lt;/code&gt;), we need to move our configuration files into the new system
by copying the repository to your &lt;code&gt;/etc/nixos&lt;/code&gt; directory, which is mounted at &lt;code&gt;/mnt/etc/nixos&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo cp -R ~/nixos /mnt/etc/nixos
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="setting-the-user-password"&gt;
 &lt;a href="#setting-the-user-password" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Setting the User Password&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h4&gt;
&lt;p&gt;Although I left a default password in the settings (&lt;code&gt;luks&lt;/code&gt;) to make the lab easier, let&amp;rsquo;s define the password manually.
We will use &lt;code&gt;nixos-enter&lt;/code&gt;, which creates a chroot environment in the new system:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo nixos-enter --root /mnt -c &lt;span class="s2"&gt;&amp;#34;passwd giggio&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: &lt;code&gt;nixos-enter&lt;/code&gt; is extremely useful for maintenance. It allows you to &amp;ldquo;enter&amp;rdquo; the installed system even before
the first boot to fix configurations or reset passwords.&lt;/p&gt;
&lt;p&gt;NixOS is installed! At this stage, we have partitioned and encrypted disks with a password entered during boot, the
btrfs file system is formatted, and the operating system is installed. Everything should work; shut down the VM, remove
the ISO from your settings, and start the VM again.&lt;/p&gt;
&lt;h3 id="first-access-and-ssh"&gt;
 &lt;a href="#first-access-and-ssh" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;First Access and SSH&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;After booting, log in with the user &lt;code&gt;giggio&lt;/code&gt; and the password defined via &lt;code&gt;passwd&lt;/code&gt; in the previous step.&lt;/p&gt;
&lt;p&gt;To access via SSH from your host machine, remember to clear the old key (from the installation environment) from your
&lt;code&gt;known_hosts&lt;/code&gt; file (created during the first access to the VM):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ssh-keygen -R &amp;lt;ip&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now, connect with the new user:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ssh giggio@&amp;lt;ip&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="what-do-we-have-so-far"&gt;
 &lt;a href="#what-do-we-have-so-far" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;What do we have so far?&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;If you&amp;rsquo;ve never used NixOS, now is the time to experiment. In practice, it’s a modern Linux with GNOME. You’ll see that
it runs with GNOME 49 (with 50 about to be integrated into nixpkgs), and it’s on the latest Kernel version - currently
version 7.0.0.&lt;/p&gt;
&lt;div class="lightbox-gallery text-center"&gt;
 
&lt;a
 href="https://giggio.net/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-3-instalando-o-so/images/nixos_about_hu_5751f340ec6b37f6.webp"
 data-pswp-width="1504"
 data-pswp-height="1135"
 target="_blank"
 class="lightbox "
 data-alt="NixOS &amp;#39;About&amp;#39; window displaying hardware information, Kernel 7.0, and system version."
 data-title="NixOS &amp;#39;About&amp;#39; window displaying hardware information, Kernel 7.0, and system version."
 data-pswp-srcset="https://giggio.net/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-3-instalando-o-so/images/nixos_about_hu_5f6e0fc542a4cb6e.webp 400w, https://giggio.net/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-3-instalando-o-so/images/nixos_about_hu_3232df680cba8cfa.webp 575w, https://giggio.net/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-3-instalando-o-so/images/nixos_about_hu_6dc64499ff2bc03a.webp 767w, https://giggio.net/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-3-instalando-o-so/images/nixos_about_hu_a0e7f14176c848f0.webp 991w, https://giggio.net/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-3-instalando-o-so/images/nixos_about_hu_add9e5087f60aea6.webp 1200w, https://giggio.net/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-3-instalando-o-so/images/nixos_about_hu_661febbe4c129883.webp 1400w, https://giggio.net/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-3-instalando-o-so/images/nixos_about_hu_b05f1e9f70b6c5b5.webp 1504w"
&gt;
 
&lt;img
 src="https://giggio.net/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-3-instalando-o-so/images/nixos_about_hu_5751f340ec6b37f6.webp"
 alt="NixOS &amp;#39;About&amp;#39; window displaying hardware information, Kernel 7.0, and system version."
 srcset="https://giggio.net/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-3-instalando-o-so/images/nixos_about_hu_5f6e0fc542a4cb6e.webp 400w, https://giggio.net/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-3-instalando-o-so/images/nixos_about_hu_3232df680cba8cfa.webp 575w, https://giggio.net/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-3-instalando-o-so/images/nixos_about_hu_6dc64499ff2bc03a.webp 767w, https://giggio.net/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-3-instalando-o-so/images/nixos_about_hu_a0e7f14176c848f0.webp 991w, https://giggio.net/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-3-instalando-o-so/images/nixos_about_hu_add9e5087f60aea6.webp 1200w, https://giggio.net/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-3-instalando-o-so/images/nixos_about_hu_661febbe4c129883.webp 1400w, https://giggio.net/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-3-instalando-o-so/images/nixos_about_hu_b05f1e9f70b6c5b5.webp 1504w"
 sizes="400px, 575px, 767px, 991px, 1200px, 1400px, 1504px"
 
 
 
 
/&gt;

&lt;/a&gt;


&lt;/div&gt;

&lt;p&gt;The system already features RAID 1 via btrfs and LUKS encryption. However, you’ll notice that it still asks for the
encryption password manually at every boot.&lt;/p&gt;
&lt;p&gt;In the next post, we’ll dive deep into Secure Boot, so I’ll stop here to keep the next one from getting too long. It’s
coming soon! See you then!&lt;/p&gt;</description></item><item><title>NixOS: Installation Guide with RAID 1, encryption, and TPM unlock (part 2 - Disko, LUKS, and btrfs)</title><link>https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-2-disko-luks-e-btrfs/</link><pubDate>Wed, 06 May 2026 11:00:00 -0300</pubDate><author>giggio@giggio.net (Giovanni Bassi)</author><guid>https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-2-disko-luks-e-btrfs/</guid><category>infra</category><description>&lt;p&gt;In this post I keep on building a NixOS setup with secure storage, now going deeper into Disko, LUKS, and btrfs.&lt;/p&gt;
&lt;p&gt;This is the second post in the series.
&lt;a href="https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-1/"&gt;Read the first one&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In the previous post I showed how to create the virtual machine and partition the disks using
&lt;a href="https://github.com/nix-community/disko/"&gt;Disko&lt;/a&gt;.
The main command was:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo nix --experimental-features &lt;span class="s2"&gt;&amp;#34;nix-command flakes&amp;#34;&lt;/span&gt; run github:nix-community/disko/latest -- &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --mode destroy,format,mount ~/nixos/disko.nix
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Although the file
&lt;a href="https://codeberg.org/giggio/nixos_raid_btrfs_luks_tpm/src/branch/main/disko.nix"&gt;disko.nix&lt;/a&gt;
is part of a
&lt;a href="https://nixos.wiki/wiki/flakes"&gt;flake&lt;/a&gt;
(a versioned Nix configuration), in this case I ran only the file on its own with &lt;code&gt;nix run&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It is also possible to run only the &lt;code&gt;mount&lt;/code&gt; step, after the disk has already been partitioned and formatted, with
&lt;code&gt;--mode mount&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="what-disko-is"&gt;
 &lt;a href="#what-disko-is" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;What Disko is&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Disko is a project that brings Nix a declarative way to partition and format disks. In Nix, everything is done
declaratively, except disk partitioning and formatting. Disko solves that. Partitioning and formatting disks is done
only at the start of an installation, and that is exactly where the project is useful.&lt;/p&gt;
&lt;p&gt;In addition, Disko provides a NixOS module that lets you use what was declared to define NixOS file systems, which are
normally declared in the file
&lt;a href="https://codeberg.org/giggio/nixos_raid_btrfs_luks_tpm/src/branch/main/hardware-configuration.nix"&gt;hardware-configuration.nix&lt;/a&gt;.
I will talk more about that at the end.&lt;/p&gt;
&lt;p&gt;Disko can also be used in the installation process imperatively, through the command line. That is what I did when
installing
&lt;a href="https://codeberg.org/giggio/nixos_serverbase/src/commit/8e8a356183698c248daa22c1a7be66e900996d7c/modules/lib.nix#L282"&gt;my servers&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="configuring-the-disk-and-its-first-partition"&gt;
 &lt;a href="#configuring-the-disk-and-its-first-partition" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Configuring the disk and its first partition&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;I am working with two disks. The second disk is where all the file system declarations live, so I will focus on it. The
first disk ends up being a bit simpler, since it will be mirrored from the first one
(I will talk about that below, in the &lt;a href="https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-2-disko-luks-e-btrfs/#raid-1-on-btrfs"&gt;RAID section&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;At the beginning I declare the disk, the device I am referring to (&lt;code&gt;/dev/vdb&lt;/code&gt; — that is, the second virtual disk), and
the first partition, which will be the EFI system partition, or &lt;code&gt;ESP&lt;/code&gt;
(&lt;a href="https://en.wikipedia.org/wiki/EFI_system_partition"&gt;EFI System Partition&lt;/a&gt;):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-nix" data-lang="nix"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;disko&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;devices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;disk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# disk1 omitted&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;disk2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;disk&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;device&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;/dev/vdb&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;gpt&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;partitions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;ESP&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;1G&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;EF00&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;EFI2&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;filesystem&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;extraArgs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;-n&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;EFI2&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;format&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;vfat&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;mountpoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;/boot&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;mountOptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;umask=0077&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# crypt_disk2 partition omitted&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note that the mount options are included, and are used both by Disko&amp;rsquo;s &lt;code&gt;mount&lt;/code&gt; command and by the Disko module when
defining file systems.&lt;/p&gt;
&lt;p&gt;In this case, an ESP partition needs to have type &lt;code&gt;EF00&lt;/code&gt; and be formatted as &lt;code&gt;vfat&lt;/code&gt;. I also set the label of the
partition to &lt;code&gt;EFI2&lt;/code&gt;, and the file system label to the same value (with &lt;code&gt;-n EFI&lt;/code&gt;, an argument passed to &lt;code&gt;mkfs.vfat&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;This part will generate something roughly like these commands:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;parted /dev/vdb mklabel gpt
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;parted /dev/vdb mkpart primary 1MiB 1025MiB
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;parted /dev/vdb &lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; esp on
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mkfs.fat -F &lt;span class="m"&gt;32&lt;/span&gt; -n EFI2 /dev/vdb1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# and, for mount:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mount /dev/disk/by-label/EFI2 /boot
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="second-partition-luks--btrfs"&gt;
 &lt;a href="#second-partition-luks--btrfs" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Second partition: LUKS + btrfs&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Next, I need to create the Linux partition, which I want to be encrypted. The encryption will be handled by
&lt;a href="https://gitlab.com/cryptsetup/cryptsetup/"&gt;LUKS&lt;/a&gt;,
so the &lt;code&gt;type&lt;/code&gt; is defined as &lt;code&gt;luks&lt;/code&gt; (in the ESP it was defined as &lt;code&gt;filesystem&lt;/code&gt;). That means the command used to set up the
partition will be &lt;code&gt;cryptsetup luksFormat&lt;/code&gt;, and it is what receives the partition label parameter &lt;code&gt;--label NIXLUKS2&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-nix" data-lang="nix"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;disko&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;devices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;disk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;disk2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;partitions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# ESP partition omitted&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;crypt_disk2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;100%&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;NIXLUKS2&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;luks&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;crypt_disk2&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;extraFormatArgs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;--label&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;NIXLUKS2&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;passwordFile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="sr"&gt;./luks-password.txt&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;allowDiscards&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt; &lt;span class="c1"&gt;# btrfs details omitted&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;};&lt;/span&gt; &lt;span class="c1"&gt;# other curly braces omitted&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;On a real NixOS system the password coming from the &lt;code&gt;luks-password.txt&lt;/code&gt; file would be encrypted. I usually use
&lt;a href="https://github.com/Mic92/sops-nix/"&gt;sops-nix&lt;/a&gt; for that.&lt;/p&gt;
&lt;p&gt;That previous part will generate something roughly like these commands:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;parted /dev/vdb mkpart primary 1025MiB 100%
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;cryptsetup --label&lt;span class="o"&gt;=&lt;/span&gt;NIXLUKS2 luksFormat --type luks2 /dev/vdb2
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# and, for mount:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;cryptsetup open /dev/vdb2 crypt_disk2
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: The password is entered interactively in the commands above.&lt;/p&gt;
&lt;p&gt;Then, in the content part, we define what goes inside the LUKS encrypted volume, in this case, a btrfs file system.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-nix" data-lang="nix"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# beginning omitted&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;partitions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# ESP partition omitted&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;crypt_disk2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# LUKS details omitted&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;btrfs&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;extraArgs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;--label&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;NIXOS&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;-d&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;raid1&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;-m&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;raid1&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;/dev/mapper/crypt_disk1&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;subvolumes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;/@&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;mountpoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;/&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;/@home&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;mountpoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;/home&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;/@root&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;mountpoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;/root&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;/@nix&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;mountpoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;/nix&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This part will generate something roughly like these commands:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;cryptsetup open /dev/vdb2 crypt_disk2
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mkfs.btrfs --label NIXOS -d raid1 -m raid1 /dev/mapper/crypt_disk1 /dev/mapper/crypt_disk2
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mkdir -p /mnt
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mount /dev/mapper/crypt_disk2 /mnt
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;btrfs subvolume create /mnt/@
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;btrfs subvolume create /mnt/@home
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;btrfs subvolume create /mnt/@root
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;btrfs subvolume create /mnt/@nix
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;umount /mnt
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# and, for mount:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mount -o &lt;span class="nv"&gt;subvol&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;@ /dev/mapper/crypt_disk2 /
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mkdir /mnt/&lt;span class="o"&gt;{&lt;/span&gt;home,nix,root,boot&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mount -o &lt;span class="nv"&gt;subvol&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;@home /dev/mapper/crypt_disk2 /home
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mount -o &lt;span class="nv"&gt;subvol&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;@nix /dev/mapper/crypt_disk2 /nix
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mount -o &lt;span class="nv"&gt;subvol&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;@root /dev/mapper/crypt_disk2 /root
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;At the end of the operation, Disko will mount btrfs at &lt;code&gt;/mnt&lt;/code&gt;.
You can verify everything by running:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# list the partitions and file systems&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;lsblk -f
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# list the btrfs subvolumes&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo btrfs subvolume list /mnt
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="raid-1-on-btrfs"&gt;
 &lt;a href="#raid-1-on-btrfs" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;RAID 1 on btrfs&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Notice that I passed the parameters &lt;code&gt;-d raid1 -m raid1 /dev/mapper/crypt_disk1&lt;/code&gt;, which means btrfs will be running in
RAID 1 for both metadata and data, and in parity with the first disk, which has also already been configured for
encryption with LUKS. The reason for defining everything on the second disk is because Disko operates in the order
defined, so when disk 1 is being prepared disk 2 still has not been partitioned.&lt;/p&gt;
&lt;p&gt;In RAID 1, everything written to one disk is immediately copied (mirrored) to the other disks. In this case, we have a
RAID 1 with two disks, controlled directly by the btrfs file system. With Linux, it is very common to do this with
&lt;a href="https://docs.kernel.org/admin-guide/md.html"&gt;mdadm&lt;/a&gt;, but in this case mdadm is unnecessary because btrfs
&lt;a href="https://btrfs.readthedocs.io/en/latest/Volume-management.html"&gt;has native RAID support&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can check the RAID status by running:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;btrfs device stats /mnt
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="subvolumes-on-btrfs"&gt;
 &lt;a href="#subvolumes-on-btrfs" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Subvolumes on btrfs&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;I defined several &lt;a href="https://btrfs.readthedocs.io/en/latest/Subvolumes.html"&gt;btrfs subvolumes&lt;/a&gt;. They are useful because
they let us manage them independently. For example, we can save everything written to a volume using snapshots, as well
as define several settings that on other file systems are available only for partitions, such as quotas, for example.
They can also be nested, inheriting the settings and actions performed within the hierarchy.&lt;/p&gt;
&lt;p&gt;Subvolumes can also be mounted wherever we want, and that is exactly what we did, for example by mounting &lt;code&gt;/home&lt;/code&gt; from
the &lt;code&gt;@home&lt;/code&gt; volume.&lt;/p&gt;
&lt;p&gt;On an already prepared system, this is the list of subvolumes:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo btrfs subvolume list /
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;ID 256 gen 1103 top level 5 path @
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;ID 257 gen 923 top level 5 path @home
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;ID 258 gen 923 top level 5 path @nix
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;ID 259 gen 695 top level 5 path @root
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;ID 260 gen 19 top level 256 path srv
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;ID 261 gen 19 top level 256 path var/lib/portables
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;ID 262 gen 19 top level 256 path var/lib/machines
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;ID 263 gen 1080 top level 256 path tmp
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;ID 264 gen 1080 top level 256 path var/tmp
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The root volume is always &lt;code&gt;5&lt;/code&gt;, which is why the four subvolumes created point to it as the parent subvolume.&lt;/p&gt;
&lt;p&gt;It is still possible to create subvolumes with &lt;code&gt;btrfs subvolume create&lt;/code&gt; and make snapshots with
&lt;code&gt;btrfs subvolume snapshot&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="using-disko-as-a-module"&gt;
 &lt;a href="#using-disko-as-a-module" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Using Disko as a module&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;As I mentioned at the start of the post, Disko lets you use it as a module that declares file systems, without needing
to declare them in the &lt;code&gt;hardware-configuration.nix&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;To use it, just add &lt;code&gt;disko.nixosModules.disko&lt;/code&gt; to the system modules, along with your Disko configuration file, which in
this example is &lt;code&gt;disko.nix&lt;/code&gt;, the same one that was used on its own to create the file system. It looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-nix" data-lang="nix"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;inputs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;nixpkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;github:NixOS/nixpkgs/nixos-unstable&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;disko&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;github:nix-community/disko/latest&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nixpkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;follows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;nixpkgs&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;outputs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nixpkgs&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;disko&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;nixosConfigurations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;nixos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nixpkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nixosSystem&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;system&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;x86_64-linux&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;modules&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="sr"&gt;./configuration.nix&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="sr"&gt;./disko.nix&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;disko&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nixosModules&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;disko&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note that my
&lt;a href="https://codeberg.org/giggio/nixos_raid_btrfs_luks_tpm/src/branch/main/hardware-configuration.nix"&gt;hardware-configuration.nix&lt;/a&gt;
file does not declare any file systems. See
&lt;a href="https://wiki.nixos.org/wiki/Filesystems"&gt;NixOS docs about file systems&lt;/a&gt;
for how this is normally declared, and, because I used Disko, I did not need to do it.&lt;/p&gt;
&lt;p&gt;If I had to do it manually, it would be more or less like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-nix" data-lang="nix"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;fileSystems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;/&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;device&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;/dev/mapper/crypt_disk1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;fsType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;btrfs&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;subvol=@&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;/home&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;device&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;/dev/mapper/crypt_disk1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;fsType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;btrfs&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;subvol=@home&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;/nix&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;device&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;/dev/mapper/crypt_disk1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;fsType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;btrfs&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;subvol=@nix&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;/root&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;device&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;/dev/mapper/crypt_disk1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;fsType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;btrfs&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;subvol=@root&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;/boot&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;device&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;/dev/disk/by-label/EFI1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;fsType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;vfat&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;fmask=0022&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;dmask=0022&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And that only after manually partitioning the main disks with LUKS and formatting them with btrfs.&lt;/p&gt;
&lt;p&gt;It becomes obvious how much Disko simplifies the whole process: we use its definitions to configure the disks during
installation, and later to bring up the operating system itself.&lt;/p&gt;
&lt;h3 id="conclusion"&gt;
 &lt;a href="#conclusion" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Conclusion&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;With a disk and file system declaration that is later used to configure it, it is possible to prepare all the storage
for a future Linux operating system using just one command line.&lt;/p&gt;
&lt;p&gt;Instead of having to keep commands in documentation or build a shell script to prepare everything, we have it all
versioned in executable files.&lt;/p&gt;
&lt;p&gt;In the next post I will show how to finally install NixOS on the file system that was mounted. In the following ones I
will demonstrate how to use TPM to obtain an encryption key that will be used to unlock LUKS without human interaction,
directly at boot.&lt;/p&gt;</description></item><item><title>NixOS: Installation Guide with RAID 1, encryption, and TPM unlock (part 1)</title><link>https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-1/</link><pubDate>Tue, 28 Apr 2026 16:34:00 -0300</pubDate><author>giggio@giggio.net (Giovanni Bassi)</author><guid>https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-1/</guid><category>infra</category><description>&lt;p&gt;It is time to migrate from Ubuntu to NixOS!&lt;/p&gt;
&lt;p&gt;For a long time, I have been using Nix on Ubuntu through
&lt;a href="https://nix-community.github.io/home-manager/"&gt;Home Manager&lt;/a&gt;,
and I have been automating my
&lt;a href="https://codeberg.org/giggio/dotfiles"&gt;dotfiles&lt;/a&gt;
more and more, steadily removing my custom setup and using Nix instead. Recently, I adopted the new
&lt;a href="https://github.com/numtide/system-manager"&gt;System Manager&lt;/a&gt;
project, whose goal is to bring system-level configuration to operating systems other than NixOS, where it was previously
only available.&lt;/p&gt;
&lt;p&gt;After quite a while on that journey, and after already migrating
&lt;a href="https://codeberg.org/giggio/nixos_serverbase"&gt;my home servers&lt;/a&gt;,
it is time to move to NixOS and leave Ubuntu behind.&lt;/p&gt;
&lt;p&gt;I want to live on the edge of what is new and configure my Linux exactly the way I want, and NixOS will let me do that.
I have also come to really like the way it keeps configuration in versioned file systems that I can control in detail.&lt;/p&gt;
&lt;p&gt;This post will show how to meet what I consider the bare minimum for security: I am going to create a NixOS system with
an encrypted disk, mirrored with RAID 1, using the most modern file system available right now (btrfs), and use the TPM
to decrypt the system without human interaction.&lt;/p&gt;
&lt;p&gt;Already know NixOS, TPM, btrfs, and the rest, and just want the code? It is free and available
&lt;a href="https://codeberg.org/giggio/nixos_raid_btrfs_luks_tpm"&gt;in a repository on my Codeberg&lt;/a&gt;.
The README explains what is going on, but I will go into more detail here on the blog, explaining the concepts in more
depth. This will be a series of posts, where I intend to explain each piece.&lt;/p&gt;
&lt;p&gt;The plan is to do something like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Creating the VM and the initial partitions (this post)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-2-disko-luks-e-btrfs/"&gt;More information about Disko, LUKS and btrfs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-3-instalando-o-so/"&gt;Installing NixOS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-4-secure-boot/"&gt;Preparing Secure Boot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-5-destrancando-o-disco-com-tpm/"&gt;Using the TPM to decrypt the disks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-6-mitigando-o-ataque-de-troca-de-volume/"&gt;Mitigating the volume-swapping attack&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-7-mitigando-o-ataque-de-troca-de-so/"&gt;Mitigating the OS-swapping attack&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Review and conclusions&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I am sure the plan will fall apart, but it gives you an idea of what I intend to cover.&lt;/p&gt;
&lt;p&gt;In this first part, we will prepare the VM and the initial partitions.&lt;/p&gt;
&lt;h3 id="preparing-the-virtual-machine"&gt;
 &lt;a href="#preparing-the-virtual-machine" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Preparing the virtual machine&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;I am on Ubuntu 24, using
&lt;a href="https://virt-manager.org/"&gt;Virtual Machine Manager&lt;/a&gt;
5.1, installed through NixOS. Adapt this to your own OS setup.&lt;/p&gt;
&lt;p&gt;Download the
&lt;a href="https://nixos.org/download/#nixos-iso"&gt;NixOS ISO&lt;/a&gt;. I used the graphical ISO, but I imagine the minimal image should work
just fine too.&lt;/p&gt;
&lt;p&gt;I chose to create the disks ahead of time with &lt;code&gt;qemu-img&lt;/code&gt;, because it creates disks that do not consume all the reserved
space right away; they grow as needed (sparse files). I noticed that when creating the disks through the VMM UI, it
creates them with the full size by default, unless you go through the custom storage option, where there is a
checkbox for &amp;ldquo;Allocate entire volume now&amp;rdquo;. In any case, I prefer the command line, so I will leave it here for
reference. In my case, I will keep the VM files under
&lt;code&gt;/mnt/data/vms/vmm&lt;/code&gt;, so adapt that to whatever directory you prefer.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /mnt/data/vms/vmm
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;qemu-img create -f qcow2 nixos1-1.qcow2 200G
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;qemu-img create -f qcow2 nixos1-2.qcow2 200G
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Copy the downloaded &lt;code&gt;.iso&lt;/code&gt; into that same directory.&lt;/p&gt;
&lt;p&gt;Create the virtual machine with those two disks and with the NixOS ISO. Configure the TPM for version 2. Enable the boot
menu. Configure the network as NAT.
If you want to see my configuration, it is available in &lt;a href="https://giggio.net/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-1/files/nixos.xml"&gt;nixos.xml&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Delete all Secure Boot keys and disable Secure Boot in the VM, because the installer does not have a signed &lt;code&gt;.efi&lt;/code&gt;
file and will not boot with Secure Boot enabled.&lt;/p&gt;
&lt;p&gt;To do that: when the VM starts, press &lt;code&gt;ESC&lt;/code&gt; to enter the boot menu. Then choose &amp;ldquo;Device Manager&amp;rdquo; and
&amp;ldquo;Secure Boot Configuration&amp;rdquo;. Select &amp;ldquo;Reset Secure Boot Keys&amp;rdquo; and confirm. That will disable Secure Boot as well. Go
back to the main menu and choose &amp;ldquo;Reset&amp;rdquo;, which will restart the boot process. It is possible that during this process
it will eject the virtual installation CD (&lt;code&gt;.iso&lt;/code&gt;); add it back in the VM settings and restart it.&lt;/p&gt;
&lt;h3 id="starting-the-installation"&gt;
 &lt;a href="#starting-the-installation" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Starting the installation&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Choose one of the installation options. I went with Gnome and the latest Kernel, but I imagine it will make little
difference; all we need is a shell.
When the installer opens, close it and open a terminal.&lt;/p&gt;
&lt;p&gt;From that point on, you can continue from that terminal.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tip&lt;/strong&gt;: I preferred to connect via SSH, since that makes it easier to copy and paste commands into the terminal. To do
that, create a password with &lt;code&gt;passwd&lt;/code&gt;. Find the VM IP with &lt;code&gt;ip a&lt;/code&gt; and connect to it from the host with
&lt;code&gt;ssh nixos@&amp;lt;ip&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="declarative-partitioning-with-disko"&gt;
 &lt;a href="#declarative-partitioning-with-disko" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Declarative partitioning with Disko&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;First, check the current partition status with &lt;code&gt;lsblk -f&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I am using &lt;a href="https://github.com/nix-community/disko/"&gt;Disko&lt;/a&gt; to create the partitions and file systems.&lt;/p&gt;
&lt;p&gt;Clone the repository into &lt;code&gt;~/nixos&lt;/code&gt;: &lt;a href="https://codeberg.org/giggio/nixos_raid_btrfs_luks_tpm"&gt;https://codeberg.org/giggio/nixos_raid_btrfs_luks_tpm&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git clone https://codeberg.org/giggio/nixos_raid_btrfs_luks_tpm.git ~/nixos
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/nixos
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git checkout 397fb28 &lt;span class="c1"&gt;# checkout the commit with message &amp;#34;Updated config&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# create the partitions&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo nix --experimental-features &lt;span class="s2"&gt;&amp;#34;nix-command flakes&amp;#34;&lt;/span&gt; run github:nix-community/disko/latest -- --mode destroy,format,mount ~/nixos/disko.nix
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# check the results&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;lsblk -f
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo fdisk -l /dev/vd&lt;span class="o"&gt;{&lt;/span&gt;a,b&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# or&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo parted /dev/vda print all
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;See the file used by Disko:
&lt;a href="https://codeberg.org/giggio/nixos_raid_btrfs_luks_tpm/src/branch/main/disko.nix"&gt;disko.nix&lt;/a&gt;.
Note that the password is &lt;code&gt;luks&lt;/code&gt;, and it comes from the file
&lt;a href="https://codeberg.org/giggio/nixos_raid_btrfs_luks_tpm/src/branch/main/luks-password.txt"&gt;luks-password.txt&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In the next post, I will explain in more detail what Disko did. For now, I can say that it already created two
partitions on each disk: one partition for EFI (ESP) and another for Linux, which will be encrypted using LUKS, and will
internally use btrfs, with subvolumes already created. I will explain all of that later.&lt;/p&gt;
&lt;p&gt;For now, enjoy figuring out how Disko managed to do all that with just one file. That is the beauty of NixOS: no
endless command typing in the terminal, everything is versioned and declarative.&lt;/p&gt;
&lt;p&gt;See you in the next post.&lt;/p&gt;</description></item></channel></rss>