Quickemu: QEMU Bilateral VM access with port forwarding
[!WARNING] Using qemu options incorrectly seems to break disk images easily! Backup your data.
If you’re just getting started with qemu/kvm, quickemu is an easy way to get running almost instantly without any issue. But as soon as the need for more complex and creative setups goes, it’s utility seems to quickly diminish.
On the other hand, if you’re not afraid of the command line and shell scripts, it leaves us with a starting point to work with.
The problem
Launching two vms with quickemu, we’d like to be able to ssh in both directions, from one vm to the other.
Checking ip a shows both vms with the same ip address 10.0.2.15. This is the default address qemu provides with user mode networking. It’s not the greatest, but is rootless and requires zero configuration.
So what do we do? By default quickemu port forwards 22220 on the host to port 22 on the guest.
Trying to modify the config file to use a different port on the host failed to boot the vm (I think it actually broke the disk, can’t remember, but I have a backup!).
If we look inside the folder quickemu created for our vm there are several files, one of them being a shell script containing the qemu command run by quickemu
#!/usr/bin/env bash
/usr/bin/qemu-system-x86_64 \
-name alpine-v3.20,process=alpine-v3.20 \
-pidfile alpine-v3.20/alpine-v3.20.pid \
-enable-kvm \
-machine q35,smm=off,vmport=off \
-cpu host,kvm=on,topoext \
-smp cores=4,threads=2,sockets=1 \
-m 1G \
-device virtio-balloon \
-vga none \
-device virtio-vga-gl,xres=1050,yres=1080 \
-display sdl,gl=on \
-audiodev pa,id=audio0 \
-device intel-hda \
-device hda-duplex,audiodev=audio0 \
-rtc base=localtime,clock=host,driftfix=slew \
-device virtio-rng-pci,rng=rng0 \
-object rng-random,id=rng0,filename=/dev/urandom \
-device qemu-xhci,id=spicepass \
-chardev spicevmc,id=usbredirchardev1,name=usbredir \
-device usb-redir,chardev=usbredirchardev1,id=usbredirdev1 \
-chardev spicevmc,id=usbredirchardev2,name=usbredir \
-device usb-redir,chardev=usbredirchardev2,id=usbredirdev2 \
-chardev spicevmc,id=usbredirchardev3,name=usbredir \
-device usb-redir,chardev=usbredirchardev3,id=usbredirdev3 \
-device pci-ohci,id=smartpass \
-device usb-ccid \
-chardev spicevmc,id=ccid,name=smartcard \
-device ccid-card-passthru,chardev=ccid \
-device usb-ehci,id=input \
-device usb-kbd,bus=input.0 \
-k en-us \
-device usb-tablet,bus=input.0 \
-device virtio-net,netdev=nic \
-netdev user,hostname=alpine-v3.20,hostfwd=tcp::22220-:22,id=nic \
-global driver=cfi.pflash01,property=secure,value=on \
-drive if=pflash,format=raw,unit=0,file=/usr/share/edk2-ovmf/x64/OVMF_CODE.fd,readonly=on \
-drive if=pflash,format=raw,unit=1,file=alpine-v3.20/OVMF_VARS.fd \
-device virtio-blk-pci,drive=SystemDisk \
-drive id=SystemDisk,if=none,format=qcow2,file=alpine-v3.20/disk.qcow2 \
-fsdev local,id=fsdev0,path=/home/ryzen/Public,security_model=mapped-xattr \
-device virtio-9p-pci,fsdev=fsdev0,mount_tag=Public-ryzen \
-monitor unix:alpine-v3.20/alpine-v3.20-monitor.socket,server,nowait \
-serial unix:alpine-v3.20/alpine-v3.20-serial.socket,server,nowait 2>/dev/null
We can use this as a starting point to modify our vm slightly to allow for some flexibility.
Were intereted in the line
-netdev user,hostname=alpine-v3.20,hostfwd=tcp::22220-:22,id=nic \
For one vm, we can change the hostfwd value from 22220 to 22221. This will forward host port 22221 to 22 (ssh) on the guest vm.
Running quickemu itself will revert any changes we made to the script. In order to run the VM with the modification we made use bash vmFolder/vmScript.sh. This needs to be run outside of the vm directory itself because of the path specified the for the disk (otherwise it won’t load the disk). The second VM can be run as normal.
Now with both VMs running, they will have the same ip (10.0.2.15), but we can ssh from one vm to the other like so:
ssh -p 22221 uservm1@hostIpHere
and
ssh -p 22220 uservm2@hostIpHere
Visual
┌───┐
│VM1│◄───────────┐
└───┘ │
│ │
│ │ ssh VM2 > VM1
│ │
│ │
│ │
│ ┌────┐
└─────────►│HOST│◄─────────┐
└────┘ │
│ │
│ │
│ │
ssh VM1 > VM2 │ │
│ │
│ ┌───┐
└───────────►│VM2│
└───┘
We are sort of “tunneling” through the host to the guest VM with the port forwarding feature we used. We can also ssh into the host from the guest, and vice versa using this default networking setup provided by qemu. It’s not as versatile as bridge networking which I’ll talk about in another post, but its’ quick and easy to get things started.