Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rofl-containers: Add support for persistent storage #2134

Merged
merged 1 commit into from
Jan 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 94 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 14 additions & 11 deletions rofl-appd/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! REST API daemon accessible by ROFL apps.

mod routes;
pub(crate) mod services;
pub mod services;
pub(crate) mod state;

use std::sync::Arc;
Expand All @@ -10,26 +10,29 @@ use rocket::{figment::Figment, routes};

use oasis_runtime_sdk::modules::rofl::app::{App, Environment};

/// API server configuration.
#[derive(Clone)]
pub struct Config<'a> {
/// Address where the service should listen on.
pub address: &'a str,
/// Key management service to use.
pub kms: Arc<dyn services::kms::KmsService>,
}

/// Start the REST API server.
pub async fn start<A>(address: &str, env: Environment<A>) -> Result<(), rocket::Error>
pub async fn start<A>(cfg: Config<'_>, env: Environment<A>) -> Result<(), rocket::Error>
where
A: App,
{
// KMS service.
let kms_service: Arc<dyn services::kms::KmsService> =
Arc::new(services::kms::OasisKmsService::new(env.clone()));
let kms_service_task = kms_service.clone();
tokio::spawn(async move { kms_service_task.start().await });

// Oasis runtime environment.
let env: Arc<dyn state::Env> = Arc::new(state::EnvImpl::new(env));

// Server configuration.
let cfg = Figment::new().join(("address", address));
let rocket_cfg = Figment::new().join(("address", cfg.address));

rocket::custom(cfg)
rocket::custom(rocket_cfg)
.manage(env)
.manage(kms_service)
.manage(cfg.kms)
.mount("/rofl/v1/app", routes![routes::app::id,])
.mount("/rofl/v1/keys", routes![routes::keys::generate,])
.launch()
Expand Down
24 changes: 24 additions & 0 deletions rofl-appd/src/services/kms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::sync::{
};

use sp800_185::KMac;
use tokio::sync::Notify;

use oasis_runtime_sdk::{
core::common::logger::get_logger,
Expand All @@ -17,6 +18,9 @@ pub trait KmsService: Send + Sync {
/// Start the KMS service.
async fn start(&self) -> Result<(), Error>;

/// Waits for the service to become ready to accept requests.
async fn wait_ready(&self) -> Result<(), Error>;

/// Generate a key based on the passed parameters.
async fn generate(&self, request: &GenerateRequest<'_>) -> Result<GenerateResponse, Error>;
}
Expand Down Expand Up @@ -94,6 +98,7 @@ pub struct OasisKmsService<A: App> {
root_key: Arc<Mutex<Option<Vec<u8>>>>,
env: Environment<A>,
logger: slog::Logger,
ready_notify: Notify,
}

impl<A: App> OasisKmsService<A> {
Expand All @@ -103,6 +108,7 @@ impl<A: App> OasisKmsService<A> {
root_key: Arc::new(Mutex::new(None)),
env,
logger: get_logger("appd/services/kms"),
ready_notify: Notify::new(),
}
}
}
Expand Down Expand Up @@ -146,11 +152,25 @@ impl<A: App> KmsService for OasisKmsService<A> {
// Store the key in memory.
*self.root_key.lock().unwrap() = Some(root_key.key);

self.ready_notify.notify_waiters();

slog::info!(self.logger, "KMS service initialized");

Ok(())
}

async fn wait_ready(&self) -> Result<(), Error> {
let handle = self.ready_notify.notified();

if self.root_key.lock().unwrap().is_some() {
return Ok(());
}

handle.await;

Ok(())
}

async fn generate(&self, request: &GenerateRequest<'_>) -> Result<GenerateResponse, Error> {
let root_key_guard = self.root_key.lock().unwrap();
let root_key = root_key_guard.as_ref().ok_or(Error::NotInitialized)?;
Expand All @@ -173,6 +193,10 @@ impl KmsService for MockKmsService {
Ok(())
}

async fn wait_ready(&self) -> Result<(), Error> {
Ok(())
}

async fn generate(&self, request: &GenerateRequest<'_>) -> Result<GenerateResponse, Error> {
let key = Kdf::derive_key(
INSECURE_MOCK_ROOT_KEY,
Expand Down
7 changes: 6 additions & 1 deletion rofl-containers/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "rofl-containers"
version = "0.1.0"
version = "0.2.0"
edition = "2021"

[dependencies]
Expand All @@ -9,4 +9,9 @@ oasis-runtime-sdk = { path = "../runtime-sdk", features = ["tdx"] }
rofl-appd = { path = "../rofl-appd" }

# Third party.
anyhow = "1.0.86"
base64 = "0.22.1"
cmd_lib = "1.9.5"
hex = "0.4.3"
nix = { version = "0.29.0", features = ["signal"] }
tokio = { version = "1.38", features = ["rt", "rt-multi-thread", "sync", "time", "macros"] }
41 changes: 38 additions & 3 deletions rofl-containers/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,15 @@
use std::env;

use base64::prelude::*;
use oasis_runtime_sdk::{cbor, modules::rofl::app::prelude::*};
use oasis_runtime_sdk::{
cbor,
core::common::{logger::get_logger, process},
modules::rofl::app::prelude::*,
};
use rofl_appd::services;

mod reaper;
mod storage;

/// UNIX socket address where the REST API server will listen on.
const ROFL_APPD_ADDRESS: &str = "unix:/run/rofl-appd.sock";
Expand Down Expand Up @@ -42,12 +50,39 @@ impl App for ContainersApp {
.expect("Corrupted ROFL_CONSENSUS_TRUST_ROOT (must be Base64-encoded CBOR).")
}

async fn run(self: Arc<Self>, env: Environment<Self>) {
async fn post_registration_init(self: Arc<Self>, env: Environment<Self>) {
// Temporarily disable the default process reaper as it interferes with scripts.
let _guard = reaper::disable_default_reaper();
let logger = get_logger("post_registration_init");

// Start the key management service and wait for it to initialize.
let kms: Arc<dyn services::kms::KmsService> =
Arc::new(services::kms::OasisKmsService::new(env.clone()));
let kms_task = kms.clone();
tokio::spawn(async move { kms_task.start().await });
let _ = kms.wait_ready().await;

// Initialize storage when configured in the kernel cmdline.
if let Err(err) = storage::init(kms.clone()).await {
slog::error!(logger, "failed to initialize stage 2 storage"; "err" => ?err);
process::abort();
}

// Start the REST API server.
let _ = rofl_appd::start(ROFL_APPD_ADDRESS, env).await;
let cfg = rofl_appd::Config {
address: ROFL_APPD_ADDRESS,
kms,
};
let _ = rofl_appd::start(cfg, env).await;
}
}

fn main() {
// Configure the binary search path.
// SAFETY: This is safe as no other threads are running yet.
unsafe {
env::set_var("PATH", "/usr/sbin:/usr/bin:/sbin:/bin");
}

ContainersApp.start();
}
33 changes: 33 additions & 0 deletions rofl-containers/src/reaper.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use nix::sys::signal::{sigaction, SaFlags, SigAction, SigHandler, SigSet, Signal};

/// Guard that re-enables the default process reaper when dropped.
pub struct DisableReaperGuard {
_internal: (),
}

impl Drop for DisableReaperGuard {
fn drop(&mut self) {
// Re-enable default kernel process reaper.
unsafe {
let _ = sigaction(
Signal::SIGCHLD,
&SigAction::new(SigHandler::SigIgn, SaFlags::empty(), SigSet::empty()),
);
}
}
}

/// Temporarily disables the default process reaper. When the returned guard gets out of scope, the
/// default reaper is re-enabled.
///
/// This assumes that the default reaper has been previously configured by core init.
pub fn disable_default_reaper() -> DisableReaperGuard {
unsafe {
let _ = sigaction(
Signal::SIGCHLD,
&SigAction::new(SigHandler::SigDfl, SaFlags::empty(), SigSet::empty()),
);
}

DisableReaperGuard { _internal: () }
}
Loading
Loading