Skip to content

Commit

Permalink
image-rs: add image block device dm-verity and mount
Browse files Browse the repository at this point in the history
Implement image block device integrity check using dm-verity and mount/umount the verity device in image-rs.

Signed-off-by: ChengyuZhu6 <[email protected]>
  • Loading branch information
ChengyuZhu6 authored and jiangliu committed Jul 26, 2023
1 parent 74b54b6 commit af99f9a
Show file tree
Hide file tree
Showing 5 changed files with 482 additions and 1 deletion.
5 changes: 4 additions & 1 deletion .github/workflows/image_rs_build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,10 @@ jobs:
run: |
sudo apt-get update
sudo apt-get install -y libtss2-dev
- name: Install dm-verity dependencies
run: |
sudo apt-get update
sudo apt-get install -y libdevmapper-dev
- name: Run cargo fmt check
uses: actions-rs/cargo@v1
with:
Expand Down
2 changes: 2 additions & 0 deletions image-rs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ async-trait.workspace = true
attestation_agent = { path = "../attestation-agent/lib", optional = true }
base64.workspace = true
cfg-if = { workspace = true, optional = true }
devicemapper = "0.33.5"
dircpy = { version = "0.3.12", optional = true }
flate2 = "1.0"
fs_extra = { version = "1.2.0", optional = true }
Expand All @@ -24,6 +25,7 @@ hex = { version = "0.4.3", optional = true }
lazy_static = { workspace = true, optional = true }
libc = "0.2"
log = "0.4.14"
loopdev ="0.4.0"
nix = { version = "0.26", optional = true }
oci-distribution = { git = "https://github.com/krustlet/oci-distribution.git", rev = "f44124c", default-features = false, optional = true }
oci-spec = "0.5.8"
Expand Down
148 changes: 148 additions & 0 deletions image-rs/src/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use anyhow::{anyhow, bail, Result};
use log::warn;
use nix::mount::MsFlags;
use oci_distribution::manifest::{OciDescriptor, OciImageManifest};
use oci_distribution::secrets::RegistryAuth;
use oci_distribution::Reference;
Expand All @@ -21,6 +22,7 @@ use crate::config::{ImageConfig, CONFIGURATION_FILE_PATH};
use crate::decoder::Compression;
use crate::meta_store::{MetaStore, METAFILE};
use crate::pull::PullClient;
use crate::verity;

#[cfg(feature = "snapshot-unionfs")]
use crate::snapshots::occlum::unionfs::Unionfs;
Expand Down Expand Up @@ -432,6 +434,38 @@ impl ImageClient {
}
}

/// mount_image_block_with_integrity creates a mapping backed by image block device <source_device_path> and
/// decoding <verity_options> for in-kernel verification. And mount the verity device
/// to <mount_path> with <mount_type>.
/// It will return the verity device path if succeeds and return an error if fails .
pub fn mount_image_block_with_integrity(
verity_options: &str,
source_device_path: &Path,
mount_path: &Path,
mount_type: &str,
) -> Result<String> {
let parsed_data = verity::decode_verity_options(verity_options)?;
let verity_device_path = verity::create_verity_device(&parsed_data, source_device_path)?;

nix::mount::mount(
Some(verity_device_path.as_str()),
mount_path,
Some(mount_type),
MsFlags::MS_RDONLY,
None::<&str>,
)?;
Ok(verity_device_path)
}
/// umount_image_block_with_integrity umounts the filesystem and closes the verity device named verity_device_name.
pub fn umount_image_block_with_integrity(
mount_path: &Path,
verity_device_name: String,
) -> Result<()> {
nix::mount::umount(mount_path)?;
verity::close_verity_device(verity_device_name)?;
Ok(())
}

/// Create image meta object with the image info
/// Return the image meta object, oci descriptors of the unique layers, and unique diff ids.
fn create_image_meta(
Expand Down Expand Up @@ -507,6 +541,9 @@ fn create_bundle(
#[cfg(test)]
mod tests {
use super::*;
use base64::Engine;
use std::fs;
use std::process::Command;

#[tokio::test]
async fn test_pull_image() {
Expand Down Expand Up @@ -577,7 +614,118 @@ mod tests {
nydus_images.len()
);
}
#[tokio::test]
async fn test_mount_and_umount_image_block_with_integrity() {
const VERITYSETUP_PATH: &[&str] = &["/sbin/veritysetup", "/usr/sbin/veritysetup"];
//create a disk image file
let work_dir = tempfile::tempdir().unwrap();
let mount_dir = tempfile::tempdir().unwrap();
let file_name: std::path::PathBuf = work_dir.path().join("test.file");
let default_hash_type = "sha256";
let default_data_block_size: u64 = 512;
let default_data_block_num: u64 = 1024;
let data_device_size = default_data_block_size * default_data_block_num;
let default_hash_size: u64 = 4096;
let default_resize_size: u64 = data_device_size * 4;
let data = vec![0u8; data_device_size as usize];
fs::write(&file_name, &data)
.unwrap_or_else(|err| panic!("Failed to write to file: {}", err));
Command::new("mkfs")
.args(&["-t", "ext4", file_name.to_str().unwrap()])
.output()
.map_err(|err| format!("Failed to format disk image: {}", err))
.unwrap_or_else(|err| panic!("{}", err));

Command::new("truncate")
.args(&[
"-s",
default_resize_size.to_string().as_str(),
file_name.to_str().unwrap(),
])
.output()
.map_err(|err| format!("Failed to resize disk image: {}", err))
.unwrap_or_else(|err| panic!("{}", err));

//find an unused loop device and attach the file to the device
let loop_control = loopdev::LoopControl::open().unwrap_or_else(|err| panic!("{}", err));
let loop_device = loop_control
.next_free()
.unwrap_or_else(|err| panic!("{}", err));
loop_device
.with()
.autoclear(true)
.attach(file_name.to_str().unwrap())
.unwrap_or_else(|err| panic!("{}", err));
let loop_device_path = loop_device
.path()
.unwrap_or_else(|| panic!("failed to get loop device path"));
let loop_device_path_str = loop_device_path
.to_str()
.unwrap_or_else(|| panic!("failed to get path string"));

let mut verity_option = verity::DmVerityOption {
hashtype: default_hash_type.to_string(),
blocksize: default_data_block_size,
hashsize: default_hash_size,
blocknum: default_data_block_num,
offset: data_device_size,
hash: "".to_string(),
};

// Calculates and permanently stores hash verification data for data_device.
let veritysetup_bin = VERITYSETUP_PATH
.iter()
.find(|&path| Path::new(path).exists())
.copied()
.unwrap_or_else(|| panic!("Veritysetup path not found"));
let output = Command::new(veritysetup_bin)
.args(&[
"format",
"--no-superblock",
"--format=1",
"-s",
"",
&format!("--hash={}", verity_option.hashtype),
&format!("--data-block-size={}", verity_option.blocksize),
&format!("--hash-block-size={}", verity_option.hashsize),
"--data-blocks",
&format!("{}", verity_option.blocknum),
"--hash-offset",
&format!("{}", verity_option.offset),
loop_device_path_str,
loop_device_path_str,
])
.output()
.unwrap_or_else(|err| panic!("{}", err));
if output.status.success() {
let stdout = String::from_utf8_lossy(&output.stdout);
let lines: Vec<&str> = stdout.lines().collect();
let hash_strings: Vec<&str> = lines[lines.len() - 1].split_whitespace().collect();
verity_option.hash = hash_strings[2].to_string()
} else {
let error_message = String::from_utf8_lossy(&output.stderr);
panic!("Failed to create hash device: {}", error_message);
}

let serialized_option = serde_json::to_vec(&verity_option)
.unwrap_or_else(|_| panic!("failed to serialize the options"));
let encoded_option = base64::engine::general_purpose::STANDARD.encode(serialized_option);
let res = mount_image_block_with_integrity(
encoded_option.as_str(),
&loop_device_path,
mount_dir.path(),
"ext4",
)
.unwrap_or_else(|err| panic!("Failed to mount image block with integrity {:?}", err));
assert!(res.contains("/dev/mapper"));
let verity_device_name = match verity::get_verity_device_name(encoded_option.as_str()) {
Ok(name) => name,
Err(err) => {
panic!("Error getting verity device name: {}", err);
}
};
assert!(umount_image_block_with_integrity(mount_dir.path(), verity_device_name).is_ok());
}
#[tokio::test]
async fn test_image_reuse() {
let work_dir = tempfile::tempdir().unwrap();
Expand Down
1 change: 1 addition & 0 deletions image-rs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ pub mod signature;
pub mod snapshots;
pub mod stream;
pub mod unpack;
pub mod verity;
Loading

0 comments on commit af99f9a

Please sign in to comment.