How to use Zoom

There is a package for the unfree video conference tool Zoom: NixOS Search

Zoom is famous for its many privacy violations. How can I use Zoom, probably in a sandboxed way, without compromising my privacy too much?

There is a blog entry that gives some recipes and recommendations: How to use Zoom with a Sandbox [Laxu Blog] Does anyone have experience with that? Did anyone already set up Zoom in a sandboxed way and is willing to share the config? It might be a nice addition to nixpkgs, I’d think.

2 Likes

Zoom is famous for its many privacy violations. How can I use Zoom, probably in a sandboxed way, without compromising my privacy too much?

Option 1 — pick some firejail/bubblewrap/nsjail recipe;

Option 2 — open the meeting link in Chromium (fresh profile I guess could be used), press «app doesn’t work» three times, it will show you «open in browser» link

5 Likes

Option 2 — open the meeting link in Chromium (fresh profile I guess could
be used), press «app doesn’t work» three times, it will show you «open in
browser» link

I had a meeting with a customer today using Zoom in a private browsing tab in Chrome without any issues.

2 Likes

I’d be interested to know how to make zoom run in nsjail.

I tried it but couldn’t get it to start.

% nsjail --env HOME=/fakehome --tmpfs /fakehome --bindmount_ro /nix/store "$(realpath "$(which zoom)")"                     [I][2021-01-22T04:02:51+0000] Mode: STANDALONE_ONCE
[I][2021-01-22T04:02:51+0000] Jail parameters: hostname:'NSJAIL', chroot:'', process:'/nix/store/5nl8s0y30lblvd6288i6gfay8krjr3kr-zoom-5.4.57862.0110/bin/zoom', bind:[::]:0, max_conns_per_ip:0, time_limit:0, personality:0, daemonize:false, clone_newnet:true, clone_newuser:true, clone_newns:true, clone_newpid:true, clone_newipc:true, clone_newuts:true, clone_newcgroup:true, keep_caps:false, disable_no_new_privs:false, max_cpus:0
[I][2021-01-22T04:02:51+0000] Mount: '/' flags:MS_RDONLY type:'tmpfs' options:'' dir:true
[I][2021-01-22T04:02:51+0000] Mount: '/fakehome' flags: type:'tmpfs' options:'size=4194304' dir:true
[I][2021-01-22T04:02:51+0000] Mount: '/nix/store' -> '/nix/store' flags:MS_RDONLY|MS_BIND|MS_REC|MS_PRIVATE type:'' options:'' dir:true
[I][2021-01-22T04:02:51+0000] Mount: '/proc' flags:MS_RDONLY type:'proc' options:'' dir:true
[I][2021-01-22T04:02:51+0000] Uid map: inside_uid:1000 outside_uid:1000 count:1 newuidmap:false
[I][2021-01-22T04:02:51+0000] Gid map: inside_gid:999 outside_gid:999 count:1 newgidmap:false
[I][2021-01-22T04:02:51+0000] Executing '/nix/store/5nl8s0y30lblvd6288i6gfay8krjr3kr-zoom-5.4.57862.0110/bin/zoom' for '[STANDALONE MODE]'
ZoomLauncher started.
Zoom path is: /nix/store/5nl8s0y30lblvd6288i6gfay8krjr3kr-zoom-5.4.57862.0110/opt/zoom
cmd line: 
CreateReportChannel bp_server_fd=4
$HOME = /fakehome 
Can't load/fakehome/.config/zoomus.conf
export SSB_HOME=/fakehome/.zoom; export QSG_INFO=1; export QT_AUTO_SCREEN_SCALE_FACTOR=1; export LD_LIBRARY_PATH=/nix/store/5nl8s0y30lblvd6288i6gfay8krjr3kr-zoom-5.4.57862.0110/opt/zoom; export BREAKPAD_CLIENT_FD=3; /nix/store/5nl8s0y30lblvd6288i6gfay8krjr3kr-zoom-5.4.57862.0110/opt/zoom/zoom "" 
fork() failed ,can't create child process.
ZoomLauncher exit.
[I][2021-01-22T04:02:51+0000] pid=14202 ([STANDALONE MODE]) exited with status: 0, (PIDs left: 0)

I don’t think it’s related to the ~/.config/zoomus.conf that it mentions.

On the other hand, running it in nsjail but under strace worked fine.

[Edit: removed erroneous command]

I think your command with strace runs it outside the jail.

Compare:

"$(realpath $(strace -f $(realpath $(which zoom))))"
"$(realpath strace) -f $(realpath $(which zoom))"

In the first case strace (and so Zoom) is launched at expansion time, before nsjail is executed

Ah you’re right, silly mistake. So nevermind that part.

Nonetheless I’m not sure what’s causing the zoom executable to fail.

Ah you’re right, silly mistake. So nevermind that part.

Nonetheless I’m not sure what’s causing the zoom executable to fail.

But now you might have a strace log of a failure? But I assume the problem is that Zoom tries to setup a non-privileged namespace which conflicts with nsjail that restricts whatever it can restrict, including nested namespaces.

nsjail \
--env HOME=/fakehome \
--env UID=1000 \
--env DISPLAY \
--env DBUS_SESSION_BUS_ADDRESS \
--env QT_DEBUG_PLUGINS=1 \
--tmpfs /fakehome \
--bindmount /tmp/.X11-unix/X0 \
--bindmount /run/user/1000/bus \
--bindmount "$HOME"/.Xauthority:/fakehome/.Xauthority \
--bindmount_ro /nix/store \
--bindmount_ro /bin/sh \
-- /nix/store/4v093kdjlqk3y6yz5cz2yq44398793kp-strace-5.10/bin/strace -f /nix/store/5nl8s0y30lblvd6288i6gfay8krjr3kr-zoom-5.4.57862.0110/bin/zoom

The section that seemed relevant:


[pid     7] connect(4, {sa_family=AF_UNIX, sun_path=@"/tmp/.X11-unix/X0"}, 20) = -1 ECONNREFUSED (Connection refused)
[pid     7] close(4)                    = 0
[pid     7] socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0) = 4
[pid     7] getsockopt(4, SOL_SOCKET, SO_SNDBUF, [212992], [4]) = 0
[pid     7] connect(4, {sa_family=AF_UNIX, sun_path="/tmp/.X11-unix/X0"}, 110) = 0
[pid     7] getpeername(4, {sa_family=AF_UNIX, sun_path="/tmp/.X11-unix/X0"}, [124->20]) = 0
[pid     7] uname({sysname="Linux", nodename="NSJAIL", ...}) = 0
[pid     7] access("/fakehome/.Xauthority", R_OK) = 0
[pid     7] openat(AT_FDCWD, "/fakehome/.Xauthority", O_RDONLY) = 5
[pid     7] fstat(5, {st_mode=S_IFREG|0600, st_size=49, ...}) = 0
[pid     7] close(5)                    = 0
[pid     7] brk(0x563e2cbd0000)         = 0x563e2cbd0000
[pid     7] fcntl(4, F_GETFL)           = 0x2 (flags O_RDWR)
[pid     7] fcntl(4, F_SETFL, O_RDWR|O_NONBLOCK) = 0
[pid     7] fcntl(4, F_SETFD, FD_CLOEXEC) = 0
[pid     7] poll([{fd=4, events=POLLIN|POLLOUT}], 1, -1) = 1 ([{fd=4, revents=POLLOUT}])
[pid     7] writev(4, [{iov_base="l\0\v\0\0\0\0\0\0\0\0\0", iov_len=12}, {iov_base="", iov_len=0}], 2) = 12
[pid     7] recvfrom(4, 0x563e2cb9e7c0, 8, 0, NULL, NULL) = -1 EAGAIN (Resource temporarily unavailable)
[pid     7] poll([{fd=4, events=POLLIN}], 1, -1) = 1 ([{fd=4, revents=POLLIN|POLLHUP}])
[pid     7] recvfrom(4, "\0\26\v\0\0\0\6\0", 8, 0, NULL, NULL) = 8
[pid     7] recvfrom(4, "No protocol specified\n\0\0", 24, 0, NULL, NULL) = 24
[pid     7] write(2, "No protocol specified\n", 22No protocol specified
) = 22
[pid     7] shutdown(4, SHUT_RDWR)      = 0
[pid     7] close(4)                    = 0
[pid     7] write(2, "qt.qpa.xcb: could not connect to"..., 44qt.qpa.xcb: could not connect to display :0
) = 44
[pid     7] write(2, "qt.qpa.plugin: Could not load th"..., 91qt.qpa.plugin: Could not load the Qt platform plugin "xcb" in "" even though it was found.
) = 91
[pid     7] write(2, "This application failed to start"..., 298This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem.

Available platform plugins are: eglfs, linuxfb, minimal, minimalegl, offscreen, vnc, wayland-egl, wayland, wayland-xcomposite-egl, wayland-xcomposite-glx, xcb.

) = 298
[pid     7] rt_sigprocmask(SIG_UNBLOCK, [ABRT], NULL, 8) = 0

Hmm, searching the error message says that your passing .Xauthority did not work. Did you try disabling access control by xhost + to localise the problem? I guess if you do not have a TCP X11 socket open, the risk is only for processes under other UIDs on the same machine and without sandboxing…

(Also, is it even relevant to Zoom? I guess getting xterm to work under a similar sandboxing could be a useful check)

You’re right it wasn’t a Zoom thing: eog doesn’t run like that either. Authorizing with xhost + makes eog run. I’d rather use a more fine-grained permission system like the XAUTHORITY one or similar but I wasn’t able to figure that out. https://github.com/google/nsjail/blob/88647a0819df87f10ff3bfe9f9f1bf961894af44/configs/home-documents-with-xorg-no-net.cfg was a good starting point.

Zoom dies with

[pid     7] execve("/zoom", ["/zoom", ""], 0x1dc2208 /* 13 vars */) = -1 ENOENT (No such file or directory)
[pid     7] stat("/zoom", 0x7fffbddf5750) = -1 ENOENT (No such file or directory)
[pid     7] stat("/zoom", 0x7fffbddf5730) = -1 ENOENT (No such file or directory)
[pid     7] write(2, "sh: /zoom: No such file or direc"..., 37sh: /zoom: No such file or directory
) = 37

[pid 7] execve(“/zoom”, [“/zoom”, “”], 0x1dc2208 /* 13 vars */) = -1 ENOENT (No such file or directory)

How it was run that it tries to start /zoom (absolute path, zoom executable in the root directory)?

% nsjail -C with-xorg.cfg -- "$(realpath "$(which strace)")" -f  "$(realpath "$(which zoom )")" 
# with-xorg.cfg
# based on https://github.com/google/nsjail/blob/88647a0819df87f10ff3bfe9f9f1bf961894af44/configs/home-documents-with-xorg-no-net.cfg


mode: ONCE
hostname: "NSJAIL"
cwd: "/user"

time_limit: 1000

envar: "DISPLAY"
envar: "HOME=/user"
envar: "TMP=/tmp"

rlimit_as: 2048
rlimit_cpu: 1000
rlimit_fsize: 1024
rlimit_nofile: 16


mount {
	src: "/bin"
	dst: "/bin"
	is_bind: true
}

mount {
	src: "/usr/bin"
	dst: "/usr/bin"
	is_bind: true
}


mount {
	dst: "/tmp"
	fstype: "tmpfs"
	rw: true
}

mount {
	dst: "/dev/shm"
	fstype: "tmpfs"
	rw: true
}

mount {
	dst: "/user"
	fstype: "tmpfs"
	rw: true
}


mount {
	src: "/tmp/.X11-unix"
	dst: "/tmp/.X11-unix"
	is_bind: true
	rw: true
}

mount {
	src: "/dev/null"
	dst: "/dev/null"
	is_bind: true
	rw: true
}

mount {
	src: "/dev/random"
	dst: "/dev/random"
	is_bind: true
	rw: true
}

mount {
	src: "/dev/urandom"
	dst: "/dev/urandom"
	is_bind: true
	rw: true
}

mount {
	src: "/etc/passwd"
	dst: "/etc/passwd"
	is_bind: true
}

mount {
	src: "/nix/store"
	dst: "/nix/store"
	is_bind: true
}

I wonder if setting PATH inside nsjail to include the zoom package directories is a good idea…

The zoom wrapper script is

#! /nix/store/dskh7v2h3ly3kdkfk3xmjlqql1zr0hnw-bash-4.4-p23/bin/bash -e
cd /nix/store/5nl8s0y30lblvd6288i6gfay8krjr3kr-zoom-5.4.57862.0110/opt/zoom
export PATH='/nix/store/h4xzk7g4bn6d4f50ga4lhj4pw9fc1n3i-coreutils-8.32/bin:/nix/store/wich8d491lank7xa6j79f34n33id6c0z-glib-2.66.4-dev/bin:/ni>
export LD_LIBRARY_PATH='/nix/store/g02m7hwaxlii7y3pq2ayav29jhbd0nb9-alsa-lib-1.2.4/lib:/nix/store/blib3gf93sn2cdgz9gakd54qqazslshq-atk-2.36.0/l>
exec "/nix/store/5nl8s0y30lblvd6288i6gfay8krjr3kr-zoom-5.4.57862.0110/opt/zoom/ZoomLauncher"  "$@"

so I tried

envar: "PATH=/nix/store/5nl8s0y30lblvd6288i6gfay8krjr3kr-zoom-5.4.57862.0110/opt/zoom:/nix/store/h4xzk7g4bn6d4f50ga4lhj4pw9fc1n3i-coreutils-8.32/bin:/nix/store/wich8d491lank7xa6j79f34n33id6c0z-glib-2.66.4-dev/bin:/nix/store/y3v622prx263g6z929wz05z082sviksw-pciutils-3.7.0/bin:/nix/store/pwaw3g7c95v08cxbd06w1fsskhnf2y88-procps-3.3.16/bin:/nix/store/rsk7xv2y03z9706d1cg965l0s70vxdsr-qttools-5.15.2-dev/bin:/nix/store/rg8c3d03nbf7lfyg50g2dgybnay7ghn9-util-linux-2.36.1-bin/bin"
envar: "LD_LIBRARY_PATH=/nix/store/g02m7hwaxlii7y3pq2ayav29jhbd0nb9-alsa-lib-1.2.4/lib:/nix/store/blib3gf93sn2cdgz9gakd54qqazslshq-atk-2.36.0/lib:/nix/store/dkydwpfwczcwzcg1466d1arf1ra3h3p0-cairo-1.16.0/lib:/nix/store/1cp11km3f68pwfry9fp2wx7pwl4kfnmr-dbus-1.12.20-lib/lib:/nix/store/7bar2wr0mak3y107q5jmjcdphbbbpnc7-libGL-1.3.2/lib:/nix/store/vfpv3gf49b1dkx040sg78bd9dc7ry9ra-fontconfig-2.13.92-lib/lib:/nix/store/j8fc8jjjqqh0sxnlvfbh6dbv6nclibb9-freetype-2.10.4/lib:/nix/store/3a81krh4hb5kkl51kfvsprvjr9y393rl-gtk+3-3.24.24/lib:/nix/store/y0h84dvs5x3igpsijiv80w9pq9zj0fkd-gdk-pixbuf-2.42.2/lib:/nix/store/xlgi1m9s42syv76l0kzqf9mcxxdvkb73-glib-2.66.4/lib:/nix/store/dmh16mpvq7fd21zflsv3bizlxy5wbfkc-pango-1.47.0/lib:/nix/store/jpf7r0zvv7a8xlk6330g0qcnimwxqc7l-gcc-10.2.0-lib/lib:/nix/store/8a4g2r9vqprdlgny6fwqx0410r3vdjr5-wayland-1.18.0/lib:/nix/store/wjz5m03hzggpzjvb5i4vxffrjlh3z66n-libX11-1.7.0/lib:/nix/store/zlqk1wz6lfgkjqrb85daxsfpnszqm38i-libxcb-1.14/lib:/nix/store/r5yckz9j5a4lssxxs2a3gjbxrdxijj84-libXcomposite-0.4.5/lib:/nix/store/c3y8m27dkr1vs608m11v3x67chwl9za6-libXext-1.3.4/lib:/nix/store/8gxlcq1cfs0hgzyrnhyfmfw5fpyy5qjm-libxkbcommon-1.0.3/lib:/nix/store/pvzavsnwns0xy54kmmf83na3vffd0p61-libXrender-0.9.10/lib:/nix/store/pvy77kjmkvcjfjhsva65cgi7v6hc1262-zlib-1.2.11/lib:/nix/store/d6xsm0dvi140dfyr117099xrfni6p5gm-xcb-util-image-0.4.0/lib:/nix/store/jy40cdxw8g9493i6i68xi8bpa2xgpkkr-xcb-util-keysyms-0.4.0/lib:/nix/store/iyl13mvabrj2gpfa5ch6gb5lsc7p289m-libXfixes-5.0.3/lib:/nix/store/ssjl4gaqwmnhn9qrbqrgd606pj1y73ny-libXtst-1.2.3/lib:/nix/store/8bsm1k11b153n3vcnj4kw3x87rmpxxk9-libpulseaudio-14.0/lib"

but no change.

ZoomLauncher is also a shell script? Are there any hints inside how it decides to exec /zoom ?

ZoomLauncher is binary.

And /zoom is exec’ed by it, right?

I’m not 100% sure.

nsjail -C with-xorg.cfg -- "$(realpath "$(which strace)")" -P /zoom -f "$(realpath "$(which zoom )")"

says

ZoomLauncher started.
Fail to readlink /proc/self/exe!
cmd line: 
CreateReportChannel bp_server_fd=4
/nix/store/4v093kdjlqk3y6yz5cz2yq44398793kp-strace-5.10/bin/strace: Process 5 attached
$HOME = /user 
Can't load/user/.config/zoomus.conf
export SSB_HOME=/user/.zoom; export QSG_INFO=1; export QT_AUTO_SCREEN_SCALE_FACTOR=1; export LD_LIBRARY_PATH=; export BREAKPAD_CLIENT_FD=3; /zoom "" 
/nix/store/4v093kdjlqk3y6yz5cz2yq44398793kp-strace-5.10/bin/strace: Process 6 attached
/nix/store/4v093kdjlqk3y6yz5cz2yq44398793kp-strace-5.10/bin/strace: Process 7 attached
[pid     7] execve("/zoom", ["/zoom", ""], 0x1128008 /* 13 vars */) = -1 ENOENT (No such file or directory)
[pid     7] stat("/zoom", 0x7fffecfa2410) = -1 ENOENT (No such file or directory)
[pid     7] stat("/zoom", 0x7fffecfa23f0) = -1 ENOENT (No such file or directory)
sh: /zoom: No such file or directory
[pid     7] +++ exited with 127 +++
[pid     6] --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=7, si_uid=1000, si_status=127, si_utime=0, si_stime=0} ---
[pid     6] +++ exited with 127 +++
[pid     5] --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=6, si_uid=1000, si_status=127, si_utime=0, si_stime=0} ---
success to create child process,status is 32512.
zoom exited normally.
Something went wrong while running zoom,exit code is 127.
ZoomLauncher exit.
[pid     5] +++ exited with 0 +++
+++ exited with 0 +++

In this issue the equivalent line ends with

/app/extra/zoom/zoom "" 

instead of just /zoom "".

I also notice in the Zoom nixpkgs expression postFixup

    for i in zopen zoom ZoomLauncher; do
      patchelf --set-interpreter "$(cat $NIX_CC/nix-support/dynamic-linker)" $out/opt/zoom/$i
    done

    # ZoomLauncher sets LD_LIBRARY_PATH before execing zoom
    wrapProgram $out/opt/zoom/zoom \
      --prefix LD_LIBRARY_PATH ":" ${libs}

    rm $out/bin/zoom
    # Zoom expects "zopen" executable (needed for web login) to be present in CWD. Or does it expect
    # everybody runs Zoom only after cd to Zoom package directory? Anyway, :facepalm:
    makeWrapper $out/opt/zoom/ZoomLauncher $out/bin/zoom \
      --run "cd $out/opt/zoom" \
      --prefix PATH : ${lib.makeBinPath [ coreutils glib.dev pciutils procps qttools.dev util-linux ]} \
      --prefix LD_LIBRARY_PATH ":" ${libs}

Those comments might be relevant.

1 Like

Sandboxing Zoom is of particular interest because Zoom scans /proc/*/{stat,cmdline} at startup.

strace -f zoom-us 2>&1 | egrep -o '/proc\S+'

Hm, now I wonder how just running it inside a chroot without /proc/ mounted would fail.

I think nsjail does the right thing about /proc by default, so that should not be the source of the problem…

If you want sandboxed zoom and do not care about reasonability that much (Zoom is unreasonable in itself, after all) you could read-only-bind-mount the probable executable to /zoom and see how it goes from there.