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

[pull] main from oasisprotocol:main #183

Merged
merged 2 commits into from
Jan 21, 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
3 changes: 2 additions & 1 deletion Cargo.lock

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

1 change: 1 addition & 0 deletions rofl-appd/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ edition = "2021"

[dependencies]
# Oasis SDK.
cbor = { version = "0.5.1", package = "oasis-cbor" }
oasis-runtime-sdk = { path = "../runtime-sdk", features = ["tdx"] }

# Third party.
Expand Down
1 change: 1 addition & 0 deletions rofl-appd/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
mod routes;
pub mod services;
pub(crate) mod state;
pub mod types;

use std::sync::Arc;

Expand Down
136 changes: 122 additions & 14 deletions rofl-appd/src/services/kms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,17 @@ use sp800_185::KMac;
use tokio::sync::Notify;

use oasis_runtime_sdk::{
core::common::logger::get_logger,
core::common::{
crypto::{mrae::deoxysii, x25519},
logger::get_logger,
},
crypto::signature::{ed25519, secp256k1, Signer},
modules,
modules::rofl::app::{client::DeriveKeyRequest, prelude::*},
};

use crate::types::SecretEnvelope;

/// A key management service.
#[async_trait]
pub trait KmsService: Send + Sync {
Expand All @@ -23,14 +29,26 @@ pub trait KmsService: Send + Sync {

/// Generate a key based on the passed parameters.
async fn generate(&self, request: &GenerateRequest<'_>) -> Result<GenerateResponse, Error>;

/// Decrypt and authenticate a secret using the secret encryption key (SEK).
async fn open_secret(
&self,
request: &OpenSecretRequest<'_>,
) -> Result<OpenSecretResponse, Error>;
}

/// Error returned by the key management service.
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("invalid argument")]
InvalidArgument,

#[error("not initialized yet")]
NotInitialized,

#[error("corrupted secret")]
CorruptedSecret,

#[error("internal error")]
Internal,

Expand Down Expand Up @@ -85,30 +103,55 @@ pub struct GenerateResponse {
pub key: Vec<u8>,
}

/// Secret decryption and authentication request.
#[derive(Clone, Debug, Default, serde::Deserialize)]
pub struct OpenSecretRequest<'r> {
/// Plain-text name associated with the secret.
pub name: &'r str,
/// Encrypted secret value.
///
/// It is expected that the value contains a CBOR-encoded `SecretEnvelope`.
pub value: &'r [u8],
}

/// Secret decryption and authentication response.
#[derive(Clone, Default, serde::Serialize)]
pub struct OpenSecretResponse {
/// Decrypted plain-text name.
pub name: Vec<u8>,
/// Decrypted plain-text value.
pub value: Vec<u8>,
}

/// Key identifier for the root key from which all per-app keys are derived. The root key is
/// retrieved from the Oasis runtime key manager on initialization and all subsequent keys are
/// derived from that key.
///
/// Changing this identifier will change all generated keys.
const OASIS_KMS_ROOT_KEY_ID: &[u8] = b"oasis-runtime-sdk/rofl-appd: root key v1";

struct Keys {
root: Vec<u8>,
sek: x25519::PrivateKey,
}

/// A key management service backed by the Oasis runtime.
pub struct OasisKmsService<A: App> {
running: AtomicBool,
root_key: Arc<Mutex<Option<Vec<u8>>>>,
env: Environment<A>,
logger: slog::Logger,
ready_notify: Notify,
keys: Arc<Mutex<Option<Keys>>>,
}

impl<A: App> OasisKmsService<A> {
pub fn new(env: Environment<A>) -> Self {
Self {
running: AtomicBool::new(false),
root_key: Arc::new(Mutex::new(None)),
env,
logger: get_logger("appd/services/kms"),
ready_notify: Notify::new(),
keys: Arc::new(Mutex::new(None)),
}
}
}
Expand All @@ -127,9 +170,11 @@ impl<A: App> KmsService for OasisKmsService<A> {
slog::info!(self.logger, "starting KMS service");

// Ensure we keep retrying until the root key is derived.
let retry_strategy = tokio_retry::strategy::ExponentialBackoff::from_millis(4)
.max_delay(std::time::Duration::from_millis(1000))
.map(tokio_retry::strategy::jitter);
let retry_strategy = || {
tokio_retry::strategy::ExponentialBackoff::from_millis(4)
.max_delay(std::time::Duration::from_millis(1000))
.map(tokio_retry::strategy::jitter)
};

slog::info!(
self.logger,
Expand All @@ -138,19 +183,41 @@ impl<A: App> KmsService for OasisKmsService<A> {

// Generate the root key for the application and store it in memory to derive all other
// requested keys.
let root_key = tokio_retry::Retry::spawn(retry_strategy, || {
let root_key_task = tokio_retry::Retry::spawn(retry_strategy(), || {
self.env.client().derive_key(
self.env.signer(),
DeriveKeyRequest {
key_id: OASIS_KMS_ROOT_KEY_ID.to_vec(),
kind: modules::rofl::types::KeyKind::EntropyV0,
..Default::default()
},
)
})
.await?;
});

// Store the key in memory.
*self.root_key.lock().unwrap() = Some(root_key.key);
// Generate the secrets encryption key (SEK) and store it in memory.
// TODO: Consider caching key in encrypted persistent storage.
let sek_task = tokio_retry::Retry::spawn(retry_strategy(), || {
self.env.client().derive_key(
self.env.identity(),
DeriveKeyRequest {
key_id: modules::rofl::ROFL_KEY_ID_SEK.to_vec(),
kind: modules::rofl::types::KeyKind::X25519,
..Default::default()
},
)
});

// Perform requests in parallel.
let (root_key, sek) = tokio::try_join!(root_key_task, sek_task,)?;

let sek: [u8; 32] = sek.key.try_into().map_err(|_| Error::Internal)?;
let sek = sek.into();

// Store the keys in memory.
*self.keys.lock().unwrap() = Some(Keys {
root: root_key.key,
sek,
});

self.ready_notify.notify_waiters();

Expand All @@ -162,7 +229,7 @@ impl<A: App> KmsService for OasisKmsService<A> {
async fn wait_ready(&self) -> Result<(), Error> {
let handle = self.ready_notify.notified();

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

Expand All @@ -172,13 +239,47 @@ impl<A: App> KmsService for OasisKmsService<A> {
}

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)?;
let keys_guard = self.keys.lock().unwrap();
let root_key = &keys_guard.as_ref().ok_or(Error::NotInitialized)?.root;

let key = Kdf::derive_key(root_key.as_ref(), request.kind, request.key_id.as_bytes())?;

Ok(GenerateResponse { key })
}

async fn open_secret(
&self,
request: &OpenSecretRequest<'_>,
) -> Result<OpenSecretResponse, Error> {
let envelope: SecretEnvelope =
cbor::from_slice(request.value).map_err(|_| Error::InvalidArgument)?;

let keys_guard = self.keys.lock().unwrap();
let sek = &keys_guard.as_ref().ok_or(Error::NotInitialized)?.sek;
let sek = sek.clone().into(); // Fine as the clone will be zeroized on drop.

// Name.
let name = deoxysii::box_open(
&envelope.nonce,
envelope.name.clone(),
b"name".into(), // Prevent mixing name and value.
&envelope.pk.0,
&sek,
)
.map_err(|_| Error::CorruptedSecret)?;

// Value.
let value = deoxysii::box_open(
&envelope.nonce,
envelope.value.clone(),
b"value".into(), // Prevent mixing name and value.
&envelope.pk.0,
&sek,
)
.map_err(|_| Error::CorruptedSecret)?;

Ok(OpenSecretResponse { name, value })
}
}

/// Insecure mock root key used to derive keys in the mock KMS.
Expand Down Expand Up @@ -206,6 +307,13 @@ impl KmsService for MockKmsService {

Ok(GenerateResponse { key })
}

async fn open_secret(
&self,
_request: &OpenSecretRequest<'_>,
) -> Result<OpenSecretResponse, Error> {
Err(Error::NotInitialized)
}
}

/// Domain separation tag for deriving a key-derivation key from a shared secret.
Expand Down
15 changes: 15 additions & 0 deletions rofl-appd/src/types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//! Various types used by rofl-appd.
use oasis_runtime_sdk::core::common::crypto::{mrae::deoxysii, x25519};

/// Envelope used for storing encrypted secrets.
#[derive(Clone, Debug, Default, cbor::Encode, cbor::Decode)]
pub struct SecretEnvelope {
/// Ephemeral public key used for X25519.
pub pk: x25519::PublicKey,
/// Nonce.
pub nonce: [u8; deoxysii::NONCE_SIZE],
/// Encrypted secret name.
pub name: Vec<u8>,
/// Encrypted secret value.
pub value: Vec<u8>,
}
2 changes: 1 addition & 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.2.1"
version = "0.3.0"
edition = "2021"

[dependencies]
Expand Down
52 changes: 52 additions & 0 deletions rofl-containers/src/containers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
use anyhow::Result;
use cmd_lib::run_cmd;

/// Initialize container environment.
pub async fn init() -> Result<()> {
// Setup networking.
run_cmd!(
mount none -t tmpfs "/tmp";
udhcpc -i eth0 -q -n;
)?;

// Mount cgroups and create /dev/shm for Podman locks.
run_cmd!(
mount -t cgroup2 none "/sys/fs/cgroup";
mkdir -p "/dev/shm";
mount -t tmpfs none "/dev/shm";
)?;

// Cleanup state after reboot.
run_cmd!(
rm -rf "/storage/containers/run";
rm -rf "/storage/containers/net";
rm -rf "/var/lib/cni";

mkdir -p "/storage/containers/run";
mkdir -p "/storage/containers/graph";
mkdir -p "/storage/containers/graph/tmp";
mkdir -p "/storage/containers/net";
)?;

// Update TUN device permissions.
run_cmd!(chmod 0666 "/dev/net/tun")?;

// Migrate existing containers if needed.
run_cmd!(
podman system migrate;
podman system prune --external;
)?;

Ok(())
}

/// Start containers.
pub async fn start() -> Result<()> {
// Bring containers up.
run_cmd!(
cd "/etc/oasis/containers";
podman-compose up --detach --remove-orphans --force-recreate;
)?;

Ok(())
}
Loading
Loading