|
1 | 1 | use std::fs::File;
|
2 | 2 | use std::os::fd::AsFd;
|
| 3 | +use std::os::unix::fs::MetadataExt; |
3 | 4 |
|
4 | 5 | use anyhow::Result;
|
| 6 | +use rustix::fs::{Gid, Uid}; |
5 | 7 | use rustix::process::Pid;
|
6 |
| -use rustix::thread::{LinkNameSpaceType, UnshareFlags}; |
| 8 | +use rustix::thread::{CapabilitiesSecureBits, LinkNameSpaceType, UnshareFlags}; |
7 | 9 |
|
8 | 10 | pub struct MntNamespace {
|
9 | 11 | fd: File,
|
@@ -33,6 +35,35 @@ impl MntNamespace {
|
33 | 35 | self.fd.as_fd(),
|
34 | 36 | Some(LinkNameSpaceType::Mount),
|
35 | 37 | )?;
|
| 38 | + |
| 39 | + // If user namespace is used, we must act like the root user *inside* |
| 40 | + // namespace to be able to create files properly (otherwise EOVERFLOW |
| 41 | + // will be returned when creating file). |
| 42 | + // |
| 43 | + // Entering the user namespace turns out to be problematic. |
| 44 | + // The reason seems to be this line [1]: |
| 45 | + // which means `CAP_MKNOD` capability of the *init* namespace is needed. |
| 46 | + // However task's associated security context is all relative to its current |
| 47 | + // user namespace [2], so once you enter a user namespace there's no way of getting |
| 48 | + // back `CAP_MKNOD` of the init namespace anymore. |
| 49 | + // (Yes this means that even if CAP_MKNOD is granted to the container, you canot |
| 50 | + // create device nodes within it.) |
| 51 | + // |
| 52 | + // https://elixir.bootlin.com/linux/v6.11.1/source/fs/namei.c#L4073 |
| 53 | + // https://elixir.bootlin.com/linux/v6.11.1/source/include/linux/cred.h#L111 |
| 54 | + let metadata = std::fs::metadata("/")?; |
| 55 | + let uid = metadata.uid(); |
| 56 | + let gid = metadata.gid(); |
| 57 | + |
| 58 | + // By default `setuid` will drop capabilities when transitioning from root |
| 59 | + // to non-root user. This bit prevents it so our code still have superpower. |
| 60 | + rustix::thread::set_capabilities_secure_bits( |
| 61 | + CapabilitiesSecureBits::NO_SETUID_FIXUP, |
| 62 | + )?; |
| 63 | + |
| 64 | + rustix::thread::set_thread_uid(unsafe { Uid::from_raw(uid) })?; |
| 65 | + rustix::thread::set_thread_gid(unsafe { Gid::from_raw(gid) })?; |
| 66 | + |
36 | 67 | Ok(f())
|
37 | 68 | })
|
38 | 69 | .join()
|
|
0 commit comments