<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Security on Jon Seager</title><link>https://jnsgr.uk/tags/security/</link><description>Recent content in Security on Jon Seager</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Thu, 26 Mar 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://jnsgr.uk/tags/security/index.xml" rel="self" type="application/rss+xml"/><item><title>ntpd-rs: it's about time!</title><link>https://jnsgr.uk/2026/03/ntpd-rs-its-about-time/</link><pubDate>Thu, 26 Mar 2026 00:00:00 +0000</pubDate><guid>https://jnsgr.uk/2026/03/ntpd-rs-its-about-time/</guid><description>&lt;blockquote&gt;
&lt;p&gt;This article was originally posted &lt;a href="https://discourse.ubuntu.com/t/ntpd-rs-its-about-time/79154" target="_blank" rel="noreferrer"&gt;on the Ubuntu Discourse&lt;/a&gt;, and is reposted here. I welcome comments and further discussion in that thread.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="introduction" class="relative group"&gt;Introduction &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#introduction" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;I am thrilled to announce the next target in our campaign to replace core system utilities with memory-safe Rust rewrites in Ubuntu. In upcoming releases, Ubuntu will be adopting &lt;a href="https://trifectatech.org/projects/ntpd-rs/" target="_blank" rel="noreferrer"&gt;ntpd-rs&lt;/a&gt; as the default time synchronization client and server, eventually replacing &lt;a href="https://chrony-project.org/" target="_blank" rel="noreferrer"&gt;&lt;code&gt;chrony&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://www.linuxptp.org/" target="_blank" rel="noreferrer"&gt;&lt;code&gt;linuxptp&lt;/code&gt;&lt;/a&gt; and with any luck, &lt;a href="https://gpsd.io/" target="_blank" rel="noreferrer"&gt;&lt;code&gt;gpsd&lt;/code&gt;&lt;/a&gt; for time syncing use-cases.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://trifectatech.org/projects/ntpd-rs/" target="_blank" rel="noreferrer"&gt;&lt;code&gt;ntpd-rs&lt;/code&gt;&lt;/a&gt; is a full-featured implementation of the Network Time Protocol (NTP), written entirely in Rust. Maintained by the Trifecta Tech Foundation as part of &lt;a href="https://github.com/pendulum-project" target="_blank" rel="noreferrer"&gt;Project Pendulum&lt;/a&gt;, &lt;code&gt;ntpd-rs&lt;/code&gt; places a strong focus on security, stability, and memory safety.&lt;/p&gt;
&lt;p&gt;To deliver on this goal, we&amp;rsquo;re building on our partnership with the &lt;a href="https://trifectatech.org/" target="_blank" rel="noreferrer"&gt;Trifecta Tech Foundation&lt;/a&gt; who are behind &lt;a href="https://trifectatech.org/projects/sudo-rs/" target="_blank" rel="noreferrer"&gt;sudo-rs&lt;/a&gt;, &lt;a href="https://trifectatech.org/projects/zlib-rs/" target="_blank" rel="noreferrer"&gt;zlib-rs&lt;/a&gt; and more. We will be funding the Trifecta Tech Foundation to build new features, enhance security isolation, and ultimately deliver a unified, memory-safe time synchronization utility for the Linux ecosystem. This work meshes well with the Trifecta Tech Foundations goals to improve the security of time synchronization everywhere.&lt;/p&gt;
&lt;h2 id="ntp-nts-and-ptp" class="relative group"&gt;NTP, NTS, and PTP &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#ntp-nts-and-ptp" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;Before diving into the mechanics and reasoning behind the transition, I wanted to give some background on the protocols at play, and the problems we&amp;rsquo;re hoping to solve. Keeping accurate time is a critical system function, not least because it involves constant interaction with the internet and forms the basis for cryptographic verification in protocols such as Transport Layer Security (TLS).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;NTP (Network Time Protocol)&lt;/strong&gt; is the foundational protocol that most operating systems implement to accurately determine the current time from a network source.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;NTS (Network Time Security)&lt;/strong&gt; is to NTP what HTTPS is to HTTP. Historically, the Network Time Protocol was used unencrypted, like many of the early web protocols. NTS introduces cryptographic security to time synchronization, ensuring that bad actors cannot intercept or spoof time data. We already pushed to make NTS the default out-of-the-box in Ubuntu 25.10, which we accomplished by migrating away from &lt;code&gt;ntpd&lt;/code&gt; to &lt;code&gt;chrony&lt;/code&gt; as the default time-syncing implementation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;PTP (Precision Time Protocol)&lt;/strong&gt; is used for systems that require sub-microsecond synchronization. While the precision offered by a standard NTP deployment is sufficient for general-purpose computing, PTP is often used for complex, specialized deployments like telecommunications networks, power grids, and automotive applications.&lt;/p&gt;
&lt;h2 id="proven-at-scale" class="relative group"&gt;Proven at Scale &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#proven-at-scale" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;Transitioning core utilities in Ubuntu comes with a responsibility to ensure that replacements are of high quality, resilient and offer something to the platform. We may be the first major Linux distribution to adopt ntpd-rs by default, but we aren&amp;rsquo;t the first to recognize the readiness of &lt;code&gt;ntpd-rs&lt;/code&gt; - it has already been &lt;a href="https://letsencrypt.org/2024/06/24/ntpd-rs-deployment" target="_blank" rel="noreferrer"&gt;proven at scale by Let&amp;rsquo;s Encrypt&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;While Let&amp;rsquo;s Encrypt&amp;rsquo;s core Certificate Authority software has always been written in memory-safe Go, their server operating systems and network infrastructure historically relied on memory-unsafe languages like C and C++, which routinely led to vulnerabilities requiring patching.&lt;/p&gt;
&lt;p&gt;Following extensive development, &lt;code&gt;ntpd-rs&lt;/code&gt; was deployed to Let&amp;rsquo;s Encrypt&amp;rsquo;s staging environment in April 2024, and rolled out to full production by June 2024, marking a major milestone for ntpd-rs.&lt;/p&gt;
&lt;p&gt;The fact that one of the world&amp;rsquo;s most prolific and security-conscious certificate authorities trusts &lt;code&gt;ntpd-rs&lt;/code&gt; to keep time across its fleet should provide us, and our enterprise customers, with tremendous confidence in its resilience and suitability.&lt;/p&gt;
&lt;h2 id="a-single-memory-safe-utility-for-ntp-and-ptp" class="relative group"&gt;A Single, Memory-Safe Utility for NTP and PTP &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#a-single-memory-safe-utility-for-ntp-and-ptp" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;We want to provide a single utility for configuring both NTP/NTS and Precision Time Protocol (PTP) on Linux. The Trifecta Tech Foundation is concurrently developing &lt;a href="https://trifectatech.org/projects/statime/" target="_blank" rel="noreferrer"&gt;Statime&lt;/a&gt;, a memory-safe PTP implementation that delivers synchronization performance on par with &lt;code&gt;linuxptp&lt;/code&gt;, but with the goal of being easier to configure and use.&lt;/p&gt;
&lt;p&gt;The goal is to integrate Statime&amp;rsquo;s PTP capabilities directly into &lt;code&gt;ntpd-rs&lt;/code&gt;, improving the user experience by bringing all time synchronization concerns into one utility with common configuration and usage patterns, obviating the need for complex manual configuration (and troubleshooting) that users of &lt;code&gt;linuxptp&lt;/code&gt; may be familiar with.&lt;/p&gt;
&lt;h2 id="timelines-and-goals" class="relative group"&gt;Timelines and Goals &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#timelines-and-goals" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;As with our transition to &lt;code&gt;sudo-rs&lt;/code&gt; and &lt;code&gt;uutils coreutils&lt;/code&gt;, leading the mainstream adoption of foundational system utilities comes with responsibility. We want to ensure that &lt;code&gt;ntpd-rs&lt;/code&gt; matches the security isolation and performance standards our users expect from &lt;code&gt;chrony&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Canonical is funding the Trifecta Tech Foundation&amp;rsquo;s development efforts toward these goals over the coming cycles. This work will take place between July 2026 and January 2027 in several major milestones. Our current timeline and targeted goals are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Ubuntu 26.10:&lt;/strong&gt; If all goes well, we aim to land the latest version of &lt;code&gt;ntpd-rs&lt;/code&gt; in the archive, making it available to test.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ubuntu 27.04:&lt;/strong&gt; By 27.04, &lt;code&gt;ntpd-rs&lt;/code&gt; should have integrated &lt;code&gt;statime&lt;/code&gt;, and we will ship the unified client/server binary for NTP, NTS and PTP in Ubuntu by default, with the aim of providing a smooth migration path for those who already manage complex &lt;code&gt;chrony&lt;/code&gt; configs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To get us there, the Trifecta Tech Foundation will be working on the following items:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Feature Parity &amp;amp; Hardware Support:&lt;/strong&gt; Adding &lt;code&gt;gpsd&lt;/code&gt; IP socket support, multi-threading support for NTP servers, and support for multi-homed servers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Security &amp;amp; Isolation:&lt;/strong&gt; &lt;code&gt;chrony&lt;/code&gt; is isolated via AppArmor and seccomp. We&amp;rsquo;ll be working on robust AppArmor and seccomp profiles for &lt;code&gt;ntpd-rs&lt;/code&gt; to ensure we don&amp;rsquo;t buy memory safety at the cost of system-level privilege boundaries. We are also ensuring &lt;code&gt;rustls&lt;/code&gt; can use &lt;code&gt;openssl&lt;/code&gt; as a crypto provider to satisfy strict corporate cryptography policies.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PTP &amp;amp; Automotive Profiles:&lt;/strong&gt; Adding support for gPTP, which will allow us to support complex deployments like the Automotive profile directly from &lt;code&gt;nptd-rs&lt;/code&gt; (via Statime). Additionally, experimental support for the proposed Client-Server PTP protocol (CSPTP, IEEE 1588.1) will be added.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Benchmarking &amp;amp; Testing:&lt;/strong&gt; Comprehensive benchmarking of long-term memory, CPU usage, and synchronization performance against &lt;code&gt;chrony&lt;/code&gt; to give our cloud partners and enterprise users complete confidence in the transition.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;User-experience:&lt;/strong&gt; Logging improvements and enhancements to configuration that help users configure the time synchronisation target to optimise network usage, as well as improvements to the ntp-cli&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="about-the-trifecta-tech-foundation" class="relative group"&gt;About the Trifecta Tech Foundation &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#about-the-trifecta-tech-foundation" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;Trifecta Tech Foundation is a non-profit and a Public Benefit Organisation (501(c)(3) equivalent) that creates open-source building blocks for critical infrastructure software. Their initiatives on data compression, time synchronization, and privilege boundary, impact the digital security of millions of people. If you&amp;rsquo;d like to support their work, please contact them via &lt;a href="https://trifectatech.org/support" target="_blank" rel="noreferrer"&gt;https://trifectatech.org/support&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="summary" class="relative group"&gt;Summary &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#summary" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;I am really excited to deepen our already productive relationship with the Trifecta Tech Foundation to make these transitions viable for the wider ecosystem. We&amp;rsquo;ll be working hard on testing and integration to ensure seamless migration paths, and heavily document the changes ahead of the 26.10 and 27.04 releases.&lt;/p&gt;
&lt;p&gt;Stay tuned!&lt;/p&gt;</description></item><item><title>An update on upki</title><link>https://jnsgr.uk/2026/02/upki-update/</link><pubDate>Mon, 16 Feb 2026 00:00:00 +0000</pubDate><guid>https://jnsgr.uk/2026/02/upki-update/</guid><description>&lt;blockquote&gt;
&lt;p&gt;This article was originally posted &lt;a href="https://discourse.ubuntu.com/t/an-update-on-upki/77063" target="_blank" rel="noreferrer"&gt;on the Ubuntu Discourse&lt;/a&gt;, and is reposted here. I welcome comments and further discussion in that thread.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Last year, I &lt;a href="https://jnsgr.uk/2025/12/addressing-linuxs-missing-pki-infra/" target="_blank" rel="noreferrer"&gt;announced&lt;/a&gt; that Canonical had begun supporting the development of &lt;a href="https://jnsgr.uk/2025/12/addressing-linuxs-missing-pki-infra/" target="_blank" rel="noreferrer"&gt;upki&lt;/a&gt;, a project that will bring browser-grade Public Key Infrastructure (PKI) to Linux. Since then, development has been moving at pace thanks to the tireless work of &lt;a href="https://dirkjan.ochtman.nl/" target="_blank" rel="noreferrer"&gt;Dirkjan&lt;/a&gt; and &lt;a href="https://jbp.io/" target="_blank" rel="noreferrer"&gt;Joe&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In this post, I’ll explore the progress we’ve made, how you can try an early version, and where we’re going next.&lt;/p&gt;
&lt;h3 id="architecture--progress" class="relative group"&gt;Architecture &amp;amp; Progress &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#architecture--progress" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;As a reminder, upki’s primary goal is to provide a reliable, privacy-preserving, and efficient certificate revocation mechanism for Linux system utilities, package managers, and language runtimes. The solution is built around &lt;a href="https://blog.mozilla.org/security/2020/01/09/crlite-part-1-all-web-pki-revocations-compressed/" target="_blank" rel="noreferrer"&gt;CRLite&lt;/a&gt;, an efficient data format that compresses and distributes certificate revocation information at scale.&lt;/p&gt;
&lt;p&gt;The upki &lt;a href="https://github.com/rustls/upki" target="_blank" rel="noreferrer"&gt;repository&lt;/a&gt; is structured as a Cargo workspace containing five crates, each serving a distinct role:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;upki&lt;/code&gt;&lt;/strong&gt;: the core library and CLI tool. This crate contains the revocation query engine, the client-side sync logic for fetching filter updates, and the command-line interface. The revocation interface was originally embedded in the CLI, but has since been promoted into the library so that other Rust projects can use it directly as a dependency.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;upki-mirror&lt;/code&gt;&lt;/strong&gt;: the server-side mirroring tool. This binary fetches and validates CRLite filters from Mozilla&amp;rsquo;s infrastructure such that they can be served using a standard web server like &lt;code&gt;nginx&lt;/code&gt; or &lt;code&gt;apache&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;upki-ffi&lt;/code&gt;&lt;/strong&gt;: the C Foreign Function Interface. Built as a &lt;code&gt;cdylib&lt;/code&gt;, this crate uses &lt;a href="https://github.com/mozilla/cbindgen" target="_blank" rel="noreferrer"&gt;&lt;code&gt;cbindgen&lt;/code&gt;&lt;/a&gt; to auto-generate a &lt;code&gt;upki.h&lt;/code&gt; header file, exposing the revocation query API to C, C++, Go and any other language with C FFI support.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;rustls-upki&lt;/code&gt;&lt;/strong&gt;: an integration crate that wires upki&amp;rsquo;s revocation engine into &lt;a href="https://github.com/rustls/rustls" target="_blank" rel="noreferrer"&gt;rustls&lt;/a&gt;, enabling any Rust application using rustls to perform CRLite-backed revocation checks transparently.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;revoke-test&lt;/code&gt;&lt;/strong&gt;: testing infrastructure for validating revocation queries against known-revoked certificates.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The team recently released &lt;a href="https://github.com/rustls/upki/releases/tag/upki-0.1.0" target="_blank" rel="noreferrer"&gt;v0.1.0&lt;/a&gt;, which should help us to gather more feedback on the work we&amp;rsquo;ve done so far.&lt;/p&gt;
&lt;h3 id="how-to-try-it" class="relative group"&gt;How to try it &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#how-to-try-it" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;If you&amp;rsquo;d like to try the code in its current form, you&amp;rsquo;ll need to have a version of the Rust toolchain installed. The easiest way to do this on Ubuntu is &lt;a href="https://documentation.ubuntu.com/ubuntu-for-developers/howto/rust-setup/#installing-the-latest-rust-toolchain-using-rustup" target="_blank" rel="noreferrer"&gt;using the &lt;code&gt;rustup&lt;/code&gt; snap&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Ensure you have a C compiler in your PATH&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt update
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install -y build-essential curl
&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;# Install the rustup snap and get the stable toolchain&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo snap install --classic rustup
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rustup install stable
&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;# Install upki&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;cargo install upki
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.cargo/bin:&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&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;# Fetch revocation data. This will be done in the background&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# when installed through the distro in the future&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;upki fetch
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;That should be all you need to install the development version of &lt;code&gt;upki&lt;/code&gt;, and you can now use it to run a revocation check by piping certificate output from &lt;code&gt;curl&lt;/code&gt; into &lt;code&gt;upki&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -sw &lt;span class="s1"&gt;&amp;#39;%{certs}&amp;#39;&lt;/span&gt; https://google.com &lt;span class="p"&gt;|&lt;/span&gt; upki revocation check
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;NotRevoked
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Early versions of docs for the &lt;a href="https://docs.rs/upki-ffi/latest/upki/" target="_blank" rel="noreferrer"&gt;C FFI crate&lt;/a&gt; and &lt;a href="https://docs.rs/upki/latest/upki/" target="_blank" rel="noreferrer"&gt;Rust crate documentation&lt;/a&gt; are available, but if you&amp;rsquo;d like to explore, build the project from source, or contribute, the &lt;a href="https://github.com/rustls/upki" target="_blank" rel="noreferrer"&gt;repository&lt;/a&gt; is the best place to start. For an example of the C FFI interface in action you can take a look at the &lt;a href="https://github.com/rustls/upki-go-demo" target="_blank" rel="noreferrer"&gt;upki-go-demo&lt;/a&gt; Dirkjan published.&lt;/p&gt;
&lt;h3 id="next-steps" class="relative group"&gt;Next Steps &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#next-steps" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;Now the foundational pieces are in place, our focus is shifting to external consumption, performance, and integration with the wider Linux ecosystem. In the coming days there should be an early &lt;code&gt;0.1.0&lt;/code&gt; binary release.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;ll also be doing some performance benchmarking on the initial fetch and of the revocation checks themselves. Currently, each revocation check reads several CRLite filter files into memory. There may be quick wins to improve this, but we’ll benchmark first and see if it warrants optimisation at this time.&lt;/p&gt;
&lt;p&gt;We also need to deploy some production infrastructure for serving the CRLite filters. If you follow the steps above, you&amp;rsquo;ll be fetching from a pre-production web server hosted at &lt;a href="https://upki.rustls.dev" target="_blank" rel="noreferrer"&gt;https://upki.rustls.dev&lt;/a&gt;. We&amp;rsquo;ve built a &lt;a href="https://github.com/jnsgruk/upki-mirror-k8s-operator" target="_blank" rel="noreferrer"&gt;Juju charm&lt;/a&gt; for operating the CRLite mirror on Kubernetes. This charm packages the &lt;code&gt;upki-mirror&lt;/code&gt; binary in a &lt;a href="https://ubuntu.com/blog/combining-distroless-and-ubuntu-chiselled-containers" target="_blank" rel="noreferrer"&gt;chiselled Rock&lt;/a&gt;, and will be deployed into Canonical&amp;rsquo;s datacentres to serve CRLite data at &lt;a href="https://crlite.ubuntu.com/" target="_blank" rel="noreferrer"&gt;crlite.ubuntu.com&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Our Ubuntu Foundations team is also working on packaging the various upki components for inclusion in the Ubuntu archive, which will enable you to &lt;code&gt;apt install upki&lt;/code&gt; in the future, and also enable us to package and enable it by default in Ubuntu 26.10 and beyond.&lt;/p&gt;
&lt;h3 id="further-down-the-road" class="relative group"&gt;Further Down the Road &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#further-down-the-road" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;While the work above covers what&amp;rsquo;s immediately in front of us, there is scope to expand upki&amp;rsquo;s capabilities further. Two areas of interest are Certificate Transparency enforcement, and support for Merkle Tree Certificates.&lt;/p&gt;
&lt;h4 id="certificate-transparency-enforcement" class="relative group"&gt;Certificate Transparency Enforcement &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#certificate-transparency-enforcement" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h4&gt;&lt;p&gt;While upki&amp;rsquo;s initial focus is on revocation checking, the project also aims to eventually support &lt;a href="https://certificate.transparency.dev/" target="_blank" rel="noreferrer"&gt;Certificate Transparency&lt;/a&gt; (CT) enforcement. CT is a more modern security measure that relies upon a set of publicly auditable, append-only logs that record every TLS certificate issued by a Certificate Authority (CA). This prevents CAs from issuing fraudulent or erroneous certificates without a means for that fraudulent activity to be discovered - a problem that has &lt;a href="https://blog.cloudflare.com/unauthorized-issuance-of-certificates-for-1-1-1-1/" target="_blank" rel="noreferrer"&gt;bitten organisations&lt;/a&gt; in the past.&lt;/p&gt;
&lt;p&gt;CT Enforcement would enable clients to refuse to establish a connection unless the server provides cryptographic proof that its certificate has been correctly logged. Browsers like Chrome and Firefox already enforce this, but the rest of the Linux ecosystem would need a tool such as upki to enable such functionality.&lt;/p&gt;
&lt;h4 id="intermediate-preloading" class="relative group"&gt;Intermediate Preloading &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#intermediate-preloading" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h4&gt;&lt;p&gt;A correctly configured TLS server should not only send its own certificate, but also the intermediate certificates needed to chain back to a trusted root. In practice, many servers omit the intermediate certificates, and because browsers have quietly worked around this for years, the misconfiguration often goes unnoticed.&lt;/p&gt;
&lt;p&gt;Firefox has been &lt;a href="https://blog.mozilla.org/security/2020/11/13/preloading-intermediate-ca-certificates-into-firefox/" target="_blank" rel="noreferrer"&gt;preloading all intermediates&lt;/a&gt; disclosed to the &lt;a href="https://www.ccadb.org/" target="_blank" rel="noreferrer"&gt;Common CA Database&lt;/a&gt; (CCADB) since Firefox 75, while Chrome and Edge will silently fetch missing intermediates using the Authority Information Access (AIA) extension in the server&amp;rsquo;s certificate. The result is that a broken certificate chain that works perfectly in every browser will produce an opaque &lt;code&gt;UNKNOWN_ISSUER&lt;/code&gt; error when accessed by Linux utilities like &lt;code&gt;curl&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Because upki already maintains a regularly synced local data store, it&amp;rsquo;s well positioned to ship the known set of intermediates alongside the CRLite filters. This wouldn&amp;rsquo;t provide a security improvement so much as a usability improvement. It would also bring non-browser clients up to parity with browsers with respect to connection reliability. There is an additional privacy benefit too: rather than fetching a missing intermediate from the issuing CA (which discloses browsing activity to the CA), the intermediate is already present locally.&lt;/p&gt;
&lt;h4 id="merkle-tree-certificates" class="relative group"&gt;Merkle Tree Certificates &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#merkle-tree-certificates" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h4&gt;&lt;p&gt;Looking even further ahead, upki could support the next generation of web PKI by including support for &lt;a href="https://datatracker.ietf.org/doc/draft-davidben-tls-merkle-tree-certs/" target="_blank" rel="noreferrer"&gt;Merkle Tree Certificates (MTCs)&lt;/a&gt;. This is an area of active development in the IETF, with Cloudflare and Chrome recently &lt;a href="https://blog.cloudflare.com/bootstrap-mtc/" target="_blank" rel="noreferrer"&gt;announcing an experimental deployment&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The motivation for MTCs comes largely from the transition to &lt;a href="https://openquantumsafe.org/post-quantum-crypto.html" target="_blank" rel="noreferrer"&gt;Post-Quantum (PQ) cryptography&lt;/a&gt;. PQ signatures are significantly larger than their non-PQ counterparts. The signatures for &lt;a href="https://openquantumsafe.org/liboqs/algorithms/sig/ml-dsa.html" target="_blank" rel="noreferrer"&gt;ML-DSA-44&lt;/a&gt; are 2,420 bytes compared to 64 bytes for ECDSA-P256. A typical TLS handshake today involves multiple signatures and public keys across the certificate chain and CT proofs, which means a simple swap to PQ algorithms would add tens of kilobytes of overhead per connection and likely a noticeable increase in connection latency.&lt;/p&gt;
&lt;p&gt;MTCs address this by rethinking how certificates are validated. Rather than transmitting a full certificate chain with multiple signatures, a Certificate Authority can batch certificates into a Merkle Tree and sign only the tree&amp;rsquo;s root hash. The client then receives just a single signature, a public key, and a compact Merkle tree inclusion proof that demonstrates the certificate&amp;rsquo;s presence in the batch. The signed tree heads can be distributed to clients out-of-band, meaning the per-handshake overhead is drastically reduced.&lt;/p&gt;
&lt;p&gt;Because upki already maintains a local data store that is regularly synced, it could cache tree head data alongside CRLite filters, thereby enabling the inclusion proofs sent during TLS handshakes to be even smaller. Rather than proving inclusion all the way from the leaf to the root, the server could send a &amp;ldquo;truncated&amp;rdquo; proof that starts partway up the tree, with the client computing the remainder from data it already has locally. There is a &lt;a href="https://datatracker.ietf.org/doc/draft-davidben-tls-merkle-tree-certs/" target="_blank" rel="noreferrer"&gt;TLS extension&lt;/a&gt; being developed to negotiate this.&lt;/p&gt;
&lt;p&gt;The implementation of MTCs for TLS is still highly experimental. MTCs are not yet deployed in any browser, but upki will lay the groundwork for Linux system utilities to benefit from this evolution as the technology is adopted.&lt;/p&gt;
&lt;h3 id="summary" class="relative group"&gt;Summary &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#summary" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;In the few weeks since we announced upki, the core revocation engine has been established and is now functional, the CRLite mirroring tool is working and a production deployment in Canonical&amp;rsquo;s datacentres is ongoing. We&amp;rsquo;re now preparing for an alpha release and remain on track for an opt-in preview for Ubuntu 26.04 LTS.&lt;/p&gt;
&lt;p&gt;Beyond revocation, we&amp;rsquo;re keeping a close eye on the evolving PKI landscape and particularly CT enforcement and Merkle Tree Certificates.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;d like to extend my thanks again to &lt;a href="https://dirkjan.ochtman.nl/" target="_blank" rel="noreferrer"&gt;Dirkjan&lt;/a&gt; and &lt;a href="https://jbp.io/" target="_blank" rel="noreferrer"&gt;Joe&lt;/a&gt; for their continued collaboration on this work, and the utmost professionalism they&amp;rsquo;ve demonstrated throughout.&lt;/p&gt;</description></item><item><title>Developing with AI on Ubuntu</title><link>https://jnsgr.uk/2026/01/developing-with-ai-on-ubuntu/</link><pubDate>Tue, 20 Jan 2026 00:00:00 +0000</pubDate><guid>https://jnsgr.uk/2026/01/developing-with-ai-on-ubuntu/</guid><description>&lt;blockquote&gt;
&lt;p&gt;This article was originally posted &lt;a href="https://discourse.ubuntu.com/t/developing-with-ai-on-ubuntu/75299" target="_blank" rel="noreferrer"&gt;on the Ubuntu Discourse&lt;/a&gt;, and is reposted here. I welcome comments and further discussion in that thread.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;AI-assisted tooling is becoming more and more common in the workflows of engineers at all experience levels. As I see it, our challenge is one of consideration, enablement and constraint. We must enable those who opt-in to safely and responsibly harness the power of these tools, while respecting those who do not wish to have their platform defined or overwhelmed by this class of software.&lt;/p&gt;
&lt;p&gt;The use of AI is a divisive topic among the tech community. I find myself a little in both camps, somewhere between sceptic and advocate. While I&amp;rsquo;m quick to acknowledge the negative impacts that the use of LLMs &lt;em&gt;can have&lt;/em&gt; on open source projects, I&amp;rsquo;m also surrounded by examples where it has been used responsibly to great effect.&lt;/p&gt;
&lt;p&gt;Examples of this include &lt;a href="https://filippo.io" target="_blank" rel="noreferrer"&gt;Filippo&lt;/a&gt;&amp;rsquo;s article &lt;a href="https://words.filippo.io/claude-debugging/" target="_blank" rel="noreferrer"&gt;debugging low-level cryptography with Claude Code&lt;/a&gt;, &lt;a href="https://mitchellh.com" target="_blank" rel="noreferrer"&gt;Mitchell&lt;/a&gt;&amp;rsquo;s article on &lt;a href="https://mitchellh.com/writing/non-trivial-vibing" target="_blank" rel="noreferrer"&gt;Vibing a Non-Trivial Ghostty Feature&lt;/a&gt;, and &lt;a href="https://github.com/crawshaw" target="_blank" rel="noreferrer"&gt;David&lt;/a&gt;&amp;rsquo;s article &lt;a href="https://crawshaw.io/blog/programming-with-agents" target="_blank" rel="noreferrer"&gt;How I Program with Agents&lt;/a&gt;. These articles come from engineers with proven expertise in careful, precise software engineering, yet they share an important sentiment: AI-assisted tools can be a remarkable force-multiplier when used &lt;em&gt;in conjunction&lt;/em&gt; with their lived experience, but care must still be taken to avoid poor outcomes.&lt;/p&gt;
&lt;p&gt;The aim of this post is not to convince you to use AI in your work, but rather to introduce the elements of Ubuntu that make it a first-class platform for safe, efficient experimentation and development. My goals for AI and Ubuntu are currently focused on enabling those who want to develop responsibly with AI tools, without negatively impacting the experience of those who&amp;rsquo;d prefer not to opt-in.&lt;/p&gt;
&lt;h3 id="hardware--drivers" class="relative group"&gt;Hardware &amp;amp; Drivers &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#hardware--drivers" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;AI-specific silicon is moving just as fast as AI software tooling, and without constant work to integrate drivers and userspace tools into Ubuntu, it would be impossible to efficiently utilise this specialised hardware.&lt;/p&gt;
&lt;p&gt;Last year we announced that we will ship both &lt;a href="https://canonical.com/blog/canonical-announces-it-will-support-and-distribute-nvidia-cuda-in-ubuntu" target="_blank" rel="noreferrer"&gt;NVIDIA&amp;rsquo;s CUDA&lt;/a&gt; and &lt;a href="https://canonical.com/blog/canonical-amd-rocm-ai-ml-hpc-libraries" target="_blank" rel="noreferrer"&gt;AMD&amp;rsquo;s ROCm&lt;/a&gt; in the Ubuntu archive for Ubuntu 26.04 LTS, in addition to our previous work on &lt;a href="https://snapcraft.io/publisher/openvino" target="_blank" rel="noreferrer"&gt;OpenVINO&lt;/a&gt;. This will make installing the latest drivers and toolkits easier and more secure, with no third-party software repositories. Distributing this software as part of Ubuntu enables us to be proactive in the delivery of security updates and the demonstration of provenance.&lt;/p&gt;
&lt;p&gt;Our work is not limited to AMD and NVIDIA; we recently &lt;a href="https://canonical.com/blog/ubuntu-ga-for-qualcomm-dragonwing" target="_blank" rel="noreferrer"&gt;announced&lt;/a&gt; support for Qualcomm&amp;rsquo;s &lt;a href="https://www.qualcomm.com/dragonwing" target="_blank" rel="noreferrer"&gt;Dragonwing&lt;/a&gt; platforms and others. You can read more about our silicon partner projects &lt;a href="https://canonical.com/partners/silicon" target="_blank" rel="noreferrer"&gt;on our website&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="inference-snaps" class="relative group"&gt;Inference Snaps &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#inference-snaps" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;At the &lt;a href="https://ubuntu.com/summit" target="_blank" rel="noreferrer"&gt;Ubuntu Summit 25.10&lt;/a&gt;, we &lt;a href="https://canonical.com/blog/canonical-releases-inference-snaps" target="_blank" rel="noreferrer"&gt;released&lt;/a&gt; &amp;ldquo;Inference Snaps&amp;rdquo; into the wild, which provide a hassle-free mechanism for obtaining the “famous model” you want to work with, but automatically receive a version of that model which is optimised for the silicon in your machine. This removes the need to spend hours on &lt;a href="https://huggingface.co/" target="_blank" rel="noreferrer"&gt;HuggingFace&lt;/a&gt; identifying the correct model to download that matches with your hardware, and obviates the need for in-depth understanding of model quantisation and tuning when getting started.&lt;/p&gt;
&lt;p&gt;Each of our inference snaps provide a consistent experience: you need only learn the basics once, but can apply those skills to different models as they emerge, whether you&amp;rsquo;re on a laptop or a server.&lt;/p&gt;
&lt;p&gt;At the time of writing, we&amp;rsquo;ve published &lt;code&gt;beta&lt;/code&gt; quality snaps for &lt;a href="https://snapcraft.io/qwen-vl" target="_blank" rel="noreferrer"&gt;qwen-vl&lt;/a&gt;, &lt;a href="https://snapcraft.io/deepseek-r1" target="_blank" rel="noreferrer"&gt;deepseek-r1&lt;/a&gt; and &lt;a href="https://snapcraft.io/gemma3" target="_blank" rel="noreferrer"&gt;gemma3&lt;/a&gt;. You can find a current list of snaps &lt;a href="https://documentation.ubuntu.com/inference-snaps/reference/snaps/" target="_blank" rel="noreferrer"&gt;in the documentation&lt;/a&gt;, along with the silicon-optimised variants.&lt;/p&gt;
&lt;h3 id="sandboxing-agents" class="relative group"&gt;Sandboxing Agents &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#sandboxing-agents" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;While many start their journey in a web browser chatting to &lt;a href="https://chat.com" target="_blank" rel="noreferrer"&gt;ChatGPT&lt;/a&gt;, &lt;a href="https://claude.ai" target="_blank" rel="noreferrer"&gt;Claude&lt;/a&gt;, &lt;a href="https://gemini.google.com/app" target="_blank" rel="noreferrer"&gt;Gemini&lt;/a&gt;, &lt;a href="https://perplexity.ai" target="_blank" rel="noreferrer"&gt;Perplexity&lt;/a&gt; or one of the myriad of alternatives, many developers will find &amp;ldquo;agentic&amp;rdquo; tools such as &lt;a href="https://github.com/features/copilot" target="_blank" rel="noreferrer"&gt;Copilot&lt;/a&gt;, &lt;a href="https://openai.com/codex/" target="_blank" rel="noreferrer"&gt;Codex&lt;/a&gt;, &lt;a href="https://claude.com/product/claude-code" target="_blank" rel="noreferrer"&gt;Claude Code&lt;/a&gt; or &lt;a href="https://ampcode.com/" target="_blank" rel="noreferrer"&gt;Amp&lt;/a&gt; quite attractive. In my experience, agents are a clear level-up in an LLM&amp;rsquo;s capability for developers, but they can still make poor decisions and are generally safer to run in sandboxed environment at the time of writing.&lt;/p&gt;
&lt;p&gt;Where a traditional chat-based AI tool responds reactively to user prompts within a single conversation, an agent operates (semi-)autonomously to pursue goals. It perceives its environment, plans, makes decisions and can call out to external tools and services to achieve those goals. If you grant permission, an agent can read and understand your code, implement features, troubleshoot bugs, optimise performance and many other tasks. The catch is that they often need &lt;em&gt;access to your system&lt;/em&gt; - whether that be to modify files or run commands.&lt;/p&gt;
&lt;p&gt;Issues such as accidental file deletion, or the inclusion of a spurious (and potentially compromised) dependency are an inevitable failure mode of the current generation of agents due to how they&amp;rsquo;re trained (see the &lt;a href="https://www.reddit.com/r/ClaudeAI/comments/1pgxckk/claude_cli_deleted_my_entire_home_directory_wiped/" target="_blank" rel="noreferrer"&gt;Reddit post&lt;/a&gt; about Claude Code deleting a user&amp;rsquo;s home directory).&lt;/p&gt;
&lt;h4 id="my-agent-sandboxes-itself" class="relative group"&gt;My agent sandboxes itself! &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#my-agent-sandboxes-itself" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h4&gt;&lt;p&gt;Some of you will be reading this wondering why additional sandboxing is required, since many of the popular agents &lt;a href="https://code.claude.com/docs/en/sandboxing" target="_blank" rel="noreferrer"&gt;advertise their own sandboxing&lt;/a&gt;. The fact that some agents include some measures to protect the user&amp;rsquo;s machine is of course a good thing. The touted benefits include filesystem isolation by restricting the agent to a specific directory, or prompting for approval before modifying files. Some agents also include network sandboxing to restrict network access to a list of approved domains, or by using a custom proxy to impose rules on outbound traffic.&lt;/p&gt;
&lt;p&gt;On Linux, these agent-imposed sandboxes are often implemented with &lt;a href="https://github.com/containers/bubblewrap" target="_blank" rel="noreferrer"&gt;bubblewrap&lt;/a&gt;, which is &amp;ldquo;a tool for constructing sandbox environments&amp;rdquo;, but note that the upstream project&amp;rsquo;s README includes &lt;a href="https://github.com/containers/bubblewrap#sandbox-security" target="_blank" rel="noreferrer"&gt;a section&lt;/a&gt; which states that it is &lt;em&gt;not&lt;/em&gt; a &amp;ldquo;ready-made sandbox with a specific security policy&amp;rdquo;. &lt;code&gt;bubblewrap&lt;/code&gt; is a relatively low-level tool that must be given its configuration, which in this case is provided &lt;em&gt;by the agent&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The limitation upon these tools is the shared kernel - a severe kernel exploit could enable an agent to escape from its sandbox. Of course, such vulnerabilities are rare, but note that even if the sandboxing technologies do their job, agents often run in the context of the user&amp;rsquo;s session, meaning they inherit environment variables which could contain sensitive information. They&amp;rsquo;re also agent specific: Claude Code&amp;rsquo;s sandboxing won&amp;rsquo;t help you if you&amp;rsquo;re using &lt;a href="https://cursor.com/" target="_blank" rel="noreferrer"&gt;Cursor&lt;/a&gt; or &lt;a href="https://antigravity.google/" target="_blank" rel="noreferrer"&gt;Antigravity&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Depending on your threat model and the project you&amp;rsquo;re working on, you may deem the built-in sandboxing of coding agents to be sufficient, but there are other options available to Ubuntu users that provide either different, or additional protection&amp;hellip;&lt;/p&gt;
&lt;h4 id="sandbox-with-lxd-containers" class="relative group"&gt;Sandbox with LXD containers &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#sandbox-with-lxd-containers" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h4&gt;&lt;p&gt;Canonical&amp;rsquo;s &lt;a href="https://canonical.com/lxd" target="_blank" rel="noreferrer"&gt;LXD&lt;/a&gt; works out-of-the-box on Ubuntu, and is a great way to sandbox an agent into a disposable environment where the blast radius is limited should the agent make a mistake. My personal workflow is to create an Ubuntu container (or VM) with my project directory mounted. This way, I can edit my code directly on my filesystem with my preferred (already configured) editor, but have the agent run inside the container.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&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;# Initialise the container&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;lxc init ubuntu:noble dev
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Mount my project directory into the container&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;lxc config device add -q dev datadir disk &lt;span class="nv"&gt;source&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/my-project&amp;#34;&lt;/span&gt; &lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/home/ubuntu/project
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Start the container&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;lxc start dev
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Get a shell inside the container as the &amp;#39;ubuntu&amp;#39; user&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;lxc &lt;span class="nb"&gt;exec&lt;/span&gt; dev -- sudo -u ubuntu -i bash
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Run a command in the container&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;lxc &lt;span class="nb"&gt;exec&lt;/span&gt; dev -- sudo -u ubuntu -i bash -c &lt;span class="s2"&gt;&amp;#34;cd project; claude&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;You can learn more about LXD in the official &lt;a href="https://documentation.ubuntu.com/lxd/stable-5.21/" target="_blank" rel="noreferrer"&gt;documentation&lt;/a&gt; and &lt;a href="https://documentation.ubuntu.com/lxd/stable-5.21/tutorial/first_steps/#first-steps" target="_blank" rel="noreferrer"&gt;tutorial&lt;/a&gt;, as well as specific instructions on &lt;a href="https://ubuntu.com/tutorials/gpu-data-processing-inside-lxd#1-overview" target="_blank" rel="noreferrer"&gt;enabling GPU data processing in containers/VMs&lt;/a&gt;. I&amp;rsquo;ve written &lt;a href="https://jnsgr.uk/2024/06/desktop-vms-lxd-multipass/" target="_blank" rel="noreferrer"&gt;previously&lt;/a&gt; about my use of LXD in development.&lt;/p&gt;
&lt;p&gt;With LXD, you can choose between running your sandbox as a container or a VM, depending on your project&amp;rsquo;s needs. If I&amp;rsquo;m working on a project that requires Kubernetes or similar, I use a VM, but for lighter projects I use system containers, preferring their lower overhead.&lt;/p&gt;
&lt;h4 id="sandbox-with-lxd-vms" class="relative group"&gt;Sandbox with LXD VMs &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#sandbox-with-lxd-vms" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h4&gt;&lt;p&gt;LXD is best known for its ability to run &amp;ldquo;system containers&amp;rdquo;, which are somewhat analogous to Docker/OCI containers, but rather than being focused on a single application (and dependencies), a system container essentially runs an entire Ubuntu user-space (including &lt;code&gt;systemd&lt;/code&gt;, etc.). Like OCI containers, however, system containers share the kernel with the host.&lt;/p&gt;
&lt;p&gt;In some situations, you may seek more isolation from your host machine by running tools inside a virtual machine with their own kernel. LXD makes this simple - you can follow the same commands as above, but add &lt;code&gt;--vm&lt;/code&gt; to the &lt;code&gt;init&lt;/code&gt; command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&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;# Initialise the virtual machine&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;lxc init --vm ubuntu:noble dev
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;You can also configure the virtual machine&amp;rsquo;s CPU, memory and disk requirements. A simple example is below:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&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;lxc init --vm ubuntu:noble dev &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -c limits.cpu&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;8&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; -c limits.memory&lt;span class="o"&gt;=&lt;/span&gt;8GiB &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -d root,size&lt;span class="o"&gt;=&lt;/span&gt;100GiB
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;You can find more details on instance configuration in the &lt;a href="https://documentation.ubuntu.com/lxd/stable-5.21/howto/instances_configure/" target="_blank" rel="noreferrer"&gt;LXD documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id="sandbox-with-multipass" class="relative group"&gt;Sandbox with Multipass &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#sandbox-with-multipass" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h4&gt;&lt;p&gt;&lt;a href="https://multipass.run/" target="_blank" rel="noreferrer"&gt;Multipass&lt;/a&gt; provides on-demand access to Ubuntu VMs from any workstation - whether that workstation is running Linux, macOS or Windows. It is designed to replicate, in a lightweight way, the experience of provisioning a simple Ubuntu VM on a cloud.&lt;/p&gt;
&lt;p&gt;Multipass&amp;rsquo; scope is more limited than LXD, but for many users it provides a simple on-ramp for development with Ubuntu. Where it lacks advanced features like GPU passthrough, it boasts a simplified CLI and a first-class &lt;a href="https://documentation.ubuntu.com/multipass/latest/reference/gui-client/" target="_blank" rel="noreferrer"&gt;GUI client&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To get started similarly to the LXD example above, try the following:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&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;# Install Multipass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo snap install multipass
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Launch an instance&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;multipass launch noble -n dev
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Mount your project directory&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;multipass mount ~/my-project dev:/home/ubuntu/project
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Get a shell in the instance&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;multipass shell dev
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Run a command in the instance&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;multipass &lt;span class="nb"&gt;exec&lt;/span&gt; dev -- claude
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;You can find more details on how to configure and manage instances &lt;a href="https://documentation.ubuntu.com/multipass/latest/" target="_blank" rel="noreferrer"&gt;in the docs&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id="sandbox-with-wsl" class="relative group"&gt;Sandbox with WSL &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#sandbox-with-wsl" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h4&gt;&lt;p&gt;If you&amp;rsquo;re on Windows, &lt;a href="https://documentation.ubuntu.com/wsl/stable/tutorials/develop-with-ubuntu-wsl/" target="_blank" rel="noreferrer"&gt;development with WSL&lt;/a&gt; includes first-class &lt;a href="https://documentation.ubuntu.com/wsl/stable/howto/gpu-cuda/" target="_blank" rel="noreferrer"&gt;support for GPU acceleration&lt;/a&gt;, and is even supported for use with the &lt;a href="https://ubuntu.com/blog/accelerate-ai-development-with-ubuntu-and-nvidia-ai-workbench" target="_blank" rel="noreferrer"&gt;NVIDIA AI Workbench&lt;/a&gt;, &lt;a href="https://docs.nvidia.com/nim/wsl2/latest/getting-started.html" target="_blank" rel="noreferrer"&gt;NVIDIA NIM&lt;/a&gt; and &lt;a href="https://learn.microsoft.com/en-us/windows/ai/directml/gpu-cuda-in-wsl" target="_blank" rel="noreferrer"&gt;CUDA&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Ubuntu is the default Linux distribution for WSL, and you can find more information about how to set up and use Ubuntu on WSL in &lt;a href="https://documentation.ubuntu.com/wsl/stable/" target="_blank" rel="noreferrer"&gt;our documentation&lt;/a&gt;. WSL benefits from all the same technologies as a &amp;ldquo;regular&amp;rdquo; Ubuntu install, including the ability to use Snaps, Docker and LXD.&lt;/p&gt;
&lt;p&gt;For the enterprise developer, we recently announced &lt;a href="https://canonical.com/blog/canonical-announces-ubuntu-pro-for-wsl" target="_blank" rel="noreferrer"&gt;Ubuntu Pro for WSL&lt;/a&gt;, as well as the ability to manage WSL instances &lt;a href="https://documentation.ubuntu.com/landscape/how-to-guides/wsl-integration/manage-wsl-instances/" target="_blank" rel="noreferrer"&gt;using Landscape&lt;/a&gt;, making it easier to get access to first-class developer tooling with Ubuntu on your corporate machine.&lt;/p&gt;
&lt;h3 id="summary" class="relative group"&gt;Summary &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#summary" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;While opinion remains divided on the value and impact of current AI tooling, its presence in modern development workflows and its demands on underlying compute infrastructure are difficult to ignore.&lt;/p&gt;
&lt;p&gt;Developers who wish to experiment need reliable access to modern hardware, predictable tooling, and strong isolation boundaries. Ubuntu’s role is not to dictate how these tools are used, but to provide a stable and dependable platform on which they can be explored and deployed safely, without compromising security, provenance, or the day-to-day experience of those who choose to opt out.&lt;/p&gt;
&lt;p&gt;In addition to powering development workflows, Ubuntu makes for a dependable production operating system for your workloads. We&amp;rsquo;re building &lt;a href="https://documentation.ubuntu.com/canonical-kubernetes/latest/" target="_blank" rel="noreferrer"&gt;Canonical Kubernetes&lt;/a&gt; with first-class GPU support, &lt;a href="https://canonical.com/mlops/kubeflow" target="_blank" rel="noreferrer"&gt;Kubeflow&lt;/a&gt; and &lt;a href="https://canonical.com/mlops/mlflow" target="_blank" rel="noreferrer"&gt;MLFlow&lt;/a&gt; for model training and serving and a suite of applications like &lt;a href="https://canonical.com/data/postgresql" target="_blank" rel="noreferrer"&gt;PostgreSQL&lt;/a&gt;, &lt;a href="https://canonical.com/data/mysql" target="_blank" rel="noreferrer"&gt;MySQL&lt;/a&gt;, &lt;a href="https://canonical.com/data/opensearch" target="_blank" rel="noreferrer"&gt;Opensearch&lt;/a&gt;, as well as other data-centric tools such as &lt;a href="https://canonical.com/data/kafka" target="_blank" rel="noreferrer"&gt;Kafka&lt;/a&gt; and &lt;a href="https://canonical.com/data/spark" target="_blank" rel="noreferrer"&gt;Spark&lt;/a&gt; that can be deployed with full &lt;a href="https://ubuntu.com/pro" target="_blank" rel="noreferrer"&gt;Ubuntu Pro&lt;/a&gt; support. Let me know if you&amp;rsquo;d find value in a follow-up post on those topics!&lt;/p&gt;</description></item><item><title>Addressing Linux's Missing PKI Infrastructure</title><link>https://jnsgr.uk/2025/12/addressing-linuxs-missing-pki-infra/</link><pubDate>Mon, 08 Dec 2025 00:00:00 +0000</pubDate><guid>https://jnsgr.uk/2025/12/addressing-linuxs-missing-pki-infra/</guid><description>&lt;blockquote&gt;
&lt;p&gt;This article was originally posted &lt;a href="https://discourse.ubuntu.com/t/addressing-linuxs-missing-pki-infrastructure/73314" target="_blank" rel="noreferrer"&gt;on the Ubuntu Discourse&lt;/a&gt;, and is reposted here. I welcome comments and further discussion in that thread.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Earlier this year, &lt;a href="https://lwn.net/" target="_blank" rel="noreferrer"&gt;LWN&lt;/a&gt; featured an excellent article titled &amp;ldquo;&lt;a href="https://lwn.net/Articles/1033809/" target="_blank" rel="noreferrer"&gt;Linux&amp;rsquo;s missing CRL infrastructure&lt;/a&gt;&amp;rdquo;. The article highlighted a number of key issues surrounding traditional Public Key Infrastructure (PKI), but critically noted how even the available measures are effectively ignored by the majority of system-level software on Linux.&lt;/p&gt;
&lt;p&gt;One of the motivators for the discussion is that the Online Certificate Status Protocol (OCSP) will cease to be supported by Let&amp;rsquo;s Encrypt. The remaining alternative is to use Certificate Revocation Lists (CRLs), yet there is little or no support for managing (or even querying) these lists in most Linux system utilities.&lt;/p&gt;
&lt;p&gt;To solve this, I&amp;rsquo;m happy to share that in partnership with &lt;a href="https://github.com/rustls/rustls" target="_blank" rel="noreferrer"&gt;rustls&lt;/a&gt; maintainers &lt;a href="https://dirkjan.ochtman.nl/" target="_blank" rel="noreferrer"&gt;Dirkjan Ochtman&lt;/a&gt; and &lt;a href="https://jbp.io/" target="_blank" rel="noreferrer"&gt;Joe Birr-Pixton&lt;/a&gt;, we&amp;rsquo;re starting the development of upki: a universal PKI tool. This project initially aims to close the revocation gap through the combination of a new system utility and eventual library support for common TLS/SSL libraries such as &lt;a href="https://openssl-library.org/" target="_blank" rel="noreferrer"&gt;OpenSSL&lt;/a&gt;, &lt;a href="https://gnutls.org/" target="_blank" rel="noreferrer"&gt;GnuTLS&lt;/a&gt; and &lt;a href="https://github.com/rustls/rustls" target="_blank" rel="noreferrer"&gt;rustls&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="the-problem" class="relative group"&gt;The Problem &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#the-problem" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;Online Certificate Authorities responsible for issuing TLS certificates have long had mechanisms for revoking known bad certificates. What constitutes a known bad certificate varies, but generally it means a certificate was issued either in error, or by a malicious actor of some form. There have been two primary mechanisms for this revocation: &lt;a href="https://datatracker.ietf.org/doc/html/rfc5280" target="_blank" rel="noreferrer"&gt;Certificate Revocation Lists&lt;/a&gt; (CRLs) and the &lt;a href="https://datatracker.ietf.org/doc/html/rfc6960" target="_blank" rel="noreferrer"&gt;Online Certificate Status Protocol&lt;/a&gt; (OCSP).&lt;/p&gt;
&lt;p&gt;In July 2024, &lt;a href="https://letsencrypt.org/" target="_blank" rel="noreferrer"&gt;Let’s Encrypt&lt;/a&gt; &lt;a href="https://letsencrypt.org/2024/07/23/replacing-ocsp-with-crls.html" target="_blank" rel="noreferrer"&gt;announced&lt;/a&gt; the deprecation of support for the Online Certificate Status Protocol (OCSP). This wasn&amp;rsquo;t entirely unexpected - the protocol has suffered from privacy defects which leak the browsing habits of users to Certificate Authorities. Various implementations have also suffered reliability issues that forced most implementers to adopt &amp;ldquo;soft-fail&amp;rdquo; policies, rendering the checks largely ineffective.&lt;/p&gt;
&lt;p&gt;The deprecation of OCSP leaves us with CRLs. Both Windows and macOS rely on operating system components to centralise the fetching and parsing of CRLs, but Linux has traditionally delegated this responsibility to individual applications. This is done most effectively in browsers such as Mozilla Firefox, Google Chrome and Chromium, but this has been achieved with bespoke infrastructure.&lt;/p&gt;
&lt;p&gt;However, Linux itself has fallen short by not providing consistent revocation checking infrastructure for the rest of userspace - tools such as curl, system package managers and language runtimes lack a unified mechanism to process this data.&lt;/p&gt;
&lt;p&gt;The ideal solution to this problem, which is slowly &lt;a href="https://letsencrypt.org/2025/12/02/from-90-to-45.html" target="_blank" rel="noreferrer"&gt;becoming more prevalent&lt;/a&gt;, is to issue short-lived credentials with an expiration of 10 days or less, somewhat removing the need for complicated revocation infrastructure, but reducing certificate lifetimes is happening slowly and requires significant automation.&lt;/p&gt;
&lt;h2 id="crlite" class="relative group"&gt;CRLite &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#crlite" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;There are several key challenges with CRLs in practice - the size of the list has grown dramatically as the web has scaled, and one must collate CRLs from all relevant certificate authorities in order to be useful. CRLite was originally proposed by researchers at IEEE S&amp;amp;P and subsequently adopted in Mozilla Firefox. It offers a pragmatic solution to the problem of distributing large CRL datasets to client machines.&lt;/p&gt;
&lt;p&gt;In a recent &lt;a href="https://hacks.mozilla.org/2025/08/crlite-fast-private-and-comprehensive-certificate-revocation-checking-in-firefox/" target="_blank" rel="noreferrer"&gt;blog post&lt;/a&gt;, Mozilla outlined how their CRLite implementation meant that on average users &amp;ldquo;downloaded 300kB of revocation data per day, a 4MB snapshot every 45 days and a sequence of &amp;ldquo;delta-updates&amp;rdquo; in-between&amp;rdquo;, which amounts to CRLite being 1000x more bandwidth-efficient than daily CRL downloads.&lt;/p&gt;
&lt;p&gt;At its core, CRLite is a data structure compressing the full set of web-PKI revocations into a compact, efficiently queryable form. You can find more information about CRLite&amp;rsquo;s design and implementation on &lt;a href="https://blog.mozilla.org/security/tag/crlite/" target="_blank" rel="noreferrer"&gt;Mozilla&amp;rsquo;s Security Blog&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="introducing-upki" class="relative group"&gt;Introducing upki &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#introducing-upki" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;Following our work on &lt;a href="https://jnsgr.uk/2025/03/carefully-but-purposefully-oxidising-ubuntu/" target="_blank" rel="noreferrer"&gt;oxidizing Ubuntu&lt;/a&gt;, &lt;a href="https://dirkjan.ochtman.nl/" target="_blank" rel="noreferrer"&gt;Dirkjan&lt;/a&gt; reached out to me with a proposal to introduce a system-level utility backed by CRLite to non-browser users.&lt;/p&gt;
&lt;p&gt;upki will be an open source project, initially packaged for Ubuntu but available to all Linux distributions, and likely portable to other Unix-like operating systems. Written in Rust, upki supports three roles:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Server-side mirroring tool&lt;/strong&gt;: responsible for downloading and mirroring the CRLite filters provided by Mozilla, enabling us to operate independent CDN infrastructure for CRLite users, and serving them to clients. This will insulate upki from changes in the Mozilla backend, and enable standing up an independent data source if required. The server-side tool will manifest as a service that periodically checks the Mozilla Firefox CRLite filters, downloads and validates the files, and serves them.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Client-side sync tool&lt;/strong&gt;: run regularly by a systemd-timer, network-up events or similar, this tool ensures the contents of the CDN are reflected in the on-disk filter cache. This will be extremely low on bandwidth and CPU usage assuming everything is up to date.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Client-side query tool&lt;/strong&gt;: a CLI interface for querying revocation data. This will be useful for monitoring and deployment workflows, as well as for users without a good C FFI.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The latter two roles are served by a single Rust binary that runs in different modes depending on how it is invoked. The server-side tool will be a separate binary, since its use will be much less widespread. Under the hood, all of this will be powered by Rust library crates that can be integrated in other projects via crates.io.&lt;/p&gt;
&lt;p&gt;For the initial release, Canonical will stand up the backend infrastructure required to mirror and serve the CRLite data for upki users, though the backend will be configurable. This prevents unbounded load on Mozilla’s infrastructure and ensures long-term stability even if Firefox’s internal formats evolve.&lt;/p&gt;
&lt;p&gt;&lt;a href="01.png"&gt;
&lt;figure&gt;
&lt;picture
class="mx-auto my-0 rounded-md"
&gt;
&lt;source
srcset="https://jnsgr.uk/2025/12/addressing-linuxs-missing-pki-infra/01_hu_443927a2cc8ea5be.webp 330w,https://jnsgr.uk/2025/12/addressing-linuxs-missing-pki-infra/01_hu_f1c7127e41b7d6cc.webp 660w
,https://jnsgr.uk/2025/12/addressing-linuxs-missing-pki-infra/01_hu_705ea1ebe4137e28.webp 1024w
,https://jnsgr.uk/2025/12/addressing-linuxs-missing-pki-infra/01_hu_be2b5fbb2881c88a.webp 1320w
"
sizes="100vw"
type="image/webp"
/&gt;
&lt;img
width="1720"
height="1670"
class="mx-auto my-0 rounded-md"
alt="architecture diagram for upki"
loading="lazy" decoding="async"
src="https://jnsgr.uk/2025/12/addressing-linuxs-missing-pki-infra/01_hu_b16265b7d66a056c.png" srcset="https://jnsgr.uk/2025/12/addressing-linuxs-missing-pki-infra/01_hu_77c0dd2534a34637.png 330w,https://jnsgr.uk/2025/12/addressing-linuxs-missing-pki-infra/01_hu_b16265b7d66a056c.png 660w
,https://jnsgr.uk/2025/12/addressing-linuxs-missing-pki-infra/01_hu_21c7d0a4f341695e.png 1024w
,https://jnsgr.uk/2025/12/addressing-linuxs-missing-pki-infra/01_hu_34470e9d78fe7948.png 1320w
"
sizes="100vw"
/&gt;
&lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="ecosystem-compatibility" class="relative group"&gt;Ecosystem Compatibility &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#ecosystem-compatibility" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;So far we&amp;rsquo;ve covered the introduction of a new Rust binary (and crate) for supporting the fetching, serving and querying of CRL data, but that doesn&amp;rsquo;t provide much service to the existing ecosystem of Linux applications and libraries in the problem statement.&lt;/p&gt;
&lt;p&gt;The upki project will also provide a shared object library for a stable ABI that allows C and C-FFI programs to make revocation queries, using the contents of the on-disk filter cache.&lt;/p&gt;
&lt;p&gt;Once &lt;code&gt;upki&lt;/code&gt; is released and available, work can begin on integrating existing crypto libraries such as OpenSSL, GNUtls and rustls. This will be performed through the shared object library by means of an optional callback mechanism these libraries can use to check the revocation lists before establishing a connection to a given server with a certificate.&lt;/p&gt;
&lt;h2 id="timeline" class="relative group"&gt;Timeline &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#timeline" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;While we&amp;rsquo;ve been discussing this project for a couple of months, ironing out the details of funding and design, work will soon begin on the initial implementation of upki.&lt;/p&gt;
&lt;p&gt;Our aim is to make upki available as an opt-in preview for the release of Ubuntu 26.04 LTS, meaning we&amp;rsquo;ll need to complete the implementation of the server/client functionality, and bootstrap the mirroring/serving infrastructure at Canonical before April 2026.&lt;/p&gt;
&lt;p&gt;In the following Ubuntu release cycle, the run up to Ubuntu 26.10, we&amp;rsquo;ll aim to ship the tool by default on Ubuntu systems, and begin work on integration with the likes of NSS, OpenSSL, GNUtls and rustls.&lt;/p&gt;
&lt;h2 id="summary" class="relative group"&gt;Summary &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#summary" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;Linux has a clear gap in its handling of revocation data for PKIs. Over the coming months we&amp;rsquo;re hoping to address that gap by developing upki not just for Ubuntu, but for the entire ecosystem. Thanks to Mozilla&amp;rsquo;s work on CRLite, and the expertise of Dirkjan and Joe, we&amp;rsquo;re confident that we&amp;rsquo;ll deliver a resilient and efficient solution that should make a meaningful contribution to systems security across the web.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;d like to do more reading on the subject, I&amp;rsquo;d recommend the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;LWN.net:&lt;/strong&gt; &lt;a href="https://lwn.net/Articles/1033809/" target="_blank" rel="noreferrer"&gt;Linux&amp;rsquo;s missing CRL infrastructure&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mozilla Security Blog:&lt;/strong&gt; &lt;a href="https://blog.mozilla.org/security/2020/01/09/crlite-part-1-all-web-pki-revocations-compressed/" target="_blank" rel="noreferrer"&gt;CRLite Part 1: All Web PKI Revocations Compressed&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mozilla Security Blog:&lt;/strong&gt; &lt;a href="https://blog.mozilla.org/security/2020/01/09/crlite-part-2-end-to-end-design/" target="_blank" rel="noreferrer"&gt;CRLite Part 2: End-to-End Design&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Let’s Encrypt:&lt;/strong&gt; &lt;a href="https://letsencrypt.org/2024/07/23/replacing-ocsp-with-crls.html" target="_blank" rel="noreferrer"&gt;Replacing OCSP with CRLs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;IEEE Symposium on Security &amp;amp; Privacy:&lt;/strong&gt; &lt;a href="https://www.google.com/search?q=https://ieeexplore.ieee.org/document/7958572" target="_blank" rel="noreferrer"&gt;CRLite: A Scalable System for Pushing All TLS Revocations to All Browsers&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Secure Boot &amp; TPM-backed Full Disk Encryption on NixOS</title><link>https://jnsgr.uk/2024/04/nixos-secure-boot-tpm-fde/</link><pubDate>Sat, 20 Apr 2024 00:00:00 +0000</pubDate><guid>https://jnsgr.uk/2024/04/nixos-secure-boot-tpm-fde/</guid><description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: Since I wrote this post, I have been made aware of a particular situation where, at the time I write this (2025-01-17), the steps described in this article will result in a setup that is still (in many cases) vulnerable to an attack where the attacker has physical access to the machine. This may be acceptable in your threat model, but I&amp;rsquo;d encourage you to read the &lt;a href="https://oddlama.org/blog/bypassing-disk-encryption-with-tpm2-unlock/" target="_blank" rel="noreferrer"&gt;excellent article&lt;/a&gt; to gain a full understanding of the issue.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="introduction" class="relative group"&gt;Introduction &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#introduction" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;For the last decade (whoa&amp;hellip;) or so, I&amp;rsquo;ve defaulted to using LUKS-encrypted drives for my machines. In general, I configure an unencrypted boot/EFI partition, then place either an ext4 or btrfs filesystem inside a LUKS container which is used for the root partition.&lt;/p&gt;
&lt;p&gt;Some of my machines also have extra disks: my desktop has a 1TB NVMe drive for root, and a 2TB NVMe &amp;ldquo;data&amp;rdquo; drive mounted in my home directory under &lt;code&gt;/home/jon/data&lt;/code&gt;. I don&amp;rsquo;t like having to type two different encryption passphrases at boot, so I usually have the extra disk automatically unlocked by putting the key in a file on the root drive, and placing an entry in &lt;a href="https://www.man7.org/linux/man-pages/man5/crypttab.5.html" target="_blank" rel="noreferrer"&gt;&lt;code&gt;/etc/crypttab&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This setup works fine for desktop machines, but it&amp;rsquo;s cumbersome on headless machines because unattended reboots require the disk passphrase to be entered at boot. Even then, all of my computers are exclusively used by me, and this setup means I have to enter two passwords on every boot to get to a working desktop environment (one for the disk, and one for the login manager).&lt;/p&gt;
&lt;p&gt;I solved this recently with a combination of Secure Boot and a Trusted Platform Module (TPM), so let&amp;rsquo;s look at those first with a brief and high-level overview of each.&lt;/p&gt;
&lt;h2 id="whats-a-tpm" class="relative group"&gt;What&amp;rsquo;s a TPM? &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#whats-a-tpm" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;Most machines that have been manufactured in the last decade, and certainly in the last 5 years, contain a cryptographic coprocessor conforming to the Trusted Platform Module (TPM) &lt;a href="https://trustedcomputinggroup.org/resource/tpm-library-specification/" target="_blank" rel="noreferrer"&gt;spec&lt;/a&gt;. The TPM is a dedicated microcontroller primarily used for verifying the integrity of a machine.&lt;/p&gt;
&lt;p&gt;TPMs can be used for storing cryptographic key material and performing basic cryptographic operations. The general premise is that keys can be loaded into the TPM, which enables the TPM to perform cryptographic operations using that key (signing, encrypting, etc.), but the key cannot be recovered or read from the TPM unless certain conditions are met. TPMs also provide other facilities such as secure random number generation, which in turn enables them to securely generate cryptographic keys.&lt;/p&gt;
&lt;p&gt;Verifying system integrity essentially boils down to being able to ensure the machine hasn&amp;rsquo;t been tampered with between boots, and that the boot process itself hasn&amp;rsquo;t been compromised. A given firmware or operating system can take hardware &amp;ldquo;measurements&amp;rdquo; and store those measurements in dedicated slots called Platform Configuration Registers (PCRs). The measurements pertain to the underlying hardware and configuration of the machine. The TPM itself never performs the actual verification of the PCRs, and in fact has no knowledge of whether a measurement is inherently &amp;ldquo;good&amp;rdquo; or &amp;ldquo;bad&amp;rdquo;, but it can provide signed attestations of their values, which are then judged by the application requesting the attestation according to some policy.&lt;/p&gt;
&lt;p&gt;Each PCR contains a hash representing a particular hardware measurement, which can be read at any time, but cannot be overwritten. Rather than allowing a traditional write operation, PCRs are updated through an &amp;ldquo;extend&amp;rdquo; operation which depends on the previous hash value, creating a chain of trust not dissimilar from how a blockchain is formed. This means that a given measurement can never be fully removed from the TPM.&lt;/p&gt;
&lt;p&gt;Once the measurements are stored in the PCRs, there are various times and purposes for which the firmware or an operating system might read them - one example is for &lt;a href="https://www.gradient.tech/faq-items/what-are-platform-configuration-registers-pcrs/" target="_blank" rel="noreferrer"&gt;remote attestation&lt;/a&gt; during login to a system. In this scenario the attestation can be used to verify that the machine hasn&amp;rsquo;t been tampered with (perhaps in an &lt;a href="https://en.wikipedia.org/wiki/Evil_maid_attack" target="_blank" rel="noreferrer"&gt;Evil Maid&lt;/a&gt; attack). One could also store a Certificate Authority signing key in a TPM, and have the Certificate Authority software interface with the TPM to sign certificates using the &lt;a href="https://en.wikipedia.org/wiki/PKCS_11" target="_blank" rel="noreferrer"&gt;PKCS#11 standard&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;My use-case is to enabling the TPM to provide the passphrase to unlock a LUKS-encrypted disk, which is what I&amp;rsquo;ll focus on in this post.&lt;/p&gt;
&lt;h2 id="secure-boot" class="relative group"&gt;Secure Boot &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#secure-boot" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;Secure Boot is the mechanism by which the code executed by a machine&amp;rsquo;s &lt;a href="https://en.wikipedia.org/wiki/UEFI" target="_blank" rel="noreferrer"&gt;Unified Extensible Firmware Interface (UEFI)&lt;/a&gt; can be verified as trusted. In the vast majority of cases, the first thing executed by the UEFI is a bootloader.&lt;/p&gt;
&lt;p&gt;When Secure Boot is enabled, each binary executed by the UEFI must contain a checksum and a signature - which the UEFI verifies before launching the code. In the case that either the checksum or signature do not match, the UEFI will refuse the execute the code, and the boot process will halt.&lt;/p&gt;
&lt;p&gt;Many OEM machines ship with Microsoft Windows installed, and thus ship with the necessary keys to validate signatures created with Microsoft&amp;rsquo;s certificate authority. Linux systems are able to utilise these keys through &lt;a href="https://github.com/rhboot/shim" target="_blank" rel="noreferrer"&gt;&lt;code&gt;shim&lt;/code&gt;&lt;/a&gt; - a small and easily verifiable piece of software which is signed by Microsoft. &lt;code&gt;shim&lt;/code&gt; sits between the UEFI and the bootloader in the boot process, obviating the need for every Linux bootloader to be signed by Microsoft on every release. The shim is designed to &lt;em&gt;extend trust&lt;/em&gt; from the keys trusted by the computer&amp;rsquo;s firmware to a new set of keys controlled by the operating system.&lt;/p&gt;
&lt;p&gt;But what does Secure Boot get us in reality? By signing the kernel, and in some cases a single UEFI PE binary known as a &lt;a href="https://wiki.archlinux.org/title/Unified_kernel_image" target="_blank" rel="noreferrer"&gt;Unified Kernel Image (UKI)&lt;/a&gt; (which contains the bootloader, the kernel, the command-line used to boot the kernel, and &lt;a href="https://uapi-group.org/specifications/specs/unified_kernel_image/#uki-components" target="_blank" rel="noreferrer"&gt;other resources&lt;/a&gt;), one can be reasonably sure that the boot process hasn&amp;rsquo;t been tampered with.&lt;/p&gt;
&lt;p&gt;This process thwarts a number of common physical attack vectors, such as &lt;a href="https://linuxconfig.org/recover-reset-forgotten-linux-root-password" target="_blank" rel="noreferrer"&gt;manipulating the kernel command line to bypass the machine&amp;rsquo;s login&lt;/a&gt; and drop straight to a root shell - and combined with disk encryption can prevent offline data transfer from the machine. It also defends against malware which compromises the operating system&amp;rsquo;s boot process such that it can start before the OS and obfuscate it&amp;rsquo;s presence.&lt;/p&gt;
&lt;h2 id="threat-modelling" class="relative group"&gt;Threat Modelling &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#threat-modelling" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;As with any security measure, Secure Boot is not a silver bullet. You should always consider your own personal threat model, and the sorts of attacks you&amp;rsquo;re looking to defend against.&lt;/p&gt;
&lt;p&gt;For example, Secure Boot can help prevent the sort of malware infections described in the previous section, but if an attacker gets physical access to your machine, and the UEFI isn&amp;rsquo;t adequately protected, they could simply disable secure boot and carry on unhindered.&lt;/p&gt;
&lt;p&gt;I mitigate this by password protecting the UEFI. This isn&amp;rsquo;t perfect, but is likely sufficient protection for my threat model which is more about protecting chancers and petty thieves from gaining access to my information, than from determined attackers who gain physical access to my property.&lt;/p&gt;
&lt;p&gt;Storing keys in a TPM is &lt;em&gt;theoretically&lt;/em&gt; safe, in that each TPM has a unique seed which cannot be retrieved, and enables the TPM to deterministically generate keys between reboots. It&amp;rsquo;s &lt;em&gt;very difficult&lt;/em&gt; to retrieve the seed, and thus &lt;em&gt;very difficult&lt;/em&gt; to duplicate a TPM, but not impossible. Even then, the Linux kernel&amp;rsquo;s communication with the TPM on-the-wire is unencrypted, and the same can be said for many other subsystems which use the TPM. A &lt;a href="https://hackaday.com/2024/02/06/beating-bitlocker-in-43-seconds/" target="_blank" rel="noreferrer"&gt;recent example&lt;/a&gt; of this vulnerability was demonstrated by sniffing a Bitlocker key off the &lt;a href="https://en.wikipedia.org/wiki/Low_Pin_Count" target="_blank" rel="noreferrer"&gt;LPC bus&lt;/a&gt; in a Lenovo X1 Carbon laptop (using a Raspberry Pi Pico, no less). In many modern machines this is mitigated by the TPM being on-CPU, but the point still stands.&lt;/p&gt;
&lt;p&gt;I choose to enroll Microsoft&amp;rsquo;s platform keys, which in theory degrades the security of my device in the case that Microsoft&amp;rsquo;s signing key is compromised, though all of my machines are compatible with &lt;code&gt;fwupd&lt;/code&gt; and can receive updates to the database through that mechanism if required (and in fact have done in the &lt;a href="https://uefi.org/revocationlistfile/archive" target="_blank" rel="noreferrer"&gt;past 18 months&lt;/a&gt;). This could be further mitigated by using custom keys and certificates for the full chain, but this is more overhead for daily operations and updates. It&amp;rsquo;s also worth considering whether you have the resources to fully secure your own chain - especially by comparison to Microsoft who spend tens of millions of dollars per year on security. If an attacker wants your information, and are able to compromise Microsoft&amp;rsquo;s CA, your own CA may not be such a hurdle.&lt;/p&gt;
&lt;p&gt;Security measures are always a trade-off between Confidentiality, Availability and Integrity (CIA). In general, the more rigidly secure boot is implemented and configured, the more you&amp;rsquo;re protecting confidentiality and integrity. The choices I&amp;rsquo;ve made are slightly more in favour of availability, but nonetheless raise the bar for any attacker significantly.&lt;/p&gt;
&lt;h2 id="enabling-secure-boot-on-nixos" class="relative group"&gt;Enabling Secure Boot on NixOS &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#enabling-secure-boot-on-nixos" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;Now for the fun part! The process for enabling Secure Boot on NixOS has simplified in recent months owing to the creation of &lt;a href="https://github.com/nix-community/lanzaboote" target="_blank" rel="noreferrer"&gt;&lt;code&gt;lanzaboote&lt;/code&gt;&lt;/a&gt; - a project which takes of preparing and signing Unified Kernel Images containing a custom stub, the bootloader, the Linux kernel, the kernel&amp;rsquo;s &lt;code&gt;initrd&lt;/code&gt; and the kernel command line. &lt;code&gt;lanzaboote&lt;/code&gt; also takes care of installing the UKI on the &lt;a href="https://en.wikipedia.org/wiki/EFI_system_partition" target="_blank" rel="noreferrer"&gt;ESP partition&lt;/a&gt; so the UEFI can execute it at boot.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;lanzaboote&lt;/code&gt; stub differs slightly from &lt;a href="https://www.freedesktop.org/software/systemd/man/latest/systemd-stub.html" target="_blank" rel="noreferrer"&gt;&lt;code&gt;systemd-stub&lt;/code&gt;&lt;/a&gt;, in that it doesn&amp;rsquo;t require the kernel and initrd to be part of the UKI. This is important for a generation-based operating system like NixOS because bundling the kernel and initrd into a new UKI for every generation would consume a lot of disk space, and quickly exhaust the ESP on most machines. In &lt;code&gt;lanzaboote&lt;/code&gt;&amp;rsquo;s implementation, the kernel and initrd are stored separately on the ESP, and the chain of trust is preserved by validating the signature of the kernel, and embedding a cryptographic hash of the initrd into the signed UKI.&lt;/p&gt;
&lt;p&gt;The project takes advantage of systems that have &lt;a href="https://github.com/NixOS/rfcs/blob/master/rfcs/0125-bootspec.md" target="_blank" rel="noreferrer"&gt;bootspec&lt;/a&gt; enabled, which is a relatively recent NixOS RFC that ensures configured machines maintain a file containing a set of memoised facts about a system&amp;rsquo;s closure. Bootspec aims to &amp;ldquo;provide more uniform feature support&amp;rdquo; to bootloaders in the NixOS ecosystem and &amp;ldquo;enable NixOS users to implement custom bootloader tools and policy&amp;rdquo; - of which &lt;code&gt;lanzaboote&lt;/code&gt; is one.&lt;/p&gt;
&lt;h3 id="generating-secure-boot-keys" class="relative group"&gt;Generating Secure Boot Keys &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#generating-secure-boot-keys" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;The first step is to generate some keys for the secure boot process. This can be achieved using the &lt;a href="https://search.nixos.org/packages?channel=unstable&amp;amp;show=sbctl&amp;amp;from=0&amp;amp;size=50&amp;amp;sort=relevance&amp;amp;type=packages&amp;amp;query=sbctl" target="_blank" rel="noreferrer"&gt;&lt;code&gt;sbctl&lt;/code&gt; package&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&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 run nixpkgs#sbctl create-keys
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Created Owner UUID 6ac34cc3-a23d-9745-ef33-a03f523d20a3
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Creating secure boot keys...✓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Secure boot keys created!
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;This should only take a few seconds at maximum, and will result in a set of keys being populated in &lt;code&gt;/etc/secureboot&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&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;❯ tree /etc/secureboot
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/etc/secureboot
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── files.db
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── GUID
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;└── keys
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ├── db
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ ├── db.key
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ └── db.pem
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ├── dbx
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ ├── dbx.key
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ └── dbx.pem
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ├── KEK
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ ├── KEK.key
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ └── KEK.pem
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └── PK
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ├── PK.key
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └── PK.pem
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="enable-bootspec" class="relative group"&gt;Enable Bootspec &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#enable-bootspec" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;Ensure that &lt;code&gt;bootspec&lt;/code&gt; is enabled in your Nix configuration. You can see this in my flake for my desktop machine &lt;a href="https://github.com/jnsgruk/nixos-config/blob/e436e046f19c76fcea0ac2570e7a747153c02ad5/host/kara/boot.nix#L5" target="_blank" rel="noreferrer"&gt;on Github&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&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;boot&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bootspec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;enabled&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;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="enable-lanzaboote" class="relative group"&gt;Enable &lt;code&gt;lanzaboote&lt;/code&gt; &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#enable-lanzaboote" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;I use a &lt;a href="https://nixos.wiki/wiki/Flakes" target="_blank" rel="noreferrer"&gt;flake&lt;/a&gt; to configure all of my machines, so I&amp;rsquo;m able to get access to &lt;code&gt;lanzaboote&lt;/code&gt; by adding the upstream flake as &lt;a href="https://github.com/jnsgruk/nixos-config/blob/e436e046f19c76fcea0ac2570e7a747153c02ad5/flake.nix#L25-L26" target="_blank" rel="noreferrer"&gt;an input&lt;/a&gt; to my own &lt;a href="https://github.com/jnsgruk/nixos-config" target="_blank" rel="noreferrer"&gt;nixos-config flake&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&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;# ...&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;lanzaboote&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:nix-community/lanzaboote&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="c1"&gt;# ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Once you&amp;rsquo;ve added &lt;code&gt;lanzaboote&lt;/code&gt; as a dependency, you&amp;rsquo;ll need to import the &lt;code&gt;lanzaboote&lt;/code&gt; module:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&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;# ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;imports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;lanzaboote&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;lanzaboote&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;# ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;In my flake, I use a custom &lt;a href="https://github.com/jnsgruk/nixos-config/blob/e436e046f19c76fcea0ac2570e7a747153c02ad5/lib/helpers.nix#L39" target="_blank" rel="noreferrer"&gt;helper function&lt;/a&gt; to build NixOS configurations, so the module is &lt;a href="https://github.com/jnsgruk/nixos-config/blob/e436e046f19c76fcea0ac2570e7a747153c02ad5/lib/helpers.nix#L59" target="_blank" rel="noreferrer"&gt;passed directly&lt;/a&gt; to &lt;code&gt;lib.nixosSystem&lt;/code&gt; through the &lt;code&gt;modules&lt;/code&gt; attribute.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;lanzaboote&lt;/code&gt; module replaces the &lt;code&gt;systemd-boot&lt;/code&gt; module, and as such you must explicitly &lt;em&gt;disable&lt;/em&gt; &lt;code&gt;systemd-boot&lt;/code&gt; when enabling &lt;code&gt;lanzaboote&lt;/code&gt;. Additionally, if you wish to use the TPM for disk unlock (described in the next section), you must use the systemd initrd hooks (or something like &lt;a href="https://github.com/latchset/clevis/" target="_blank" rel="noreferrer"&gt;&lt;code&gt;clevis&lt;/code&gt;&lt;/a&gt;):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&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="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;initrd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;systemd&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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&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="n"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mkForce&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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&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;/etc/secureboot&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;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;This is represented in my config &lt;a href="https://github.com/jnsgruk/nixos-config/blob/e436e046f19c76fcea0ac2570e7a747153c02ad5/host/kara/boot.nix#L6-L10" target="_blank" rel="noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Once enabled, rebuild your system (in my case with &lt;code&gt;sudo nixos-rebuild switch --flake /home/jon/nixos-config&lt;/code&gt;) and verify that your machine is ready for Secure Boot. Don&amp;rsquo;t panic about the kernel images being reported as not signed, this is expected:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;❯ sudo nix run unstable#sbctl verify
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Verifying file database and EFI images in /boot...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;✓ /boot/EFI/BOOT/BOOTX64.EFI is signed
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;✓ /boot/EFI/Linux/nixos-generation-414-376jna572gsb23snqs67t7s4bwxzb3epblmdnzweghuepopml2va.efi is signed
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;✓ /boot/EFI/Linux/nixos-generation-415-iqulgohymbdppgtxzho6ou3fcuxjbxhumpzm4vojmipwy3sbmuna.efi is signed
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;✓ /boot/EFI/Linux/nixos-generation-416-kxnzioafnduwwck3oypo7rqwtoat745czp2bpehoufp4yqiawypa.efi is signed
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;✗ /boot/EFI/nixos/kernel-6.8.2-242idodyvf36cpl6s5dskjy6mo4tjhszuwa3hye7qcjyuo5vnehq.efi is not signed
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;✗ /boot/EFI/nixos/kernel-6.8.5-zqulrwsucm6okcyns6v2jhh6fregk3bvsdth3yloqfymfbgnh64a.efi is not signed
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;✗ /boot/EFI/nixos/kernel-6.8.7-6mmixkr6ewywm5swgbi5ethbpgnyia4borzmkevcjx7n7t3mtida.efi is not signed
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;✓ /boot/EFI/systemd/systemd-bootx64.efi is signed
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="prepare-the-uefi" class="relative group"&gt;Prepare the UEFI &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#prepare-the-uefi" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;Reboot your machine and enter the UEFI interface. This part of the process will vary from machine to machine depending on the UEFI implementation, but you&amp;rsquo;re looking to enable Secure Boot, and clear the preloaded Secure Boot keys. This may be referred to as &amp;ldquo;Setup Mode&amp;rdquo;, or erasing the &amp;ldquo;Platform Keys&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;While you&amp;rsquo;re here, I&amp;rsquo;d also advise setting a UEFI password before rebooting back into NixOS.&lt;/p&gt;
&lt;h3 id="enroll-secure-boot-keys" class="relative group"&gt;Enroll Secure Boot Keys &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#enroll-secure-boot-keys" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;The final stage in the process is to enroll your newly generated Secure Boot keys from step 1 into the UEFI. This is again achieved with &lt;code&gt;sbctl&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;❯ sudo nix run nixpkgs#sbctl enroll-keys -- --microsoft
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Enrolling keys to EFI variables...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;With vendor keys from microsoft...✓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Enrolled keys to the EFI variables!
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;I chose to use the &lt;code&gt;--microsoft&lt;/code&gt; option to also enroll the UEFI vendor certificates from Microsoft. Some systems contain firmware that is signed and validated when Secure Boot is enabled, and omitting the Microsoft keys could prevent your device from booting - omit this option with caution!&lt;/p&gt;
&lt;h3 id="verify-secure-boot" class="relative group"&gt;Verify Secure Boot &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#verify-secure-boot" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;Once you&amp;rsquo;ve enrolled the keys, reboot the machine back into NixOS and use &lt;code&gt;bootctl&lt;/code&gt; to confirm that Secure Boot is in fact enabled:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;❯ bootctl status
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;System:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Firmware: UEFI 2.80 &lt;span class="o"&gt;(&lt;/span&gt;American Megatrends 5.26&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Firmware Arch: x64
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Secure Boot: enabled &lt;span class="o"&gt;(&lt;/span&gt;user&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; TPM2 Support: yes
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Boot into FW: supported
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="tpm-unlock-of-root-partition" class="relative group"&gt;TPM Unlock of Root Partition &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#tpm-unlock-of-root-partition" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;Now that we&amp;rsquo;re (reasonably) confident that no one can tamper with the boot process, we can progress to allowing the machine to auto-unlock the encrypted disk using a key stored in the TPM.&lt;/p&gt;
&lt;p&gt;This is actually more common than you might think - Windows has enabled this behaviour by default for some time with Bitlocker disk encryption, and Canonical is also &lt;a href="https://ubuntu.com/blog/tpm-backed-full-disk-encryption-is-coming-to-ubuntu" target="_blank" rel="noreferrer"&gt;working on&lt;/a&gt; bringing TPM-backed full disk encryption to Ubuntu.&lt;/p&gt;
&lt;p&gt;This is probably the easiest step of them all! A simple invocation of &lt;code&gt;systemd-cryptenroll&lt;/code&gt; is all that&amp;rsquo;s required. The arguments below instruct the machine that PCRs 0, 2, 7 and 12 should be measured and verified before the TPM is allowed to unlock the disk.&lt;/p&gt;
&lt;p&gt;According to the &lt;a href="https://uapi-group.org/specifications/specs/linux_tpm_pcr_registry/" target="_blank" rel="noreferrer"&gt;Linux TPM PCR Regsitry&lt;/a&gt;, this means the following are measured before the LUKS key is presented:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;PCR 0: Core system firmware executable code&lt;/li&gt;
&lt;li&gt;PCR 2: Extended or pluggable executable code&lt;/li&gt;
&lt;li&gt;PCR 7: SecureBoot state&lt;/li&gt;
&lt;li&gt;PCR 12: Kernel command line, system credentials and system configuration images&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&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;0+2+7+12 --wipe-slot&lt;span class="o"&gt;=&lt;/span&gt;tpm2 /dev/nvme0n1p2
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;And that&amp;rsquo;s it! The next time you reboot, your disk should be automatically unlocked by the TPM, and your machine should boot straight to your display manager, or the TTY login if no display manager is configured.&lt;/p&gt;
&lt;h2 id="useful-resources" class="relative group"&gt;Useful Resources &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#useful-resources" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;None of the knowledge in this post is novel, but rather the culmination of some knowledge acquired over the past few years, and some more targeted reading more recently. In the process, I learned a bunch from the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/nix-community/lanzaboote" target="_blank" rel="noreferrer"&gt;Lanzaboote on Github&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://wiki.archlinux.org/title/Unified_Extensible_Firmware_Interface/Secure_Boot" target="_blank" rel="noreferrer"&gt;ArchWiki on UEFI Secure Boot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://discourse.nixos.org/t/full-disk-encryption-tpm2/29454" target="_blank" rel="noreferrer"&gt;NixOS Discourse Post on TPM Unlock&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/NixOS/rfcs/blob/master/rfcs/0125-bootspec.md" target="_blank" rel="noreferrer"&gt;Bootspec RFC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fosdem.org/2024/schedule/event/fosdem-2024-1985-ukis-tpms-immutable-initrds-and-full-disk-encryption-what-distributions-should-keep-in-mind-when-hopping-onto-the-system-integrity-train/" target="_blank" rel="noreferrer"&gt;Lennart Poettering&amp;rsquo;s UKI Talk at FOSDEM 2024&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fosdem.org/2024/schedule/event/fosdem-2024-3141-linux-kernel-tpm-security-and-trusted-key-updates/" target="_blank" rel="noreferrer"&gt;James Bottomley&amp;rsquo;s TPM Talk at FOSDEM 2024&lt;/a&gt;
&lt;a href="https://ericchiang.github.io/post/tpm-keys/" target="_blank" rel="noreferrer"&gt;The Trusted Platform Module Key Hierarchy&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="summary" class="relative group"&gt;Summary &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#summary" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;About 5 years ago, I was sporting a fully secure-boot enabled Dell XPS 13 running Arch Linux. Back then, the process was complicated, manual, and required a lot of maintenance between upgrades. For me, it was more pain than gain, but an interesting learning experience nonetheless.&lt;/p&gt;
&lt;p&gt;When I sat down earlier this year to enable Secure Boot on NixOS, I&amp;rsquo;d set aside a few hours. I was astounded that 10 minutes later I was finished. I wrote this post as a memo to my future self, but also to illustrate how simple it can be to enable Secure Boot and TPM disk unlock in 2024.&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t claim to be an expert on the inner workings of TPMs, nor Secure Boot. The things I can say for certain are that TPMs are complex, that there are improvements that could be made to Linux&amp;rsquo;s interactions with the TPM, and that a determined and well-resourced attacker is likely going to succeed one way or another.&lt;/p&gt;
&lt;p&gt;If you spot an inaccuracy in this post, reach out and let me know on Mastodon, on Telegram, by email, or however you prefer!&lt;/p&gt;
&lt;p&gt;Until next time!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Update 2024/04/29: Thanks to &lt;a href="https://github.com/pimeys/" target="_blank" rel="noreferrer"&gt;@pimeys&lt;/a&gt; for pointing out that one must enable the systemd initrd hooks &lt;code&gt;systemd-cryptenroll&lt;/code&gt; to function correctly, and also that PCR 12 must be measured to prevent the LUKS key from being released if the kernel command line has been modified.&lt;/p&gt;
&lt;/blockquote&gt;</description></item><item><title>Zero-Trust SSH on Microsoft Azure</title><link>https://jnsgr.uk/2020/10/zero-trust-ssh-azure/</link><pubDate>Mon, 12 Oct 2020 00:00:00 +0000</pubDate><guid>https://jnsgr.uk/2020/10/zero-trust-ssh-azure/</guid><description>&lt;blockquote&gt;
&lt;p&gt;This post was originally posted &lt;a href="https://www.linkedin.com/pulse/zero-trust-ssh-microsoft-azure-jon-seager/" target="_blank" rel="noreferrer"&gt;on LinkedIn&lt;/a&gt; on 12 October 2020. I&amp;rsquo;ve posted it to my blog retrospectively, but the article is unchanged. SSHizzle was released as open source software back in 2020, but has not received any maintenance since I left Thales. I do not recommend deploying SSHizzle, but the article will hopefully remain interesting for the background and principles behind the project.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="introduction" class="relative group"&gt;Introduction &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#introduction" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;Since its introduction, Secure Shell (ssh) has become the defacto solution for remotely managing Linux and Unix based systems, and has become only more prolific with the increasing popularity of the cloud. I can&amp;rsquo;t think of a cloud provider that supplies access to Linux machines for whom SSH is not the default, and likely most active way for their customers to manage their resources. The same applies in large enterprise networks, with OpenSSH Server and Client packages even available in Windows 10 and Windows Server 2019 in more recent releases.&lt;/p&gt;
&lt;p&gt;A key challenge with SSH is managing the authentication of trusted users at scale. OpenSSH supports three options by default, including password authentication, key authentication and certificate authentication. All of these methods pose varying challenges both to operations and security.&lt;/p&gt;
&lt;h2 id="hello-sshizzle" class="relative group"&gt;Hello, SSHizzle &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#hello-sshizzle" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;Today, I&amp;rsquo;m delighted to release &lt;a href="https://github.com/thalesgroup/sshizzle" target="_blank" rel="noreferrer"&gt;SSHizzle&lt;/a&gt; on behalf of Thales Secure Communications &amp;amp; Information Systems UK, a tool designed to illustrate the simplified management of SSH users and authentication both for administrators and users using commodity cloud resources. SSHizzle was inspired by the work of Jeremy Stott (&lt;a href="https://github.com/stoggi" target="_blank" rel="noreferrer"&gt;@stoggi&lt;/a&gt;) and the &lt;a href="https://github.com/stoggi/sshrimp" target="_blank" rel="noreferrer"&gt;sshrimp&lt;/a&gt; project which implements a similar solution for AWS.&lt;/p&gt;
&lt;p&gt;SSHizzle strives to be as simple to configure, use and run as possible. It benefits from tight integration with Microsoft Azure Active Directory, Azure Key Vault and works seamlessly with almost all existing workflows. SShizzle simplifies the adoption and management of SSH Certificates.&lt;/p&gt;
&lt;p&gt;In summary, SSHizzle aims to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Reduce the complexity of on-boarding new users into environments&lt;/li&gt;
&lt;li&gt;Provide a good user experience to engineers and developers&lt;/li&gt;
&lt;li&gt;Increase the observability of SSH credentials&lt;/li&gt;
&lt;li&gt;Eliminate the need to distribute many public keys in production&lt;/li&gt;
&lt;li&gt;Reduce the need to manage the lifecycle of SSH keys in production&lt;/li&gt;
&lt;li&gt;Be compatible with existing SSH-based workflows&lt;/li&gt;
&lt;li&gt;Require minimal configuration of hosts and servers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;SSHizzle comprises of two key components:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A serverless SSH Certificate Authority (CA)&lt;/li&gt;
&lt;li&gt;An SSH Agent for client machines&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="sshizzles-serverless-certificate-authority" class="relative group"&gt;SSHizzle&amp;rsquo;s Serverless Certificate Authority &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#sshizzles-serverless-certificate-authority" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;SSHizzle&amp;rsquo;s SSH Certificate Authority (CA) stores its key in an Azure Key Vault and operates from a serverless function operated by Azure Functions. Azure Key Vault is a managed service provided by Microsoft that enables the secure storage and management of secrets, keys and certificates. In the case of SSHizzle, the Azure Key Vault stores the private key for the CA.&lt;/p&gt;
&lt;p&gt;A serverless function is, generally, a programmatic function whose execution is triggered and run ephemerally on managed infrastructure. This means there is no dedicated server consistently running or listening for connections as with traditional server deployments. In the case of SSHizzle, the serverless function is triggered by an HTTP listener, which when called by an authenticated user triggers the Azure Functions backend to run the function on any available, suitable infrastructure. Once the function has run and returned, the context that executed the function (perhaps a container or a VM in the underlying implementation) is destroyed. This has numerous benefits including more granular billing (serverless functions are often billed by the minute for execution), but also a reduced attack surface as the authentication and frontend to the function are all controlled by Microsoft Azure.&lt;/p&gt;
&lt;p&gt;SSHizzle&amp;rsquo;s backend function can only be invoked by a user authenticated against its parent Azure Active Directory tenant. Once a user has authenticated, it takes a randomly generated SSH public key, signs it using the key stored in the Azure Key Vault, and returns an SSH certificate to the agent that invoked the function.&lt;/p&gt;
&lt;p&gt;The SSH certificate has a number of key advantages over traditional SSH keys:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Expiry Time&lt;/strong&gt;: SSHizzle only issues credentials valid for 2 minutes, eliminating the need to manage the lifecycle of SSH user credentials.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Access Limitations&lt;/strong&gt;: SSHizzle will issue credentials that are only valid for use from the IP address that requested them. Should the credential be intercepted on the way back to the agent, it will be invalid for use unless the connection is from the original IP.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Forced Commands&lt;/strong&gt;: SSHizzle does not yet implement a UI to manage this feature but with minor modifications, SSH certificates can be issued that are only valid for specific command executions. This allows the issuance of single-purpose, scope limited credentials; essentially a light form of Privileged Access Management.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Feature Restriction&lt;/strong&gt;: SSH certificates have the ability to enable or disable certain SSH session functionality such as Agent Forwarding, Port Forwarding, X11 Forwarding etc.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Observable ID&lt;/strong&gt;: The ID of each certificate (which appears in auth logs and system logs) contains key information to track how and when each credential was issued - making forensic investigation easier.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="sshizzles-ssh-agent" class="relative group"&gt;SSHizzle&amp;rsquo;s SSH Agent &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#sshizzles-ssh-agent" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;An SSH agent performs the function of a key manager for SSH. In a typical setup, a user might store their SSH keys in their home directory. The SSH agent is a background process that is invoked when the user attempts to SSH into a remote host. Its job is to sign challenges with the user&amp;rsquo;s private keys in order to authenticate with a remote host. An SSH Agent cannot perform any function other than signing messages; it does not write key material to disk and it does not permit the export of private keys.&lt;/p&gt;
&lt;p&gt;Where the default SSH Agent reads its private keys from the user&amp;rsquo;s home directory, SSHizzle&amp;rsquo;s agent behaves quite differently because it needs to have a public key signed by the key stored in the Azure Key Vault. If a user&amp;rsquo;s SSH config invokes the SSHizzle Agent to authenticate with a host, the following process occurs:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The agent generates a new SSH public/private key pair.&lt;/li&gt;
&lt;li&gt;The agent checks if it is authenticated with Azure Active Directory. If required, the agent opens a browser and asks the user to authenticate.&lt;/li&gt;
&lt;li&gt;The agent invokes the serverless function, passing it the generated public key.&lt;/li&gt;
&lt;li&gt;The serverless function signs the public key and returns an SSH certificate to the agent.&lt;/li&gt;
&lt;li&gt;The agent uses the newly acquired certificate to authenticate with the host.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="benefits" class="relative group"&gt;Benefits &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#benefits" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;Using this approach, SSHizzle is able to meet its goals:&lt;/p&gt;
&lt;p&gt;Onboarding complexity is reduced; irrespective of the class of user (administrator, developer, analyst, etc.), if a user exists in the Azure Active Directory tenant, and possess the relevant roles, they can authenticate using SSHizzle.&lt;/p&gt;
&lt;p&gt;The user experience is slick; users are prompted to authenticate with Azure AD in the same way they are to interact with Microsoft 365 and any other services available through the tenant. Additionally, they can use the same frictionless multi-factor authentication methods such as the Microsoft Authenticator app with no additional setup. This not only provides a consistent UX, but increases the security of the solution.&lt;/p&gt;
&lt;p&gt;Credential issuance is more observable; the certificate IDs are verbose and easy to identify in system logs. There is detailed audit information available about specific serverless function invocations through the Azure Portal.&lt;/p&gt;
&lt;p&gt;There is no need to distribute many keys to SSH hosts; servers must simply be configured to trust a single user certificate authority.&lt;/p&gt;
&lt;p&gt;Credential lifecycle management complexity is reduced; because credentials are only valid for 2 minutes by default, there is no need to revoke credentials. There is little chance that a valid SSH certificate will leak. Off-boarding users is as simple as disabling/removing their account in Azure Active Directory.&lt;/p&gt;
&lt;p&gt;The SSHizzle Agent is compatible with existing workflows; provided the user&amp;rsquo;s local SSH config is configured such that all hosts requiring SSHizzle authentication invoke the agent, then other applications like scp, rsync and Visual Studio Code Remote Development work seamlessly. This configuration is particularly simple when combined with wildcard host matching - i.e. configure all SSH connections to *.your-corp-domain.org to invoke the agent.&lt;/p&gt;
&lt;p&gt;SSHizzle is trivial to configure; servers require the inclusion of a single CA public key file, and single line of config in the sshd_config file. On client machines, the SSHizzle Agent is a single binary that can be started in the background.&lt;/p&gt;
&lt;p&gt;SSHizzle is extremely cost effective to deploy and run. Azure Functions are free for the first 1,000,000 invocations per month, and the corresponding storage and key vault operations aren&amp;rsquo;t likely to total more than a a few tens of dollars per month with moderate usage.&lt;/p&gt;
&lt;h2 id="limitations-and-possible-extensions" class="relative group"&gt;Limitations and Possible Extensions &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#limitations-and-possible-extensions" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;As previously alluded to, the released version of SSHizzle is a proof-of-concept. It demonstrates the concept of utilising a serverless function performing the role of an SSH Certificate Authority, combined with a strong identity provider to simplify the adoption and management of SSH Certificates across an enterprise.&lt;/p&gt;
&lt;p&gt;Currently, it lacks a few features one might consider useful for widespread adoption:&lt;/p&gt;
&lt;p&gt;Per-user policy for ForceCommands and session features (port forwarding, etc.)&lt;/p&gt;
&lt;p&gt;Ability to check if the user authenticating should be interacting with the specified host over SSH, currently if the user is valid in the tenant, a certificate is issued. Their actual access to the end host is governed by whether or not an account exists with their username on the server.&lt;/p&gt;
&lt;p&gt;Provisioning of a Host Certificate Authority to avoid host key warnings and prompts&lt;/p&gt;
&lt;p&gt;Exercise caution before deploying into production, but do experiment, and perhaps even submit a pull request&amp;hellip;&lt;/p&gt;
&lt;h2 id="try-it-out" class="relative group"&gt;Try it out! &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#try-it-out" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;The code for SSHizzle is available &lt;a href="https://github.com/thalesgroup/sshizzle" target="_blank" rel="noreferrer"&gt;on Github&lt;/a&gt;, complete with Terraform automation to deploy demonstration resources on Microsoft Azure.&lt;/p&gt;</description></item></channel></rss>