Skip to content

Commit 201c439

Browse files
committed
install: Change SELinux state into enum, serialize to aleph
We want to support the "installing SELinux target from SELinux-disabled host" - but in case we run into problems, let's serialize the state of things at install time into the aleph data, for the same reason we save other relevant environmental data like the kernel version. Signed-off-by: Colin Walters <[email protected]>
1 parent bab50e1 commit 201c439

File tree

1 file changed

+50
-18
lines changed

1 file changed

+50
-18
lines changed

lib/src/install.rs

Lines changed: 50 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -288,9 +288,7 @@ pub(crate) struct SourceInfo {
288288
pub(crate) struct State {
289289
pub(crate) source: SourceInfo,
290290
/// Force SELinux off in target system
291-
pub(crate) override_disable_selinux: bool,
292-
#[allow(dead_code)]
293-
pub(crate) setenforce_guard: Option<crate::lsm::SetEnforceGuard>,
291+
pub(crate) selinux_state: SELinuxFinalState,
294292
#[allow(dead_code)]
295293
pub(crate) config_opts: InstallConfigOpts,
296294
pub(crate) target_imgref: ostree_container::OstreeImageReference,
@@ -303,7 +301,7 @@ impl State {
303301
#[context("Loading SELinux policy")]
304302
pub(crate) fn load_policy(&self) -> Result<Option<ostree::SePolicy>> {
305303
use std::os::fd::AsRawFd;
306-
if !self.source.selinux || self.override_disable_selinux {
304+
if !self.selinux_state.enabled() {
307305
return Ok(None);
308306
}
309307
// We always use the physical container root to bootstrap policy
@@ -334,6 +332,8 @@ struct InstallAleph {
334332
timestamp: Option<chrono::DateTime<Utc>>,
335333
/// The `uname -r` of the kernel doing the installation
336334
kernel: String,
335+
/// The state of SELinux at install time
336+
selinux: String,
337337
}
338338

339339
/// A mount specification is a subset of a line in `/etc/fstab`.
@@ -687,6 +687,7 @@ async fn initialize_ostree_root_from_self(
687687
version: imgstate.version().as_ref().map(|s| s.to_string()),
688688
timestamp,
689689
kernel: uname.release().to_str()?.to_string(),
690+
selinux: state.selinux_state.to_aleph().to_string(),
690691
};
691692

692693
Ok(aleph)
@@ -777,23 +778,53 @@ impl RootSetup {
777778
}
778779
}
779780

781+
pub(crate) enum SELinuxFinalState {
782+
/// Host and target both have SELinux, but user forced it off for target
783+
ForceTargetDisabled,
784+
/// Host and target both have SELinux
785+
Enabled(Option<crate::lsm::SetEnforceGuard>),
786+
/// Host has SELinux disabled, target is enabled.
787+
HostDisabled,
788+
/// Neither host or target have SELinux
789+
Disabled,
790+
}
791+
792+
impl SELinuxFinalState {
793+
/// Returns true if the target system will have SELinux enabled.
794+
pub(crate) fn enabled(&self) -> bool {
795+
match self {
796+
SELinuxFinalState::ForceTargetDisabled | SELinuxFinalState::Disabled => false,
797+
SELinuxFinalState::Enabled(_) | SELinuxFinalState::HostDisabled => true,
798+
}
799+
}
800+
801+
/// Returns the canonical stringified version of self. This is only used
802+
/// for debugging purposes.
803+
pub(crate) fn to_aleph(&self) -> &'static str {
804+
match self {
805+
SELinuxFinalState::ForceTargetDisabled => "force-target-disabled",
806+
SELinuxFinalState::Enabled(_) => "enabled",
807+
SELinuxFinalState::HostDisabled => "host-disabled",
808+
SELinuxFinalState::Disabled => "disabled",
809+
}
810+
}
811+
}
812+
780813
/// If we detect that the target ostree commit has SELinux labels,
781814
/// and we aren't passed an override to disable it, then ensure
782815
/// the running process is labeled with install_t so it can
783816
/// write arbitrary labels.
784817
pub(crate) fn reexecute_self_for_selinux_if_needed(
785818
srcdata: &SourceInfo,
786819
override_disable_selinux: bool,
787-
) -> Result<(bool, Option<crate::lsm::SetEnforceGuard>)> {
788-
let mut ret_did_override = false;
820+
) -> Result<SELinuxFinalState> {
789821
// If the target state has SELinux enabled, we need to check the host state.
790-
let mut g = None;
791822
if srcdata.selinux {
792823
let host_selinux = crate::lsm::selinux_enabled()?;
793824
tracing::debug!("Target has SELinux, host={host_selinux}");
794-
if override_disable_selinux {
795-
ret_did_override = true;
796-
println!("notice: Target has SELinux enabled, overriding to disable")
825+
let r = if override_disable_selinux {
826+
println!("notice: Target has SELinux enabled, overriding to disable");
827+
SELinuxFinalState::ForceTargetDisabled
797828
} else if host_selinux {
798829
// /sys/fs/selinuxfs is not normally mounted, so we do that now.
799830
// Because SELinux enablement status is cached process-wide and was very likely
@@ -802,17 +833,20 @@ pub(crate) fn reexecute_self_for_selinux_if_needed(
802833
// so let's just fall through to that.
803834
setup_sys_mount("selinuxfs", SELINUXFS)?;
804835
// This will re-execute the current process (once).
805-
g = crate::lsm::selinux_ensure_install_or_setenforce()?;
836+
let g = crate::lsm::selinux_ensure_install_or_setenforce()?;
837+
SELinuxFinalState::Enabled(g)
806838
} else {
807839
// This used to be a hard error, but is now a mild warning
808840
crate::utils::medium_visibility_warning(
809841
"Host kernel does not have SELinux support, but target enables it by default; this is less well tested. See https://github.com/containers/bootc/issues/419",
810842
);
811-
}
843+
SELinuxFinalState::HostDisabled
844+
};
845+
Ok(r)
812846
} else {
813847
tracing::debug!("Target does not enable SELinux");
848+
Ok(SELinuxFinalState::Disabled)
814849
}
815-
Ok((ret_did_override, g))
816850
}
817851

818852
/// Trim, flush outstanding writes, and freeze/thaw the target mounted filesystem;
@@ -1071,8 +1105,7 @@ async fn prepare_install(
10711105
setup_sys_mount("efivarfs", EFIVARFS)?;
10721106

10731107
// Now, deal with SELinux state.
1074-
let (override_disable_selinux, setenforce_guard) =
1075-
reexecute_self_for_selinux_if_needed(&source, config_opts.disable_selinux)?;
1108+
let selinux_state = reexecute_self_for_selinux_if_needed(&source, config_opts.disable_selinux)?;
10761109

10771110
println!("Installing image: {:#}", &target_imgref);
10781111
if let Some(digest) = source.digest.as_deref() {
@@ -1094,8 +1127,7 @@ async fn prepare_install(
10941127
// so we can pass it to worker threads too. Right now this just
10951128
// combines our command line options along with some bind mounts from the host.
10961129
let state = Arc::new(State {
1097-
override_disable_selinux,
1098-
setenforce_guard,
1130+
selinux_state,
10991131
source,
11001132
config_opts,
11011133
target_imgref,
@@ -1107,7 +1139,7 @@ async fn prepare_install(
11071139
}
11081140

11091141
async fn install_to_filesystem_impl(state: &State, rootfs: &mut RootSetup) -> Result<()> {
1110-
if state.override_disable_selinux {
1142+
if matches!(state.selinux_state, SELinuxFinalState::ForceTargetDisabled) {
11111143
rootfs.kargs.push("selinux=0".to_string());
11121144
}
11131145

0 commit comments

Comments
 (0)