Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check for CAP_SYS_ADMIN instead of root #3588

Merged
merged 3 commits into from
Mar 12, 2025
Merged
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
25 changes: 18 additions & 7 deletions mkosi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,12 @@
from mkosi.kmod import gen_required_kernel_modules, loaded_modules, process_kernel_modules
from mkosi.log import ARG_DEBUG, complete_step, die, log_notice, log_step
from mkosi.manifest import Manifest
from mkosi.mounts import finalize_certificate_mounts, finalize_source_mounts, mount_overlay
from mkosi.mounts import (
finalize_certificate_mounts,
finalize_source_mounts,
finalize_volatile_tmpdir,
mount_overlay,
)
from mkosi.pager import page
from mkosi.partition import Partition, finalize_root, finalize_roothash
from mkosi.qemu import (
Expand All @@ -114,6 +119,7 @@
workdir,
)
from mkosi.sandbox import (
CAP_SYS_ADMIN,
CLONE_NEWNS,
MOUNT_ATTR_NODEV,
MOUNT_ATTR_NOEXEC,
Expand All @@ -123,6 +129,7 @@
MS_SLAVE,
__version__,
acquire_privileges,
have_effective_cap,
join_new_session_keyring,
mount,
mount_rbind,
Expand Down Expand Up @@ -499,7 +506,12 @@ def setup_build_overlay(context: Context, volatile: bool = False) -> Iterator[No
if volatile:
context.lowerdirs = [d]
context.upperdir = Path(
stack.enter_context(tempfile.TemporaryDirectory(prefix="volatile-overlay"))
stack.enter_context(
tempfile.TemporaryDirectory(
prefix="volatile-overlay.",
dir=finalize_volatile_tmpdir(),
)
)
)
os.chmod(context.upperdir, d.stat().st_mode)
else:
Expand Down Expand Up @@ -4888,12 +4900,11 @@ def run_build(
metadata_dir: Path,
package_dir: Optional[Path] = None,
) -> None:
if os.getuid() != 0:
if not have_effective_cap(CAP_SYS_ADMIN):
acquire_privileges()

unshare(CLONE_NEWNS)

if os.getuid() == 0:
unshare(CLONE_NEWNS)
else:
unshare(CLONE_NEWNS)
mount("", "/", "", MS_SLAVE | MS_REC, "")

# For extra safety when running as root, remount a bunch of directories read-only unless the output
Expand Down
32 changes: 29 additions & 3 deletions mkosi/mounts.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

from mkosi.config import BuildSourcesEphemeral, Config
from mkosi.log import die
from mkosi.sandbox import OverlayOperation
from mkosi.sandbox import OVERLAYFS_SUPER_MAGIC, OverlayOperation, statfs
from mkosi.util import PathString, flatten


Expand All @@ -30,6 +30,20 @@ def delete_whiteout_files(path: Path) -> None:
entry.unlink()


def finalize_volatile_tmpdir() -> Path:
if tempfile.tempdir and statfs(tempfile.tempdir) != OVERLAYFS_SUPER_MAGIC:
return Path(tempfile.tempdir)

for path in ("/var/tmp", "/tmp", "/dev/shm"):
if Path(path).exists() and statfs(path) != OVERLAYFS_SUPER_MAGIC:
return Path(path)

die(
"Cannot find temporary directory for volatile overlayfs upperdir",
hint="Either /var/tmp, /tmp or /dev/shm must exist and not be located on overlayfs",
)


@contextlib.contextmanager
def mount_overlay(
lowerdirs: Sequence[Path],
Expand All @@ -39,7 +53,14 @@ def mount_overlay(
) -> Iterator[Path]:
with contextlib.ExitStack() as stack:
if upperdir is None:
upperdir = Path(stack.enter_context(tempfile.TemporaryDirectory(prefix="volatile-overlay")))
upperdir = Path(
stack.enter_context(
tempfile.TemporaryDirectory(
prefix="volatile-overlay.",
dir=finalize_volatile_tmpdir(),
)
)
)
st = lowerdirs[-1].stat()
os.chmod(upperdir, st.st_mode)

Expand Down Expand Up @@ -85,7 +106,12 @@ def finalize_source_mounts(
upperdir.mkdir(mode=src.stat().st_mode, exist_ok=True)
else:
upperdir = Path(
stack.enter_context(tempfile.TemporaryDirectory(prefix="volatile-overlay."))
stack.enter_context(
tempfile.TemporaryDirectory(
prefix="volatile-overlay.",
dir=finalize_volatile_tmpdir(),
)
)
)
os.chmod(upperdir, src.stat().st_mode)

Expand Down
2 changes: 1 addition & 1 deletion mkosi/sandbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,7 @@ def become_user(uid: int, gid: int) -> None:


def acquire_privileges(*, become_root: bool = False) -> bool:
if os.getuid() == 0 or (not become_root and have_effective_cap(CAP_SYS_ADMIN)):
if have_effective_cap(CAP_SYS_ADMIN) and (os.getuid() == 0 or not become_root):
return False

if become_root:
Expand Down
8 changes: 5 additions & 3 deletions mkosi/tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ def maybe_make_nocow(path: Path) -> None:
raise


def tree_has_selinux_xattr(path: Path) -> bool:
return any("security.selinux" in os.listxattr(p) for p in (path, *path.rglob("*")))


def copy_tree(
src: Path,
dst: Path,
Expand All @@ -109,9 +113,7 @@ def copy_tree(
attrs += ",timestamps,ownership"

# Trying to copy selinux xattrs to overlayfs fails with "Operation not supported" in containers.
if statfs(os.fspath(dst.parent)) != OVERLAYFS_SUPER_MAGIC or "security.selinux" not in os.listxattr(
src
):
if statfs(os.fspath(dst.parent)) != OVERLAYFS_SUPER_MAGIC or not tree_has_selinux_xattr(src):
attrs += ",xattr"

def copy() -> None:
Expand Down