Skip to content

Commit

Permalink
feat: id mapped mounts (#13)
Browse files Browse the repository at this point in the history
  • Loading branch information
raphaelcoeffic authored Nov 14, 2024
1 parent e5c83e9 commit f19fee6
Show file tree
Hide file tree
Showing 7 changed files with 408 additions and 96 deletions.
8 changes: 8 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
name = "dive"
version = "0.1.2"
edition = "2021"
default-run = "dive"

[features]
embedded_image = []
Expand All @@ -15,6 +16,8 @@ exitcode = "1.1.2"
fd-lock = "4.0.2"
include_dir = "0.7.4"
indicatif = "0.17.8"
ioctl-sys = "0.8.0"
libc = "0.2.162"
liblzma = { version = "0.3.5", features = ["static"] }
log = "0.4.22"
procfs = { version= "0.17.0" }
Expand Down
3 changes: 1 addition & 2 deletions src/base_image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ where
None
};

log::debug!("image = {:?}", image);
let image_sha256 = if let Some(image) = &image {
Some(
fs::read(format!("{}.sha256", image.as_ref().display()))?
Expand All @@ -43,7 +42,7 @@ where

#[cfg(not(feature = "embedded_image"))]
{
log::debug!("no image");
log::debug!("no embedded image");
None
}
};
Expand Down
29 changes: 14 additions & 15 deletions src/bin/dive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::{
io,
os::unix::process::CommandExt,
path::{Path, PathBuf},
process::{self, exit, Command},
process::{exit, Command},
};

use anyhow::{bail, Context, Result};
Expand All @@ -16,17 +16,15 @@ use rustix::{

use dive::base_image::*;
use dive::namespaces::*;
use dive::overlay::*;
use dive::pid_lookup::*;
use dive::shared_mount::*;
use dive::shell::*;

const APP_NAME: &str = "dive";
const IMG_DIR: &str = "base-img";
const OVL_DIR: &str = "overlay";

const ENV_IMG_DIR: &str = "_IMG_DIR";
const ENV_OVL_DIR: &str = "_OVL_DIR";
const ENV_STATE_DIR: &str = "_STATE_DIR";
const ENV_LEAD_PID: &str = "_LEAD_PID";

/// Container debug CLI
Expand Down Expand Up @@ -62,11 +60,11 @@ fn get_img_dir(args: &Args) -> PathBuf {
dirs::state_dir().unwrap().join(APP_NAME).join(IMG_DIR)
}

fn get_overlay_dir() -> PathBuf {
if let Ok(ovl_dir) = std::env::var(ENV_OVL_DIR) {
fn get_state_dir() -> PathBuf {
if let Ok(ovl_dir) = std::env::var(ENV_STATE_DIR) {
return PathBuf::from(ovl_dir);
}
dirs::state_dir().unwrap().join(APP_NAME).join(OVL_DIR)
dirs::state_dir().unwrap().join(APP_NAME)
}

fn init_logging() {
Expand All @@ -82,7 +80,7 @@ fn reexec_with_sudo(
container_id: &str,
lead_pid: i32,
img_dir: &Path,
overlay_dir: &Path,
state_dir: &Path,
) -> Result<(), io::Error> {
let self_exe = read_link("/proc/self/exe")?;
let loglevel = std::env::var("LOGLEVEL").unwrap_or_default();
Expand All @@ -91,7 +89,7 @@ fn reexec_with_sudo(
format!("LOGLEVEL={}", loglevel),
format!("{}={}", ENV_LEAD_PID, lead_pid),
format!("{}={}", ENV_IMG_DIR, img_dir.display()),
format!("{}={}", ENV_OVL_DIR, overlay_dir.display()),
format!("{}={}", ENV_STATE_DIR, state_dir.display()),
format!("{}", self_exe.display()),
])
.arg(container_id)
Expand All @@ -108,10 +106,11 @@ fn prepare_shell_environment(
) -> Result<()> {
let detached_mount = match shared_mount.make_detached_mount() {
Err(err) => {
bail!("could not make detached mount: {err}");
bail!("could not make detached mount: {:?}", err);
}
Ok(m) => m,
};

if let Err(err) = enter_namespaces_as_root(lead_pid) {
bail!("cannot enter container namespaces: {err}");
}
Expand All @@ -123,7 +122,6 @@ fn prepare_shell_environment(

fn wait_for_child(child_pid: rustix::thread::Pid) -> Result<()> {
// TODO: propagate return code properly
log::debug!("parent pid = {}", process::id());
let _ = waitpid(Some(child_pid), WaitOptions::empty())
.context("waitpid failed")?;
Ok(())
Expand All @@ -146,15 +144,16 @@ fn main() -> Result<()> {
update_base_image(&img_dir, args.base_img)?;
}

let overlay_dir = get_overlay_dir();
let overlay_builder = OverlayBuilder::new(&overlay_dir, &img_dir)?;
let state_dir = get_state_dir();
let mnt_builder = SharedMountBuilder::new(&state_dir, &img_dir)?;

if !geteuid().is_root() {
log::debug!("re-executing with sudo...");
reexec_with_sudo(&args.container_id, lead_pid, &img_dir, &overlay_dir)?
reexec_with_sudo(&args.container_id, lead_pid, &img_dir, &state_dir)?
}

let shared_mount = SharedMount::new(&overlay_dir, overlay_builder)
let shared_mount = mnt_builder
.make_mount(lead_pid)
.context("could not init shared mount")?;

match unsafe { fork()? } {
Expand Down
52 changes: 48 additions & 4 deletions src/namespaces.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
use std::{
collections::HashSet, ffi::OsString, iter::IntoIterator, os::fd::AsFd,
collections::HashSet,
ffi::OsString,
iter::IntoIterator,
os::fd::{AsFd, AsRawFd},
path::Path,
process::exit,
};

use anyhow::{bail, Result};
use procfs::process::{Namespace, Process};
use rustix::{
fs::{open, Mode, OFlags},
process::{pidfd_open, Pid, PidfdFlags},
thread::{
move_into_thread_name_spaces, set_thread_gid, set_thread_uid, Gid,
Expand Down Expand Up @@ -55,9 +60,6 @@ pub fn enter_namespaces_as_root(lead_pid: i32) -> Result<()> {
};

let me = Process::myself()?;
let me_id = me.pid;
log::debug!("own pid is {}", me_id);

let my_ns = match namespace_set(&me) {
Err(err) => {
bail!("cannot inspect own namespaces: {err}");
Expand Down Expand Up @@ -91,3 +93,45 @@ pub fn enter_namespaces_as_root(lead_pid: i32) -> Result<()> {

Ok(())
}

// #define NSIO 0xb7
// #define NS_GET_OWNER_UID _IO(NSIO, 0x4)
//
// uid_t uid;
// ioctl(userns_fd, NS_GET_OWNER_UID, &uid);

use libc::uid_t;
use std::os::raw::c_ulong;

const NSIO: u32 = 0xb7;
const NS_GET_OWNER_UID: c_ulong = ioctl_sys::io!(NSIO, 0x4) as c_ulong;

pub fn get_userns_uid(pid: i32) -> Result<u32> {
let proc_pid = Path::new("/proc").join(pid.to_string());
if !proc_pid.exists() {
bail!("process does not exist");
}

let userns_file = proc_pid.join("ns/user");
match open(userns_file, OFlags::RDONLY, Mode::empty()) {
Err(err) => bail!("cannot open userns: {:?}", err),
Ok(userns_fd) => {
let mut uid: uid_t = 0;
let ret = unsafe {
ioctl_sys::ioctl(
userns_fd.as_raw_fd(),
NS_GET_OWNER_UID,
&mut uid as *mut uid_t,
)
};
if ret < 0 {
bail!(
"cannot get userns uid: {}",
std::io::Error::last_os_error()
)
} else {
Ok(uid as u32)
}
}
}
}
24 changes: 16 additions & 8 deletions src/overlay.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::fs::create_dir_all;
use std::fs::{create_dir_all, OpenOptions};
use std::path::Path;
use std::{os::fd::OwnedFd, path::PathBuf};

Expand All @@ -13,17 +13,20 @@ use rustix::mount::{
pub struct OverlayMount(OwnedFd);

impl OverlayMount {
pub fn new<L: Into<PathBuf>, D: AsRef<Path>>(
pub fn new<L: Into<PathBuf>, U: Into<PathBuf>, W: Into<PathBuf>>(
lower_dir: L,
upper_dir: D,
work_dir: D,
upper_dir: U,
work_dir: W,
) -> Result<Self> {
let lower_dir = lower_dir.into().canonicalize()?;
let upper_dir = upper_dir.into().canonicalize()?;
let work_dir = work_dir.into().canonicalize()?;

let fsfd = fsopen("overlay", FsOpenFlags::FSOPEN_CLOEXEC)?;
fsconfig_set_string(fsfd.as_fd(), "source", "user-data")?;
fsconfig_set_string(fsfd.as_fd(), "lowerdir", lower_dir)?;
fsconfig_set_string(fsfd.as_fd(), "upperdir", upper_dir.as_ref())?;
fsconfig_set_string(fsfd.as_fd(), "workdir", work_dir.as_ref())?;
fsconfig_set_string(fsfd.as_fd(), "upperdir", upper_dir)?;
fsconfig_set_string(fsfd.as_fd(), "workdir", work_dir)?;
fsconfig_create(fsfd.as_fd())?;

let fd_mnt = fsmount(
Expand Down Expand Up @@ -69,8 +72,8 @@ impl OverlayBuilder {
let lower_dir = lower_dir.as_ref().to_owned();

let merged_dir = base_dir.join("merged");
let upper_dir = base_dir.join("upper");
let work_dir = base_dir.join("work");
let upper_dir = base_dir.join("layers/upper");
let work_dir = base_dir.join("layers/work");
let pids_dir = base_dir.join("pids");

create_dir_all(&merged_dir)
Expand All @@ -79,6 +82,11 @@ impl OverlayBuilder {
.and(create_dir_all(&pids_dir))
.context("could not create overlay directories")?;

let _ = OpenOptions::new()
.append(true)
.create(true)
.open(base_dir.join("lock"))?;

Ok(OverlayBuilder {
lower_dir,
merged_dir,
Expand Down
Loading

0 comments on commit f19fee6

Please sign in to comment.