summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xexamples/userchroot.py63
1 files changed, 63 insertions, 0 deletions
diff --git a/examples/userchroot.py b/examples/userchroot.py
new file mode 100755
index 0000000..315a437
--- /dev/null
+++ b/examples/userchroot.py
@@ -0,0 +1,63 @@
+#!/usr/bin/python3
+# Copyright 2024 Helmut Grohne <helmut@subdivi.de>
+# SPDX-License-Identifier: GPL-3
+
+"""Perform something like an unprivileged chroot via a user and mount
+namespace. This is similar to "unshare --map-auto --mount-proc --root=$1", but
+it also mounts /sys and /dev and performs a full pivot_root that cannot be
+escaped using chroot.
+"""
+
+import os
+import pathlib
+import sys
+
+if __file__.split("/")[-2:-1] == ["examples"]:
+ sys.path.insert(0, "/".join(__file__.split("/")[:-2]))
+
+import linuxnamespaces
+
+
+def open_tree_clone_rec(
+ path: linuxnamespaces.PathConvertible,
+) -> linuxnamespaces.AtLocation:
+ return linuxnamespaces.open_tree(
+ path,
+ linuxnamespaces.OpenTreeFlags.OPEN_TREE_CLONE
+ | linuxnamespaces.OpenTreeFlags.AT_RECURSIVE,
+ )
+
+
+def main() -> None:
+ chrootdir = pathlib.Path(sys.argv[1])
+ assert chrootdir.is_dir()
+
+ subuids = linuxnamespaces.IDAllocation.loadsubid("uid")
+ subgids = linuxnamespaces.IDAllocation.loadsubid("gid")
+ uidmap = subuids.allocatemap(65536, 0)
+ gidmap = subgids.allocatemap(65536, 0)
+
+ linuxnamespaces.unshare_user_idmap(
+ [uidmap],
+ [gidmap],
+ linuxnamespaces.CloneFlags.NEWUSER | linuxnamespaces.CloneFlags.NEWNS,
+ )
+
+ os.setregid(0, 0)
+ os.setreuid(0, 0)
+
+ linuxnamespaces.bind_mount(chrootdir, "/mnt", recursive=True)
+ linuxnamespaces.bind_mount(chrootdir / "proc", "/proc", recursive=True)
+ linuxnamespaces.bind_mount(chrootdir / "sys", "/sys", recursive=True)
+ linuxnamespaces.populate_dev("/", "/mnt", pidns=False)
+ os.chdir("/mnt")
+ linuxnamespaces.pivot_root(".", ".")
+ linuxnamespaces.umount(".", linuxnamespaces.UmountFlags.DETACH)
+ if len(sys.argv) > 2:
+ os.execvp(sys.argv[2], sys.argv[2:])
+ else:
+ os.execvp(os.environ["SHELL"], [os.environ["SHELL"]])
+
+
+if __name__ == "__main__":
+ main()