Skip to content

Commit

Permalink
Merge pull request #505 from cgwalters/install-to-filesystem-warning-…
Browse files Browse the repository at this point in the history
…more

install: Add prominent warning+timeout when targeting host root
  • Loading branch information
jeckersb committed May 3, 2024
2 parents dd09f47 + 2094895 commit 5140bd4
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 10 deletions.
16 changes: 8 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ jobs:
- name: Install
run: sudo tar -C / -xvf bootc.tar.zst
- name: Integration tests
run: sudo podman run --rm -ti --privileged -v /run/systemd:/run/systemd -v /:/run/host -v /usr/bin/bootc:/usr/bin/bootc --pid=host quay.io/fedora/fedora-coreos:testing-devel bootc internal-tests run-privileged-integration
run: sudo podman run --rm --privileged -v /run/systemd:/run/systemd -v /:/run/host -v /usr/bin/bootc:/usr/bin/bootc --pid=host quay.io/fedora/fedora-coreos:testing-devel bootc internal-tests run-privileged-integration
container-tests:
if: ${{ !contains(github.event.pull_request.labels.*.name, 'control/skip-ci') }}
name: "Container testing"
Expand Down Expand Up @@ -146,18 +146,18 @@ jobs:
set -xeuo pipefail
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 \
sudo podman run --rm --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 --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
grep authorized_keys /ostree/deploy/default/deploy/*/etc/tmpfiles.d/bootc-root-ssh.conf
# TODO fix https://github.com/containers/bootc/pull/137
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
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
sudo podman run --rm --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 --acknowledge-destructive
sudo podman run --rm --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') }}
name: "Test install-to-existing-root"
Expand All @@ -177,7 +177,7 @@ jobs:
# so we bind mount an empty directory over /usr/lib/bootc/install.
empty=$(mktemp -d)
image=quay.io/centos-bootc/centos-bootc-dev:stream9
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 -v ${empty}:/usr/lib/bootc/install --pid=host --security-opt label=disable \
sudo podman run --rm --privileged --env RUST_LOG=debug -v /:/target -v /var/lib/containers:/var/lib/containers -v ./usr/bin/bootc:/usr/bin/bootc -v ${empty}:/usr/lib/bootc/install --pid=host --security-opt label=disable \
${image} bootc install to-existing-root
install-to-loopback:
if: ${{ !contains(github.event.pull_request.labels.*.name, 'control/skip-ci') }}
Expand All @@ -197,5 +197,5 @@ jobs:
image=quay.io/centos-bootc/centos-bootc-dev:stream9
tmpdisk=$(mktemp -p /var/tmp)
truncate -s 20G ${tmpdisk}
sudo podman run --rm -ti --privileged --env RUST_LOG=debug -v /dev:/dev -v /:/target -v /var/lib/containers:/var/lib/containers -v ./usr/bin/bootc:/usr/bin/bootc --pid=host --security-opt label=disable \
sudo podman run --rm --privileged --env RUST_LOG=debug -v /dev:/dev -v /:/target -v /var/lib/containers:/var/lib/containers -v ./usr/bin/bootc:/usr/bin/bootc --pid=host --security-opt label=disable \
-v ${tmpdisk}:/disk ${image} bootc install to-disk --via-loopback /disk
45 changes: 44 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,10 @@ 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.
#[clap(long)]
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 +274,10 @@ pub(crate) struct InstallToExistingRootOpts {
#[clap(flatten)]
pub(crate) config_opts: InstallConfigOpts,

/// Accept that this is a destructive action and skip a warning timer.
#[clap(long)]
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 +1382,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 +1428,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 +1596,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
2 changes: 1 addition & 1 deletion tests/kolainst/install
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ case "${AUTOPKGTEST_REBOOT_MARK:-}" in
COPY usr usr
EOF
podman build -t localhost/testimage .
podman run --rm -ti --privileged --pid=host --env RUST_LOG=error,bootc_lib::install=debug \
podman run --rm --privileged --pid=host --env RUST_LOG=error,bootc_lib::install=debug \
localhost/testimage bootc install to-disk --skip-fetch-check --karg=foo=bar ${DEV}
# In theory we could e.g. wipe the bootloader setup on the primary disk, then reboot;
# but for now let's just sanity test that the install command executes.
Expand Down

0 comments on commit 5140bd4

Please sign in to comment.