[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