Nydus Data Integrity Mechanisms: Chunk Digests, Metadata Verification, and dm-verity Explained
Nydus implements multiple layered data integrity mechanisms including per-chunk SHA-256 or Blake3 cryptographic digests, RAFS bootstrap metadata hashing, and optional Merkle-tree based dm-verity for kernel-enforced filesystem protection.
The dragonflyoss/nydus repository provides a production-ready container image service that relies on robust data integrity mechanisms to ensure content remains unmodified from build to runtime. These mechanisms operate at different granularities—from individual data chunks to entire filesystem blocks—providing defense in depth against corruption and tampering.
Chunk-Level Digests: Cryptographic Verification of Data Blocks
Nydus protects individual data chunks using cryptographic hashing. When the image builder (nydus-image) constructs a layer, it divides file content into chunks and computes a digest for each using either SHA-256 or Blake3.
Implementation in utils/src/digest.rs
The digest computation is handled by RafsDigest::from_buf, defined in utils/src/digest.rs:
use nydus_utils::digest::{Algorithm, RafsDigest};
let data = b"some payload";
let digest = RafsDigest::from_buf(data, Algorithm::Sha256);
// `digest.data` now holds the 32-byte SHA-256 hash
The resulting 32-byte digest is stored in the chunk dictionary (builder/src/core/chunk_dict.rs) and embedded in the layer metadata. During runtime, nydusd recomputes the digest of retrieved chunks and compares them against these stored values, detecting any corruption or modification before the data reaches the container.
Metadata Integrity: Protecting the RAFS Bootstrap
While chunk digests protect data content, Nydus must also ensure the structural integrity of the image metadata. The RAFS (Remote Access File System) bootstrap contains inode tables, chunk tables, and directory structures that define how the filesystem is assembled.
Bootstrap Digest Calculation
In builder/src/core/bootstrap.rs, the builder calculates a top-level digest for the entire bootstrap blob:
use nydus_builder::bootstrap::Bootstrap;
use nydus_utils::digest::RafsDigest;
// Load the bootstrap bytes (e.g., from a file)
let boot_bytes = std::fs::read("bootstrap.bin")?;
let bootstrap = Bootstrap::load(&boot_bytes)?;
// Compute the digest of the loaded bootstrap
let computed = RafsDigest::from_buf(&boot_bytes, bootstrap.digest_algorithm());
// Compare with the embedded digest
assert_eq!(computed, bootstrap.digest(), "Bootstrap integrity check failed");
The bootstrap header includes a RafsDigest field calculated from the entire bootstrap blob using the configured hash algorithm. When nydusd mounts an image, it validates this digest before processing the metadata, ensuring that the filesystem structure itself has not been tampered with or corrupted.
Merkle-Tree Based dm-verity: Kernel-Enforced Filesystem Integrity
For deployments requiring the highest level of assurance, Nydus supports dm-verity through a Merkle tree implementation. This mechanism provides kernel-level verification of every filesystem block, protecting against persistent tampering and offline attacks.
How dm-verity Works in Nydus
When building with nydus-image build --verity or running nydusd --verity, Nydus generates a Merkle tree over the data pages. Each leaf node contains the SHA-256 digest of a 4 KiB data page, while internal nodes contain digests of their child nodes. The root digest is stored in the superblock and passed to the Linux dm-verity driver.
Implementation in utils/src/verity.rs
The VerityGenerator struct in utils/src/verity.rs handles tree construction:
use nydus_utils::verity::VerityGenerator;
use std::fs::OpenOptions;
// Open the block device file (or a regular file for testing)
let file = OpenOptions::new().read(true).write(true).open("/dev/nbd0")?;
let data_pages = 1024; // number of 4 KiB pages
let verity_offset = 0; // where the tree will be placed
let mut generator = VerityGenerator::new(file, verity_offset, data_pages)?;
generator.initialize()?; // fill tree area with placeholder digests
// Set leaf digests (example: all zero pages)
let zero_digest = RafsDigest::from_buf(&[0u8; 4096], Algorithm::Sha256);
for i in 0..data_pages {
generator.set_digest(1, i, &zero_digest.data)?;
}
// Generate all intermediate levels and the root digest
let root = generator.generate_all_digests()?;
// `root` can now be passed to the dm-verity driver
At runtime, service/src/block_device.rs drives the dm-verity option when launching nydusd. The kernel verifies each read block against the Merkle tree before delivering it to userspace, ensuring that any modification to the underlying storage is detected immediately.
End-to-End Image Integrity
According to the design documentation in docs/nydus-design.md, Nydus implements end-to-end image integrity by combining these mechanisms. The build process embeds SHA-256 or Blake3 digests into image metadata, while optional dm-verity provides runtime protection for block-device deployments.
This layered approach ensures that:
- Individual chunks are verified against their cryptographic digests
- The filesystem structure is validated through bootstrap hashing
- The entire block device is protected by kernel-level Merkle tree verification
Summary
Nydus employs a defense-in-depth strategy for data integrity that operates at multiple granularities:
- Chunk-level digests (
utils/src/digest.rs): SHA-256 or Blake3 hashes for individual data blocks, verified during read operations - Metadata verification (
builder/src/core/bootstrap.rs): Cryptographic validation of the RAFS bootstrap structure before mounting - Merkle-tree dm-verity (
utils/src/verity.rs): Kernel-enforced block-level integrity for block-device deployments - End-to-end protection: Combined mechanisms ensuring integrity from image build through runtime execution
Frequently Asked Questions
How does Nydus detect corrupted data during container runtime?
Nydus detects corruption through chunk-level digest verification. When nydusd reads a data chunk, it recomputes the SHA-256 or Blake3 hash using RafsDigest::from_buf and compares it against the digest stored in the chunk metadata from builder/src/core/chunk_dict.rs. If the hashes mismatch, the read fails before the corrupted data reaches the container.
What is the difference between chunk digests and dm-verity in Nydus?
Chunk digests provide per-block integrity verification at the application level, protecting individual data chunks within a Nydus layer. In contrast, dm-verity implements a Merkle tree over the entire block device at the kernel level, as implemented in utils/src/verity.rs. While chunk digests protect against corruption in individual chunks, dm-verity protects the entire filesystem image from tampering and is enforced by the Linux kernel before data reaches userspace.
Can Nydus use Blake3 instead of SHA-256 for data integrity?
Yes, Nydus supports both SHA-256 and Blake3 as digest algorithms. The Algorithm enum in utils/src/digest.rs allows builders to select either hash function when computing RafsDigest values for chunks and bootstrap metadata. Blake3 offers faster hashing performance while maintaining cryptographic security, making it suitable for environments where build speed is critical.
Where does Nydus store integrity verification data?
Nydus stores integrity data in multiple locations depending on the mechanism. Chunk digests are stored in the chunk dictionary (builder/src/core/chunk_dict.rs) and embedded in the RAFS bootstrap metadata. The bootstrap itself contains a top-level digest calculated in builder/src/core/bootstrap.rs. For dm-verity deployments, the Merkle tree is written to a dedicated region on the block device adjacent to the data pages, as managed by utils/src/verity.rs.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →