diff --git a/.github/workflows/build-rpm.yml b/.github/workflows/build-rpm.yml index 5b9579a..88c2d2e 100644 --- a/.github/workflows/build-rpm.yml +++ b/.github/workflows/build-rpm.yml @@ -15,50 +15,116 @@ jobs: fail-fast: false steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - submodules: 'true' + - name: Checkout repository + uses: actions/checkout@v4 + with: + submodules: 'true' - - name: Check pre-release - run: | - tag="${GITHUB_REF#refs/*/}" - echo "tag=tag" - if [[ ${tag} == *alpha* || ${tag} == *beta* ]] - then - prerelease=true - else - prerelease=false - fi - echo "prerelease=$prerelease" - echo "PRE_RELEASE=$prerelease" >> $GITHUB_ENV + - name: Check pre-release + run: | + tag="${GITHUB_REF#refs/*/}" + echo "tag=tag" + if [[ ${tag} == *alpha* || ${tag} == *beta* ]] + then + prerelease=true + else + prerelease=false + fi + echo "prerelease=$prerelease" + echo "PRE_RELEASE=$prerelease" >> $GITHUB_ENV - - uses: dtolnay/rust-toolchain@1.75.0 + - uses: dtolnay/rust-toolchain@1.75.0 - - name: Build RPM package - run: | - make create-tarball - make rpm-build-in-docker - mkdir -p $GITHUB_WORKSPACE/rpmbuild - cp -r ~/rpmbuild/SRPMS/ $GITHUB_WORKSPACE/rpmbuild/ - cp -r ~/rpmbuild/RPMS/ $GITHUB_WORKSPACE/rpmbuild/ + - name: Build RPM package + run: | + make create-tarball + make rpm-build-in-docker + mkdir -p $GITHUB_WORKSPACE/rpmbuild + cp -r ~/rpmbuild/SRPMS/ $GITHUB_WORKSPACE/rpmbuild/ + cp -r ~/rpmbuild/RPMS/ $GITHUB_WORKSPACE/rpmbuild/ + mkdir -p $GITHUB_WORKSPACE/rpmbuild/SOURCES/ + cp -r /tmp/cryptpilot-*.tar.gz $GITHUB_WORKSPACE/rpmbuild/SOURCES/ - - name: Upload artifact - uses: actions/upload-artifact@v4 - with: - name: rpm-packages - if-no-files-found: error - path: | - ./rpmbuild/SRPMS/*.src.rpm - ./rpmbuild/RPMS/*/*.rpm + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: rpm-packages + if-no-files-found: error + path: | + ./rpmbuild/SOURCES/cryptpilot-*.tar.gz + ./rpmbuild/SRPMS/*.src.rpm + ./rpmbuild/RPMS/*/*.rpm - - name: Release - uses: softprops/action-gh-release@v2 - if: startsWith(github.ref, 'refs/tags/') - with: - fail_on_unmatched_files: true - prerelease: ${{ env.PRE_RELEASE }} - files: | - /tmp/cryptpilot-*.tar.gz - ./rpmbuild/SRPMS/*.src.rpm - ./rpmbuild/RPMS/*/*.rpm + test: + strategy: + fail-fast: false + matrix: + distro: ["alibaba-cloud-linux-3-registry.cn-hangzhou.cr.aliyuncs.com/alinux3/alinux3:latest", "registry.openanolis.cn/openanolis/anolisos:8", "registry.openanolis.cn/openanolis/anolisos:23"] + runs-on: ubuntu-latest + defaults: + run: + shell: bash + needs: build + container: + image: ${{ matrix.distro }} + options: --privileged + steps: + - name: Update yum mirror + run: | + set -e + set -x + + # replace the mirror + sed -i -E 's|https?://mirrors.openanolis.cn/anolis/|https://mirrors.aliyun.com/anolis/|g' /etc/yum.repos.d/*.repo + sed -i -E 's|https?://mirrors.cloud.aliyuncs.com/|https://mirrors.aliyun.com/|g' /etc/yum.repos.d/*.repo + + # install development tools + yum install -y autoconf automake binutils bison flex gcc gcc-c++ gdb glibc-devel libtool make pkgconf pkgconf-m4 pkgconf-pkg-config rpm-build rpm-sign strace asciidoc byacc ctags diffstat elfutils-libelf-devel git intltool patchutils perl-Fedora-VSP perl-Sys-Syslog perl-generators pesign source-highlight systemtap valgrind valgrind-devel cmake expect rpmdevtools rpmlint perl clang + + # install rpmdevtools + yum install -y git yum-utils + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@1.75.0 + + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + path: ./ + merge-multiple: false + - name: Install RPM packages + run: | + set -e + set -x + + # test rpm package install + yum install -y ./rpm-packages/RPMS/*/cryptpilot-*.rpm + cryptpilot --version + - name: Checkout repository + uses: actions/checkout@v4 + with: + submodules: 'true' + - name: Install build dependencies + run: yum-builddep -y ./cryptpilot.spec + - name: Run test script from repo + run: make run-test + + release: + if: startsWith(github.ref, 'refs/tags/') + runs-on: ubuntu-latest + needs: test + steps: + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + path: ./ + merge-multiple: false + - name: Release + uses: softprops/action-gh-release@v2 + with: + fail_on_unmatched_files: true + prerelease: ${{ env.PRE_RELEASE }} + files: | + ./rpm-packages/SOURCES/cryptpilot-*.tar.gz + ./rpm-packages/SRPMS/*.src.rpm + ./rpm-packages/RPMS/*/*.rpm diff --git a/Cargo.lock b/Cargo.lock index a15b520..8f8f9da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -454,6 +454,19 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[package]] +name = "cgroups-rs" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db7c2f5545da4c12c5701455d9471da5f07db52e49b9cccb4f5512226dd0836" +dependencies = [ + "libc", + "log", + "nix 0.25.1", + "regex", + "thiserror 1.0.65", +] + [[package]] name = "chrono" version = "0.4.38" @@ -753,6 +766,7 @@ dependencies = [ "base64 0.22.1", "bindgen 0.71.1", "block-devs", + "cgroups-rs", "clap", "comfy-table", "ctor", @@ -1955,6 +1969,18 @@ dependencies = [ "memoffset 0.6.5", ] +[[package]] +name = "nix" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" +dependencies = [ + "autocfg", + "bitflags 1.3.2", + "cfg-if", + "libc", +] + [[package]] name = "nix" version = "0.26.4" diff --git a/Cargo.toml b/Cargo.toml index 66ad717..f0d3376 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,6 +56,7 @@ shadow-rs = "0.35.2" ttrpc-codegen = {version = "0.4.2", optional = true} [dev-dependencies] +cgroups-rs = "0.3.4" ctor = "=0.4.1" rstest = "0.25.0" rstest_reuse = "0.7.0" @@ -63,6 +64,7 @@ two-rusty-forks = {version = "0.4.0", features = ["macro"]} [features] default = ["provider-kbs", "provider-kms", "provider-otp", "provider-tpm2", "provider-oidc", "provider-exec"] +provider-exec = [] provider-kbs = [ "dep:ttrpc-codegen", "dep:ttrpc", @@ -72,4 +74,3 @@ provider-kms = ["dep:kms"] provider-oidc = ["serde_json"] provider-otp = [] provider-tpm2 = [] -provider-exec = [] diff --git a/README.md b/README.md index 8a4f280..2cfb180 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # cryptpilot: The confidentiality for OS booting and data at rest in confidential computing environments [![Building](/../../actions/workflows/build-rpm.yml/badge.svg)](/../../actions/workflows/build-rpm.yml) +![GitHub Release](https://img.shields.io/github/v/release/openanolis/cryptpilot) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) The cryptpilit project aims to provide a way that allows you to securely boot your system while ensuring the encryption and measurability of the entire operating system, as well as encryption and integrity protection for data at rest. diff --git a/cryptpilot.spec b/cryptpilot.spec index 63ca425..92f5796 100644 --- a/cryptpilot.spec +++ b/cryptpilot.spec @@ -1,13 +1,14 @@ %global debug_package %{nil} +%define release_num 1 Name: cryptpilot Version: 0.2.2 -Release: 1%{?dist} +Release: %{release_num}%{?dist} Summary: A utility for protecting data at rest in confidential environment Group: Applications/System License: ASL 2.0 URL: www.alibaba.com -Source0: https://github.com/openanolis/cryptpilot/releases/download/v%{version}/cryptpilot-%{version}.tar.gz +Source0: https://github.com/openanolis/cryptpilot/releases/download/v%{version}-%{release_num}/cryptpilot-%{version}.tar.gz Source1: config @@ -19,6 +20,14 @@ Requires: systemd Requires: veritysetup Requires: veritysetup Requires: device-mapper-libs +# mkfs.vfat +Requires: dosfstools +# mkfs.xfs +Requires: xfsprogs +# mkfs.ext4 +Requires: e2fsprogs +# swapon +Requires: util-linux # If not installed, the kbs and kms-oidc keyprovider will not work. Recommends: confidential-data-hub diff --git a/src/fs/block/dummy.rs b/src/fs/block/dummy.rs index 6874a4b..ed55722 100644 --- a/src/fs/block/dummy.rs +++ b/src/fs/block/dummy.rs @@ -49,7 +49,8 @@ impl DummyDevice { sparse_file.seek(std::io::SeekFrom::Start(device_size - 1))?; sparse_file.write_all(&[0])?; - let lc = LoopControl::open()?; + let lc = LoopControl::open() + .context("Failed to open loop control, maybe forgot to run 'sudo modprobe loop'?")?; // Retry to avoid conflicts and waiting for avaliable loop device let ld = RetryPolicy::exponential(Duration::from_millis(1)) .with_max_retries(200) diff --git a/src/fs/mkfs.rs b/src/fs/mkfs.rs index 22edb77..58d0539 100644 --- a/src/fs/mkfs.rs +++ b/src/fs/mkfs.rs @@ -220,6 +220,7 @@ pub mod tests { config::{ encrypt::{EncryptConfig, KeyProviderConfig}, volume::{ExtraConfig, VolumeConfig}, + ConfigBundle, }, provider::otp::OtpConfig, }; @@ -244,6 +245,13 @@ pub mod tests { }, }; + crate::config::source::set_config_source(ConfigBundle { + global: None, + fde: None, + volumes: vec![volume_config.clone()], + }) + .await; + // Close the volume if it is already opened CloseCommand { close_options: CloseOptions { diff --git a/src/lib.rs b/src/lib.rs index 45caad3..3f23560 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -137,7 +137,7 @@ pub async fn run() -> Result<()> { /// /// # Examples /// -/// ``` +/// ```ignore /// async_defer!(async { /// // Do some cleanup /// }); diff --git a/src/provider/exec.rs b/src/provider/exec.rs index 56c246c..1244c4f 100644 --- a/src/provider/exec.rs +++ b/src/provider/exec.rs @@ -94,8 +94,9 @@ pub mod tests { #[apply(test_volume_base)] async fn test_volume(makefs: &str, integrity: bool) -> Result<()> { - run_test_on_volume(&format!( - r#" + run_test_on_volume( + &format!( + r#" volume = "" dev = "" auto_open = true @@ -106,7 +107,9 @@ pub mod tests { command = "echo" args = ["-n", "test-passphrase"] "#, - )) + ), + false, + ) .await } } diff --git a/src/provider/kbs.rs b/src/provider/kbs.rs index 55de6ff..b8eeeb7 100644 --- a/src/provider/kbs.rs +++ b/src/provider/kbs.rs @@ -125,8 +125,9 @@ pub mod tests { #[apply(test_volume_base)] async fn test_volume(makefs: &str, integrity: bool) -> Result<()> { - run_test_on_volume(&format!( - r#" + run_test_on_volume( + &format!( + r#" volume = "" dev = "" auto_open = true @@ -143,7 +144,9 @@ pub mod tests { -----END CERTIFICATE----- """ "#, - )) + ), + false, + ) .await } } diff --git a/src/provider/kms.rs b/src/provider/kms.rs index dcf066e..9940694 100644 --- a/src/provider/kms.rs +++ b/src/provider/kms.rs @@ -94,8 +94,9 @@ pub mod tests { #[apply(test_volume_base)] async fn test_volume(makefs: &str, integrity: bool) -> Result<()> { - run_test_on_volume(&format!( - r#" + run_test_on_volume( + &format!( + r#" volume = "" dev = "" auto_open = true @@ -159,7 +160,9 @@ pub mod tests { -----END CERTIFICATE----- """ "#, - )) + ), + false, + ) .await } } diff --git a/src/provider/mod.rs b/src/provider/mod.rs index 6d6de0a..2f934df 100644 --- a/src/provider/mod.rs +++ b/src/provider/mod.rs @@ -1,3 +1,5 @@ +#[cfg(feature = "provider-exec")] +pub mod exec; #[cfg(feature = "provider-kbs")] pub mod kbs; #[cfg(feature = "provider-kms")] @@ -8,8 +10,6 @@ pub mod oidc; pub mod otp; #[cfg(feature = "provider-tpm2")] pub mod tpm2; -#[cfg(feature = "provider-exec")] -pub mod exec; use anyhow::Result; @@ -39,7 +39,7 @@ pub enum VolumeType { #[cfg(test)] pub mod tests { - use std::{future::Future, path::PathBuf}; + use std::{future::Future, io::Error, path::PathBuf}; use crate::{ async_defer, @@ -55,6 +55,7 @@ pub mod tests { use anyhow::{Context, Result}; use block_devs::BlckExt as _; + use cgroups_rs::{cgroup_builder::CgroupBuilder, Cgroup, CgroupPid}; use tokio::{ fs::File, io::{AsyncReadExt, AsyncWriteExt}, @@ -141,7 +142,7 @@ pub mod tests { .await } - pub async fn run_test_on_volume(config_str: &str) -> Result<()> { + pub async fn run_test_on_volume(config_str: &str, use_external_suite: bool) -> Result<()> { let mut volume_config: VolumeConfig = toml::from_str(config_str)?; // Random volume name @@ -181,31 +182,52 @@ pub mod tests { Some(MakeFsType::Swap) => { // Open and swapon open_and_swapon(&volume_config, |volume_config| async move { - let swap_device_size = File::open(volume_config.volume_path()) - .await? - .into_std() - .await - .get_block_device_size()?; - - // Run stress-ng to consume swap memory - Command::new("systemd-run") - .arg("--wait") - .arg("--property=MemoryMax=128M") - .arg("--property=MemorySwapMax=infinity") - .arg("--") - .arg("stress-ng") - .arg("--timeout") - .arg("10") - .arg("--vm") - .arg("1") - .arg("--vm-hang") - .arg("0") - .arg("--vm-method") - .arg("zero-one") - .arg("--vm-bytes") - .arg(swap_device_size.to_string()) - .run() + if use_external_suite { + let swap_device_size = File::open(volume_config.volume_path()) + .await? + .into_std() + .await + .get_block_device_size()?; + + let hier = cgroups_rs::hierarchies::auto(); + let cg: Cgroup = CgroupBuilder::new(&format!( + "cryptpilot-test-{}", + rand::random::() + )) + .memory() + .memory_hard_limit(128 * 1024 * 1024 /* 128M */) + .memory_swap_limit(-1 /* infinity */) + .done() + .build(hier)?; + + let cg_clone = cg.clone(); + async_defer! {async{ + async{ + cg_clone.delete() + } + }} + + // Run stress-ng to consume swap memory + unsafe { + Command::new("stress-ng") + .arg("--timeout") + .arg("10") + .arg("--vm") + .arg("1") + .arg("--vm-hang") + .arg("0") + .arg("--vm-method") + .arg("zero-one") + .arg("--vm-bytes") + .arg(swap_device_size.to_string()) + .pre_exec(move || { + cg.add_task(CgroupPid::from(std::process::id() as u64)) + .map_err(|e| Error::other(e)) + }) + .run() + } .await?; + } Ok(()) }) @@ -253,17 +275,19 @@ pub mod tests { ) .await?; - // Open again and test with pjdfstest - open_and_mount(&volume_config, |_, mount_dir: PathBuf| async move { - Command::new("prove") - .arg("-rv") - .arg("/tmp/pjdfstest/tests") - .current_dir(mount_dir) - .run() - .await?; - Ok(()) - }) - .await?; + if use_external_suite { + // Open again and test with pjdfstest + open_and_mount(&volume_config, |_, mount_dir: PathBuf| async move { + Command::new("prove") + .arg("-rv") + .arg("/tmp/pjdfstest/tests") + .current_dir(mount_dir) + .run() + .await?; + Ok(()) + }) + .await?; + } } None => { // Just Open it and do nothing diff --git a/src/provider/oidc.rs b/src/provider/oidc.rs index 4815616..979263c 100644 --- a/src/provider/oidc.rs +++ b/src/provider/oidc.rs @@ -202,8 +202,9 @@ mod tests { #[apply(test_volume_base)] async fn test_volume(makefs: &str, integrity: bool) -> Result<()> { - run_test_on_volume(&format!( - r#" + run_test_on_volume( + &format!( + r#" volume = "" dev = "" auto_open = true @@ -225,7 +226,9 @@ mod tests { role_arn = "acs:ram::113511544585:role/testoidc" region_id = "cn-beijing" "#, - )) + ), + false, + ) .await } } diff --git a/src/provider/otp.rs b/src/provider/otp.rs index 18f873e..02c6649 100644 --- a/src/provider/otp.rs +++ b/src/provider/otp.rs @@ -37,8 +37,9 @@ pub mod tests { #[apply(test_volume_base)] async fn test_volume(makefs: &str, integrity: bool) -> Result<()> { - run_test_on_volume(&format!( - r#" + run_test_on_volume( + &format!( + r#" volume = "" dev = "" auto_open = true @@ -47,7 +48,9 @@ pub mod tests { [encrypt.otp] "#, - )) + ), + false, + ) .await } }