vsh - Vsock SHell

vsh is a remote shell that operates over vsock. Think of ssh, but replace the first ‘s’ with a ‘v’.

Why vsh?

SSH is the de-facto standard for getting a shell on a remote machine over a network. This makes a lot of sense for a machine under your desk or a VM in the cloud.

The most critical features SSH provides are:

  • Host authentication ensures you're talking to the right machine.
  • User authentication ensures only authorized users can sign in, and only as themselves.
  • Encryption prevents parties between the two machines snooping on your traffic.

vsh is focused on one use case only: connect to a VM on the same machine. This lets us simplify the requirements substantially.

  • Host authentication is provided by vsock. Unlike IP, a VM cannot spoof its vsock context id. vsock is machine-local, so there is no concern with MitM attacks.

  • User authentication is not required. vsh is used exclusively from host to guest, and the host is always considered more privileged.

  • Encryption isn't necessary since vsock cannot be routed, and the traffic will transit directly between host and guest memory. Without a host kernel exploit, a VM cannot observe traffic intended for another VM.

Without the need for any encryption, vsh is simple to set up, and easily integrated into a VM project. There's no need to have the guest OS configure a network, since vsock does not require guest-side configuration.

NOTE: Although the guest-side vshd does not authenticate users, it does limit which user a shell can be launched with. For example, Crostini's termina VM will only allow logging in as chronos. Developers must build a test image of termina to allow logging in as root.


vsh can:

  • Run both interactive shells (like bash), as well as one-shot commands.

  • Detect interactive vs non-interactive usage. This allows piping binary data through vsh without triggering terminal escape codes, as well as using vsh to pipe stdout and stderr output separately.

  • Forward exit status from the guest-side process.

  • Forward certain signals. Sending SIGHUP, SIGINT, SIGQUIT, or SIGTERM will forward the signal to the remote process. This is useful in non-interactive environments such as Tast to force the guest-side process to exit cleanly.

Usage examples

Launches a shell on the VM with vsock cid 3. This will work even with a manually launched crosvm instance, as long as the guest is running vshd. Check here for an example.

(device) # vsh --cid=3

Launches a shell on the VM named foo. Names are managed by vm_concierge, so this VM must have been started through the vm_concierge API. The $CROS_USER_ID_HASH variable is only set for crosh-spawned shells. The included one-liner can set it for you.

(device) # export CROS_USER_ID_HASH="$(cryptohome --action=status | \
                                       python3 -c 'import sys, json; \

(device) # vsh --vm_name=foo --owner_id="${CROS_USER_ID_HASH}"

Launches a root shell on the VM named foo. This may require that the VM is running a developer-built test image.

(device) # vsh --vm_name=foo --owner_id="${CROS_USER_ID_HASH}" --user=root

Runs lxc list in the termina VM with required environment variables set.

(device) # vsh --vm_name=termina \
               --owner_id="${CROS_USER_ID_HASH}" \
               -- \
               LXD_DIR=/mnt/stateful/lxd \
               LXD_CONF=/mnt/stateful/lxd_conf \
               lxc list

Writes a host file foo to /bar in the crostini container penguin.

(device) # cat foo | vsh --vm_name=termina \
                         --owner_id="${CROS_USER_ID_HASH}" \
                         --target_container=penguin \
                         -- \
                         sh -c "cat - > /bar"

Writes a file /foo from crostini container penguin to bar on the host.

(device) # vsh --vm_name=termina \
               --owner_id="${CROS_USER_ID_HASH}" \
               --target_container=penguin \
               -- \
               cat /foo > bar

Setup a crosvm instance for vsh

First, the guest needs the vshd executable. termina dlc is one way to get it. vshd is contained in vm_tools.img

(device) # dlcservice_util --install --id=termina-dlc
# Prints the root mount of termina-dlc, which should be `/run/imageloader/termina-dlc/package/root`
(device) # dlcservice_util --list

Launch crosvm with --cid=3 and vm_tools.img.

(device) # crosvm run --cid=3 --disk /run/imageloader/termina-dlc/package/root/vm_tools.img ...

Then, run the following commands in guest to launch vshd.

(guest) # mount -t proc proc /proc
(guest) # mount -t sysfs sys /sys
(guest) # mount -t tmpfs tmp /tmp
(guest) # mount -t tmpfs run /run
(guest) # mkdir /dev/pts
(guest) # mount -t devpts devpts /dev/pts -o mode=0620,ptmxmode=666
(guest) # mkdir /tmp/vm_tools
(guest) # mount /dev/vda /tmp/vm_tools # or vdx depending on disks available
(guest) # /tmp/vm_tools/bin/vshd