From 0f6bd98be0f676c9acb782c1163514a2ed37ea45 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 28 Sep 2021 18:03:34 -0400 Subject: [PATCH] tar/write: Don't actually derive from base when using selinux Using `--selinux-policy-from-base` is very convenient but wrong here because it means that each derived commit also has the whole base filesystem tree, which greatly obscures its logical content. Instead, we get the base when we dynamically union things at the end. As noted the API we really want here is https://github.com/ostreedev/ostree/pull/2447 but it will take a bit for ostree to release with it. And even once it does, we need to do some other changes to switch over to parsing the tarball directly too. --- lib/src/tar/write.rs | 47 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/lib/src/tar/write.rs b/lib/src/tar/write.rs index ac8e655f..b8ed3821 100644 --- a/lib/src/tar/write.rs +++ b/lib/src/tar/write.rs @@ -9,8 +9,11 @@ use crate::cmdext::CommandRedirectionExt; use crate::Result; -use anyhow::anyhow; +use anyhow::{anyhow, Context}; +use ostree::gio; +use ostree::prelude::FileExt; use std::os::unix::prelude::AsRawFd; +use std::path::Path; use tokio::io::AsyncReadExt; use tracing::instrument; @@ -24,6 +27,31 @@ pub struct WriteTarOptions<'a> { pub selinux: bool, } +struct TempSePolicy { + tempdir: tempfile::TempDir, +} + +// Copy of logic from https://github.com/ostreedev/ostree/pull/2447 +// to avoid waiting for backport + releases +fn sepolicy_from_base(repo: &ostree::Repo, base: &str) -> Result { + let cancellable = gio::NONE_CANCELLABLE; + let policypath = "usr/etc/selinux"; + let tempdir = tempfile::tempdir()?; + let (root, _) = repo.read_commit(base, cancellable)?; + let policyroot = root.resolve_relative_path(policypath); + if policyroot.query_exists(cancellable) { + let policydest = tempdir.path().join(policypath); + std::fs::create_dir_all(policydest.parent().unwrap())?; + let opts = ostree::RepoCheckoutAtOptions { + mode: ostree::RepoCheckoutMode::User, + subpath: Some(Path::new(policypath).to_owned()), + ..Default::default() + }; + repo.checkout_at(Some(&opts), ostree::AT_FDCWD, policydest, base, cancellable)?; + } + Ok(TempSePolicy { tempdir: tempdir }) +} + /// Write the contents of a tarball as an ostree commit. #[allow(unsafe_code)] // For raw fd bits #[instrument(skip(repo, src))] @@ -35,6 +63,15 @@ pub async fn write_tar( ) -> Result { use std::process::Stdio; let options = options.unwrap_or_default(); + let sepolicy = if options.selinux { + if let Some(base) = options.base { + Some(sepolicy_from_base(repo, base).context("tar: Preparing sepolicy")?) + } else { + None + } + } else { + None + }; let mut c = std::process::Command::new("ostree"); let repofd = repo.dfd_as_file()?; { @@ -45,11 +82,9 @@ pub async fn write_tar( .args(&["commit"]); c.take_fd_n(repofd.as_raw_fd(), 3); c.arg("--repo=/proc/self/fd/3"); - if let Some(base) = options.base { - if options.selinux { - c.arg("--selinux-policy-from-base"); - } - c.arg(&format!("--tree=ref={}", base)); + if let Some(sepolicy) = sepolicy { + c.arg("--selinux-policy"); + c.arg(sepolicy.tempdir.path()); } c.args(&[ "--no-bindings",