Shared Clipboard fixed

Hi, I wrote a script that solves the problem (no changes to the code, no need to compile binaries, etc.). My script start custom service in the system root, and your clipboard will work (user mode also):

I’m posting this because I want others who have encountered the same problem to get a quick, effective solution without having to run third-party code on the host machine.

I ask everyone, including @arraybolt3 @Patrick to take note of my solution (from an OS security perspective)

!!!

  1. Update your VirtualBox

  2. login to system like sysmaint

  3. run script

  4. set bidirectional

  5. reboot system

(I understand how this looks from the outside, so I suggest and ask everyone to familiarize themselves with this script (don’t trust people on the internet, especially on a forum about an anonymous OS; there’s nothing criminal here, just automation so you don’t have to type things manually)

After you run it, don’t forget to set it to Bidirectional, and the buffer will work the same way as a root/user

Let me know if you run into any issues :upside_down_face:

download file:
https[:]//filebin[.]net/562pahe0lulimhj7/setup.sh

or copy it:

#!/bin/bash
set -o nounset
set -o pipefail

UNIT_NAME='vbox-wayland-clipboard.service'
UNIT_PATH='/usr/lib/systemd/user/vbox-wayland-clipboard.service'
LAUNCH_PATH='/usr/libexec/vm-config-dist/vbox-wayland-clipboard-autostart'
DESK_PATH='/etc/xdg/autostart/vbox-wayland-clipboard.desktop'

red() { printf '\033[31m%s\033[0m\n' "$*"; }
grn() { printf '\033[32m%s\033[0m\n' "$*"; }
ylw() { printf '\033[33m%s\033[0m\n' "$*"; }

if [ "$(id -u)" -ne 0 ]; then
   red "ERROR: run as root in the 'sysmaint' boot mode:  sudo bash $0"
   exit 1
fi

search_dirs=(/etc/systemd/user /run/systemd/user /usr/lib/systemd/user \
             /usr/local/lib/systemd/user /root/.config/systemd/user)
for h in /home/*; do
   [ -d "$h/.config/systemd/user" ] && search_dirs+=("$h/.config/systemd/user")
done

echo "=== INSTALL ==="

echo "[1/6] clearing masks (/dev/null symlinks) and empty unit files ..."
for d in "${search_dirs[@]}"; do
   f="$d/$UNIT_NAME"
   if [ -L "$f" ] || { [ -e "$f" ] && [ ! -s "$f" ]; }; then
      echo "  removing $f"
      rm -f "$f"
   fi
done
systemctl --global unmask "$UNIT_NAME" 2>/dev/null || true

echo "[2/6] writing $UNIT_PATH ..."
install -d /usr/lib/systemd/user
cat > "$UNIT_PATH" <<'UNIT'
[Unit]
Description=VirtualBox shared clipboard for Wayland (opt-in, default-off)
ConditionVirtualization=oracle
ConditionEnvironment=WAYLAND_DISPLAY
StartLimitIntervalSec=10
StartLimitBurst=3

[Service]
Type=simple
ExecStart=/usr/bin/VBoxClient --foreground --wayland
Restart=on-failure
RestartSec=2s

[Install]
WantedBy=default.target
UNIT

echo "[3/6] writing $LAUNCH_PATH ..."
install -d /usr/libexec/vm-config-dist
cat > "$LAUNCH_PATH" <<'LAUNCH'
#!/bin/bash
set -o nounset
set -o pipefail
PATH=/usr/bin:/bin
unit='vbox-wayland-clipboard.service'
state="$(systemctl --user is-enabled "$unit" 2>/dev/null || true)"
case "$state" in
   enabled|enabled-runtime) ;;
   *) exit 0 ;;
esac
counter=0
while [ "$counter" -lt 50 ]; do
   systemctl --user show-environment 2>/dev/null | grep -q '^WAYLAND_DISPLAY=' && break
   counter="$(( counter + 1 ))"
   sleep 0.2
done
systemctl --user reset-failed "$unit" 2>/dev/null || true
exec systemctl --user start "$unit"
LAUNCH
chmod 755 "$LAUNCH_PATH"

echo "[4/6] writing $DESK_PATH ..."
cat > "$DESK_PATH" <<'DESK'
[Desktop Entry]
Type=Application
Name=VirtualBox shared clipboard for Wayland (opt-in)
Exec=/usr/libexec/vm-config-dist/vbox-wayland-clipboard-autostart
StartupNotify=false
NoDisplay=true
NotShowIn=QUBES;
DESK

echo "[5/6] Guest Additions device (/dev/vboxuser) on every boot ..."
systemctl enable --now virtualbox-guest-utils.service 2>/dev/null \
   || ylw "  virtualbox-guest-utils.service not found; loading module only."
echo vboxguest > /etc/modules-load.d/vboxguest.conf
modprobe vboxguest 2>/dev/null || true

echo "[6/6] enabling for ALL users ..."
systemctl daemon-reload
systemctl --global enable "$UNIT_NAME"

echo
echo "=== CHECKUP ==="
fail=0; warn=0

chk_file() {
   local p="$1" min="$2" sz
   if [ -L "$p" ]; then red "  FAIL  $p is a symlink (masked?)"; fail=$((fail+1)); return; fi
   if [ ! -f "$p" ]; then red "  FAIL  $p missing"; fail=$((fail+1)); return; fi
   sz="$(wc -c < "$p")"
   if [ "$sz" -lt "$min" ]; then red "  FAIL  $p is $sz bytes (empty/too small)"; fail=$((fail+1)); return; fi
   grn "  OK    $p ($sz bytes)"
}
chk_file "$UNIT_PATH"   100
chk_file "$LAUNCH_PATH" 100
chk_file "$DESK_PATH"    50

leftover=0
for d in "${search_dirs[@]}"; do
   f="$d/$UNIT_NAME"
   [ "$f" = "$UNIT_PATH" ] && continue
   if [ -L "$f" ] || { [ -e "$f" ] && [ ! -s "$f" ]; }; then
      red "  FAIL  leftover mask/empty: $f"; leftover=$((leftover+1)); fail=$((fail+1))
   fi
done
[ "$leftover" -eq 0 ] && grn "  OK    no leftover masks / empty unit files"

if [ -x "$LAUNCH_PATH" ]; then grn "  OK    launcher is executable"; else red "  FAIL  launcher not executable"; fail=$((fail+1)); fi

if [ -L "/etc/systemd/user/default.target.wants/$UNIT_NAME" ]; then
   grn "  OK    enabled for all users (default.target.wants symlink present)"
else
   ylw "  WARN  global enable symlink missing"; warn=$((warn+1))
fi

if command -v VBoxClient >/dev/null 2>&1; then
   grn "  OK    VBoxClient present ($(VBoxClient --version 2>/dev/null | head -n1))"
else
   red "  FAIL  /usr/bin/VBoxClient not found (Guest Additions missing)"; fail=$((fail+1))
fi

if lsmod 2>/dev/null | grep -q '^vboxguest'; then grn "  OK    vboxguest module loaded"; else ylw "  WARN  vboxguest module not loaded"; warn=$((warn+1)); fi
if [ -e /dev/vboxuser ]; then grn "  OK    /dev/vboxuser present"; else red "  FAIL  /dev/vboxuser missing (VBoxClient will fail VbglR3InitUser)"; fail=$((fail+1)); fi

echo
if [ "$fail" -eq 0 ]; then grn "RESULT: install OK ($warn warning(s))."; else red "RESULT: $fail problem(s), $warn warning(s) — fix the FAIL lines above."; fi
echo
echo "NEXT:"
echo "  1) On the HOST (normal user, VM powered off):"
echo "       VBoxManage modifyvm \"<vm-name-or-uuid>\" --clipboard-mode bidirectional"
echo "     and launch the VM as your NORMAL user, not root."
echo "  2) Reboot Whonix into the normal 'user' mode."
echo "  3) As 'user' (no sudo) verify:"
echo "       systemctl --user is-enabled $UNIT_NAME    # => enabled  (NOT masked)"
echo "       systemctl --user status    $UNIT_NAME     # => active (running)"

[ "$fail" -eq 0 ] && exit 0 || exit 1

(Note: I have not reviewed the script in full detail, my comment should not be taken by forum readers as an indication that I am sure the script is non-malicious or functional.)

I assume one must update Guest Additions inside the VM to a version matching the latest VirtualBox upstream for this to work? I.e., the Guest Additions packages in Debian Trixie’s main repository are not enough?

2 Likes

Hi, everything works fine with the standard Debian Trixie packages you don’t need other repos just update VirtualBox itself to the latest version 7.2.8 or higher

My script is full of tests(checking like is everything ok)
but here’s the basic mechanism:
New service (VboxClient --wayland is now fixed):

ExecStart=/usr/bin/VBoxClient --foreground --wayland

module:

echo vboxguest > /etc/modules-load.d/vboxguest.conf

so yeah just try it
I think if you integrate VboxClient --wayland
it’ll save all the system’s users a lot of headaches.

1 Like

I can confirm that:

  • When using a host system running VirtualBox 7.2.10,
  • with a fully updated Kicksecure 18 system using the version of Guest Additions already present in Trixie,
  • if I boot a Kicksecure 18 VM in PERSISTENT Mode | USER Session,
  • then click (on the host) Devices > Shared Clipboard > Bidirectional,
  • then open a terminal in the VM and run VBoxClient --wayland,
  • that I am now able to copy and paste arbitrary text between the host and the guest. Shared clipboard is fixed. I did not need to change anything in /etc/modules-load.d. EDIT: This didn’t end up being quite true upon further testing, some clipboard sharing is working but other clipboard sharing is still very much broken.

I grepped for VBoxClient in /etc and discovered that VirtualBox Guest Additions is supposed to do this automatically, in /etc/X11/Xsession.d/98vboxadd-xclient. There is a block in that file that reads:

vbox_wl_check=$(/usr/bin/vboxwl --check 2> /dev/null)
if test "$vbox_wl_check" = "WL"; then
    /usr/bin/VBoxClient --wayland || true
else
    ...
fi

This however isn’t kicking in for a couple of reasons:

  • One, it’s in /etc/X11/Xsession.d. labwc does not execute scripts in this directory. It needs to be added to /usr/share/desktop-config-dist/labwc/autostart or some similar autostart mechanism to be run at all. (Edit: This was reported to Debian already: I Challenge Thee Seems that VirtualBox’s Guest Additions ISO solves this by using /etc/xdg/autostart, which seems like a good solution to me.)
  • Two, even if you run it, it silently fails, because /usr/bin/vboxwl does not exist. I would guess that the VirtualBox Guest Additions package in Debian is building it, but isn’t installing it, since that’s was the case with /usr/bin/VBoxDRMClient some time back (it’s required for dynamic screen resize to work).

Ideally, we should get that mechanism working rather than making our own service or similar. However, as an immediate workaround, we can document in the Wiki that users can run VBoxClient --wayland in a terminal to get shared clipboard working (assuming that they are running a new enough VirtualBox and have enabled clipboard sharing in VirtualBox).


EDIT: So unfortunately this isn’t the total fix I hoped. I am able to copy and paste between the host and guest, so long as I am copying from and pasting to the terminal window in the VM that I used to launch VBoxClient. I am not able to copy and paste between a separately launched FeatherPad window and the host.

If I install VirtualBox 7.2.10’s Guest Additions in the VM using the Guest Additions ISO provided by the host system, clipboard sharing mostly works. It sometimes takes some “shaking” to get it working (i.e. pasting multiple times, or copying from the host before trying to copy from the guest), but it gets the job done. However, one still has to run VBoxClient --wayland to get things to work, since vboxwl thinks that an X11 session is being used instead of Wayland and reports that to the launcher script. So… yeah. There is finally a workaround, but it is still a mess.

EDIT 2: And the reason vboxwl is reporting X11 is because…

    else if (enmType == VBGHDISPLAYSERVERTYPE_XWAYLAND)
    {
        /* In case of XWayland, X11 version of VBoxClient still can
         * work, however with some DEs, such as Plasma on Wayland,
         * this will no longer work. Detect such DEs here. */

        /* Try to detect Plasma. */
        const char *pcszDesktopSession = RTEnvGet(VBGH_ENV_DESKTOP_SESSION);
        if (   RT_VALID_PTR(pcszDesktopSession)
            && (   RTStrIStr(pcszDesktopSession, "plasmawayland")
                || RTStrIStr(pcszDesktopSession, "plasma")))
            fWayland = true;
    }

So apparently if XWayland is present, only KDE Plasma gets treated as Wayland anyway. We need this same kind of treatment though. Sigh…

EDIT 3: Ended up going down a very deep rabbit hole trying to debug this and figure out who to report bugs to. The tl;dr: is that Wayland and even wlroots is fragmented enough that it’s unlikely we will be able to get Oracle to help with fixing the bugs here. labwc has a very specific set of behaviors that combine to make clipboard sharing a total nightmare even with VirtualBox 7.2.10, and VirtualBox also has some behaviors related to detecting what clipboard sharing method to use that are problematic when dealing with the specific combination of LXQt and labwc. Maybe everything could be gotten to work out of the box if both labwc and VirtualBox were changed, but that would require someone with the time to make and file the patches.

There is likely a way to get things to work with some glue code in Kicksecure. I’ll most likely leave an update if we start implementing this.