Skip to content

Commit

Permalink
install: Add prominent warning+timeout when targeting host root
Browse files Browse the repository at this point in the history
It was raised in a discussion that it's rather easy to copy-paste
this command you may find in a document and not fully understand
what it does.

Let's prominently warn by default, with a timeout.

Signed-off-by: Colin Walters <walters@verbum.org>
  • Loading branch information
cgwalters committed May 3, 2024
1 parent dd09f47 commit 85a2fcf
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 3 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ jobs:
image=quay.io/centos-bootc/centos-bootc-dev:stream9
echo 'ssh-ed25519 ABC0123 testcase@example.com' > test_authorized_keys
sudo podman run --rm -ti --privileged -v ./test_authorized_keys:/test_authorized_keys --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 \
${image} bootc install to-filesystem \
${image} bootc install to-filesystem --acknowledge-destructive \
--karg=foo=bar --disable-selinux --replace=alongside --root-ssh-authorized-keys=/test_authorized_keys /target
ls -al /boot/loader/
sudo grep foo=bar /boot/loader/entries/*.conf
Expand All @@ -156,7 +156,7 @@ jobs:
sudo chattr -i /ostree/deploy/default/deploy/*
sudo rm /ostree/deploy/default -rf
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 \
${image} bootc install to-existing-root
${image} bootc install to-existing-root --acknowledge-destructive
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
install-to-existing-root:
if: ${{ !contains(github.event.pull_request.labels.*.name, 'control/skip-ci') }}
Expand Down
43 changes: 42 additions & 1 deletion lib/src/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use std::path::Path;
use std::process::Command;
use std::str::FromStr;
use std::sync::Arc;
use std::time::Duration;

use anyhow::Ok;
use anyhow::{anyhow, Context, Result};
Expand Down Expand Up @@ -230,6 +231,9 @@ pub(crate) struct InstallTargetFilesystemOpts {
#[clap(long)]
pub(crate) replace: Option<ReplaceMode>,

/// If the target is the running system's root filesystem, this will skip any warnings.
pub(crate) acknowledge_destructive: bool,

/// The default mode is to "finalize" the target filesystem by invoking `fstrim` and similar
/// operations, and finally mounting it readonly. This option skips those operations. It
/// is then the responsibility of the invoking code to perform those operations.
Expand Down Expand Up @@ -269,6 +273,9 @@ pub(crate) struct InstallToExistingRootOpts {
#[clap(flatten)]
pub(crate) config_opts: InstallConfigOpts,

/// Accept that this is a destructive action and skip a warning timer.
pub(crate) acknowledge_destructive: bool,

/// Path to the mounted root; it's expected to invoke podman with
/// `-v /:/target`, then supplying this argument is unnecessary.
#[clap(default_value = "/target")]
Expand Down Expand Up @@ -1373,6 +1380,34 @@ fn find_root_args_to_inherit(cmdline: &[&str], root_info: &Filesystem) -> Result
Ok(RootMountInfo { mount_spec, kargs })
}

fn warn_on_host_root(rootfs_fd: &Dir) -> Result<()> {
// Seconds for which we wait while warning
const DELAY_SECONDS: u64 = 20;

let host_root_dfd = &Dir::open_ambient_dir("/proc/1/root", cap_std::ambient_authority())?;
let host_root_devstat = rustix::fs::fstatvfs(host_root_dfd)?;
let target_devstat = rustix::fs::fstatvfs(rootfs_fd)?;
if host_root_devstat.f_fsid != target_devstat.f_fsid {
tracing::debug!("Not the host root");
return Ok(());
}
let dashes = "----------------------------";
let timeout = Duration::from_secs(DELAY_SECONDS);
eprintln!("{dashes}");
crate::utils::medium_visibility_warning(
"WARNING: This operation will OVERWRITE THE BOOTED HOST ROOT FILESYSTEM and is NOT REVERSIBLE.",
);
eprintln!("Waiting {timeout:?} to continue; interrupt (Control-C) to cancel.");
eprintln!("{dashes}");

let bar = indicatif::ProgressBar::new_spinner();
bar.enable_steady_tick(Duration::from_millis(100));
std::thread::sleep(timeout);
bar.finish();

Ok(())
}

/// Implementation of the `bootc install to-filsystem` CLI command.
#[context("Installing to filesystem")]
pub(crate) async fn install_to_filesystem(
Expand All @@ -1391,7 +1426,12 @@ pub(crate) async fn install_to_filesystem(
let rootfs_fd = Dir::open_ambient_dir(root_path, cap_std::ambient_authority())
.with_context(|| format!("Opening target root directory {root_path}"))?;
if let Some(false) = ostree_ext::mountutil::is_mountpoint(&rootfs_fd, ".")? {
anyhow::bail!("Not a root mountpoint: {root_path}");
anyhow::bail!("Not a mountpoint: {root_path}");
}

// Check to see if this happens to be the real host root
if !fsopts.acknowledge_destructive {
warn_on_host_root(&rootfs_fd)?;
}

// Gather global state, destructuring the provided options
Expand Down Expand Up @@ -1554,6 +1594,7 @@ pub(crate) async fn install_to_existing_root(opts: InstallToExistingRootOpts) ->
boot_mount_spec: None,
replace: opts.replace,
skip_finalize: true,
acknowledge_destructive: opts.acknowledge_destructive,
},
source_opts: opts.source_opts,
target_opts: opts.target_opts,
Expand Down

0 comments on commit 85a2fcf

Please sign in to comment.