Skip to content

Commit

Permalink
lsm: Forcibly relabel all initial state
Browse files Browse the repository at this point in the history
This is attempting to handle the "install selinux-enabled target from selinux-disabled host".
We're kind of papering over effectively ostree design bugs here
as in this case libostree itself should be setting the labels,
but doing that is a bit hard and awkward right now.

Signed-off-by: Colin Walters <[email protected]>
  • Loading branch information
cgwalters committed Feb 1, 2024
1 parent c6d2002 commit 4f31c45
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 31 deletions.
40 changes: 14 additions & 26 deletions lib/src/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ use serde::{Deserialize, Serialize};

use self::baseline::InstallBlockDeviceOpts;
use crate::containerenv::ContainerExecutionInfo;
use crate::lsm::selinux_label_recurse;
use crate::task::Task;
use crate::utils::sigpolicy_from_opts;

Expand Down Expand Up @@ -263,21 +264,6 @@ pub(crate) struct State {
pub(crate) install_config: config::InstallConfiguration,
}

impl State {
// Wraps core lsm labeling functionality, conditionalizing based on source state
pub(crate) fn lsm_label(
&self,
target: &Utf8Path,
as_path: &Utf8Path,
recurse: bool,
) -> Result<()> {
if !self.source.selinux {
return Ok(());
}
crate::lsm::lsm_label(target, as_path, recurse)
}
}

/// Path to initially deployed version information
const BOOTC_ALEPH_PATH: &str = ".bootc-aleph.json";

Expand Down Expand Up @@ -466,9 +452,15 @@ async fn initialize_ostree_root_from_self(
let rootfs = root_setup.rootfs.as_path();
let cancellable = gio::Cancellable::NONE;

// Ensure that the physical root is labeled.
// Another implementation: https://github.com/coreos/coreos-assembler/blob/3cd3307904593b3a131b81567b13a4d0b6fe7c90/src/create_disk.sh#L295
state.lsm_label(rootfs, "/".into(), false)?;
let sepolicy = if state.override_disable_selinux {
None
} else {
let root = ostree::gio::File::for_path("/");
Some(ostree::SePolicy::new(
&root,
ostree::gio::Cancellable::NONE,
)?)
};

// TODO: make configurable?
let stateroot = STATEROOT_DEFAULT;
Expand All @@ -478,12 +470,6 @@ async fn initialize_ostree_root_from_self(
["admin", "init-fs", "--modern", rootfs.as_str()],
)?;

// And also label /boot AKA xbootldr, if it exists
let bootdir = rootfs.join("boot");
if bootdir.try_exists()? {
state.lsm_label(&bootdir, "/boot".into(), false)?;
}

// Default to avoiding grub2-mkconfig etc., but we need to use zipl on s390x.
// TODO: Lower this logic into ostree proper.
let bootloader = if cfg!(target_arch = "s390x") {
Expand All @@ -509,8 +495,10 @@ async fn initialize_ostree_root_from_self(
.cwd(rootfs_dir)?
.run()?;

// Ensure everything in the ostree repo is labeled
state.lsm_label(&rootfs.join("ostree"), "/usr".into(), true)?;
if let Some(policy) = sepolicy.as_ref() {
selinux_label_recurse(policy, &root_setup.rootfs_fd, "./".into())
.context("Labeling physical root")?;
}

let sysroot = ostree::Sysroot::new(Some(&gio::File::for_path(rootfs)));
sysroot.load(cancellable)?;
Expand Down
5 changes: 0 additions & 5 deletions lib/src/install/baseline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -364,15 +364,10 @@ pub(crate) fn install_create_rootfs(
.collect::<Vec<_>>();

mount::mount(&rootdev, &rootfs)?;
state.lsm_label(&rootfs, "/".into(), false)?;
let rootfs_fd = Dir::open_ambient_dir(&rootfs, cap_std::ambient_authority())?;
let bootfs = rootfs.join("boot");
std::fs::create_dir(&bootfs).context("Creating /boot")?;
// The underlying directory on the root should be labeled
state.lsm_label(&bootfs, "/boot".into(), false)?;
mount::mount(bootdev, &bootfs)?;
// And we want to label the root mount of /boot
state.lsm_label(&bootfs, "/boot".into(), false)?;

// Create the EFI system partition, if applicable
if let Some(esp_partno) = esp_partno {
Expand Down
44 changes: 44 additions & 0 deletions lib/src/lsm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ use std::process::Command;

use anyhow::{Context, Result};
use camino::{Utf8Path, Utf8PathBuf};
use cap_std_ext::cap_std::fs::{Dir, MetadataExt};
use fn_error_context::context;
#[cfg(feature = "install")]
use gvariant::{aligned_bytes::TryAsAligned, Marker, Structure};
#[cfg(feature = "install")]
use ostree_ext::ostree;
use rustix::fd::AsRawFd;

use crate::task::Task;

Expand Down Expand Up @@ -168,6 +170,48 @@ fn selinux_label_for_path(target: &str) -> Result<String> {
Ok(label.trim().to_string())
}

fn selinux_set_one_label(
policy: &ostree::SePolicy,
root: &Dir,
path: &Utf8Path,
mode: u32,
) -> Result<()> {
let label = policy.label(path.as_str(), mode, ostree::gio::Cancellable::NONE)?;
if let Some(label) = label {
let selfpath = format!("/proc/self/fd/{}", root.as_raw_fd());
rustix::fs::lsetxattr(
&selfpath,
SELINUX_XATTR,
label.as_bytes(),
rustix::fs::XattrFlags::empty(),
)?;
}
Ok(())
}

pub(crate) fn selinux_label_recurse(
policy: &ostree::SePolicy,
root: &Dir,
path: &Utf8Path,
) -> Result<()> {
let meta = root.symlink_metadata(path)?;
selinux_set_one_label(policy, root, path, meta.mode())?;
if meta.is_dir() {
for ent in root.read_dir(path)? {
let ent = ent?;
let name = ent.file_name();
let name = if let Some(name) = name.to_str() {
name
} else {
anyhow::bail!("Invalid filename: {name:?}");
};
let path = path.join(name);
selinux_label_recurse(policy, root, &path)?;
}
}
Ok(())
}

// Write filesystem labels (currently just for SELinux)
#[context("Labeling {as_path}")]
pub(crate) fn lsm_label(target: &Utf8Path, as_path: &Utf8Path, recurse: bool) -> Result<()> {
Expand Down

0 comments on commit 4f31c45

Please sign in to comment.