[demo] Add runtime mount and boot occlum instance demo
This commit is contained in:
parent
2347951743
commit
926d80430b
75
demos/runtime_boot/README.md
Normal file
75
demos/runtime_boot/README.md
Normal file
@ -0,0 +1,75 @@
|
||||
# Runtime boot pre-generated UnionFS image
|
||||
|
||||
Generally, every Occlum instance has to pass the `Occlum build` process.
|
||||
In some scenarios, mount and boot a pre-generated UnionFS image without `Occlum build` is a good feature. This demo introduces a way to runtime boot a BASH demo.
|
||||
|
||||
## Flow
|
||||
|
||||
### First, build a [`BASH`](../bash) Occlum instance
|
||||
|
||||
The later step will use the image content to generate UnionFS image.
|
||||
|
||||
### Build and start a [`gen_rootfs`](./gen_rootfs) Occlum instance
|
||||
|
||||
This `gen_rootfs` mounts a empty UnionFS, copy the BASH Occlum image content to the mount point, unmount the UnionFS. It generates an encrypted UnionFS image containing the BASH image content. The **key** used in this demo is `"c7-32-b3-ed-44-df-ec-7b-25-2d-9a-32-38-8d-58-61"`.
|
||||
|
||||
### Build customized [`init`](./init)
|
||||
|
||||
Occlum [`default init`](../../tools/init) calls syscall (363) `MountRootFS` to mount and boot Occlum instance generated by normal `occlum build`.
|
||||
```
|
||||
(MountRootFS = 363) => do_mount_rootfs(key_ptr: *const sgx_key_128bit_t, rootfs_config: *const user_rootfs_config)
|
||||
```
|
||||
The first parameter `key_ptr` is optional.
|
||||
The second parameter `rootfs_config` needs to be set as NULL.
|
||||
|
||||
But for runtime booting pre-generated UnionFS image, The first parameter `key_ptr` is must to have, the second parameter `rootfs_config` needs have valid members.
|
||||
```
|
||||
struct user_rootfs_config {
|
||||
// UnionFS type rootfs upper layer, read-write layer
|
||||
upper_layer_path: *const i8,
|
||||
// UnionFS type rootfs lower layer, read-only layer
|
||||
lower_layer_path: *const i8,
|
||||
entry_point: *const i8,
|
||||
// HostFS source path
|
||||
hostfs_source: *const i8,
|
||||
// HostFS target path, default value is "/host"
|
||||
hostfs_target: *const i8,
|
||||
}
|
||||
```
|
||||
|
||||
In this demo, parameters values are provided as below.
|
||||
|
||||
* **rootfs_key**
|
||||
The key to encrypt/decrypt the rootfs, here it is `"c7-32-b3-ed-44-df-ec-7b-25-2d-9a-32-38-8d-58-61".
|
||||
|
||||
* **rootfs_upper_layer**
|
||||
The upper layer path of the unionfs type rootfs. In this case, it is relative path `"../gen_rootfs_instance/mnt_unionfs/upper"`.
|
||||
|
||||
* **rootfs_lower_layer**
|
||||
The lower layer path of the unionfs type rootfs. In this case, it is relative path `"../gen_rootfs_instance/mnt_unionfs/lower"`.
|
||||
|
||||
* **rootfs_entry**
|
||||
The entry point of the rootfs. In his case, it is `"/bin"`.
|
||||
|
||||
* **hostfs_source**
|
||||
It is set to be `/tmp` in this case.
|
||||
|
||||
In this example customized init, the above parameters are declared in the source [`main.rs`](./init/src/main.rs). In real case, they could be acquired by LA/RA or by modifying the PAL api `pal_run_init_process`.
|
||||
|
||||
### Build a boot template Occlum instance
|
||||
|
||||
This template uses the customized init. The RootFS image is not important, which will be replaced during boot.
|
||||
|
||||
All above steps could be done with one [`script`](./build_content.sh).
|
||||
```
|
||||
./build_content.sh
|
||||
```
|
||||
|
||||
After running the script, runtime boot BASH could be done as below even if the default RootFS image has no BASH function.
|
||||
```
|
||||
# cd boot_instance
|
||||
# occlum run /bin/occlum_bash_test.sh
|
||||
```
|
||||
|
||||
|
||||
|
7
demos/runtime_boot/boot_template.yaml
Normal file
7
demos/runtime_boot/boot_template.yaml
Normal file
@ -0,0 +1,7 @@
|
||||
includes:
|
||||
- base.yaml
|
||||
targets:
|
||||
- target: /bin
|
||||
copy:
|
||||
- files:
|
||||
- /usr/bin/hostname
|
90
demos/runtime_boot/build_content.sh
Executable file
90
demos/runtime_boot/build_content.sh
Executable file
@ -0,0 +1,90 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
|
||||
export BASH_DEMO_DIR="${script_dir}/../bash"
|
||||
export INIT_DIR="${script_dir}/init"
|
||||
|
||||
UNIONFS_DIR="${script_dir}/gen_rootfs_instance/mnt_unionfs"
|
||||
ENCRIP_KEY="c7-32-b3-ed-44-df-ec-7b-25-2d-9a-32-38-8d-58-61"
|
||||
|
||||
function build_bash_demo()
|
||||
{
|
||||
pushd ${BASH_DEMO_DIR}
|
||||
rm -rf occlum_instance && occlum new occlum_instance
|
||||
|
||||
cd occlum_instance
|
||||
rm -rf image
|
||||
copy_bom -f ../bash.yaml --root image --include-dir /opt/occlum/etc/template
|
||||
|
||||
new_json="$(jq '.resource_limits.user_space_size = "600MB" |
|
||||
.resource_limits.kernel_space_stack_size ="2MB" ' Occlum.json)" && \
|
||||
echo "${new_json}" > Occlum.json
|
||||
|
||||
occlum build
|
||||
popd
|
||||
}
|
||||
|
||||
function build_init()
|
||||
{
|
||||
pushd ${INIT_DIR}
|
||||
occlum-cargo clean
|
||||
occlum-cargo build --release
|
||||
popd
|
||||
}
|
||||
|
||||
function build_and_gen_rootfs()
|
||||
{
|
||||
pushd gen_rootfs
|
||||
cargo build
|
||||
popd
|
||||
|
||||
# initialize occlum workspace
|
||||
rm -rf gen_rootfs_instance && occlum new gen_rootfs_instance
|
||||
pushd gen_rootfs_instance
|
||||
|
||||
new_json="$(jq '.resource_limits.user_space_size = "1000MB" |
|
||||
.resource_limits.kernel_space_heap_size= "512MB" |
|
||||
.resource_limits.kernel_space_stack_size= "16MB" ' Occlum.json)" && \
|
||||
echo "${new_json}" > Occlum.json
|
||||
|
||||
rm -rf image
|
||||
copy_bom -f ../gen_rootfs.yaml --root image --include-dir /opt/occlum/etc/template
|
||||
|
||||
occlum build
|
||||
|
||||
mkdir -p mnt_unionfs/lower
|
||||
mkdir -p mnt_unionfs/upper
|
||||
mkdir rootfs
|
||||
cp -rf ${BASH_DEMO_DIR}/occlum_instance/image/* rootfs/
|
||||
|
||||
occlum run /bin/gen_rootfs ${ENCRIP_KEY}
|
||||
|
||||
popd
|
||||
}
|
||||
|
||||
function build_boot_template()
|
||||
{
|
||||
rm -rf boot_instance && occlum new boot_instance
|
||||
pushd boot_instance
|
||||
|
||||
new_json="$(jq '.resource_limits.user_space_size = "600MB" |
|
||||
.resource_limits.kernel_space_stack_size ="2MB" ' Occlum.json)" && \
|
||||
echo "${new_json}" > Occlum.json
|
||||
|
||||
rm -rf image
|
||||
copy_bom -f ../boot_template.yaml --root image --include-dir /opt/occlum/etc/template
|
||||
|
||||
# Update init
|
||||
rm -rf initfs
|
||||
copy_bom -f ../init.yaml --root initfs --include-dir /opt/occlum/etc/template
|
||||
|
||||
occlum build
|
||||
popd
|
||||
}
|
||||
|
||||
build_bash_demo
|
||||
build_and_gen_rootfs
|
||||
build_init
|
||||
build_boot_template
|
7
demos/runtime_boot/gen_rootfs.yaml
Normal file
7
demos/runtime_boot/gen_rootfs.yaml
Normal file
@ -0,0 +1,7 @@
|
||||
includes:
|
||||
- base.yaml
|
||||
targets:
|
||||
- target: /bin
|
||||
copy:
|
||||
- files:
|
||||
- ../gen_rootfs/target/debug/gen_rootfs
|
11
demos/runtime_boot/gen_rootfs/Cargo.toml
Normal file
11
demos/runtime_boot/gen_rootfs/Cargo.toml
Normal file
@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "gen_rootfs"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2"
|
||||
nix = "0.23.0"
|
||||
fs_extra = "1.2"
|
74
demos/runtime_boot/gen_rootfs/src/main.rs
Normal file
74
demos/runtime_boot/gen_rootfs/src/main.rs
Normal file
@ -0,0 +1,74 @@
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use nix::mount::{mount, umount, MsFlags};
|
||||
use fs_extra::dir::{copy, CopyOptions};
|
||||
|
||||
fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
println!("{:?}", args);
|
||||
fs::create_dir("/mount").unwrap();
|
||||
|
||||
let fs_type = "unionfs";
|
||||
let mount_path = Path::new("/mount");
|
||||
let source = Path::new("unionfs");
|
||||
let flags = MsFlags::empty();
|
||||
let key = &args[1];
|
||||
let options = format!(
|
||||
"lowerdir={},upperdir={},key={}",
|
||||
"./mnt_unionfs/lower",
|
||||
"./mnt_unionfs/upper",
|
||||
key
|
||||
);
|
||||
|
||||
println!("{:#?} {:#?} {:#?} {:#?} {:#?}", source, mount_path, fs_type, flags, options.as_str());
|
||||
mount(
|
||||
Some(source),
|
||||
mount_path,
|
||||
Some(fs_type),
|
||||
flags,
|
||||
Some(options.as_str()),
|
||||
).unwrap();
|
||||
|
||||
println!("Copy rootfs content to /mount");
|
||||
let copy_options = CopyOptions::new();
|
||||
let paths = fs::read_dir("/host/rootfs").unwrap();
|
||||
|
||||
for entry in paths {
|
||||
let path = entry.unwrap().path();
|
||||
println!("Name: {}", path.display());
|
||||
copy(path, "/mount", ©_options).unwrap();
|
||||
}
|
||||
|
||||
println!("List directories in {:#?}", mount_path);
|
||||
let paths = fs::read_dir("/mount").unwrap();
|
||||
|
||||
for path in paths {
|
||||
println!("Name: {}", path.unwrap().path().display())
|
||||
}
|
||||
|
||||
println!("Unmount {:#?}", mount_path);
|
||||
umount(mount_path).unwrap();
|
||||
|
||||
println!("Do mount again");
|
||||
fs::create_dir("/mount2").unwrap();
|
||||
let mount_path = Path::new("/mount2");
|
||||
println!("{:#?} {:#?} {:#?} {:#?} {:#?}", source, mount_path, fs_type, flags, options.as_str());
|
||||
mount(
|
||||
Some(source),
|
||||
mount_path,
|
||||
Some(fs_type),
|
||||
flags,
|
||||
Some(options.as_str()),
|
||||
).unwrap();
|
||||
|
||||
println!("List directories in {:#?}", mount_path);
|
||||
let paths = fs::read_dir("/mount2").unwrap();
|
||||
|
||||
for path in paths {
|
||||
println!("Name: {}", path.unwrap().path().display())
|
||||
}
|
||||
|
||||
println!("Unmount {:#?}", mount_path);
|
||||
umount(mount_path).unwrap();
|
||||
}
|
7
demos/runtime_boot/init.yaml
Normal file
7
demos/runtime_boot/init.yaml
Normal file
@ -0,0 +1,7 @@
|
||||
includes:
|
||||
- base.yaml
|
||||
targets:
|
||||
- target: /bin/
|
||||
copy:
|
||||
- files:
|
||||
- ${INIT_DIR}/target/x86_64-unknown-linux-musl/release/init
|
10
demos/runtime_boot/init/Cargo.toml
Normal file
10
demos/runtime_boot/init/Cargo.toml
Normal file
@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "init"
|
||||
version = "0.0.1"
|
||||
authors = ["LI Qing geding.lq@antgroup.com"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.84"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
117
demos/runtime_boot/init/src/main.rs
Normal file
117
demos/runtime_boot/init/src/main.rs
Normal file
@ -0,0 +1,117 @@
|
||||
extern crate libc;
|
||||
extern crate serde;
|
||||
extern crate serde_json;
|
||||
|
||||
use libc::syscall;
|
||||
|
||||
use std::io::prelude::*;
|
||||
use std::error::Error;
|
||||
use std::fs::File;
|
||||
use std::io::{ErrorKind, Read};
|
||||
|
||||
use std::ffi::CString;
|
||||
use std::env;
|
||||
|
||||
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
println!("{:?}", args);
|
||||
|
||||
// TODO: Get the rootfs key and other parameters through RA/LA or PAL
|
||||
let rootfs_key = b"c7-32-b3-ed-44-df-ec-7b-25-2d-9a-32-38-8d-58-61";
|
||||
let rootfs_upper_layer = "../gen_rootfs_instance/mnt_unionfs/upper";
|
||||
let rootfs_lower_layer = "../gen_rootfs_instance/mnt_unionfs/lower";
|
||||
let rootfs_entry = "/bin";
|
||||
|
||||
// Get the key of FS image if needed
|
||||
let key = {
|
||||
// TODO: Get the key through RA or LA
|
||||
let mut file = File::create("/etc/image_key")?;
|
||||
// Writes key.
|
||||
file.write(rootfs_key)?;
|
||||
|
||||
const IMAGE_KEY_FILE: &str = "/etc/image_key";
|
||||
let key_str = load_key(IMAGE_KEY_FILE)?;
|
||||
let mut key: sgx_key_128bit_t = Default::default();
|
||||
parse_str_to_bytes(&key_str, &mut key)?;
|
||||
Some(key)
|
||||
};
|
||||
|
||||
let key_ptr = key
|
||||
.as_ref()
|
||||
.map(|key| key as *const sgx_key_128bit_t)
|
||||
.unwrap_or(std::ptr::null());
|
||||
|
||||
// Mount the image
|
||||
const SYS_MOUNT_FS: i64 = 363;
|
||||
|
||||
// Set rootfs parameters
|
||||
let upper_layer_path =
|
||||
CString::new(rootfs_upper_layer).expect("CString::new failed");
|
||||
let lower_layer_path =
|
||||
CString::new(rootfs_lower_layer).expect("CString::new failed");
|
||||
let entry_point = CString::new(rootfs_entry).expect("CString::new failed");
|
||||
let hostfs_source = CString::new("/tmp").expect("CString::new failed");
|
||||
let rootfs_config: user_rootfs_config = user_rootfs_config {
|
||||
upper_layer_path: upper_layer_path.as_ptr(),
|
||||
lower_layer_path: lower_layer_path.as_ptr(),
|
||||
entry_point: entry_point.as_ptr(),
|
||||
hostfs_source: hostfs_source.as_ptr(),
|
||||
hostfs_target: std::ptr::null()
|
||||
};
|
||||
|
||||
let ret = unsafe { syscall(
|
||||
SYS_MOUNT_FS,
|
||||
key_ptr,
|
||||
&rootfs_config)
|
||||
};
|
||||
if ret < 0 {
|
||||
return Err(Box::new(std::io::Error::last_os_error()));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
type sgx_key_128bit_t = [u8; 16];
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[allow(non_camel_case_types)]
|
||||
struct user_rootfs_config {
|
||||
// UnionFS type rootfs upper layer, read-write layer
|
||||
upper_layer_path: *const i8,
|
||||
// UnionFS type rootfs lower layer, read-only layer
|
||||
lower_layer_path: *const i8,
|
||||
entry_point: *const i8,
|
||||
// HostFS source path
|
||||
hostfs_source: *const i8,
|
||||
// HostFS target path, default value is "/host"
|
||||
hostfs_target: *const i8,
|
||||
}
|
||||
|
||||
fn load_key(key_path: &str) -> Result<String, Box<dyn Error>> {
|
||||
let mut key_file = File::open(key_path)?;
|
||||
let mut key = String::new();
|
||||
key_file.read_to_string(&mut key)?;
|
||||
Ok(key.trim_end_matches(|c| c == '\r' || c == '\n').to_string())
|
||||
}
|
||||
|
||||
fn parse_str_to_bytes(arg_str: &str, bytes: &mut [u8]) -> Result<(), Box<dyn Error>> {
|
||||
let bytes_str_vec = {
|
||||
let bytes_str_vec: Vec<&str> = arg_str.split('-').collect();
|
||||
if bytes_str_vec.len() != bytes.len() {
|
||||
return Err(Box::new(std::io::Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
"The length or format of Key/MAC string is invalid",
|
||||
)));
|
||||
}
|
||||
bytes_str_vec
|
||||
};
|
||||
|
||||
for (byte_i, byte_str) in bytes_str_vec.iter().enumerate() {
|
||||
bytes[byte_i] = u8::from_str_radix(byte_str, 16)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
Loading…
Reference in New Issue
Block a user