Add support for wrapping binaries (rpm, dracut, grubby)
We need to be friendlier to people who are transitioning from "traditional" yum managed systems. This patchset starts to lay out the groundwork for supporting "intercepting" binaries that are in the tree. For backwards compatibility, this feature is disabled by default, to enable it, one can add `cliwrap: true` to the manifest. To start with for example, we wrap `/usr/bin/rpm` and cause it to drop privileges. This way it can't corrupt anything; we're not just relying on the read-only bind mount. For example nothing will accidentally get written to `/var/lib/rpm`. Now a tricky thing with this one is we *do* want it to write if we're in an unlocked state. There are various other examples of binaries we want to intercept, among them: - `grubby` -> `rpm-ostree kargs` - `dracut` -> `rpm-ostree initramfs` - `yum` -> well...we'll talk about that later
This commit is contained in:
parent
357c527320
commit
e41a8ab26f
@ -29,6 +29,7 @@ rpm_ostree_SOURCES = src/app/main.c \
|
|||||||
src/app/rpmostree-builtin-reload.c \
|
src/app/rpmostree-builtin-reload.c \
|
||||||
src/app/rpmostree-builtin-rebase.c \
|
src/app/rpmostree-builtin-rebase.c \
|
||||||
src/app/rpmostree-builtin-cancel.c \
|
src/app/rpmostree-builtin-cancel.c \
|
||||||
|
src/app/rpmostree-builtin-cliwrap.c \
|
||||||
src/app/rpmostree-builtin-cleanup.c \
|
src/app/rpmostree-builtin-cleanup.c \
|
||||||
src/app/rpmostree-builtin-initramfs.c \
|
src/app/rpmostree-builtin-initramfs.c \
|
||||||
src/app/rpmostree-builtin-livefs.c \
|
src/app/rpmostree-builtin-livefs.c \
|
||||||
@ -102,7 +103,7 @@ librpmostree_rust_path = @abs_top_builddir@/target/@RUST_TARGET_SUBDIR@/librpmos
|
|||||||
# If the target directory exists, and isn't owned by our uid, then
|
# If the target directory exists, and isn't owned by our uid, then
|
||||||
# we exit with a fatal error, since someone probably did `make && sudo make install`,
|
# we exit with a fatal error, since someone probably did `make && sudo make install`,
|
||||||
# and in this case cargo will download into ~/.root which we don't want.
|
# and in this case cargo will download into ~/.root which we don't want.
|
||||||
LIBRPMOSTREE_RUST_SRCS = $(wildcard rust/src/*.rs) rust/cbindgen.toml
|
LIBRPMOSTREE_RUST_SRCS = $(shell find rust/src/ -name '*.rs') rust/cbindgen.toml
|
||||||
$(librpmostree_rust_path): Makefile $(LIBRPMOSTREE_RUST_SRCS)
|
$(librpmostree_rust_path): Makefile $(LIBRPMOSTREE_RUST_SRCS)
|
||||||
cd $(top_srcdir)/rust && \
|
cd $(top_srcdir)/rust && \
|
||||||
export CARGO_TARGET_DIR=@abs_top_builddir@/target && \
|
export CARGO_TARGET_DIR=@abs_top_builddir@/target && \
|
||||||
|
@ -94,6 +94,12 @@ It supports the following parameters:
|
|||||||
specific filesystem drivers are included. If not specified,
|
specific filesystem drivers are included. If not specified,
|
||||||
`--no-hostonly` will be used.
|
`--no-hostonly` will be used.
|
||||||
|
|
||||||
|
* `cliwrap`: boolean, optional. Defaults to `false`. If enabled,
|
||||||
|
rpm-ostree will replace binaries such as `/usr/bin/rpm` with
|
||||||
|
wrappers that intercept unsafe operations, or adjust functionality.
|
||||||
|
|
||||||
|
The default is `false` out of conservatism; you likely want to enable this.
|
||||||
|
|
||||||
* `remove-files`: Array of files to delete from the generated tree.
|
* `remove-files`: Array of files to delete from the generated tree.
|
||||||
|
|
||||||
* `remove-from-packages`: Array, optional: Delete from specified packages
|
* `remove-from-packages`: Array, optional: Delete from specified packages
|
||||||
|
143
rust/src/cliwrap.rs
Normal file
143
rust/src/cliwrap.rs
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2019 Red Hat, Inc.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
use anyhow::{bail, Result};
|
||||||
|
use std::io::prelude::*;
|
||||||
|
use std::{io, path};
|
||||||
|
|
||||||
|
use openat_ext::OpenatDirExt;
|
||||||
|
use rayon::prelude::*;
|
||||||
|
mod cliutil;
|
||||||
|
mod dracut;
|
||||||
|
mod grubby;
|
||||||
|
mod rpm;
|
||||||
|
|
||||||
|
/// Location for the underlying (not wrapped) binaries.
|
||||||
|
pub const CLIWRAP_DESTDIR: &'static str = "usr/libexec/rpm-ostree/wrapped";
|
||||||
|
|
||||||
|
/// Our list of binaries that will be wrapped. Must be a relative path.
|
||||||
|
static WRAPPED_BINARIES: &[&str] = &["usr/bin/rpm", "usr/bin/dracut", "usr/sbin/grubby"];
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub(crate) enum RunDisposition {
|
||||||
|
Ok,
|
||||||
|
Warn,
|
||||||
|
Notice(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Main entrypoint for cliwrap
|
||||||
|
fn cliwrap_main(args: &Vec<String>) -> Result<()> {
|
||||||
|
// We'll panic here if the vector is empty, but that is intentional;
|
||||||
|
// the outer code should always pass us at least one arg.
|
||||||
|
let name = args[0].as_str();
|
||||||
|
let name = match std::path::Path::new(name).file_name() {
|
||||||
|
Some(name) => name,
|
||||||
|
None => bail!("Invalid wrapped binary: {}", name),
|
||||||
|
};
|
||||||
|
// We know we had a string from above
|
||||||
|
let name = name.to_str().unwrap();
|
||||||
|
|
||||||
|
let args: Vec<&str> = args.iter().skip(1).map(|v| v.as_str()).collect();
|
||||||
|
|
||||||
|
// If we're not booted into ostree, just run the child directly.
|
||||||
|
if !cliutil::is_ostree_booted() {
|
||||||
|
cliutil::exec_real_binary(name, &args)
|
||||||
|
} else {
|
||||||
|
match name {
|
||||||
|
"rpm" => self::rpm::main(&args),
|
||||||
|
"dracut" => self::dracut::main(&args),
|
||||||
|
"grubby" => self::grubby::main(&args),
|
||||||
|
_ => bail!("Unknown wrapped binary: {}", name),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Move the real binaries to a subdir, and replace them with
|
||||||
|
/// a shell script that calls our wrapping code.
|
||||||
|
fn write_wrappers(rootfs_dfd: &openat::Dir) -> Result<()> {
|
||||||
|
let destdir = std::path::Path::new(CLIWRAP_DESTDIR);
|
||||||
|
rootfs_dfd.ensure_dir(destdir.parent().unwrap(), 0o755)?;
|
||||||
|
rootfs_dfd.ensure_dir(destdir, 0o755)?;
|
||||||
|
WRAPPED_BINARIES.par_iter().try_for_each(|&bin| {
|
||||||
|
let binpath = path::Path::new(bin);
|
||||||
|
|
||||||
|
if !rootfs_dfd.exists(binpath)? {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let name = binpath.file_name().unwrap().to_str().unwrap();
|
||||||
|
let destpath = format!("{}/{}", CLIWRAP_DESTDIR, name);
|
||||||
|
rootfs_dfd.local_rename(bin, destpath.as_str())?;
|
||||||
|
|
||||||
|
let f = rootfs_dfd.write_file(binpath, 0o755)?;
|
||||||
|
let mut f = io::BufWriter::new(f);
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"#!/bin/sh
|
||||||
|
# Wrapper created by rpm-ostree to override
|
||||||
|
# behavior of the underlying binary. For more
|
||||||
|
# information see `man rpm-ostree`. The real
|
||||||
|
# binary is now located at: {}
|
||||||
|
exec /usr/bin/rpm-ostree cliwrap $0 \"$@\"
|
||||||
|
",
|
||||||
|
binpath.to_str().unwrap()
|
||||||
|
)?;
|
||||||
|
f.flush()?;
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
mod ffi {
|
||||||
|
use super::*;
|
||||||
|
use crate::ffiutil::*;
|
||||||
|
use anyhow::Context;
|
||||||
|
use glib;
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use libc;
|
||||||
|
use std::ffi::CString;
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn ror_cliwrap_write_wrappers(
|
||||||
|
rootfs_dfd: libc::c_int,
|
||||||
|
gerror: *mut *mut glib_sys::GError,
|
||||||
|
) -> libc::c_int {
|
||||||
|
let rootfs_dfd = ffi_view_openat_dir(rootfs_dfd);
|
||||||
|
int_glib_error(
|
||||||
|
write_wrappers(&rootfs_dfd).with_context(|| format!("cli wrapper replacement failed")),
|
||||||
|
gerror,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn ror_cliwrap_entrypoint(
|
||||||
|
argv: *mut *mut libc::c_char,
|
||||||
|
gerror: *mut *mut glib_sys::GError,
|
||||||
|
) -> libc::c_int {
|
||||||
|
let v: Vec<String> = unsafe { glib::translate::FromGlibPtrContainer::from_glib_none(argv) };
|
||||||
|
int_glib_error(cliwrap_main(&v), gerror)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn ror_cliwrap_destdir() -> *const libc::c_char {
|
||||||
|
lazy_static! {
|
||||||
|
static ref CLIWRAP_DESTDIR_C: CString = CString::new(CLIWRAP_DESTDIR).unwrap();
|
||||||
|
}
|
||||||
|
CLIWRAP_DESTDIR_C.as_ptr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub use self::ffi::*;
|
101
rust/src/cliwrap/cliutil.rs
Normal file
101
rust/src/cliwrap/cliutil.rs
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
use anyhow::Result;
|
||||||
|
use nix::sys::statvfs;
|
||||||
|
use std::os::unix::process::CommandExt;
|
||||||
|
use std::{path, thread, time};
|
||||||
|
|
||||||
|
use crate::cliwrap;
|
||||||
|
|
||||||
|
/// Returns true if the current process is booted via ostree.
|
||||||
|
pub fn is_ostree_booted() -> bool {
|
||||||
|
path::Path::new("/run/ostree-booted").exists()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if /usr is not a read-only bind mount
|
||||||
|
pub fn is_unlocked() -> Result<bool> {
|
||||||
|
Ok(!statvfs::statvfs("/usr")?
|
||||||
|
.flags()
|
||||||
|
.contains(statvfs::FsFlags::ST_RDONLY))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if the current process is running as root.
|
||||||
|
pub fn am_privileged() -> bool {
|
||||||
|
nix::unistd::getuid() == nix::unistd::Uid::from_raw(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the absolute path to the underlying wrapped binary
|
||||||
|
fn get_real_binary_path(bin_name: &str) -> String {
|
||||||
|
format!("/{}/{}", cliwrap::CLIWRAP_DESTDIR, bin_name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wrapper for execv which accepts strings
|
||||||
|
pub fn exec_real_binary<T: AsRef<str> + std::fmt::Display>(bin_name: T, argv: &[T]) -> Result<()> {
|
||||||
|
let bin_name = bin_name.as_ref();
|
||||||
|
let real_bin = get_real_binary_path(bin_name);
|
||||||
|
let mut proc = std::process::Command::new(real_bin);
|
||||||
|
proc.args(argv.iter().map(|s| s.as_ref()));
|
||||||
|
Err(proc.exec().into())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Run a subprocess synchronously as user `bin` (dropping all capabilities).
|
||||||
|
pub fn run_unprivileged<T: AsRef<str>>(
|
||||||
|
with_warning: bool,
|
||||||
|
target_bin: &str,
|
||||||
|
argv: &[T],
|
||||||
|
) -> Result<()> {
|
||||||
|
// `setpriv` is in util-linux; we could do this internally, but this is easier.
|
||||||
|
let setpriv_argv = &[
|
||||||
|
"setpriv",
|
||||||
|
"--no-new-privs",
|
||||||
|
"--reuid=bin",
|
||||||
|
"--regid=bin",
|
||||||
|
"--init-groups",
|
||||||
|
"--bounding-set",
|
||||||
|
"-all",
|
||||||
|
"--",
|
||||||
|
];
|
||||||
|
|
||||||
|
let argv: Vec<&str> = argv.into_iter().map(AsRef::as_ref).collect();
|
||||||
|
let drop_privileges = am_privileged();
|
||||||
|
let app_name = "rpm-ostree";
|
||||||
|
if with_warning {
|
||||||
|
let delay_s = 5;
|
||||||
|
eprintln!(
|
||||||
|
"{name}: NOTE: This system is ostree based.",
|
||||||
|
name = app_name
|
||||||
|
);
|
||||||
|
if drop_privileges {
|
||||||
|
eprintln!(
|
||||||
|
r#"{name}: Dropping privileges as `{bin}` was executed with not "known safe" arguments."#,
|
||||||
|
name = app_name,
|
||||||
|
bin = target_bin
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
eprintln!(
|
||||||
|
r#"{name}: Wrapped binary "{bin}" was executed with not "known safe" arguments."#,
|
||||||
|
name = app_name,
|
||||||
|
bin = target_bin
|
||||||
|
);
|
||||||
|
}
|
||||||
|
eprintln!(
|
||||||
|
r##"{name}: You may invoke the real `{bin}` binary in `/{wrap_destdir}/{bin}`.
|
||||||
|
{name}: Continuing execution in {delay} seconds.
|
||||||
|
"##,
|
||||||
|
name = app_name,
|
||||||
|
wrap_destdir = cliwrap::CLIWRAP_DESTDIR,
|
||||||
|
bin = target_bin,
|
||||||
|
delay = delay_s,
|
||||||
|
);
|
||||||
|
thread::sleep(time::Duration::from_secs(delay_s));
|
||||||
|
}
|
||||||
|
|
||||||
|
if drop_privileges {
|
||||||
|
let real_bin = get_real_binary_path(target_bin);
|
||||||
|
let mut proc = std::process::Command::new("setpriv");
|
||||||
|
proc.args(setpriv_argv);
|
||||||
|
proc.arg(real_bin);
|
||||||
|
proc.args(argv);
|
||||||
|
Err(proc.exec().into())
|
||||||
|
} else {
|
||||||
|
exec_real_binary(target_bin, &argv)
|
||||||
|
}
|
||||||
|
}
|
17
rust/src/cliwrap/dracut.rs
Normal file
17
rust/src/cliwrap/dracut.rs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
use anyhow::Result;
|
||||||
|
|
||||||
|
use crate::cliwrap::cliutil;
|
||||||
|
|
||||||
|
/// Primary entrypoint to running our wrapped `dracut` handling.
|
||||||
|
pub(crate) fn main(argv: &[&str]) -> Result<()> {
|
||||||
|
eprintln!(
|
||||||
|
"This system is rpm-ostree based; initramfs handling is
|
||||||
|
integrated with the underlying ostree transaction mechanism.
|
||||||
|
Use `rpm-ostree initramfs` to control client-side initramfs generation."
|
||||||
|
);
|
||||||
|
if argv.len() > 0 {
|
||||||
|
Ok(cliutil::run_unprivileged(true, "dracut", argv)?)
|
||||||
|
} else {
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
10
rust/src/cliwrap/grubby.rs
Normal file
10
rust/src/cliwrap/grubby.rs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
use anyhow::Result;
|
||||||
|
|
||||||
|
/// Primary entrypoint to running our wrapped `grubby` handling.
|
||||||
|
pub(crate) fn main(_argv: &[&str]) -> Result<()> {
|
||||||
|
eprintln!(
|
||||||
|
"This system is rpm-ostree based; grubby is not used.
|
||||||
|
Use `rpm-ostree kargs` instead."
|
||||||
|
);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
137
rust/src/cliwrap/rpm.rs
Normal file
137
rust/src/cliwrap/rpm.rs
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
use anyhow::Result;
|
||||||
|
use clap::{App, Arg};
|
||||||
|
|
||||||
|
use crate::cliwrap::cliutil;
|
||||||
|
use crate::cliwrap::RunDisposition;
|
||||||
|
|
||||||
|
fn new_rpm_app<'r>() -> App<'r, 'static> {
|
||||||
|
let name = "cli-ostree-wrapper-rpm";
|
||||||
|
App::new(name)
|
||||||
|
.bin_name(name)
|
||||||
|
.version("0.1")
|
||||||
|
.about("Wrapper for rpm")
|
||||||
|
.arg(Arg::with_name("verify").short("V"))
|
||||||
|
.arg(Arg::with_name("version"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// clap doesn't easily allow us to parse unknown arguments right now,
|
||||||
|
// scan argv manually.
|
||||||
|
// https://github.com/clap-rs/clap/issues/873#issuecomment-436546860
|
||||||
|
fn has_query(argv: &[&str]) -> bool {
|
||||||
|
for a in argv {
|
||||||
|
let a = *a;
|
||||||
|
if a == "--query" {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if a.starts_with("-") && !a.starts_with("--") {
|
||||||
|
for c in a.chars().skip(1) {
|
||||||
|
if c == 'q' {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn disposition(argv: &[&str]) -> Result<RunDisposition> {
|
||||||
|
// Today rpm has --query take precendence over --erase and --install
|
||||||
|
// apparently, so let's just accept anything with --query as there
|
||||||
|
// are a lot of sub-options for that.
|
||||||
|
if has_query(argv) {
|
||||||
|
return Ok(RunDisposition::Ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut app = new_rpm_app();
|
||||||
|
let matches = match app.get_matches_from_safe_borrow(std::iter::once(&"rpm").chain(argv.iter()))
|
||||||
|
{
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) if e.kind == clap::ErrorKind::VersionDisplayed => return Ok(RunDisposition::Ok),
|
||||||
|
_ => return Ok(RunDisposition::Warn),
|
||||||
|
};
|
||||||
|
|
||||||
|
if matches.is_present("verify") {
|
||||||
|
Ok(RunDisposition::Notice(
|
||||||
|
"rpm --verify is not necessary for ostree-based systems.
|
||||||
|
All binaries in /usr are underneath a read-only bind mount.
|
||||||
|
If you wish to verify integrity, use `ostree fsck`."
|
||||||
|
.to_string(),
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
// This currently really shoudln't happen, but in the future we might
|
||||||
|
// clearly whitelist other arguments besides --query.
|
||||||
|
Ok(RunDisposition::Ok)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Primary entrypoint to running our wrapped `rpm` handling.
|
||||||
|
pub(crate) fn main(argv: &[&str]) -> Result<()> {
|
||||||
|
if cliutil::is_unlocked()? {
|
||||||
|
// For now if we're unlocked, just directly exec rpm. In the future we
|
||||||
|
// may choose to take over installing a package live.
|
||||||
|
cliutil::exec_real_binary("rpm", argv)
|
||||||
|
} else {
|
||||||
|
match disposition(argv)? {
|
||||||
|
RunDisposition::Ok => cliutil::run_unprivileged(false, "rpm", argv),
|
||||||
|
RunDisposition::Warn => cliutil::run_unprivileged(true, "rpm", argv),
|
||||||
|
RunDisposition::Notice(ref s) => {
|
||||||
|
println!("{}", s);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_version() -> Result<()> {
|
||||||
|
assert_eq!(disposition(&["--version"])?, RunDisposition::Ok);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_query_all() -> Result<()> {
|
||||||
|
assert_eq!(disposition(&["-qa"])?, RunDisposition::Ok);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_query_file() -> Result<()> {
|
||||||
|
assert_eq!(
|
||||||
|
disposition(&["--query", "-f", "/usr/bin/bash"])?,
|
||||||
|
RunDisposition::Ok
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_query_requires() -> Result<()> {
|
||||||
|
assert_eq!(
|
||||||
|
disposition(&["--requires", "-q", "blah"])?,
|
||||||
|
RunDisposition::Ok
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_query_erase() -> Result<()> {
|
||||||
|
// Note --query overrides --erase today
|
||||||
|
assert_eq!(disposition(&["-qea", "bash"])?, RunDisposition::Ok);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_erase() -> Result<()> {
|
||||||
|
assert_eq!(disposition(&["--erase", "bash"])?, RunDisposition::Warn);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_shorterase() -> Result<()> {
|
||||||
|
assert_eq!(disposition(&["-e", "bash"])?, RunDisposition::Warn);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
@ -7,6 +7,8 @@
|
|||||||
// pub(crate) utilities
|
// pub(crate) utilities
|
||||||
mod ffiutil;
|
mod ffiutil;
|
||||||
|
|
||||||
|
mod cliwrap;
|
||||||
|
pub use cliwrap::*;
|
||||||
mod composepost;
|
mod composepost;
|
||||||
pub use self::composepost::*;
|
pub use self::composepost::*;
|
||||||
mod history;
|
mod history;
|
||||||
|
@ -296,6 +296,7 @@ fn treefile_merge(dest: &mut TreeComposeConfig, src: &mut TreeComposeConfig) {
|
|||||||
include,
|
include,
|
||||||
container,
|
container,
|
||||||
recommends,
|
recommends,
|
||||||
|
cliwrap,
|
||||||
documentation,
|
documentation,
|
||||||
boot_location,
|
boot_location,
|
||||||
tmp_is_dir,
|
tmp_is_dir,
|
||||||
@ -684,6 +685,8 @@ struct TreeComposeConfig {
|
|||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
#[serde(rename = "initramfs-args")]
|
#[serde(rename = "initramfs-args")]
|
||||||
initramfs_args: Option<Vec<String>>,
|
initramfs_args: Option<Vec<String>>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
cliwrap: Option<bool>,
|
||||||
|
|
||||||
// Tree layout options
|
// Tree layout options
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
@ -1366,6 +1369,12 @@ mod ffi {
|
|||||||
tf.checksum.as_ptr()
|
tf.checksum.as_ptr()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn ror_treefile_get_cliwrap(tf: *mut Treefile) -> bool {
|
||||||
|
let tf = ref_from_raw_ptr(tf);
|
||||||
|
tf.parsed.cliwrap.unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn ror_treefile_free(tf: *mut Treefile) {
|
pub extern "C" fn ror_treefile_free(tf: *mut Treefile) {
|
||||||
if tf.is_null() {
|
if tf.is_null() {
|
||||||
|
@ -128,6 +128,8 @@ static RpmOstreeCommand commands[] = {
|
|||||||
NULL, rpmostree_builtin_start_daemon },
|
NULL, rpmostree_builtin_start_daemon },
|
||||||
{ "finalize-deployment", RPM_OSTREE_BUILTIN_FLAG_HIDDEN,
|
{ "finalize-deployment", RPM_OSTREE_BUILTIN_FLAG_HIDDEN,
|
||||||
NULL, rpmostree_builtin_finalize_deployment },
|
NULL, rpmostree_builtin_finalize_deployment },
|
||||||
|
{ "cliwrap", RPM_OSTREE_BUILTIN_FLAG_LOCAL_CMD | RPM_OSTREE_BUILTIN_FLAG_HIDDEN,
|
||||||
|
NULL, rpmostree_builtin_cliwrap },
|
||||||
{ NULL }
|
{ NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
48
src/app/rpmostree-builtin-cliwrap.c
Normal file
48
src/app/rpmostree-builtin-cliwrap.c
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||||
|
*
|
||||||
|
* Copyright (C) 2019 Red Hat, Inc.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published
|
||||||
|
* by the Free Software Foundation; either version 2 of the licence or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General
|
||||||
|
* Public License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
||||||
|
* Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <glib-unix.h>
|
||||||
|
#include <gio/gio.h>
|
||||||
|
|
||||||
|
#include "rpmostree-builtins.h"
|
||||||
|
#include "rpmostree-libbuiltin.h"
|
||||||
|
#include "rpmostree-rust.h"
|
||||||
|
|
||||||
|
#include <libglnx.h>
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
rpmostree_builtin_cliwrap (int argc,
|
||||||
|
char **argv,
|
||||||
|
RpmOstreeCommandInvocation *invocation,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
if (argc < 2)
|
||||||
|
return glnx_throw (error, "cliwrap: missing required subcommand");
|
||||||
|
|
||||||
|
g_autoptr(GPtrArray) args = g_ptr_array_new ();
|
||||||
|
for (int i = 1; i < argc; i++)
|
||||||
|
g_ptr_array_add (args, argv[i]);
|
||||||
|
g_ptr_array_add (args, NULL);
|
||||||
|
return ror_cliwrap_entrypoint ((char**)args->pdata, error);
|
||||||
|
}
|
@ -31,6 +31,7 @@ G_BEGIN_DECLS
|
|||||||
GCancellable *cancellable, GError **error)
|
GCancellable *cancellable, GError **error)
|
||||||
|
|
||||||
BUILTINPROTO(compose);
|
BUILTINPROTO(compose);
|
||||||
|
BUILTINPROTO(cliwrap);
|
||||||
BUILTINPROTO(upgrade);
|
BUILTINPROTO(upgrade);
|
||||||
BUILTINPROTO(reload);
|
BUILTINPROTO(reload);
|
||||||
BUILTINPROTO(usroverlay);
|
BUILTINPROTO(usroverlay);
|
||||||
|
@ -4425,6 +4425,12 @@ rpmostree_context_assemble (RpmOstreeContext *self,
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (self->treefile_rs && ror_treefile_get_cliwrap (self->treefile_rs))
|
||||||
|
{
|
||||||
|
if (!ror_cliwrap_write_wrappers (tmprootfs_dfd, error))
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
/* Undo the /etc move above */
|
/* Undo the /etc move above */
|
||||||
if (renamed_etc && !rpmostree_core_redo_usretc (tmprootfs_dfd, error))
|
if (renamed_etc && !rpmostree_core_redo_usretc (tmprootfs_dfd, error))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
@ -38,6 +38,7 @@
|
|||||||
#include "rpmostree-core.h"
|
#include "rpmostree-core.h"
|
||||||
#include "rpmostree-kernel.h"
|
#include "rpmostree-kernel.h"
|
||||||
#include "rpmostree-bwrap.h"
|
#include "rpmostree-bwrap.h"
|
||||||
|
#include "rpmostree-rust.h"
|
||||||
#include "rpmostree-util.h"
|
#include "rpmostree-util.h"
|
||||||
|
|
||||||
static const char usrlib_ostreeboot[] = "usr/lib/ostree-boot";
|
static const char usrlib_ostreeboot[] = "usr/lib/ostree-boot";
|
||||||
@ -488,12 +489,15 @@ rpmostree_run_dracut (int rootfs_dfd,
|
|||||||
*/
|
*/
|
||||||
static const char rpmostree_dracut_wrapper_path[] = "usr/bin/rpmostree-dracut-wrapper";
|
static const char rpmostree_dracut_wrapper_path[] = "usr/bin/rpmostree-dracut-wrapper";
|
||||||
/* This also hardcodes a few arguments */
|
/* This also hardcodes a few arguments */
|
||||||
static const char rpmostree_dracut_wrapper[] =
|
g_autofree char * rpmostree_dracut_wrapper =
|
||||||
|
g_strdup_printf (
|
||||||
"#!/usr/bin/bash\n"
|
"#!/usr/bin/bash\n"
|
||||||
"set -euo pipefail\n"
|
"set -euo pipefail\n"
|
||||||
|
"export PATH=%s:${PATH}\n"
|
||||||
"extra_argv=; if (dracut --help; true) | grep -q -e --reproducible; then extra_argv=\"--reproducible --gzip\"; fi\n"
|
"extra_argv=; if (dracut --help; true) | grep -q -e --reproducible; then extra_argv=\"--reproducible --gzip\"; fi\n"
|
||||||
"mkdir -p /tmp/dracut && dracut $extra_argv -v --add ostree --tmpdir=/tmp/dracut -f /tmp/initramfs.img \"$@\"\n"
|
"mkdir -p /tmp/dracut && dracut $extra_argv -v --add ostree --tmpdir=/tmp/dracut -f /tmp/initramfs.img \"$@\"\n"
|
||||||
"cat /tmp/initramfs.img >/proc/self/fd/3\n";
|
"cat /tmp/initramfs.img >/proc/self/fd/3\n",
|
||||||
|
ror_cliwrap_destdir ());
|
||||||
g_autoptr(RpmOstreeBwrap) bwrap = NULL;
|
g_autoptr(RpmOstreeBwrap) bwrap = NULL;
|
||||||
g_autoptr(GPtrArray) rebuild_argv = NULL;
|
g_autoptr(GPtrArray) rebuild_argv = NULL;
|
||||||
g_auto(GLnxTmpfile) tmpf = { 0, };
|
g_auto(GLnxTmpfile) tmpf = { 0, };
|
||||||
@ -537,7 +541,7 @@ rpmostree_run_dracut (int rootfs_dfd,
|
|||||||
O_RDWR | O_CLOEXEC,
|
O_RDWR | O_CLOEXEC,
|
||||||
&tmpf, error))
|
&tmpf, error))
|
||||||
goto out;
|
goto out;
|
||||||
if (glnx_loop_write (tmpf.fd, rpmostree_dracut_wrapper, sizeof (rpmostree_dracut_wrapper)) < 0
|
if (glnx_loop_write (tmpf.fd, rpmostree_dracut_wrapper, strlen (rpmostree_dracut_wrapper)) < 0
|
||||||
|| fchmod (tmpf.fd, 0755) < 0)
|
|| fchmod (tmpf.fd, 0755) < 0)
|
||||||
{
|
{
|
||||||
glnx_set_error_from_errno (error);
|
glnx_set_error_from_errno (error);
|
||||||
|
@ -86,6 +86,28 @@ fi
|
|||||||
assert_file_has_content err.txt 'ReloadConfig not allowed for user'
|
assert_file_has_content err.txt 'ReloadConfig not allowed for user'
|
||||||
echo "ok auth"
|
echo "ok auth"
|
||||||
|
|
||||||
|
wrapdir="/usr/libexec/rpm-ostree/wrapped"
|
||||||
|
if [ -d "${wrapdir}" ]; then
|
||||||
|
# Test wrapped functions for rpm
|
||||||
|
rpm --version
|
||||||
|
rpm -qa > /dev/null
|
||||||
|
rpm --verify >out.txt
|
||||||
|
assert_file_has_content out.txt "rpm --verify is not necessary for ostree-based systems"
|
||||||
|
rm -f out.txt
|
||||||
|
if rpm -e bash 2>out.txt; then
|
||||||
|
fatal "rpm -e worked"
|
||||||
|
fi
|
||||||
|
assert_file_has_content out.txt 'Dropping privileges as `rpm` was executed with not "known safe" arguments'
|
||||||
|
|
||||||
|
if dracut --blah 2>out.txt; then
|
||||||
|
fatal "dracut worked"
|
||||||
|
fi
|
||||||
|
assert_file_has_content out.txt 'This system is rpm-ostree based'
|
||||||
|
rm -f out.txt
|
||||||
|
else
|
||||||
|
echo "Missing ${wrapdir}; cliwrap not enabled"
|
||||||
|
fi
|
||||||
|
|
||||||
# Test coreos-rootfs
|
# Test coreos-rootfs
|
||||||
vm_shell_inline > coreos-rootfs.txt << EOF
|
vm_shell_inline > coreos-rootfs.txt << EOF
|
||||||
mkdir /var/tmp/coreos-rootfs
|
mkdir /var/tmp/coreos-rootfs
|
||||||
|
Loading…
Reference in New Issue
Block a user