What is the underlying problem?
The elephant in the room is that we blindly trust the binary that is allowed to read our private keys and perform critical actions. The trust boundary should be at the bottom of the stack, at the kernel, and we need to verify that binaries (such as sshd) that have access to our private keys haven’t been tampered with.
The community has been addressing problems with respect to having policies at the kernel (eBPF), but verifying the actor (binary) that performs the action is a missing piece.
What is fs-verity?
The Linux kernel solved the above with fs-verity. 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. In the 6.8 version of the kernel, fs-verity support was added to eBPF via bpf_get_fsverity_digest. To read more about fs-verity, here is a blog post that explains it in more detail.
What threat vector does it prevent?
As mentioned above, most eBPF solutions are built to trust a binary to perform an action on a target (file/folder). This is usually achieved with LSM and an eBPF hook like bprm_check_security.
Here is our policy, which has something similar.
policies:
- executable: "filepath = /usr/bin/cat"
can_access_dirs:
- "/home/user/go/src/github.com/bomfather/bomfather-private/agent2/example/protected/protected1/protected.txt:read"
In the above example, we are providing access to protected.txt only to /usr/bin/cat, and this works for most use cases, but we need to know from the kernel that the cat was the executable we trusted and hadn’t been tampered with.
How does it prevent it?
To prevent this, we enable fs-verity and add the necessary binaries to it, ensuring the hash is calculated and available on demand for integrity verification.
policies:
- executable: "filepath = /usr/bin/cat"
can_access_dirs:
- "/home/user/go/src/github.com/bomfather/bomfather-private/agent2/example/protected/protected1/protected.txt:read"
# fs-verity_digest pins the trusted digest for this binary.
# Obtain it with: fsverity measure /path/to/binary
fsverity_digest: "sha256:bfbdbae0bd2e9a83a416a338661fdad056570ba62c08f7ad11bfebb8eef68ad4"
The code has a map in the kernel with /usr/bin/cat as the key and sha256:bfbdbae0bd2e9a83a416a338661fdad056570ba62c08f7ad11bfebb8eef68ad4 as the value, which is the hash. The code checks bpf_get_fsverity_digest when cat is invoked to ensure the hash is valid before accessing the protected resource.
What does it not prevent?
Unauthorized copy on another host still verifies: You trust /usr/bin/cat with: fsverity_digest = sha256:ABC… and permission to read /etc/secret/key.pem on Host A (prod), that is intended. The attacker copies the exact same fs-verity-enabled cat binary (same digest ABC…) to Host B (a rogue VM, a stolen cloud account, or the wrong environment). So, fs-verity verifies file integrity, not workload identity.
The same machine impostor: The policy says /usr/bin/cat may read /home/app/protected.txt, pinned with fs-verity digest. On the same machine, appuser legitimately runs cat as part of the normal workflow. Also, attackeruser (or compromised service account) runs /usr/bin/cat /home/app/protected.txt; both invocations look equivalent from an fs-verity standpoint. The solution does not differentiate which human/operator launched it, which service instance launched it, or whether the launch context is expected or malicious.