summaryrefslogtreecommitdiff
path: root/examples/userchroot.py
blob: 315a4373ad88a4237049e24e7e410dd7e43fa98a (plain)
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
61
62
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()