diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ab527c04..201edd9e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: dtolnay/rust-toolchain@stable + - uses: dtolnay/rust-toolchain@1.60 - uses: Swatinem/rust-cache@v2 - run: rustup component add clippy - run: cargo clippy --all diff --git a/Cargo.lock b/Cargo.lock index 8a794150..3f1e037f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -251,6 +251,21 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "command-executer" +version = "0.1.0" +dependencies = [ + "byteorder", + "clap", + "log", + "nix 0.26.2", + "num", + "num-derive", + "num-traits", + "serde", + "serde_json", +] + [[package]] name = "core-foundation-sys" version = "0.8.3" @@ -1098,6 +1113,41 @@ dependencies = [ "winapi", ] +[[package]] +name = "num" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8536030f9fea7127f841b45bb6243b27255787fb4eb83958aa1ef9d2fdc0c36" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95" +dependencies = [ + "autocfg", + "num-traits", +] + [[package]] name = "num-derive" version = "0.3.3" @@ -1109,6 +1159,39 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" +dependencies = [ + "autocfg", + "num-bigint", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.15" diff --git a/Cargo.toml b/Cargo.toml index 27adc146..59f5aa0b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,7 +41,7 @@ num-traits = "0.2" tempfile = "3.5" [workspace] -members = [".", "driver-bindings", "eif_loader", "enclave_build", "vsock_proxy"] +members = [".", "samples/command_executer", "driver-bindings", "eif_loader", "enclave_build", "vsock_proxy"] [features] default = [] diff --git a/README.md b/README.md index ced005fb..b7c17467 100644 --- a/README.md +++ b/README.md @@ -159,7 +159,7 @@ This repository contains a collection of tools and commands used for managing th - 'ci_entrypoint.sh': The script which launches the CLI continuous integration tests - - 'run_tests.sh': The continuous integration test suite for the CLI across all supported platforms + - 'scripts/run_tests.sh': The continuous integration test suite for the CLI across all supported platforms ## Security issue notifications diff --git a/ci_entrypoint.sh b/ci_entrypoint.sh index 241b9cae..cd94f3b3 100755 --- a/ci_entrypoint.sh +++ b/ci_entrypoint.sh @@ -47,7 +47,7 @@ STATE="pending" status_update set +e -./run_tests.sh 2>&1 | tee test_logs.out +./scripts/run_tests.sh 2>&1 | tee test_logs.out TEST_RESULTS=$? set -e diff --git a/run_tests.sh b/run_tests.sh deleted file mode 100755 index 816aab60..00000000 --- a/run_tests.sh +++ /dev/null @@ -1,137 +0,0 @@ -#!/bin/bash -x -# -# Copyright 2020-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. -# SPDX-License-Identifier: Apache-2.0 -# -# Script used for running all the tests we have on a EC2 instance that has -# --enclave-options set to true -# -TEST_SUITES_FAILED=0 -TEST_SUITES_TOTAL=0 - -SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" -export NITRO_CLI_BLOBS="${SCRIPTDIR}/blobs" -export NITRO_CLI_ARTIFACTS="${SCRIPTDIR}/build" -ARCH="$(uname -m)" - -AWS_ACCOUNT_ID=667861386598 - -# Indicate that the test suite has failed -function register_test_fail() { - TEST_SUITES_FAILED=$((TEST_SUITES_FAILED + 1)) -} - -# Clean up and exit with the current test suite's status -function clean_up_and_exit() { - [ "$(lsmod | grep -cw nitro_enclaves)" -eq 0 ] || rmmod nitro_enclaves || register_test_fail - make clean - rm -rf test_images - - # Cleanup pulled images during testing - docker rmi public.ecr.aws/aws-nitro-enclaves/hello:v1 2> /dev/null || true - docker rmi hello-world:latest 2> /dev/null || true - - rm -rf examples/"${ARCH}"/hello-entrypoint - docker rmi hello-entrypoint-usage:latest 2> /dev/null || true - - exit $TEST_SUITES_FAILED -} - -# Force the test suite to end in failure -function test_failed() { - register_test_fail - clean_up_and_exit -} - -# Remove the Nitro Enclaves driver -function remove_ne_driver() { - [ "$(lsmod | grep -cw nitro_enclaves)" -eq 0 ] || rmmod nitro_enclaves || test_failed -} - -# Configure and insert the Nitro Enclaves driver -function configure_ne_driver() { - if [ "$(lsmod | grep -cw nitro_enclaves)" -eq 0 ] - then - # Preallocate 2046 Mb, that should be enough for all the tests. We explicitly - # pick this value to have both 1 GB and 2 MB pages if the system allows it. - source build/install/etc/profile.d/nitro-cli-env.sh || test_failed - ./build/install/etc/profile.d/nitro-cli-config -m 2046 -t 2 || test_failed - fi -} - -# First run the instalation test, before we change the environement -pytest-3 tests/integration/test_installation.py || test_failed - -# Clean up build artefacts -make clean - -# Setup the environement with everything needed to run the integration tests -make command-executer || test_failed -make nitro-tests || test_failed -make nitro_enclaves || test_failed -make nitro-cli || test_failed -make vsock-proxy || test_failed -make install || test_failed - -# Ensure the Nitro Enclaves driver is inserted at the beginning. -configure_ne_driver - -# Create directories for enclave process sockets and logs -mkdir -p /run/nitro_enclaves || test_failed -mkdir -p /var/log/nitro_enclaves || test_failed - -# Build EIFS for testing -mkdir -p test_images || test_failed -export HOME="/root" - -# Simple EIF -nitro-cli build-enclave --docker-uri public.ecr.aws/aws-nitro-enclaves/hello:v1 \ - --output-file test_images/hello.eif || test_failed - -# Generate signing certificate -openssl ecparam -name secp384r1 -genkey -out test_images/key.pem || test_failed -openssl req -new -key test_images/key.pem -sha384 -nodes \ - -subj "/CN=AWS/C=US/ST=WA/L=Seattle/O=Amazon/OU=AWS" -out test_images/csr.pem || test_failed -openssl x509 -req -days 20 -in test_images/csr.pem -out test_images/cert.pem \ - -sha384 -signkey test_images/key.pem || test_failed -# Signed EIF -nitro-cli build-enclave --docker-uri public.ecr.aws/aws-nitro-enclaves/hello:v1 \ - --output-file test_images/hello-signed.eif \ - --private-key test_images/key.pem --signing-certificate test_images/cert.pem || test_failed - - -# Build enclave image using Docker ENTRYPOINT instruction -mkdir -p examples/"${ARCH}"/hello-entrypoint || test_failed -cp -r examples/"${ARCH}"/hello/* examples/"${ARCH}"/hello-entrypoint || test_failed - -sed -i 's/CMD/ENTRYPOINT/g' examples/"${ARCH}"/hello-entrypoint/Dockerfile || test_failed - -nitro-cli build-enclave --docker-dir examples/"${ARCH}"/hello-entrypoint --docker-uri hello-entrypoint-usage \ - --output-file test_images/hello-entrypoint-usage.eif || test_failed - -# Load vsock_loopback module for connection_test test of vsock-proxy -if [ "$(lsmod | grep -cw vsock_loopback)" -eq 0 ]; then - modprobe vsock_loopback || echo "Module vsock_loopback not available." -fi - -# Run all unit tests -while IFS= read -r test_line -do - TEST_SUITES_TOTAL=$((TEST_SUITES_TOTAL + 1)) - test_module="$(echo ${test_line} | cut -d' ' -f2)" - test_exec_name="$(basename $(echo ${test_line} | cut -d' ' -f1))" - - configure_ne_driver - - timeout 7m \ - ./build/nitro_cli/"${ARCH}"-unknown-linux-musl/release/deps/"${test_exec_name}" \ - --test-threads=1 --nocapture || test_failed -done < <(grep -v '^ *#' < build/test_executables.txt) - -# Ensure the Nitro Enclaves driver is inserted for the remaining integration tests. -configure_ne_driver - -# Run integration tests except the instalation test -pytest-3 tests/integration/ --ignore tests/integration/test_installation.py || test_failed - -clean_up_and_exit diff --git a/samples/command_executer/Cargo.toml b/samples/command_executer/Cargo.toml index b57c7295..f98ebb5e 100644 --- a/samples/command_executer/Cargo.toml +++ b/samples/command_executer/Cargo.toml @@ -17,5 +17,3 @@ byteorder = "1.3" num = "0.2" num-derive = "0.3" num-traits = "0.2" - -[workspace] diff --git a/samples/command_executer/src/command_parser.rs b/samples/command_executer/src/command_parser.rs index 0d321f44..9feaf7c3 100644 --- a/samples/command_executer/src/command_parser.rs +++ b/samples/command_executer/src/command_parser.rs @@ -101,11 +101,7 @@ fn parse_command(args: &ArgMatches) -> Result { } fn parse_no_wait(args: &ArgMatches) -> bool { - if args.is_present("no-wait") { - true - } else { - false - } + args.is_present("no-wait") } fn parse_localfile(args: &ArgMatches) -> Result { diff --git a/samples/command_executer/src/lib.rs b/samples/command_executer/src/lib.rs index afa635e5..f2c0bf44 100644 --- a/samples/command_executer/src/lib.rs +++ b/samples/command_executer/src/lib.rs @@ -120,7 +120,7 @@ fn run_server(fd: RawFd, no_wait: bool) -> Result<(), String> { let buf = json_output.as_bytes(); let len: u64 = buf.len().try_into().map_err(|err| format!("{:?}", err))?; send_u64(fd, len)?; - send_loop(fd, &buf, len)?; + send_loop(fd, buf, len)?; Ok(()) } @@ -261,7 +261,7 @@ pub fn run(args: RunArgs) -> Result { let buf = args.command.as_bytes(); let len: u64 = buf.len().try_into().map_err(|err| format!("{:?}", err))?; send_u64(socket_fd, len)?; - send_loop(socket_fd, &buf, len)?; + send_loop(socket_fd, buf, len)?; // recv output let mut buf = [0u8; BUF_MAX_LEN]; @@ -304,7 +304,7 @@ pub fn recv_file(args: FileArgs) -> Result<(), String> { let buf = args.remotefile.as_bytes(); let len: u64 = buf.len().try_into().map_err(|err| format!("{:?}", err))?; send_u64(socket_fd, len)?; - send_loop(socket_fd, &buf, len)?; + send_loop(socket_fd, buf, len)?; // Receive filesize let filesize = recv_u64(socket_fd)?; @@ -344,7 +344,7 @@ pub fn send_file(args: FileArgs) -> Result<(), String> { let buf = args.remotefile.as_bytes(); let len: u64 = buf.len().try_into().map_err(|err| format!("{:?}", err))?; send_u64(socket_fd, len)?; - send_loop(socket_fd, &buf, len)?; + send_loop(socket_fd, buf, len)?; let filesize = file .metadata() diff --git a/samples/command_executer/src/protocol_helpers.rs b/samples/command_executer/src/protocol_helpers.rs index 39d297c1..2cc171fe 100644 --- a/samples/command_executer/src/protocol_helpers.rs +++ b/samples/command_executer/src/protocol_helpers.rs @@ -7,7 +7,7 @@ use std::os::unix::io::RawFd; pub fn send_u64(fd: RawFd, val: u64) -> Result<(), String> { let mut buf = [0u8; 9]; LittleEndian::write_u64(&mut buf, val); - send_loop(fd, &mut buf, 9)?; + send_loop(fd, &buf, 9)?; Ok(()) } @@ -21,7 +21,7 @@ pub fn recv_u64(fd: RawFd) -> Result { pub fn send_i32(fd: RawFd, val: i32) -> Result<(), String> { let mut buf = [0u8; 4]; LittleEndian::write_i32(&mut buf, val); - send_loop(fd, &mut buf, 4)?; + send_loop(fd, &buf, 4)?; Ok(()) } diff --git a/scripts/lib.sh b/scripts/lib.sh new file mode 100755 index 00000000..9a89fb46 --- /dev/null +++ b/scripts/lib.sh @@ -0,0 +1,159 @@ +#!/bin/bash -x +# +# Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# +TEST_SUITES_FAILED=0 +TEST_SUITES_TOTAL=0 + +SCRIPT_DIR="$(realpath $(dirname ${BASH_SOURCE[0]}))" +export NITRO_CLI_BLOBS="$SCRIPT_DIR/../blobs" +export NITRO_CLI_ARTIFACTS="$SCRIPT_DIR/../build" + +ARCH="$(uname -m)" +AWS_ACCOUNT_ID=667861386598 + +test_start() { + TEST_SUITES_TOTAL=$(($TEST_SUITES_TOTAL + 1)) +} + +# Indicate that the test suite has failed +register_test_fail() { + TEST_SUITES_FAILED=$(($TEST_SUITES_FAILED + 1)) +} + +# Force the test suite to end in failure +test_failed() { + register_test_fail + clean_up_and_exit +} + +# Clean up and exit with the current test suite's status +clean_up_and_exit() { + driver_unload nitro_enclaves || register_test_fail + + make clean + + enclaves_images_clean + + exit $TEST_SUITES_FAILED +} + +driver_is_not_loaded() { + local driver_name="$1" + [ "$(lsmod | grep -cw $driver_name)" -eq 0 ] +} + +driver_unload() { + local driver_name="$1" + driver_is_not_loaded $driver_name || rmmod $driver_name +} + +ne_driver_is_not_loaded() { + driver_is_not_loaded nitro_enclaves +} + +# Remove the Nitro Enclaves driver +ne_driver_remove() { + ne_driver_is_not_loaded || rmmod nitro_enclaves +} + +# Configure and insert the Nitro Enclaves driver +ne_driver_configure() { + if ne_driver_is_not_loaded; then + # Preallocate 2046 Mb, that should be enough for all the tests. We explicitly + # pick this value to have both 1 GB and 2 MB pages if the system allows it. + source build/install/etc/profile.d/nitro-cli-env.sh + ./build/install/etc/profile.d/nitro-cli-config -m 2046 -t 2 + fi +} + +build_and_install() { + # First run the instalation test, before we change the environement + pytest-3 tests/integration/test_installation.py + + # Clean up build artifacts + make clean + + # Setup the environment with everything needed to run the integration tests + make command-executer + make nitro-tests + make nitro_enclaves + make nitro-cli + make vsock-proxy + make install +} + +prepare_env() { + # Ensure the Nitro Enclaves driver is inserted + ne_driver_configure + + # Load vsock_loopback module for connection_test test of vsock-proxy + if driver_is_not_loaded vsock_loopback; then + modprobe vsock_loopback || echo "Module vsock_loopback not available." + fi + + # Create directories for enclave process sockets and logs + mkdir -p /run/nitro_enclaves + mkdir -p /var/log/nitro_enclaves +} + +IMAGES_DIR="test_images" +EXAMPLES_DIR="examples/$ARCH" +HELLO_ENTRYPOINT_DIR="$EXAMPLES_DIR/hello-entrypoint" +HELLO_ENTRYPOINT_URI="hello-entrypoint-usage" + +# Build EIFS for testing +enclaves_images_build() { + mkdir -p "$IMAGES_DIR" + + # (1) Simple EIF + nitro-cli build-enclave \ + --docker-uri public.ecr.aws/aws-nitro-enclaves/hello:v1 \ + --output-file "$IMAGES_DIR"/hello.eif + + # Generate signing certificate + openssl ecparam -name secp384r1 -genkey -out "$IMAGES_DIR"/key.pem + openssl req -new -key "$IMAGES_DIR"/key.pem -sha384 -nodes \ + -subj "/CN=AWS/C=US/ST=WA/L=Seattle/O=Amazon/OU=AWS" -out "$IMAGES_DIR"/csr.pem + openssl x509 -req -days 20 -in "$IMAGES_DIR"/csr.pem -out "$IMAGES_DIR"/cert.pem \ + -sha384 -signkey "$IMAGES_DIR"/key.pem + + # (2) Signed EIF + nitro-cli build-enclave \ + --docker-uri public.ecr.aws/aws-nitro-enclaves/hello:v1 \ + --output-file "$IMAGES_DIR"/hello-signed.eif \ + --private-key "$IMAGES_DIR"/key.pem \ + --signing-certificate "$IMAGES_DIR"/cert.pem + + # (3) Build enclave image using Docker ENTRYPOINT instruction + mkdir -p "$HELLO_ENTRYPOINT_DIR" + cp -r "$EXAMPLES_DIR"/hello/* "$HELLO_ENTRYPOINT_DIR" + + sed -i 's/CMD/ENTRYPOINT/g' "$HELLO_ENTRYPOINT_DIR/Dockerfile" + + nitro-cli build-enclave \ + --docker-dir "$HELLO_ENTRYPOINT_DIR" \ + --docker-uri $HELLO_ENTRYPOINT_URI \ + --output-file "$IMAGES_DIR"/$HELLO_ENTRYPOINT_URI.eif +} + +enclaves_images_clean() { + rm -rf "$IMAGES_DIR" + + # Cleanup pulled images during testing + docker rmi public.ecr.aws/aws-nitro-enclaves/hello:v1 2> /dev/null || true + docker rmi hello-world:latest 2> /dev/null || true + + rm -rf "$HELLO_ENTRYPOINT_DIR" + docker rmi $HELLO_ENTRYPOINT_URI:latest 2> /dev/null || true +} + +run_integration_tests() { + # Ensure the Nitro Enclaves driver is inserted for the remaining integration tests. + ne_driver_configure + + # Run integration tests except the installation test + pytest-3 tests/integration/ --ignore tests/integration/test_installation.py +} + diff --git a/scripts/run_tests.sh b/scripts/run_tests.sh new file mode 100755 index 00000000..e5bc0deb --- /dev/null +++ b/scripts/run_tests.sh @@ -0,0 +1,43 @@ +#!/bin/bash -x +# +# Copyright 2020-2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# Script used for running all the tests we have on a EC2 instance that has +# --enclave-options set to true +# + +SCRIPT_DIR="$(realpath $(dirname ${BASH_SOURCE[0]}))" + +source "$SCRIPT_DIR/lib.sh" + +trap test_failed ERR + +# Build and install NE CLI +build_and_install + +# Prepare environment +prepare_env + +# Build EIFS for testing +enclaves_images_build + +# Run all unit tests +while IFS= read -r test_line; do + test_start + test_module="$(echo $test_line | cut -d' ' -f2)" + test_exec_name="$(basename $(echo $test_line | cut -d' ' -f1))" + + ne_driver_configure + + timeout 7m \ + ./build/nitro_cli/$ARCH-unknown-linux-musl/release/deps/$test_exec_name \ + --test-threads=1 --nocapture +done < <(grep -v '^ *#' < build/test_executables.txt) + +# Run integration tests +run_integration_tests + +# Clean-up +clean_up_and_exit +