Authenticated Filesystem (AuthFS)ΒΆ
OverviewΒΆ
AuthFS is a FUSE-based filesystem that provides authenticated read access to files whose storage cannot be trusted. It is used in Bunker-Centric Architecture (BCA) to expose files originating from Open World to Bunker OS with a cryptographic integrity guarantee: even if Open World is fully compromised, an attacker cannot silently substitute or tamper with a file without Bunker OS detecting it before any byte of content is consumed.
AuthFS is a fork of Android AuthFS, rewritten for Vanilla Linux using Reference RPC Implementation: Turmux over Communication Layer (vsock).
Note
AuthFS is designed for read-only integrity verification. Write-capable mode maintains the Merkle tree entirely in memory and is not persistent across restarts. The use case described in this document focuses exclusively on read-only, integrity-verified file access.
Trusted Package Delivery: The Reference Use CaseΒΆ
Consider the following scenario, which is the canonical use case for AuthFS in BCA:
A software package (firmware update, model weights, configuration bundle, etc.) is published by a trusted vendor who signs the package before distribution.
Open World - the untrusted guest VM - downloads the package from the vendorβs server and stores it on its local filesystem.
Bunker OS needs to read and process the package.
The problem is that by the time Bunker OS requests the file, Open World is the sole custodian of the bytes on disk. Open World cannot be trusted: a compromised or malicious Open World process could modify the package contents, swap the file for a different one, or introduce corruption after the hash was checked.
AuthFS solves the tampering problem by making integrity verification continuous and automatic: every 4 KiB block is re-verified against a Merkle tree on each read. The root of that Merkle tree - the fs-verity digest - is the cryptographic fingerprint of the file.
However, in BCA there is no trusted channel through which Bunker OS can receive a digest at
runtime: all package metadata, including the digest itself, arrives from Open World. A compromised
Open World could therefore fabricate a digest for a malicious file. The PKCS#7 signature the
vendor attached to the digest at build time is what closes this gap. Bunker OS verifies the
signature against the vendor certificate enrolled in its kernel .fs-verity keyring at
deployment time. Only after that verification passes is the digest trusted and passed to AuthFS
for mounting.
sequenceDiagram
participant Vendor as Trusted Vendor
participant OW as Open World (Untrusted)
participant BOS as Bunker OS (Trusted)
participant FUSE as authfs FUSE driver
note over BOS: Deployment time: vendor cert enrolled<br/>in kernel .fs-verity keyring
Vendor->>Vendor: Enable fs-verity, compute digest, sign digest
Vendor->>OW: Deliver package + package.bin.sig + Merkle tree
OW->>OW: Store files on local filesystem
OW->>BOS: "Mount request: FD=5, digest=X, sig=package.bin.sig"
BOS->>OW: ReadFsveritySignature(fd=5)
OW-->>BOS: PKCS#7 signature bytes
BOS->>BOS: Verify signature against .fs-verity keyring
note over BOS: Abort if signature invalid -<br/>digest X is not vendor-endorsed
BOS->>FUSE: Mount(AuthFsConfig { fd=5, expected_digest=X })
loop per read block
FUSE->>OW: ReadFile / ReadFsverityMerkleTree
OW-->>FUSE: Raw block + Merkle tree node
FUSE->>FUSE: Verify block hash against Merkle tree
note over FUSE: EIO returned to app if block hash mismatches
end
FUSE-->>BOS: Verified file bytes
The expected digest is authoritative. If Open World tampers with even a single byte of the file,
the corresponding block hash will not match its parent node in the Merkle tree, and the read
system call inside Bunker OS will return EIO.
How fs-verity WorksΒΆ
fs-verity is a Linux kernel feature (CONFIG_FS_VERITY) for file-level integrity verification
using a Merkle tree. It is supported on ext4 (Linux 5.4+), f2fs (Linux 5.4+), and
btrfs (Linux 5.15+).
Merkle Tree ConstructionΒΆ
When fs-verity is enabled on a file, the kernel divides the file contents into 4 KiB blocks and builds a hash tree bottom-up:
Leaf level: each 4 KiB data block is hashed with SHA-256.
Inner levels: groups of 128 leaf hashes (128 Γ 32 bytes = 4096 bytes, one tree node) are hashed together to produce a parent hash. The tree is thus 128-ary.
Root hash: the single hash at the apex of the tree.
The kernel then computes the fs-verity file digest - a SHA-256 hash of a descriptor struct that encodes the root hash together with fixed fields (hash algorithm, block size, file size). This digest is the value you pass to AuthFS and is the only thing Bunker OS needs to trust.
File data (4 KiB blocks)
ββββββββββ¬βββββββββ¬βββββββββ¬βββββββββ
β B[0] β B[1] β B[2] β ... β
βββββ¬βββββ΄ββββ¬βββββ΄ββββ¬βββββ΄βββββββββ
β β β
Hβ Hβ Hβ ... β leaf hashes (level 0)
ββββββββββ΄βββββββββ
Hβββββ ... β level 1 nodes (128 hashes/node)
βββββββ ... βββββββββ
root β root hash
β
fsverity_digest = SHA-256(descriptor || root)
Lazy, Per-Block VerificationΒΆ
A key property of fs-verity is lazy verification: only the blocks that are actually read are
verified. When a process reads byte range [offset, offset+len), the kernel fetches only the
relevant 4 KiB blocks and walks the Merkle tree from those leaves up to the root, confirming each
node matches its stored hash. The entire file is never hashed upfront.
This makes AuthFS efficient for large files: a 1 GiB firmware image incurs zero per-byte overhead unless those bytes are actually accessed.
Preparing a File with fs-verity (Vendor Side)ΒΆ
The vendor performs this process once, before distributing the package. The result is a digest value (hex string) that is later embedded in metadata or a manifest file consumed by AuthFS.
PrerequisitesΒΆ
Install the fsverity-utils package:
# Debian / Ubuntu
apt install fsverity-utils
# Fedora / RHEL
dnf install fsverity-utils
The filesystem hosting the file must have fs-verity support enabled:
# ext4: verify the feature flag is present
tune2fs -l /dev/sdXn | grep verity
# ext4: enable the feature if missing (offline, unmounted)
tune2fs -O verity /dev/sdXn
Step 1 - Enable fs-verity on the FileΒΆ
The file must be read-only before enabling verity (no pending writes). Use the
fsverity enable command:
fsverity enable package.bin
This causes the kernel to build the Merkle tree, store it in a filesystem-specific location
associated with the file (invisible to normal directory listings), and make the file permanently
read-only. Subsequent writes to the file will be rejected with EPERM.
Step 2 - Compute the DigestΒΆ
Retrieve the fs-verity file digest:
fsverity digest package.bin
Example output:
sha256:3d8f2b1a9e4c5d0f7a6b2e8c1f4d9a3b5e0c7f2a1d6b8e4c9f3a5b0d2e7c1f4a package.bin
The hex string after sha256: is the digest you will embed in the manifest or pass directly
to AuthFS as the --remote-ro-file argument.
Note
The digest is deterministic for a given file and hash algorithm. Recomputing it on a different machine or at a different time will yield the same value as long as the file content is identical.
Step 3 - Generate a Signing Key Pair (first time only)ΒΆ
# Generate a 4096-bit RSA key
openssl genrsa -out vendor-key.pem 4096
# Extract the public key
openssl rsa -in vendor-key.pem -pubout -out vendor-key-pub.pem
Step 4 - Sign the DigestΒΆ
fs-verity can store a PKCS#7 signature alongside the Merkle tree. Bunker OS (or the trust anchor holding the public key) can later verify this signature before accepting the digest value.
fsverity sign package.bin package.bin.sig \
--key=vendor-key.pem \
--cert=vendor-cert.pem
The signature file package.bin.sig is a DER-encoded PKCS#7 ContentInfo over the
fs-verity file measurement. Distribute it alongside the package.
Important
In BCAβs deployment model there is no trusted channel for delivering digests at runtime. All package metadata - including the digest - arrives from Open World and must be treated as untrusted. The PKCS#7 signature is therefore required: it is the only mechanism by which Bunker OS can establish that a digest value was endorsed by the vendor and not fabricated by a compromised Open World.
Bunker OS must verify the signature against the vendor certificate enrolled in the kernel
.fs-verity keyring before passing the digest to authfs at mount time. The vendor
certificate is enrolled at device provisioning time and never changes at runtime.
Step 5 - Export the Merkle Tree (Sidecar Metadata)ΒΆ
For files that will be served by fd_server without kernel fs-verity support on the Open World
host (e.g., downloaded to a tmpfs), export the Merkle tree as sidecar data:
fsverity digest --out-merkle-tree=package.bin.tree package.bin
The .tree file is a raw byte blob of the concatenated Merkle tree levels, bottom-up. Pass it
alongside the package to Open World.
Note
If the file resides on an ext4/f2fs/btrfs filesystem with fs-verity enabled, AuthFS can
retrieve the Merkle tree from the kernel via FS_IOC_READ_VERITY_METADATA (Linux 5.12+).
Sidecar metadata is only required when that ioctl is unavailable.
Mounting an Authenticated File in Bunker OSΒΆ
AuthFS ComponentsΒΆ
Component |
Role |
|---|---|
|
FUSE filesystem driver. Serves authenticated files at the mount point; contacts Open World via Turmux RPC for raw data and Merkle tree blocks. |
|
Runs in Open World. Bridges AuthFS RPC requests to local file operations. Exposes open file
descriptors over the |
|
Optional service manager in Bunker OS. Manages multiple AuthFS mount instances and exposes
|
Data FlowΒΆ
graph LR
subgraph BOS["Bunker OS"]
APP["Application"]
FUSE["authfs FUSE driver"]
SVC["authfs_service"]
end
subgraph OW["Open World"]
FDS["fd_server (VirtFdService)"]
FS["Local Filesystem (package.bin)"]
end
APP -->|"read(fd)"| FUSE
FUSE -->|"ReadFile / ReadFsverityMerkleTree (Turmux RPC)"| FDS
FDS --> FS
FS --> FDS
FDS -->|"raw block + Merkle node"| FUSE
FUSE -->|"verify block against Merkle tree EIO on mismatch"| APP
SVC -.->|"Mount(AuthFsConfig)"| FUSE
Command-Line UsageΒΆ
Mount a single verified read-only file:
authfs /mnt/authfs \
--socket /tmp/turmux.sock \
--remote-ro-file 5:sha256-3d8f2b1a9e4c5d0f7a6b2e8c1f4d9a3b5e0c7f2a1d6b8e4c9f3a5b0d2e7c1f4a
The file is accessible inside Bunker OS at /mnt/authfs/5. The number 5 is the file
descriptor number that fd_server has open on the Open World side for package.bin.
Mount a directory from a digest manifest:
authfs /mnt/authfs \
--socket /tmp/turmux.sock \
--remote-ro-dir 5:/path/to/digests.pb:packages/
digests.pb is a serialized FSVerityDigests protobuf (see RPC Protobuf Definitions)
mapping each file path to its expected digest.
Option |
Description |
|---|---|
|
Unix socket path to the Turmux daemon. Default: |
|
Read-only file with integrity check. |
|
Read-only file without integrity check (pass-through). Use only when integrity is enforced by another mechanism. |
|
Read-only directory. |
|
New writable file; integrity is tracked in memory (not persistent). |
|
New writable directory; integrity is tracked in memory (not persistent). |
|
Extra FUSE mount options (e.g., |
|
Number of threads to serve FUSE requests. |
|
Enable debug logging. |
RPC Protobuf DefinitionsΒΆ
AuthFS exposes three Turmux RPC services, all defined under the turmux.authfs package.
VirtFdService - File I/O from Open WorldΒΆ
Runs in Open World (fd_server). Provides raw file content and fs-verity metadata to AuthFS.
RPC Method |
Description |
|---|---|
|
Returns up to |
|
Returns bytes of the fs-verity Merkle tree blob at the given offset. AuthFS uses this to walk tree nodes during block verification. |
|
Returns the PKCS#7 signature stored with the file, if present. |
|
Returns the total size of the file in bytes. |
|
Writes bytes to the file (used for write-mode only). |
|
Resizes the file (used for write-mode only). |
|
Opens a file inside a remote directory by name. |
|
Creates a new file inside a remote directory. |
AuthFsService - Mount ManagementΒΆ
Runs in Bunker OS (authfs_service). Creates and manages AuthFS mount instances.
service AuthFsService {
// Creates an AuthFS mount given the configuration.
rpc Mount(MountRequest) returns (MountResponse);
}
message MountRequest {
AuthFsConfig config = 1;
}
message MountResponse {
bool ok = 1;
string error = 2; // Non-empty on failure
uint32 instance_id = 3; // Valid only when ok = true
}
AuthFs - Per-Instance File AccessΒΆ
service AuthFs {
// Returns the local path of a remote file on the FUSE mount.
rpc OpenFile(AuthFsOpenFileRequest) returns (AuthFsOpenFileResponse);
// Returns the mount point of the AuthFS instance.
rpc GetMountPoint(GetMountPointRequest) returns (GetMountPointResponse);
}
AuthFsConfig - Mount ConfigurationΒΆ
message AuthFsConfig {
int32 port = 1;
repeated InputFdAnnotation input_fd_annotations = 2;
repeated OutputFdAnnotation output_fd_annotations = 3;
repeated InputDirFdAnnotation input_dir_fd_annotations = 4;
repeated OutputDirFdAnnotation output_dir_fd_annotations = 5;
}
message InputFdAnnotation {
int32 fd = 1; // Remote FD number; matched against --remote-ro-file
}
message InputDirFdAnnotation {
int32 fd = 1;
string manifest_path = 2; // Path to FSVerityDigests protobuf
string prefix = 3; // Path prefix to strip
}
FSVerityDigest and FSVerityDigests - Digest ManifestΒΆ
Used in directory manifests (--remote-ro-dir). Maps each file path to its expected
fs-verity digest.
message FSVerityDigest {
string file_path = 1;
string hash_alg = 2; // e.g. "sha256"
bytes digest = 3; // Raw digest bytes (32 bytes for SHA-256)
}
message FSVerityDigests {
map<string, FSVerityDigest> digests = 1;
}
Security PropertiesΒΆ
Property |
Guarantee |
|---|---|
Tamper detection |
Any modification to a data block is detected on read; the kernel or AuthFS FUSE driver
returns |
Replay prevention |
The digest uniquely identifies a specific version of the file. Substituting an older or different valid file with a different digest is detected because the digest does not match. |
No trust in Open World |
AuthFS never trusts the Open World storage layer for correctness. All block data is independently verified against the Merkle tree. Open World can only deny service (by returning garbage), not substitute content silently. |
Digest authenticity |
The digest value itself originates in Open World (as part of the signed package metadata) and is untrusted until Bunker OS verifies the PKCS#7 signature against the vendor certificate enrolled at deployment time. A compromised Open World cannot forge a signature for an arbitrary digest without the vendorβs private key. |
Deployment-time key provisioning |
The only secret that needs to be in Bunker OS before runtime is the vendor certificate in
the |
Warning
AuthFS does not protect against denial of service: a compromised Open World can refuse to serve blocks, return truncated data, or saturate the RPC channel. Availability guarantees must be provided by the watchdog and OTA recovery mechanisms described in Update Management.
See AlsoΒΆ
Communication Layer (vsock) - transport layer used by Turmux RPC
Reference RPC Implementation: Turmux - RPC framing, method routing, and language bindings
Security Model - threat model and trust boundaries
Root FS Integrity and Encryption - dm-verity and rootfs integrity