1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
use std::fs::File;
use std::io::{self, Write};
use std::os::unix::io::AsRawFd;

pub fn unshare_user() -> Result<(), io::Error> {
    let uid = unsafe { libc::geteuid() };
    let gid = unsafe { libc::getegid() };

    unsafe { errno!(libc::unshare(libc::CLONE_NEWUSER))? };

    let mut f = File::create("/proc/self/uid_map")?;
    let s = format!("0 {uid} 1\n");
    f.write_all(s.as_bytes())?;

    let mut f = File::create("/proc/self/setgroups")?;
    f.write_all(b"deny\n")?;

    let mut f = File::create("/proc/self/gid_map")?;
    let s = format!("0 {gid} 1\n");
    f.write_all(s.as_bytes())?;

    Ok(())
}

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Namespace {
    pid: i32,
    tid: i64,
}

impl Namespace {
    pub fn current() -> Result<Self, io::Error> {
        unsafe {
            let pid = errno!(libc::getpid())?;
            let tid = errno!(libc::syscall(libc::SYS_gettid))?;
            Ok(Self { pid, tid })
        }
    }

    pub fn unshare() -> Result<Self, io::Error> {
        unsafe {
            errno!(libc::unshare(libc::CLONE_NEWNET | libc::CLONE_NEWUTS))?;
        }
        Self::current()
    }

    pub fn enter(&self) -> Result<(), io::Error> {
        let fd = File::open(self.to_string())?;
        unsafe {
            errno!(libc::setns(fd.as_raw_fd(), libc::CLONE_NEWNET))?;
        }
        Ok(())
    }
}

impl std::fmt::Display for Namespace {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "/proc/{}/task/{}/ns/net", self.pid, self.tid)
    }
}