Alpine Linux on Qemu

This post demonstrates how to setup a fresh installation of Alpine Linux on a virtual machine on macOS using Qemu. Copy-and-pastable commands are used as much as possible to avoid GUI setup.

Codesign Qemu

In order to run Qemu with Apple’s hypervisor framework on macOS, Qemu must be signed. In this post, we will use qemu-system-x86_64. On an M1 mac, qemu-system-aarch64 should be used.

Create an app.entitlements file with the following contents:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>com.apple.security.hypervisor</key>
	<true/>
</dict>
</plist>

Then sign Qemu using the app.entitlements as follows:

codesign -s - --entitlements app.entitlements --force `which qemu-system-x86_64`

For more resources on this topic, see [1] and [2].

Download Alpine

ARCH="x86_64"
VER="3.14"
URL="https://dl-cdn.alpinelinux.org/alpine/v${VER}/releases/${ARCH}/alpine-virt-${VER}.0-${ARCH}.iso"
ISO=`basename $URL`

if [ ! -f $ISO ]; then
    curl -O $URL
    curl -O ${URL}.sha512
    SHA512=`cat *sha512 | awk '{print $1}'`
    SCHECK=`openssl dgst -sha512 alpine*.iso | awk '{print $2}'`
    if [ "${SHA512}" != "${SCHECK}" ]; then echo "[FATAL] Invalid hash: ${SCHECK}" >&2 && rm -f ${ISO}*; fi
fi

Install Alpine

NAME=alpine
N_HD_GB=64
N_RAM_GB=4
N_CPU=2
qemu-img create -f vmdk "${NAME}".vmdk ${N_HD_GB}G
qemu-system-x86_64 -accel hvf -cpu max,enforce -m ${N_RAM_GB}G -smp $N_CPU -hda $NAME.vmdk -cdrom $NAME*.iso -nographic

Wait for the localhost login: prompt. Log in with root. Then paste in:

setup-keymap us us
setup-hostname "desktop-`hexdump -n4 -e'"%x"' /dev/urandom`"
setup-interfaces -a
rc-service networking start
rc-update add networking boot

setup-timezone -z America/New_York
setup-sshd -c none

echo -e 'https://alpine.global.ssl.fastly.net/alpine/v3.14/main\nhttps://alpine.global.ssl.fastly.net/alpine/v3.14/community\n' > /etc/apk/repositories

apk add adwaita-icon-theme chrony clang elogind emacs firefox g++ git htop ip6tables iptables lightdm-gtk-greeter make polkit-elogind setxkbmap sudo tmux vim xdg-utils xfce4 xfce4-terminal xorg-server

setup-ntp -c chrony

setup-xorg-base
X -configure
mv xorg.conf.new /etc/X11/xorg.conf
sed -i 's/#Option     "SWcursor/Option      SWcursor/' /etc/X11/xorg.conf
rc-update add dbus

iptables -P FORWARD DROP
iptables -P INPUT DROP
iptables -A INPUT -m state --state ESTABLISHED -j ACCEPT
ip6tables -P FORWARD DROP
ip6tables -P INPUT DROP
ip6tables -A INPUT -m state --state ESTABLISHED -j ACCEPT
iptables -L
ip6tables -L
rc-update add iptables
rc-update add ip6tables
/etc/init.d/iptables save
/etc/init.d/ip6tables save

rc-update add lightdm

yes | setup-disk -m sys /dev/sda

reboot

After reboot, wait for the localhost login: prompt. Log in with root. Then paste in:

update-extlinux
sed -i 's/TIMEOUT 30/TIMEOUT 1/' /boot/extlinux.conf

sed -i 's/\/root:\/bin\/ash/\/root:\/sbin\/nologin/' /etc/passwd
echo "root:`tr -dc A-Za-z0-9 </dev/urandom | head -c 32`" | chpasswd
passwd -l root

echo '%wheel ALL=(ALL) ALL' > /etc/sudoers.d/wheel
addgroup -g 1000 user
adduser -u 1000 -G user user

Next, enter a password twice for the new user.

And finally, type adduser user wheel and poweroff. The root user is now disabled.

Launch Alpine GUI

qemu-system-x86_64 -accel hvf -cpu max,enforce -m 4G -smp 2 -hda ./alpine.vmdk

Note, we have replaced N_RAM_GB with 4G; N_CPU with 2; and NAME with ./alpine above. Log in with the user’s password. Change the display resolution (Applications > Settings > Display).

Troubleshooting

mmu_gva_to_gpa

If Qemu is started with -cpu host, then an error of this form might occur eventually:

vmx_write_mem: mmu_gva_to_gpa ffff8a8d3fb19000 failed

Anecdotally, changing -cpu host to -cpu max,enforce works. See [3] for alternative suggestions, which are to specify the correct physical CPU or to use -cpu qemu64.

VirtualBox

If Qemu fails, then try VirtualBox.

Here are the equivalent commands:

NAME=alpine
N_HD_GB=64
N_RAM_GB=4
N_CPU=2
vboxmanage createvm --name "${NAME}" --ostype Linux_64 --register
vboxmanage createmedium disk --size $(( $N_HD_GB * 1024 )) --format VMDK --filename "${NAME}".vmdk
vboxmanage storagectl "${NAME}" --name SATA --add sata --bootable on
vboxmanage storageattach "${NAME}" --storagectl SATA --port 0 --device 0 --type hdd --medium "${NAME}".vmdk
vboxmanage storagectl "${NAME}" --name IDE --add ide
vboxmanage storageattach "${NAME}" --storagectl IDE --port 0 --device 0 --type dvddrive --medium "${NAME}"*.iso
vboxmanage modifyvm "${NAME}" --cpus $N_CPU --memory $(( $N_RAM_GB * 1024 )) --vram 64 --accelerate3d on --graphicscontroller vmsvga
vboxmanage modifyvm "${NAME}" --boot1 disk --boot2 dvd --boot3 none --boot4 none
vboxmanage modifyvm "${NAME}" --uart1 0x3F8 4 --uartmode1 server /tmp/tty0
vboxmanage startvm "${NAME}" --type headless
nc -U /tmp/tty0

Now that VirtualBox is started, follow along the same initialization commands as described above. Note: after rebooting, hit enter before typing in the root username, since control characters do not appear to be handled [4].

After copy and pasting all those commands, remove the installation disk and start in graphical mode:

vboxmanage storageattach "${NAME}" --storagectl IDE --port 0 --device 0 --type dvddrive --medium none
vboxmanage startvm "${NAME}"

After logging in as user, change the display resolution if needed (Applications > Settings > Display).

Miscellaneous VirtualBox Commands

To remove the headless serial connection:

vboxmanage modifyvm "${NAME}" --uart1 off

To turn off the virtual machine:

vboxmanage controlvm "${NAME}" poweroff soft

To delete the virtual machine:

vboxmanage list vms
vboxmanage unregistervm --delete "${NAME}"

Coda

In this post, we aimed to run Alpine Linux on macOS using Qemu, with minimal user setup. We also showed how to use VirtualBox instead. If a VM is setup with VirtualBox, it can also be run with Qemu if the *.vmdk file is specified. For example, either command works vboxmanage startvm alpine or qemu-system-x86_64 -accel hvf -cpu max,enforce -m 4G -smp 2 -hda ./alpine.vmdk.