What if we could create a volatile VM on OpenBSD to try things quickly or make a service like a VPN on virtual memory... ephemeral as a fart in the air?
I won't delve much into the specific service you can run in it (perhaps a VPN, or utilizing your Mullvad config to NAT traffic, among other things...) but I'll give you the idea to build the VM and then explore that concept.
OpenBSD has toons of tools to play with and even by using a system for many years, you always discover something new or that you never used before. In case you weren't aware, there's a tool called: mount_mfs(8), from the man:
mount_mfs is used to build a file system in virtual memory and then mount it on a specified node. mount_mfs exits and the contents of the file system are lost when the file system is unmounted. If mount_mfs is sent a signal while running, for example during system shutdown, it will attempt to unmount its corresponding file system. The parameters to mount_mfs are the same as those to newfs. The special file is only used to read the disk label which provides a set of configuration parameters for the memory based file system. The special file is typically that of the primary swap area, since that is where the file system will be backed up when free memory gets low and the memory supporting the file system has to be paged. If the keyword “swap” is used instead of a special file name, default configuration parameters will be used. (This option is useful when trying to use mount_mfs on a machine without any disks.)
The idea is to create a virtual memory space that will contain the disk image of our VM, and we'll install OpenBSD in it. The tools for it are all in base and are basically: mount_mfs(8), pf(4), vmd(8), httpd(8), dhcpd(8), and rdomain(4).
We'll set up a few services for the environment to play nicely with the volatile VM, but of course, you can cut out some stuff if you just want to run one VM. The roadmap will be setting up pf(4) rules, adding the veb(4)/vport(4) interface that vmd(8) will use, setting up a dhcpd(8) server to give away some IPs, and also running a personal httpd(8) server with the installation files for our new ephemeral VM made with mount_mfs(8). Ah, you need a couple GB of free RAM.
For pf(4), we will not need more than what the FAQ says, but to make it a bit more isolated and clean, I will use rdomain(4). As you probably already have a pf conf running, we can just include the file for vmd(8) and keep it separate from the main ruleset.
$ doas mkdir /etc/pf
$ doas cat /etc/pf.conf | grep include
...
include "/etc/pf/vmd.conf"
...
$ doas cat /etc/pf/vmd.conf
pass on VMD rtable 5
match out on egress inet from 100.64.0.0/24 to any nat-to (egress)
We have now our little ruleset for the vms but not enable, to do that we need to create first the veb(4)/vport(4) interface and add it to the rdomain(4) (as you see I used the table 5, you can assign any other number):
$ doas ifconfig veb5 rdomain 5 group VMD up
$ doas ifconfig vport5 rdomain 5 group VMD
$ doas ifconfig vport5 inet 100.64.0.1/24 up
$ doas ifconfig veb5 add vport5
By now an ifconfig veb5 or ifconfig vport5 will show you the interfaces that we just added and now you will be able to reload pf(4) with the new rules without any errors of missing interfaces
$ doas pfctl -f /etc/pf.conf
$
We can assign an IP to the VM manually, or we can also just set up a dhcpd(8) server with the VMs' network pool to make it more tidy. For this, we just need to modify or create the file /etc/dhcpd.conf like this:
$ doas cat /etc/dhcpd.conf
shared-network VMs-NETWORK {
subnet 100.64.0.0 netmask 255.255.255.0 {
range 100.64.0.100 100.64.0.210;
option subnet-mask 255.255.255.0;
option broadcast-address 100.64.0.255;
option routers 100.64.0.1;
## change me
option domain-name-servers 9.9.9.9;
filename "auto_install";
## change me
host fart {
hardware ethernet ff:e2:ca:01:04:01;
option host-name "fart";
}
}
}
I set the filename as auto_install to use it later with autoinstall(8). Pretty simple, right? But remember that we are using rdomain(4), so to make this dhcpd(8) server play with our VMs, we should tell it to use the table 5:
$ doas rcctl enable dhcpd
$ doas rcctl set dhcpd rtable 5
$ doas rcctl set dhcpd flags vport5
$ doas rcctl start dhcpd
No more networking so far, let's move on to the next step, vmd(8).
The vmd(8) setup will be very short since we will only setup the networking part we pre-configured and that's all, to turn on the machines we will use just vmctl(8).
$ doas cat /etc/vm.conf
switch "veb5" {
interface veb5
rdomain 5
group VMD
}
That is pretty much all we need, let's start the service and move on to httpd(8):
$ doas enable vmd
$ doas rcctl start vmd
$ vmctl status
ID PID VCPUS MAXMEM CURMEM TTY OWNER STATE NAME
I like to have my own repository or mirror of files. You can skip this and just use upstream or another way, but it's nice to have it all "local." I will be mirroring snapshots for amd64, but you can use stable or any other, of course. The point of httpd(8) is to use it with autoinstall(8):
# mkdir -p /var/www/htdocs/pub/OpenBSD/snapshots/amd64
# cd /var/www/htdocs/pub/OpenBSD/snapshots/amd64
# rsync -av rsync://mirror.leaseweb.com/openbsd/snapshots/amd64/ .
...
A LOT OF MAGIC
...
We have our directory populated with the snapshot files now, so it's time to set up httpd(8) to serve them for our little vms.
$ cat /etc/httpd.conf
server "default" {
listen on * port www
log style combined
root "/htdocs"
}
types { include "/usr/share/misc/mime.types" }
Autoinstall(8) is a beautiful thing, and the only file you need is install.conf with the configuration you want to set on your new VM. Place it in the root of your HTTP server:
$ doas cat /var/www/htdocs/install.conf
System hostname = test
Which network interface do you wish to configure = vio0
IPv4 address for vio0 = autoconf
IPv6 address for vio0 = none
IPv6 address = none
IPv6 default router = none
X Window System = no
Which speed should com0 use = 115200
Password for root account = r00t
Setup a user = gonzalo
Password for user = AAAAC3NzaC1lZDI1NTE5AAAAIPxVebz
Public ssh key for user = ssh-ed25519 AAAAC3NzAADgasd324AIPxVebz+gL0DqbsikzlMBA0SM059VkOmEGly3b24SnNH
Allow root ssh login = prohibit-password
What timezone are you in = America/Buenos_Aires
Which disk is the root disk = sd0
Use (W)hole disk MBR, whole disk (G)PT, (O)penBSD area or (E)dit = whole
Use (A)uto layout, (E)dit auto layout, or create (C)ustom layout = a
Start sshd(8) by default = yes
Change the default console to com0 = yes
Location of sets = http
HTTP proxy URL = none
HTTP Server = 100.64.0.1
Server directory = pub/OpenBSD/snapshots/amd64
Set name(s) = -game* -comp* -man* -x*
Continue without verification = yes
Exit to (S)hell, (H)alt or (R)eboot = h
I don't want any x* package or comp* or games, adjust that and the other options for your needs but in a regular scenario that file should be more than enough to install the vm
Done! Now we enable it and put it on the right table with:
$ doas rcctl enable httpd
$ doas rcctl set httpd rtable 5
$ doas rcctl start httpd
So far we have the interfaces, the dhcpd server for IPs, vmd waiting for vms and a http server ready to serve the files... let's move on to the vm itself.
I will create the mfs directory on /mnt but you can create it anywhere your user can write on. The memory/space I will use for the vm will be 2GB according to some tests I did that it kind of the amount of space you need to install the base system without x, comp, man and games.
$ doas mkdir -p /mnt/mfs
$ doas mount_mfs -s 2048m swap /mnt/mfs
$
Let's create the vm disk image inside of the mfs space:
$ doas vmctl create -s 2000m /mnt/mfs/test.qcow2
vmctl: qcow2 imagefile created
$ df -h /mnt/mfs
Filesystem Size Used Avail Capacity Mounted on
mfs:62373 1.9G 41.0K 1.8G 1% /mnt/mfs
Time to fire up the vm, I will set 512m of ram, set it up to use the veb(4) interface and I will boot from my system bsd.rd:
$ vmctl start -m 512m -B net -n veb5 -b /bsd.rd -c -d /mnt/mfs/test.qcow2 volatile
After that command you will see the new machine booting and waiting for 5 seconds for some input otherwise it will continue with Autoinstall(8) (we told to do that by setting "-B net"), after finished the installation it should turn off itself. Now let's turn it on to use it:
$ vmctl start -m 512m -n veb5 -c -d /mnt/mfs/test.qcow2 volatile
...
OpenBSD/amd64 (test.my.domain) (tty00)
login:
Password:
And we're done! Of course, this was adjusted to my personal existing configuration (especially the networking). You must change it to make it work better in yours. What to do with the volatile VM is another story, and the options are endless. I would recommend playing with ansible to deploy everything you want after the first boot. Keep in mind that you need to use rdomain(4) to connect to the machine (if you used it as I did), so SSH into it should be something like:
$ route -T5 exec ssh 100.64.0.111
To recap the full installation with everything working together, I wrote a little script to do it all together:
Aaaaaaaaaaaanddd... it's gone.