README.md
Rendering markdown...
#!/usr/bin/env python3
import ctypes
import ctypes.util
import os
import subprocess
import sys
CLONE_NEWUSER = 0x10000000
CLONE_NEWNET = 0x40000000
_lib = ctypes.util.find_library("c")
if not _lib:
sys.exit("libc not found")
libc = ctypes.CDLL(_lib, use_errno=True)
libc.unshare.argtypes = (ctypes.c_int,)
libc.unshare.restype = ctypes.c_int
def unshare(flags):
if libc.unshare(ctypes.c_int(flags)) != 0:
sys.exit(f"unshare: {os.strerror(ctypes.get_errno())}")
def write_proc(path, data):
with open(path, "w", encoding="ascii") as f:
f.write(data)
def parent_maps(pid, uid, gid):
p = f"/proc/{pid}"
write_proc(f"{p}/uid_map", f"0 {uid} 1\n")
write_proc(f"{p}/setgroups", "deny\n")
write_proc(f"{p}/gid_map", f"0 {gid} 1\n")
def repl():
while True:
try:
line = input("ns> ")
except EOFError:
print()
break
cmd = line.strip()
if not cmd:
continue
if cmd.lower() in ("exit", "quit"):
break
try:
subprocess.run(cmd, shell=True, executable="/bin/sh")
except OSError as e:
print(e, file=sys.stderr)
def main():
if not sys.platform.startswith("linux") or not hasattr(os, "fork"):
sys.exit("need Linux + fork")
uid, gid = os.getuid(), os.getgid()
r1, w1 = os.pipe()
r2, w2 = os.pipe()
try:
pid = os.fork()
except OSError as e:
for fd in (r1, w1, r2, w2):
try:
os.close(fd)
except OSError:
pass
sys.exit(f"fork: {e}")
if pid == 0:
os.close(r1)
os.close(w2)
unshare(CLONE_NEWUSER)
os.write(w1, b"!")
os.close(w1)
if os.read(r2, 1) != b"!":
os._exit(1)
os.close(r2)
unshare(CLONE_NEWNET)
subprocess.run(
["ip", "link", "set", "lo", "up"],
capture_output=True,
check=False,
)
subprocess.run(
["ip", "addr", "add", "127.0.0.1/8", "dev", "lo"],
capture_output=True,
check=False,
)
repl()
os._exit(0)
os.close(w1)
os.close(r2)
if os.read(r1, 1) != b"!":
os.close(w2)
os.waitpid(pid, 0)
sys.exit(1)
os.close(r1)
try:
parent_maps(pid, uid, gid)
except OSError as e:
os.close(w2)
os.waitpid(pid, 0)
sys.exit(f"maps: {e}")
os.write(w2, b"!")
os.close(w2)
os.waitpid(pid, 0)
if __name__ == "__main__":
main()