Skip to content

Commit

Permalink
Don't use overlayfs for /etc and /opt in sandbox
Browse files Browse the repository at this point in the history
Unprivileged overlayfs isn't available everywhere (see #3054). So
let's try to accomodate this a little by not using overlayfs for /etc
and /opt from the sandbox tree and instead mounting them read-only
into the sandbox. If required, scripts can still mount an overlayfs
onto these if needed, we just don't do it by default anymore.

This does mean we need to set up /etc with mountpoints and symlinks
beforehand in install_sandbox_trees(), but this shouldn't be a huge
problem.
  • Loading branch information
DaanDeMeyer authored and keszybz committed Sep 28, 2024
1 parent 87284b0 commit 8a44bbc
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 17 deletions.
49 changes: 39 additions & 10 deletions mkosi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1067,11 +1067,6 @@ def install_sandbox_trees(config: Config, dst: Path) -> None:
# Ensure /etc exists in the sandbox
(dst / "etc").mkdir(exist_ok=True)

if Path("/etc/passwd").exists():
shutil.copy("/etc/passwd", dst / "etc/passwd")
if Path("/etc/group").exists():
shutil.copy("/etc/passwd", dst / "etc/group")

if (p := config.tools() / "etc/crypto-policies").exists():
copy_tree(
p,
Expand All @@ -1081,12 +1076,46 @@ def install_sandbox_trees(config: Config, dst: Path) -> None:
sandbox=config.sandbox,
) # fmt: skip

if not config.sandbox_trees:
return
if config.sandbox_trees:
with complete_step("Copying in sandbox trees…"):
for tree in config.sandbox_trees:
install_tree(config, tree.source, dst, target=tree.target, preserve=False)

with complete_step("Copying in sandbox trees…"):
for tree in config.sandbox_trees:
install_tree(config, tree.source, dst, target=tree.target, preserve=False)
if Path("/etc/passwd").exists():
shutil.copy("/etc/passwd", dst / "etc/passwd")
if Path("/etc/group").exists():
shutil.copy("/etc/passwd", dst / "etc/group")

if not (dst / "etc/mtab").is_symlink():
(dst / "etc/mtab").symlink_to("../proc/self/mounts")

Path(dst / "etc/resolv.conf").unlink(missing_ok=True)
Path(dst / "etc/resolv.conf").touch()

Path(dst / "etc/static").unlink(missing_ok=True)
if (config.tools() / "etc/static").is_symlink():
(dst / "etc/static").symlink_to((config.tools() / "etc/static").readlink())

# Create various mountpoints in /etc as /etc from the sandbox tree is mounted read-only into the sandbox.

for d in (
"etc/pki",
"etc/ssl",
"etc/ca-certificates",
"var/lib/ca-certificates",
"etc/pacman.d/gnupg",
"etc/alternatives",
):
(dst / d).mkdir(parents=True, exist_ok=True)

for f in (
"etc/passwd",
"etc/group",
"etc/shadow",
"etc/gshadow",
"etc/ld.so.cache",
):
(dst / f).touch(exist_ok=True)


def install_package_directories(context: Context, directories: Sequence[Path]) -> None:
Expand Down
6 changes: 1 addition & 5 deletions mkosi/mounts.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,6 @@ def finalize_crypto_mounts(config: Config) -> list[PathString]:
Path("etc/pki"),
Path("etc/ssl"),
Path("etc/ca-certificates"),
Path("etc/static"),
Path("var/lib/ca-certificates"),
)
if (root / subdir).exists()
Expand All @@ -105,7 +104,4 @@ def finalize_crypto_mounts(config: Config) -> list[PathString]:
if (config.tools() / "etc/pacman.d/gnupg").exists():
mounts += [(config.tools() / "etc/pacman.d/gnupg", Path("/etc/pacman.d/gnupg"))]

return flatten(
("--symlink", src.readlink(), target) if src.is_symlink() else ("--ro-bind", src, target)
for src, target in sorted(set(mounts), key=lambda s: s[1])
)
return flatten(("--ro-bind", src, target) for src, target in sorted(set(mounts), key=lambda s: s[1]))
9 changes: 7 additions & 2 deletions mkosi/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -538,7 +538,6 @@ def sandbox_cmd(
"--dir", "/var/tmp",
"--dir", "/var/log",
"--unshare-ipc",
"--symlink", "../proc/self/mounts", "/etc/mtab",
] # fmt: skip

if devices:
Expand Down Expand Up @@ -566,7 +565,13 @@ def sandbox_cmd(
yield [*cmdline, "--bind", tmp, "/var/tmp", *options, "--"]
return

for d in ("etc", "opt", "srv", "media", "mnt", "var", "run", "tmp"):
for d in ("etc", "opt"):
if overlay and (overlay / d).exists():
cmdline += ["--ro-bind", overlay / d, Path("/") / d]
else:
cmdline += ["--dir", Path("/") / d]

for d in ("srv", "media", "mnt", "var", "run", "tmp"):
tmp = None
if d not in ("run", "tmp"):
with umask(~0o755):
Expand Down

0 comments on commit 8a44bbc

Please sign in to comment.