ch-runner: run CH in bwrap

Albeit rather relaxed
This commit is contained in:
Else Someone 2026-02-23 06:35:32 +02:00
parent a3ed9cb4ea
commit 04befb6328
3 changed files with 87 additions and 52 deletions

View file

@ -30,6 +30,8 @@ stdenv.mkDerivation {
rustc rustc
]; ];
buildInputs = [ ch-proxy ]; buildInputs = [ ch-proxy ];
meta.mainProgram = "taps";
} }
# { lib, rustPlatform }: # { lib, rustPlatform }:
# #

View file

@ -278,6 +278,7 @@ in
(lib.getBin package) (lib.getBin package)
(lib.getBin pkgs.virtiofsd) (lib.getBin pkgs.virtiofsd)
(lib.getBin pkgs.bubblewrap) (lib.getBin pkgs.bubblewrap)
(lib.getBin pkgs.strace)
uvmsPkgs.taps uvmsPkgs.taps
]; ];
@ -403,7 +404,6 @@ in
**kwargs, **kwargs,
) )
@contextmanager
def bwrap( def bwrap(
self, self,
*bwrap_args, *bwrap_args,
@ -420,20 +420,26 @@ in
unshare_uts=None, unshare_uts=None,
unshare_cgroup_try=True, unshare_cgroup_try=True,
bind=(), bind=(),
dev_bind=("/dev/kvm", "/dev/vfio"), dev_bind=(),
dev_bind_implicit=("/dev/kvm", "/dev/vfio"),
dev="/dev", dev="/dev",
proc="/proc", proc="/proc",
ro_bind=( ro_bind_implicit=(
"/etc", "/etc",
"/sys", "/sys",
"/proc/sys", "/proc/sys",
"/dev/null", "/dev/null",
"/proc/kallsyms", "/proc/kallsyms",
*CLOSURE), *CLOSURE),
ro_bind_extra=(), ro_bind=(),
remount_ro=("/proc/fs", "/proc/irq"), remount_ro=("/proc/fs", "/proc/irq"),
tmpfs=("/dev/shm", "/tmp", "/var/tmp", "/proc/fs", "/proc/irq"), tmpfs_implicit=(
tmpfs_extra=(), "/dev/shm",
"/tmp",
"/var/tmp",
"/proc/fs",
"/proc/irq"),
tmpfs=(),
pass_fds=(2,), pass_fds=(2,),
**popen_kwargs): **popen_kwargs):
@ -455,23 +461,29 @@ in
print_arg("--unshare-pid") print_arg("--unshare-pid")
if unshare_net: if unshare_net:
print_arg("--unshare-net") print_arg("--unshare-net")
elif unshare_net is False:
print_arg("--share-net")
if unshare_uts: if unshare_uts:
print_arg("--unshare-uts") print_arg("--unshare-uts")
if unshare_cgroup_try: if unshare_cgroup_try:
print_arg("--unshare-cgroup-try") print_arg("--unshare-cgroup-try")
if die_with_parent: if die_with_parent:
print_arg("--die-with-parent") print_arg("--die-with-parent")
if dev:
print_arg("--dev", dev)
if proc:
print_arg("--proc", proc)
for p in bind: for p in bind:
p1, p2 = (p, p) if isinstance(p, str) else p p1, p2 = (p, p) if isinstance(p, str) else p
print_arg("--bind", p1, p2) print_arg("--bind", p1, p2)
for p in (*ro_bind, *ro_bind_extra): for p in (*ro_bind, *ro_bind_implicit):
p1, p2 = (p, p) if isinstance(p, str) else p p1, p2 = (p, p) if isinstance(p, str) else p
print_arg("--ro-bind", p1, p2) print_arg("--ro-bind", p1, p2)
for p in dev_bind: for p in (*dev_bind, *dev_bind_implicit):
p1, p2 = (p, p) if isinstance(p, str) else p p1, p2 = (p, p) if isinstance(p, str) else p
print_arg("--dev-bind", p1, p2) print_arg("--dev-bind", p1, p2)
for p in (*tmpfs, *tmpfs_extra): for p in (*tmpfs, *tmpfs_implicit):
print_arg("--tmpfs", p) print_arg("--tmpfs", p)
# Hunch: order might matter... # Hunch: order might matter...
for p in remount_ro: for p in remount_ro:
@ -487,44 +499,48 @@ in
pass_fds=(*pass_fds, remote.fileno()), pass_fds=(*pass_fds, remote.fileno()),
) )
with proc as p: return proc
try:
yield p
finally:
try:
p.poll()
except: # noqa: E722
pass
if p.returncode is None:
p.terminate()
p.wait()
@contextmanager @contextmanager
def run_ch(self): def run_ch(self):
args = [
SOCKETBINDER_PATH,
"-B",
self.prefix + "/vmm.sock",
CH_PATH,
"--api-socket",
"fd=0",
]
p = self.popen(
*args,
shell=False,
stdin=subprocess.DEVNULL,
stdout=subprocess.DEVNULL,
pass_fds=())
try:
p.wait(0.125)
needs_cleanup = False
except subprocess.TimeoutExpired:
needs_cleanup = True
if not os.path.exists(self.prefix + "/vmm.sock"):
raise RuntimeError(f"{self.prefix}/vmm.sock should exist by now")
if p.returncode is not None:
raise RuntimeError("CH exited early")
try: try:
# s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM, 0)
# s.set_inheritable(True)
# s.setblocking(True)
# s.bind(self.prefix + "/vmm.sock")
args = [
SOCKETBINDER_PATH,
"-B",
self.prefix + "/vmm.sock",
# "${lib.getExe pkgs.strace}", # noqa: E501
# "-Z",
# "-ff",
CH_PATH,
"--api-socket",
"fd=0",
# f"fd={s.fileno()}"
]
p = self.bwrap(
*args,
bind=[self.prefix],
# Probably just need the path to vmlinux
ro_bind=("/nix/store",), # A give up
unshare_net=False,
shell=False,
stderr=None,
# pass_fds=(s.fileno(),)
)
# s.close()
try:
p.wait(0.125)
needs_cleanup = False
except subprocess.TimeoutExpired:
needs_cleanup = True
if not os.path.exists(self.prefix + "/vmm.sock"):
raise RuntimeError(
f"{self.prefix}/vmm.sock should exist by now")
if p.returncode is not None:
raise RuntimeError("CH exited early")
yield p yield p
finally: finally:
try: try:
@ -598,7 +614,7 @@ in
kwargs = { kwargs = {
# If bwrap(): # If bwrap():
# "bind": [], # "bind": [],
# ("ro_bind_extra" if ro else "bind"): # ("ro_bind" if ro else "bind"):
# [*subdirs] # [*subdirs]
# if subdirs is not None # if subdirs is not None
# else [root_dir], # else [root_dir],

View file

@ -1,13 +1,30 @@
with import <nixpkgs> { }; with import <nixpkgs> { };
let
uvmPkgs = callPackage ./pkgs { };
in
mkShell.override { stdenv = stdenvNoCC; } { mkShell.override { stdenv = stdenvNoCC; } {
packages = map lib.getBin [ inputsFrom = with uvmPkgs; [
cloud-hypervisor ch-proxy
virtiofsd taps
crosvm # virtio-gpu writeErofsLayers
npins request-usb
] ++ [
man-pages
linux-manual
]; ];
packages =
map lib.getBin [
cloud-hypervisor
virtiofsd
crosvm # virtio-gpu
npins
strace
python3
execline
s6
wayland-proxy-virtwl
uvmPkgs.taps
]
++ [
man-pages
linux-manual
];
} }