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:

  1. A software package (firmware update, model weights, configuration bundle, etc.) is published by a trusted vendor who signs the package before distribution.

  2. Open World - the untrusted guest VM - downloads the package from the vendor’s server and stores it on its local filesystem.

  3. 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

authfs

FUSE filesystem driver. Serves authenticated files at the mount point; contacts Open World via Turmux RPC for raw data and Merkle tree blocks.

fd_server

Runs in Open World. Bridges AuthFS RPC requests to local file operations. Exposes open file descriptors over the VirtFdService RPC interface.

authfs_service

Optional service manager in Bunker OS. Manages multiple AuthFS mount instances and exposes AuthFsService and AuthFs RPC endpoints for programmatic mount management.

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.

authfs CLI OptionsΒΆ

Option

Description

--socket <path>

Unix socket path to the Turmux daemon. Default: /tmp/turmux.sock

--remote-ro-file <fd>:<digest>

Read-only file with integrity check. <digest> must be sha256-<hex>. Can be repeated for multiple files.

--remote-ro-file-unverified <fd>

Read-only file without integrity check (pass-through). Use only when integrity is enforced by another mechanism.

--remote-ro-dir <fd>:<manifest>:<prefix>

Read-only directory. <manifest> is a serialized FSVerityDigests protobuf. <prefix> is stripped from manifest paths when matching remote FD paths.

--remote-new-rw-file <fd>

New writable file; integrity is tracked in memory (not persistent).

--remote-new-rw-dir <fd>

New writable directory; integrity is tracked in memory (not persistent).

-o <options>

Extra FUSE mount options (e.g., allow_other, default_permissions).

-j <n>

Number of threads to serve FUSE requests.

--debug

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

ReadFile(fd, offset, size)

Returns up to size bytes of the file starting at offset.

ReadFsverityMerkleTree(fd, offset, size)

Returns bytes of the fs-verity Merkle tree blob at the given offset. AuthFS uses this to walk tree nodes during block verification.

ReadFsveritySignature(fd)

Returns the PKCS#7 signature stored with the file, if present.

GetFileSize(fd)

Returns the total size of the file in bytes.

WriteFile(fd, buf, offset)

Writes bytes to the file (used for write-mode only).

Resize(fd, size)

Resizes the file (used for write-mode only).

OpenFileInDirectory(dir_fd, name)

Opens a file inside a remote directory by name.

CreateFileInDirectory(dir_fd, 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 EIO before the modified bytes reach the application.

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 .fs-verity keyring. Everything else - package, digest, Merkle tree, signature - can arrive from Open World at any time without weakening the security guarantee.

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ΒΆ