fs-verity is a support layer built into the Linux kernel to provide integrity and authenticity protection for read-only files, and it can retrieve file hashes in constant time. This provides a mechanism to ensure the file hasn’t been tampered with, even for very large files, without hashing the entire file, and it uses a Merkle tree to validate and prove it. Unlike an ahead of time hash, fs-verity also re-verifies data each time it’s paged in, ensuring that malicious disk firmware can’t undetectably change the file’s contents at runtime. Its origins are in the Android project from Google, and it supports ext4 and f2fs.
fs-verity works at the filesystem level, whereas dm-verity works at the block level on a read-only device. fs-verity is for files that must live on a read-write filesystem because they are independently updated, so dm-verity cannot be used. Since Linux 5.19, the Integrity Measurement Architecture (IMA) can use fs-verity https://lwn.net/Articles/888543/ digests in place of full-file hashes for performance, which is a huge performance win.
fs-verity files are read-only and cannot be opened for writing or truncate(), but metadata changes like owner, mode, etc. are allowed because they are not measured.
Verification occurs on demand as data is paged in. The performance cost is low because fs-verity operates on block reads, and its runtime cost is low because it is only needed to verify the block being accessed.
To prevent this kind of compromised attack, we released a new feature that, by default, blocks loading eBPF code and Pinning (https://docs.ebpf.io/linux/concepts/pinning/) even with privileged access, preventing a large class of attack vectors.
Bomfather supports kernel versions 5.18 to 6.18, and we wanted to ensure it is available to all our customers. But, the critical API that provided this functionality had a breaking change in kernel version 6.15 , int BPF_PROG(lsm_bpf, int cmd, union bpf_attr *attr, unsigned int size, bool kernel) which included the additional parameter bool kernel and this was not supported in the previous versions int BPF_PROG(lsm_bpf_compat, int cmd, union bpf_attr *attr, unsigned int size). We dealt with this issue by compiling the bpf code with both the handlers and the userspace code, and loading only the necessary function based on the kernel version.
The threat model it helps mitigate is an attacker who can modify file contents on the storage device, including offline, like pulling a drive or editing a VM image, since the data is re-verified on every page read.