#!/bin/bash this_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" occlum_dir="$( cd "$( dirname "$this_dir/../../../" )" >/dev/null 2>&1 && pwd )" build_makefile=$occlum_dir/build/bin/occlum_build.mk if [[ "$occlum_dir" == "/opt/occlum" ]]; then version_header=$occlum_dir/include/occlum_version.h occlum_sgx_env=$occlum_dir/sgxsdk-tools/environment else version_header=$occlum_dir/src/pal/include/occlum_version.h occlum_sgx_env=$occlum_dir/etc/environment fi # For deployment environment, version header file may not exist if [ -f "$version_header" ]; then major_ver=`grep '\#define OCCLUM_MAJOR_VERSION' $version_header | awk '{print $3}'` minor_ver=`grep '\#define OCCLUM_MINOR_VERSION' $version_header | awk '{print $3}'` patch_ver=`grep '\#define OCCLUM_PATCH_VERSION' $version_header | awk '{print $3}'` occlum_version="$major_ver.$minor_ver.$patch_ver" fi instance_dir=`pwd` status_file=$instance_dir/.__occlum_status # For deployment environment, env for sgx-sdk may not exist if [ -f "$occlum_sgx_env" ]; then source $occlum_sgx_env SGX_GDB="$SGX_SDK/bin/sgx-gdb" ENCLAVE_SIGN_TOOL="$SGX_SDK/bin/x64/sgx_sign" ENCLAVE_SIGN_KEY="$occlum_dir/etc/template/Enclave.pem" fi get_enclave_debuggable_flag() { jq '.metadata.debuggable' $instance_dir/Occlum.json } exit_error() { echo "Error: $@" >&2 exit 1 } report_arg_error() { echo $1 >&2 echo "" cat < Create a new directory at and initialize as the Occlum instance. occlum init Initialize a directory as the Occlum instance. occlum build [--sign-key ] [--sign-tool ] [--image-key ] [--buildin-image-key] [-f/--force] Build and sign an Occlum SGX enclave (.so) and generate its associated secure FS image according to the user-provided image directory and Occlum.json config file. The whole building process is incremental: the building artifacts are built only when needed. To force rebuilding all artifacts, give the [-f/--force] flag. occlum run Run the user program inside an SGX enclave. occlum package [.tar.gz] Generate a minimal, self-contained package (.tar.gz) for the Occlum instance. The resulting package can then be copied to a deployment environment and unpacked as a runnable Occlum instance. All runtime dependencies required by the Occlum instance---except Intel SGX driver, enable_rdfsbase kernel module, and Intel SGX PSW---are included in the package. If package_name is not specified, the directory name of Occlum instance will be used. occlum gdb Debug the program running inside an SGX enclave with GDB. occlum mount [--sign-key ] [--sign-tool ] [--image-key ] Mount the secure FS image of the Occlum instance as a Linux FS at an existing . This makes it easy to access and manipulate Occlum's secure FS for debug purpose. occlum gen-image-key Generate a file consists of a randomly generated 128-bit key for encryption of the FS image. EOF } check_has_init() { if [ ! -f "$status_file" ]; then echo "Error: the current working directory is not initialized as an Occlum instance. Need to run \"occlum init\" first." exit 1 fi } check_has_built() { check_has_init if [ ! -d "$instance_dir/run/mount/__ROOT" ]; then echo "Error: the Occlum image and enclave are not built yet. Need to run \"occlum build\" first." exit 1 fi } check_has_run() { if pgrep --full "$instance_dir/build/bin/occlum-run" > /dev/null ; then echo "Error: the process of current Occlum instance is running. Need to wait for the process to finish or kill it first." exit 1 fi } check_aesm_service() { # Ignore AESM service status for simulation mode if [ "$(cat $instance_dir/.sgx_mode 2>/dev/null)" != "HW" ]; then return fi AESM_SOCKET_FILE=/var/run/aesmd/aesm.socket for i in $(seq 1 3); do if [ -S "$AESM_SOCKET_FILE" ]; then return else sleep 5 fi done echo "Error: AESM service is not started yet. Need to start it first" exit 1 } cmd_new() { if [ -z $@ ]; then echo "Error: target directory is not set" exit 1 fi dir_path="$@" if [[ "$dir_path" != "/"* ]]; then dir_path="$instance_dir/$@" fi if [[ -e "$dir_path" ]]; then echo "Error: destination \"$dir_path\" already exists" exit 1 fi mkdir -p $dir_path instance_dir=$dir_path status_file=$instance_dir/.__occlum_status cd $dir_path && cmd_init } cmd_init() { if [ -f "$status_file" ]; then echo "Error: the current working directory has been initialized as an Occlum instance" exit 1 fi echo "initialized" > $status_file cd "$instance_dir" mkdir -p image mkdir -p image/bin mkdir -p image/lib mkdir -p image/lib64 mkdir -p image/root mkdir -p image/host mkdir -p image/tmp mkdir -p image/dev mkdir -p image/proc mkdir -p image/etc local occlum_glibc_lib=/opt/occlum/glibc/lib local occlum_glibc_etc=/opt/occlum/glibc/etc local cpu_lib=/sys/devices/system/cpu if [ -d "$occlum_glibc_lib" ]; then mkdir -p "image/$occlum_glibc_lib" mkdir -p "image/$cpu_lib" mkdir -p "image/$occlum_glibc_etc" fi # add default /etc/hosts echo "127.0.0.1 localhost" > image/etc/hosts # add default timezone file cp /etc/localtime image/etc/ # add musl local occlum_musl_lib=/usr/local/occlum/x86_64-linux-musl/lib cp -t image/lib \ /lib/ld-musl-x86_64.so.1 \ "$occlum_musl_lib/libc.so" \ "$occlum_musl_lib/libstdc++.so.6" \ "$occlum_musl_lib/libgcc_s.so.1" \ "$occlum_musl_lib/libgomp.so.1" # add glibc if [ -d "$occlum_glibc_lib" ]; then cp -t image/lib64 \ "$occlum_glibc_lib/ld-linux-x86-64.so.2" ln -sf /lib64/ld-linux-x86-64.so.2 "image/$occlum_glibc_lib/ld-linux-x86-64.so.2" cp -t "image/$occlum_glibc_lib" \ "$occlum_glibc_lib/libc.so.6" \ "$occlum_glibc_lib/libpthread.so.0" \ "$occlum_glibc_lib/libm.so.6" local os_release=`awk -F= '/^NAME/{print $2}' /etc/os-release` if [ "$os_release" == "\"Ubuntu\"" ]; then cp -t "image/$occlum_glibc_lib" \ "/usr/lib/x86_64-linux-gnu/libstdc++.so.6" \ "/lib/x86_64-linux-gnu/libgcc_s.so.1" else cp -t "image/$occlum_glibc_lib" \ "/usr/lib64/libstdc++.so.6" \ "/usr/lib64/libgcc_s.so.1" fi cp -t "image/$occlum_glibc_etc" \ /etc/localtime cp -t "image/$cpu_lib" \ "$cpu_lib/online" fi mkdir -p initfs mkdir -p initfs/bin mkdir -p initfs/lib mkdir -p initfs/dev mkdir -p initfs/proc mkdir -p initfs/etc # add default /etc/hosts echo "127.0.0.1 localhost" > initfs/etc/hosts # add default timezone file cp /etc/localtime initfs/etc/ # add musl local occlum_musl_lib=/usr/local/occlum/x86_64-linux-musl/lib cp -t initfs/lib \ /lib/ld-musl-x86_64.so.1 \ "$occlum_musl_lib/libc.so" \ "$occlum_musl_lib/libstdc++.so.6" \ "$occlum_musl_lib/libgcc_s.so.1" \ "$occlum_musl_lib/libgomp.so.1" cp "$occlum_dir"/build/bin/init initfs/bin/ cp "$occlum_dir"/etc/template/Occlum.json "$instance_dir"/ chmod 644 "$instance_dir"/Occlum.json echo "$instance_dir initialized as an Occlum instance" } cmd_build() { check_has_init pal_lib=libocclum-pal.so libos_lib=libocclum-libos.so BUILDIN_IMAGE_KEY=false while [ -n "$1" ]; do case "$1" in --sign-key) [ -n "$2" ] && ENCLAVE_SIGN_KEY=$2 ; shift 2 || exit_error "empty signing key path" ;; --sign-tool) [ -n "$2" ] && ENCLAVE_SIGN_TOOL=$2 ; shift 2 || exit_error "empty signing tool path" ;; --sgx-mode) [[ -n "$2" && "$2" != "HW" ]] && export SGX_MODE=SIM ; shift 2 || exit_error "empty sgx mode";; --image-key) [ -n "$2" ] && SECURE_IMAGE_KEY=$2 ; shift 2 || exit_error "empty secure image key path" ;; --buildin-image-key) BUILDIN_IMAGE_KEY=true ; shift ;; --force | -f) MAKE_OPTION="clean" ; shift ;; *) exit_error "Unknown option: $1" ;; esac done [ -e "$ENCLAVE_SIGN_KEY" ] || exit_error "invalid signing key path: $ENCLAVE_SIGN_KEY" [ -e "$ENCLAVE_SIGN_TOOL" ] || exit_error "invalid signing tool path: $ENCLAVE_SIGN_TOOL" if [ -n "$SECURE_IMAGE_KEY" ]; then [ -e "$SECURE_IMAGE_KEY" ] || exit_error "invalid secure image key path: $SECURE_IMAGE_KEY" fi echo "Enclave sign-tool: $ENCLAVE_SIGN_TOOL" echo "Enclave sign-key: $ENCLAVE_SIGN_KEY" [ -n "$SECURE_IMAGE_KEY" ] && echo "Image encryption key: $SECURE_IMAGE_KEY" if [[ -n $SGX_MODE && "$SGX_MODE" != "HW" ]]; then export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$SGX_SDK/sdk_libs pal_lib=libocclum-pal_sim.so libos_lib=libocclum-libos_sim.so echo "SGX mode: $SGX_MODE" else echo "SGX mode: HW" fi # If sgx mode is changed, build thoroughly again if [[ -n $SGX_MODE && "$SGX_MODE" != "HW" ]]; then if [ "$(cat $instance_dir/.sgx_mode 2>/dev/null)" != "SIM" ]; then MAKE_OPTION="clean" fi else #HW mode if [ "$(cat $instance_dir/.sgx_mode 2>/dev/null)" != "HW" ]; then MAKE_OPTION="clean" fi fi rm -rf "$instance_dir/run" if [[ -n $MAKE_OPTION ]]; then occlum_dir=$occlum_dir instance_dir=$instance_dir \ make -f $build_makefile $MAKE_OPTION fi occlum_dir=$occlum_dir instance_dir=$instance_dir pal_lib=$pal_lib major_ver=$major_ver \ occlum_version=$occlum_version libos_lib=$libos_lib \ ENCLAVE_SIGN_KEY=$ENCLAVE_SIGN_KEY ENCLAVE_SIGN_TOOL=$ENCLAVE_SIGN_TOOL \ SECURE_IMAGE_KEY=$SECURE_IMAGE_KEY BUILDIN_IMAGE_KEY=$BUILDIN_IMAGE_KEY \ make -f $build_makefile --no-builtin-rules cd "$instance_dir" echo "built" > $status_file if [[ -n $SGX_MODE && "$SGX_MODE" != "HW" ]]; then echo "SIM" > .sgx_mode else echo "HW" > .sgx_mode fi mkdir -p "$instance_dir/run/mount/__ROOT" mkdir -p "$instance_dir/run/mount/tmp" mkdir -p "$instance_dir/run/initfs/__ROOT" echo "Built the Occlum image and enclave successfully" } cmd_run() { check_has_built check_has_run check_aesm_service SGX_MODE=$(cat $instance_dir/.sgx_mode) if [[ -n $SGX_MODE && "$SGX_MODE" != "HW" ]]; then export LD_LIBRARY_PATH="$instance_dir/build/lib:$SGX_SDK/sdk_libs/" else export LD_LIBRARY_PATH="$instance_dir/build/lib" fi echo "running" > $status_file if [ "`get_enclave_debuggable_flag`" == "false" ]; then export OCCLUM_RELEASE_ENCLAVE=1 fi RUST_BACKTRACE=1 "$instance_dir/build/bin/occlum-run" "$@" echo "built" > $status_file } cmd_start() { check_has_built check_aesm_service SGX_MODE=$(cat $instance_dir/.sgx_mode) if [[ -n $SGX_MODE && "$SGX_MODE" != "HW" ]]; then export LD_LIBRARY_PATH="$instance_dir/build/lib:$SGX_SDK/sdk_libs/" else export LD_LIBRARY_PATH="$instance_dir/build/lib" fi echo "running" > $status_file if [ "`get_enclave_debuggable_flag`" == "false" ]; then export OCCLUM_RELEASE_ENCLAVE=1 fi RUST_BACKTRACE=1 "$instance_dir/build/bin/occlum_exec_client" start echo "built" > $status_file } cmd_exec() { check_has_built SGX_MODE=$(cat $instance_dir/.sgx_mode) if [[ -n $SGX_MODE && "$SGX_MODE" != "HW" ]]; then export LD_LIBRARY_PATH="$instance_dir/build/lib:$SGX_SDK/sdk_libs/" else export LD_LIBRARY_PATH="$instance_dir/build/lib" fi echo "running" > "$status_file" RUST_BACKTRACE=1 "$instance_dir/build/bin/occlum_exec_client" exec -- "$@" echo "built" > "$status_file" } cmd_stop() { check_has_built SGX_MODE=$(cat $instance_dir/.sgx_mode) if [[ -n $SGX_MODE && "$SGX_MODE" != "HW" ]]; then export LD_LIBRARY_PATH="$instance_dir/build/lib:$SGX_SDK/sdk_libs/" else export LD_LIBRARY_PATH="$instance_dir/build/lib" fi echo "running" > "$status_file" RUST_BACKTRACE=1 "$instance_dir/build/bin/occlum_exec_client" stop -t 0 echo "built" > "$status_file" } cmd_package() { check_has_built SGX_MODE=$(cat $instance_dir/.sgx_mode) if [[ -n $SGX_MODE && "$SGX_MODE" != "HW" ]]; then echo '"occlum package" command should only be used for an Occlum instance of SGX hardware mode, not the simulation mode.' echo 'Please run "occlum build --sgx-mode HW" and then use "occlum package"' exit 1 fi instance_base_name=$(basename $instance_dir) if [[ -z "$@" ]]; then package_name="$instance_base_name.tar.gz" else if [[ "$@" == *.tar.gz ]];then package_name="$@" else package_name="$@.tar.gz" fi fi rm -f $package_name cd .. && tar -cvzf $instance_dir/$package_name $instance_base_name/Occlum.json $instance_base_name/build/bin \ $instance_base_name/build/lib/libocclum-libos.signed.so $instance_base_name/build/lib/libocclum-pal.so* \ $instance_base_name/build/mount $instance_base_name/build/Occlum.json.protected \ $instance_base_name/build/initfs $instance_base_name/build/.Occlum_sys.json.protected \ $instance_base_name/initfs $instance_base_name/run \ $instance_base_name/.__occlum_status $instance_base_name/.sgx_mode if [ "`get_enclave_debuggable_flag`" == "true" ]; then echo 'Warning: current Occlum instance is configured as "debuggable".' echo '(If it is not expected, you can modify the Occlum.json "metadata" - "debuggable" field to "false" and build again. And then use "occlum package")' fi echo "The package $package_name is generated successfully" } cmd_gdb() { check_has_built SGX_MODE=$(cat $instance_dir/.sgx_mode) if [[ -n $SGX_MODE && "$SGX_MODE" != "HW" ]]; then export LD_LIBRARY_PATH="$instance_dir/build/lib:$SGX_SDK/sdk_libs/" else export LD_LIBRARY_PATH="$instance_dir/build/lib" fi echo "debugging" > "$status_file" OCCLUM_GDB=1 $SGX_GDB --args "$instance_dir/build/bin/occlum-run" "$@" echo "built" > "$status_file" } cmd_mount() { check_has_built while [ -n "$1" ]; do case "$1" in --sign-key) [ -n "$2" ] && ENCLAVE_SIGN_KEY=$2 ; shift 2 || exit_error "empty signing key path" ;; --sign-tool) [ -n "$2" ] && ENCLAVE_SIGN_TOOL=$2 ; shift 2 || exit_error "empty signing tool path" ;; --image-key) [ -n "$2" ] && SECURE_IMAGE_KEY=$2 ; shift 2 || exit_error "empty secure image key path" ;; *) MNT_POINT=$1 ; shift ;; esac done [ -e "$ENCLAVE_SIGN_KEY" ] || exit_error "invalid signing key path: $ENCLAVE_SIGN_KEY" [ -e "$ENCLAVE_SIGN_TOOL" ] || exit_error "invalid signing tool path: $ENCLAVE_SIGN_TOOL" if [ -n "$SECURE_IMAGE_KEY" ]; then [ -e "$SECURE_IMAGE_KEY" ] || exit_error "invalid secure image key path: $SECURE_IMAGE_KEY" fi [ -d "$MNT_POINT" ] || exit_error "invalid mount point: $MNT_POINT" echo "Mount tool sign-tool: $ENCLAVE_SIGN_TOOL" echo "Mount tool sign-key: $ENCLAVE_SIGN_KEY" [ -n "$SECURE_IMAGE_KEY" ] && echo "Image decryption key: $SECURE_IMAGE_KEY" echo "Mount point: $MNT_POINT" SGX_MODE=$(cat $instance_dir/.sgx_mode) if [[ -n $SGX_MODE && "$SGX_MODE" != "HW" ]]; then sefs_cli="$occlum_dir/build/bin/sefs-cli_sim" sefs_cli_lib="$occlum_dir/build/lib/libsefs-cli_sim.so" echo "SGX mode: $SGX_MODE" else sefs_cli="$occlum_dir/build/bin/sefs-cli" sefs_cli_lib="$occlum_dir/build/lib/libsefs-cli.so" echo "SGX mode: HW" fi signed_sefs_cli_lib="$instance_dir/build/lib/libsefs-cli.signed.so" echo "Signing the mount tool..." "$ENCLAVE_SIGN_TOOL" sign \ -key "$ENCLAVE_SIGN_KEY" \ -config "$occlum_dir/build/sefs-cli.Enclave.xml" \ -enclave "$sefs_cli_lib" \ -out "$signed_sefs_cli_lib" image_fs="$instance_dir/build/mount/__ROOT" if [ -e "$instance_dir/run/mount/__ROOT/metadata" ]; then container_fs="$instance_dir/run/mount/__ROOT" else container_fs="" fi [ -n "$SECURE_IMAGE_KEY" ] && SECURE_IMAGE_KEY_OPTION="--key $SECURE_IMAGE_KEY" echo "Start to mount the FS..." LD_LIBRARY_PATH="$SGX_SDK/sdk_libs" "$sefs_cli" \ --enclave "$signed_sefs_cli_lib" \ mount \ $SECURE_IMAGE_KEY_OPTION \ "$image_fs" \ "$container_fs" \ "$MNT_POINT" # After mounting the FS, remove the signed mount tool rm -f "$signed_sefs_cli_lib" } cmd_status() { cat "$status_file" } cmd_gen_image_key() { if [ -z $@ ]; then echo "Error: target file is not set" exit 1 fi key_path="$@" if [[ "$key_path" != "/"* ]]; then key_path="$instance_dir/$@" fi cat /dev/urandom | tr -dc 'a-f0-9' | fold -w 32 | head -n 1 | sed -r 's/.{2}/&-/g; s/.$//' > $key_path } set -e if [[ ( "$#" < 1 ) ]] ; then report_arg_error "Error: no sub-command is given" exit 1 fi cmd=$1 case "$cmd" in new) cmd_new "${@:2:1}" ;; init) cmd_init ;; build) cmd_build "${@:2}" ;; run) cmd_run "${@:2}" ;; start) cmd_start ;; exec) cmd_exec "${@:2}" ;; stop) cmd_stop ;; package) cmd_package "${@:2}" ;; gdb) cmd_gdb "${@:2}" ;; mount) cmd_mount "${@:2}" ;; status) cmd_status ;; gen-image-key) cmd_gen_image_key "${@:2:1}" ;; *) report_arg_error "Error: unknown sub-command $cmd" exit 1 esac