Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions crates/initramfs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ use linux_kernel_cmdline::utf8::Cmdline;

// mount_setattr syscall support
const MOUNT_ATTR_RDONLY: u64 = 0x00000001;
const MOUNT_ATTR_NOSUID: u64 = 0x00000002;
const MOUNT_ATTR_NODEV: u64 = 0x00000004;
const MOUNT_ATTR_NOSYMFOLLOW: u64 = 0x00200000;

#[repr(C)]
struct MountAttr {
Expand Down Expand Up @@ -91,6 +94,25 @@ pub fn set_mount_tree_readonly(fd: impl AsFd) -> Result<()> {
mount_setattr(fd, libc::AT_EMPTY_PATH | libc::AT_RECURSIVE, &attr)
}

/// Set a mount tree read-only, nosuid, nodev, and prevent symlink traversal within it.
///
/// A bind-mounted image tree is not a chroot: absolute and escaping symlinks
/// can otherwise resolve against the caller's root. `nosymfollow` keeps the
/// exposed tree confined to the image while leaving the underlying image
/// untouched. `nosuid` also prevents set-user-ID and set-group-ID bits in the
/// image from affecting processes that access the mounted tree. This deliberately
/// does not set `noexec`, so image executables remain runnable.
#[context("Setting mount tree readonly and nosymfollow")]
pub fn set_mount_tree_readonly_nosymfollow(fd: impl AsFd) -> Result<()> {
let attr = MountAttr {
attr_set: MOUNT_ATTR_RDONLY | MOUNT_ATTR_NOSUID | MOUNT_ATTR_NODEV | MOUNT_ATTR_NOSYMFOLLOW,
attr_clr: 0,
propagation: 0,
userns_fd: 0,
};
mount_setattr(fd, libc::AT_EMPTY_PATH | libc::AT_RECURSIVE, &attr)
}

/// Types of mounts supported by the configuration
#[derive(Clone, Copy, Debug, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
Expand Down
6 changes: 6 additions & 0 deletions crates/lib/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -765,6 +765,10 @@ pub(crate) enum ImageOpts {
/// The image to pull
image: String,
},
/// Mount content from the booted or staged deployment image read-only.
Mount(crate::image_mount::MountOpts),
/// Unmount the named non-root mount (non-recursive; bootc provenance is not checked).
Unmount(crate::image_mount::UnmountOpts),
/// Wrapper for selected `podman image` subcommands in bootc storage.
#[clap(subcommand)]
Cmd(ImageCmdOpts),
Expand Down Expand Up @@ -2496,6 +2500,8 @@ async fn run_from_opt(opt: Opt) -> Result<CliExitStatus> {
.pull_from_host_storage(&image)
.await
}
ImageOpts::Mount(opts) => crate::image_mount::mount(opts).await,
ImageOpts::Unmount(opts) => crate::image_mount::unmount(opts),
ImageOpts::Cmd(opt) => {
let storage = get_storage().await?;
let imgstore = storage.get_ensure_imgstore()?;
Expand Down
Loading
Loading