Security After Remote Attestation - Liam Wisehart
Liam Wisehart
SubscribeSign in
Security After Remote Attestation<br>For what happens after boot
Liam Wisehart<br>Jul 22, 2026
Share
In the previous post I talked about how we can remotely attest and trust hosts. This led to a spirited discussion on hacker news.<br>There were some good points about vendor lock in and the (supposed) death of general purpose computing. There are always tradeoffs. But the truth is that for the most part you don’t want a 100% general purpose computer. You want a device that does exactly what you want it to do, and not much else.<br>I don’t want my router to DDOS the internet.<br>I don’t want my phone or other devices to spy on me (any more then they already do!)<br>I don’t want my laptop to encrypt all my files and extort me for the decryption key.<br>And at work, I want my servers to run production workloads, not malware.<br>So how do we make that happen? It takes a lot more then just remote attestation. That was just the first step.
I want the train to stay on the tracks.<br>If you haven’t read the previous post about remote attestation, a lot of this post will not make sense. So go read that first.<br>So now that the host was booted in a way we can trust, how do we maintain that trust? How do we prove at any given moment that a host should still be trusted?<br>We can’t.<br>Once a computer has booted it can do more or less anything, and the problem space rapidly expands to be unmanageable. How can I scan every byte of memory, every file, every packet, and prove that a host isn’t compromised? It is an intractable problem.<br>I can’t just grab a host of the rack and prove that it should be trusted. But I can start a host in a way that it is significantly harder for it to go off the rails later, and then prove at boot that the host has those guardrails.<br>Some example guardrails:<br>Only trusted code can be run in the kernel (signed kernel modules)
Only trusted code can have root, or maybe only trusted code can run at all (signed binaries)
System files can’t be overwritten (immutable filesystem)
The EDR (Endpoint Detect and Respond) is running
My LSM (Linux Security Module) rules of choice are engaged
It goes on…
If we can lock down enough of the different directions a system can go in, we make our attacker’s life that much harder. We have this one shot, at the beginning, where we have the crypto that backs up a claim about the state of the system. If that claim includes a bunch of security features, then we know that (at least at one point in time), those security features where on, and that an attacker’s life is that much harder.<br>Also all of our logs can be signed with the LDevID we minted as part of remote attestation, which makes them unspoofable, which is a neat property.<br>dm-verity
The best guardrail is to make things immutable. If state simply can’t be changed after boot then we have nothing to worry about. The train can’t leave the tracks.<br>dm-verity is kernel level block device verification, originally developed for android. It makes whatever filesystem it is applied to immutable and allows recording a hash of the filesystem contents so the kernel can detect tampering. If the filesystem is changed (even out of band, say by someone writing to the device directly) the kernel can detect that modification.<br>This verification is done efficiently using a Merkle tree. TLDR a Merkle tree speeds up hashing by constructing a tree of blocks that contain the raw bytes of the image. Instead of a naive hash which has time complexity N with respect to the number of blocks (it has to touch each one), hashing a Merkle tree has time complexity log N to validate the contents of a block. The root hash is what we save/sign, and what will be changed if any block in the tree is modified.<br>The way to think about it is like this. I can’t efficiently record the hash of every block. I want just a single hash that tells me “was the filesystem changed or not.” We want a single hash for the whole filesystem. And we can’t just hash the whole filesystem on each read for obvious reasons. Instead we store the root hash of the Merkle tree, and walk the tree from leaf to root every time a block is read, and check that the root hash computes correctly.<br>The reason this works is that each node’s hash is the hash of child nodes. Walking up the tree from one leaf to the root, only sibling nodes need to be explored. This sounds expensive, and is by some standards (performance numbers vary heavily by workload), but the kernel caches pages in the page cache, and the tree itself, keeping things fast in most cases.<br>The point of this exercise is that any mutation invalidates the root hash. We can keep that root hash somewhere we attested (like the kernel command line) or sign it, if the dm-verity partition is not included in the measured boot.<br>Upgrading Immutable Systems
dm-verity makes your filesystem completely immutable. If you are using dm-verity with your root filesystem, then you can’t update...