Skip to content
Open
Show file tree
Hide file tree
Changes from 20 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
4 changes: 3 additions & 1 deletion crates/trident/src/engine/clean_install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,8 @@ fn stage_clean_install(
return Err(original_error).message("Failed to execute in chroot");
}

engine::update_host_configuration(subsystems, &mut ctx)?;

// At this point, clean install has been staged, so update Host Status
debug!(
"Updating host's servicing state to '{:?}'",
Expand All @@ -265,7 +267,7 @@ fn stage_clean_install(
state.with_host_status(|hs| {
*hs = HostStatus {
servicing_state: ServicingState::CleanInstallStaged,
spec: host_config.clone(),
spec: ctx.spec,
spec_old: Default::default(),
ab_active_volume: None,
partition_paths: ctx.partition_paths,
Expand Down
26 changes: 26 additions & 0 deletions crates/trident/src/engine/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use crate::{
engine::boot::BootSubsystem,
subsystems::{
esp::EspSubsystem,
extensions::ExtensionsSubsystem,
hooks::HooksSubsystem,
initrd::InitrdSubsystem,
management::ManagementSubsystem,
Expand Down Expand Up @@ -101,6 +102,11 @@ pub(crate) trait Subsystem: Send {
fn configure(&mut self, _ctx: &EngineContext) -> Result<(), TridentError> {
Ok(())
}

/// Update the Host Configuration with information from the subsystem.
fn update_host_configuration(&self, _ctx: &mut EngineContext) -> Result<(), TridentError> {
Ok(())
}
}

lazy_static::lazy_static! {
Expand All @@ -115,6 +121,7 @@ lazy_static::lazy_static! {
Box::<HooksSubsystem>::default(),
Box::<InitrdSubsystem>::default(),
Box::<SelinuxSubsystem>::default(),
Box::<ExtensionsSubsystem>::default(),
]);
}

Expand Down Expand Up @@ -319,6 +326,25 @@ fn configure(
Ok(())
}

fn update_host_configuration(
subsystems: &[Box<dyn Subsystem>],
ctx: &mut EngineContext,
) -> Result<(), TridentError> {
info!("Starting step 'Update Host Configuration'");
for subsystem in subsystems {
debug!(
"Starting step 'Update Host Configuration' for subsystem '{}'",
subsystem.name()
);
subsystem.update_host_configuration(ctx).message(format!(
"Step 'Update Host Configuration' failed for subsystem '{}'",
subsystem.name()
))?;
}
debug!("Finished step 'Update Host Configuration'");
Ok(())
}

pub fn reboot() -> Result<(), TridentError> {
// Sync all writes to the filesystem.
info!("Syncing filesystem");
Expand Down
5 changes: 5 additions & 0 deletions crates/trident/src/engine/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,9 @@ fn stage_update(
mpsc::UnboundedSender<Result<grpc::HostStatusState, tonic::Status>>,
>,
) -> Result<(), TridentError> {
// Make mutable instance of EngineContext.
let mut ctx = ctx;

match ctx.servicing_type {
ServicingType::CleanInstall => {
return Err(TridentError::new(
Expand Down Expand Up @@ -253,6 +256,8 @@ fn stage_update(
engine::configure(subsystems, &ctx)?;
};

engine::update_host_configuration(subsystems, &mut ctx)?;

// At this point, deployment has been staged, so update servicing state
debug!(
"Updating host's servicing state to '{:?}'",
Expand Down
139 changes: 139 additions & 0 deletions crates/trident/src/subsystems/extensions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,47 @@ impl Subsystem for ExtensionsSubsystem {

Ok(())
}

fn update_host_configuration(&self, ctx: &mut EngineContext) -> Result<(), TridentError> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this not need to happen for install?
this function only has sysext-related code, perhaps it should be named to reflect that? also add doc comments

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean? This function is called in clean_install.rs so it should be called for clean install as well.
Since this is a Subsystem method, the doc comments for it are inside crates/trident/src/engine/mod.rs

// Update paths of sysexts in the Host Configuration.
for sysext in self
.extensions
.iter()
.filter(|ext| ext.ext_type == ExtensionType::Sysext)
{
// Find corresponding sysext in Host Configuration.
let hc_ext = ctx
.spec
.os
.sysexts
.iter_mut()
.find(|ext| ext.sha384 == sysext.sha384)
.structured(InternalError::Internal(
"Failed to find previously processed sysext in Host Configuration",
))?;
hc_ext.path = Some(sysext.path.clone());
}

// Update paths of confexts in the Host Configuration.
for confext in self
.extensions
.iter()
.filter(|ext| ext.ext_type == ExtensionType::Confext)
{
// Find corresponding confext in Host Configuration.
let hc_ext = ctx
.spec
.os
.confexts
.iter_mut()
.find(|ext| ext.sha384 == confext.sha384)
.structured(InternalError::Internal(
"Failed to find previously processed confext in Host Configuration",
))?;
hc_ext.path = Some(confext.path.clone());
}
Ok(())
}
}

impl ExtensionsSubsystem {
Expand Down Expand Up @@ -469,6 +510,7 @@ mod tests {
use super::*;

use tempfile::TempDir;
use url::Url;

#[test]
fn test_populate_extensions_empty() {
Expand Down Expand Up @@ -567,6 +609,103 @@ mod tests {
assert!(mount_path.path().join("usr/lib/confexts").exists());
assert!(mount_path.path().join("usr/local/lib/confexts").exists());
}

#[test]
fn test_update_host_configuration_sysexts() {
let mut ctx = EngineContext::default();
ctx.spec.os.sysexts = vec![
Extension {
url: Url::parse("https://example.com/sysext1.raw").unwrap(),
sha384: Sha384Hash::from("a".repeat(96)),
path: None,
},
Extension {
url: Url::parse("https://example.com/sysext2.raw").unwrap(),
sha384: Sha384Hash::from("b".repeat(96)),
path: Some(PathBuf::from("/etc/extensions/sysext2.raw")),
},
];

let subsystem = ExtensionsSubsystem {
extensions: vec![
ExtensionData {
id: "sysext1".to_string(),
name: "sysext1".to_string(),
sha384: Sha384Hash::from("a".repeat(96)),
path: PathBuf::from("/var/lib/extensions/sysext1.raw"),
temp_path: PathBuf::from(EXTENSION_IMAGE_STAGING_DIRECTORY).join("sysext1.raw"),

ext_type: ExtensionType::Sysext,
},
ExtensionData {
id: "sysext2".to_string(),
name: "sysext2".to_string(),
sha384: Sha384Hash::from("b".repeat(96)),
path: PathBuf::from("/etc/extensions/sysext2.raw"),
temp_path: PathBuf::from(EXTENSION_IMAGE_STAGING_DIRECTORY).join("sysext2.raw"),

ext_type: ExtensionType::Sysext,
},
],
extensions_old: vec![],
};
subsystem.update_host_configuration(&mut ctx).unwrap();

for i in 0..subsystem.extensions.len() {
assert_eq!(
ctx.spec.os.sysexts[i].path,
Some(subsystem.extensions[i].path.clone())
)
}
}

#[test]
fn test_update_host_configuration_confexts() {
let mut ctx = EngineContext::default();
ctx.spec.os.confexts = vec![
Extension {
url: Url::parse("https://example.com/confext1.raw").unwrap(),
sha384: Sha384Hash::from("a".repeat(96)),
path: None,
},
Extension {
url: Url::parse("https://example.com/confext2.raw").unwrap(),
sha384: Sha384Hash::from("b".repeat(96)),
path: Some(PathBuf::from("/usr/lib/confexts/confext2.raw")),
},
];

let subsystem = ExtensionsSubsystem {
extensions: vec![
ExtensionData {
id: "confext1".to_string(),
name: "confext1".to_string(),
sha384: Sha384Hash::from("a".repeat(96)),
path: PathBuf::from("/var/lib/confexts/confext1.raw"),
temp_path: PathBuf::from(EXTENSION_IMAGE_STAGING_DIRECTORY)
.join("confext1.raw"),
ext_type: ExtensionType::Confext,
},
ExtensionData {
id: "confext2".to_string(),
name: "confext2".to_string(),
sha384: Sha384Hash::from("b".repeat(96)),
path: PathBuf::from("/usr/lib/confexts/confext2.raw"),
temp_path: PathBuf::from("/var/lib/extensions/.staging/confext2.raw"),
ext_type: ExtensionType::Confext,
},
],
extensions_old: vec![],
};
subsystem.update_host_configuration(&mut ctx).unwrap();

for i in 0..subsystem.extensions.len() {
assert_eq!(
ctx.spec.os.confexts[i].path,
Some(subsystem.extensions[i].path.clone())
)
}
}
}

#[cfg(feature = "functional-test")]
Expand Down
10 changes: 8 additions & 2 deletions packaging/selinux-policy-trident/trident.te
Original file line number Diff line number Diff line change
Expand Up @@ -275,8 +275,8 @@ files_var_lib_filetrans(trident_t, trident_var_lib_t, { dir file lnk_file })

# Allow trident_t domain to interact with files and directories labeled as trident_var_lib_t
# Necessary so Trident can interact with the datastore at /var/lib/trident
allow trident_t trident_var_lib_t:dir { getattr search read write add_name create remove_name open mounton relabelto };
allow trident_t trident_var_lib_t:file { getattr setattr create open read write unlink lock relabelto };
allow trident_t trident_var_lib_t:dir { getattr search read write add_name create remove_name open mounton relabelto rmdir };
allow trident_t trident_var_lib_t:file { getattr setattr create open read write unlink lock relabelto rename };

# Allow Trident to relabel its executable
allow trident_t trident_exec_t:file relabelto;
Expand Down Expand Up @@ -857,6 +857,9 @@ allow fsadm_t trident_t:process { siginh rlimitinh noatsecure transition sigchld
allow fsadm_t fixed_disk_device_t:blk_file { open read write getattr ioctl };
allow fsadm_t unlabeled_t:file map;

# Allow fsadm_t to use losetup utility on Trident-created files in /var/lib/. Necessary to attach device to extension image.
allow fsadm_t trident_var_lib_t:file { getattr open read write };

# Create, read, write, and delete files on a efivarfs filesystem
fs_manage_efivarfs_files(fsadm_t)
fs_manage_tmpfs_dirs(fsadm_t)
Expand Down Expand Up @@ -925,6 +928,9 @@ allow udev_t cloud_init_t:fifo_file { append write getattr };
allow udev_t lvm_t:process { noatsecure rlimitinh siginh };
allow udev_t unlabeled_t:file getattr;

# Allow losetup to attach an extension image file as a loopback device.
allow udev_t trident_var_lib_t:file getattr;

files_read_generic_tmp_files(udev_t)

#============= udevadm_t ==============
Expand Down
Loading