Skip to content

Commit 81fbd24

Browse files
authored
Merge pull request #420 from cgwalters/allow-no-selinux
install: Change no-SELinux -> SELinux to a warning && serialize to aleph
2 parents e0b7b63 + 7842a61 commit 81fbd24

File tree

3 files changed

+88
-33
lines changed

3 files changed

+88
-33
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,6 @@ jobs:
143143
# TODO fix https://github.com/containers/bootc/pull/137
144144
sudo chattr -i /ostree/deploy/default/deploy/*
145145
sudo rm /ostree/deploy/default -rf
146-
sudo podman run --rm -ti --privileged --env BOOTC_SKIP_SELINUX_HOST_CHECK=1 --env RUST_LOG=debug -v /:/target -v /var/lib/containers:/var/lib/containers -v ./usr/bin/bootc:/usr/bin/bootc --pid=host --security-opt label=disable \
146+
sudo podman run --rm -ti --privileged --env RUST_LOG=debug -v /:/target -v /var/lib/containers:/var/lib/containers -v ./usr/bin/bootc:/usr/bin/bootc --pid=host --security-opt label=disable \
147147
${image} bootc install to-existing-root
148148
sudo podman run --rm -ti --privileged -v /:/target -v ./usr/bin/bootc:/usr/bin/bootc --pid=host --security-opt label=disable ${image} bootc internal-tests verify-selinux /target/ostree --warn

lib/src/install.rs

Lines changed: 67 additions & 29 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,27 +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;
791-
// We don't currently quite support installing SELinux enabled systems
792-
// from SELinux disabled hosts, but this environment variable can be set
793-
// to test it out anyways.
794-
let skip_check_envvar = "BOOTC_SKIP_SELINUX_HOST_CHECK";
795822
if srcdata.selinux {
796823
let host_selinux = crate::lsm::selinux_enabled()?;
797824
tracing::debug!("Target has SELinux, host={host_selinux}");
798-
if override_disable_selinux {
799-
ret_did_override = true;
800-
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
801828
} else if host_selinux {
802829
// /sys/fs/selinuxfs is not normally mounted, so we do that now.
803830
// Because SELinux enablement status is cached process-wide and was very likely
@@ -806,21 +833,20 @@ pub(crate) fn reexecute_self_for_selinux_if_needed(
806833
// so let's just fall through to that.
807834
setup_sys_mount("selinuxfs", SELINUXFS)?;
808835
// This will re-execute the current process (once).
809-
g = crate::lsm::selinux_ensure_install_or_setenforce()?;
810-
} else if std::env::var_os(skip_check_envvar).is_some() {
811-
eprintln!(
812-
"Host kernel does not have SELinux support, but target enables it by default; {} is set, continuing anyways",
813-
skip_check_envvar
814-
);
836+
let g = crate::lsm::selinux_ensure_install_or_setenforce()?;
837+
SELinuxFinalState::Enabled(g)
815838
} else {
816-
anyhow::bail!(
817-
"Host kernel does not have SELinux support, but target enables it by default"
839+
// This used to be a hard error, but is now a mild warning
840+
crate::utils::medium_visibility_warning(
841+
"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",
818842
);
819-
}
843+
SELinuxFinalState::HostDisabled
844+
};
845+
Ok(r)
820846
} else {
821847
tracing::debug!("Target does not enable SELinux");
848+
Ok(SELinuxFinalState::Disabled)
822849
}
823-
Ok((ret_did_override, g))
824850
}
825851

826852
/// Trim, flush outstanding writes, and freeze/thaw the target mounted filesystem;
@@ -1079,8 +1105,7 @@ async fn prepare_install(
10791105
setup_sys_mount("efivarfs", EFIVARFS)?;
10801106

10811107
// Now, deal with SELinux state.
1082-
let (override_disable_selinux, setenforce_guard) =
1083-
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)?;
10841109

10851110
println!("Installing image: {:#}", &target_imgref);
10861111
if let Some(digest) = source.digest.as_deref() {
@@ -1102,8 +1127,7 @@ async fn prepare_install(
11021127
// so we can pass it to worker threads too. Right now this just
11031128
// combines our command line options along with some bind mounts from the host.
11041129
let state = Arc::new(State {
1105-
override_disable_selinux,
1106-
setenforce_guard,
1130+
selinux_state,
11071131
source,
11081132
config_opts,
11091133
target_imgref,
@@ -1115,7 +1139,7 @@ async fn prepare_install(
11151139
}
11161140

11171141
async fn install_to_filesystem_impl(state: &State, rootfs: &mut RootSetup) -> Result<()> {
1118-
if state.override_disable_selinux {
1142+
if matches!(state.selinux_state, SELinuxFinalState::ForceTargetDisabled) {
11191143
rootfs.kargs.push("selinux=0".to_string());
11201144
}
11211145

@@ -1218,6 +1242,20 @@ pub(crate) async fn install_to_disk(mut opts: InstallToDiskOpts) -> Result<()> {
12181242
loopback_dev.close()?;
12191243
}
12201244

1245+
// At this point, all other threads should be gone.
1246+
if let Some(state) = Arc::into_inner(state) {
1247+
// If we had invoked `setenforce 0`, then let's re-enable it.
1248+
match state.selinux_state {
1249+
SELinuxFinalState::Enabled(Some(guard)) => {
1250+
guard.consume()?;
1251+
}
1252+
_ => {}
1253+
}
1254+
} else {
1255+
// This shouldn't happen...but we will make it not fatal right now
1256+
tracing::warn!("Failed to consume state Arc");
1257+
}
1258+
12211259
installation_complete();
12221260

12231261
Ok(())

lib/src/lsm.rs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,12 +99,29 @@ pub(crate) fn selinux_ensure_install() -> Result<bool> {
9999
/// gain the `mac_admin` permission (install_t).
100100
#[cfg(feature = "install")]
101101
#[must_use]
102-
pub(crate) struct SetEnforceGuard;
102+
pub(crate) struct SetEnforceGuard(Option<()>);
103+
104+
#[cfg(feature = "install")]
105+
impl SetEnforceGuard {
106+
pub(crate) fn new() -> Self {
107+
SetEnforceGuard(Some(()))
108+
}
109+
110+
pub(crate) fn consume(mut self) -> Result<()> {
111+
// SAFETY: The option cannot have been consumed until now
112+
self.0.take().unwrap();
113+
// This returns errors
114+
selinux_set_permissive(false)
115+
}
116+
}
103117

104118
#[cfg(feature = "install")]
105119
impl Drop for SetEnforceGuard {
106120
fn drop(&mut self) {
107-
let _ = selinux_set_permissive(false);
121+
// A best-effort attempt to re-enable enforcement on drop (installation failure)
122+
if let Some(()) = self.0.take() {
123+
let _ = selinux_set_permissive(false);
124+
}
108125
}
109126
}
110127

@@ -121,7 +138,7 @@ pub(crate) fn selinux_ensure_install_or_setenforce() -> Result<Option<SetEnforce
121138
let g = if std::env::var_os("BOOTC_SETENFORCE0_FALLBACK").is_some() {
122139
tracing::warn!("Failed to enter install_t; temporarily setting permissive mode");
123140
selinux_set_permissive(true)?;
124-
Some(SetEnforceGuard)
141+
Some(SetEnforceGuard::new())
125142
} else {
126143
let current = get_current_security_context()?;
127144
anyhow::bail!("Failed to enter install_t (running as {current}) - use BOOTC_SETENFORCE0_FALLBACK=1 to override");

0 commit comments

Comments
 (0)