Eric Radman : a Journal

OpenBSD Auto-Install

One of the features that may be underutilized is OpenBSD's uncomplicated auto-installer. I do not mean that all of the mechanisms that need to be in place for a real-world use case are simple, but the OpenBSD parts are simple.

TFTP Boot

The first task of booting via PXE is to hand out an address and name the file to fetch over TFTP

# /etc/dhcpd.conf
option  domain-name "eradman.com";
option  domain-name-servers 172.16.0.1;

subnet 172.16.0.1 netmask 255.255.255.0 {
    option routers 172.16.0.1;
    range 172.16.0.2 172.16.0.100;

    host openbsd1 {
        hardware ethernet fe:e1:bb:d1:cd:29;
        filename "auto_install";
        next-server 172.16.0.1;
        option host-name "openbsd1";
        fixed-address 172.16.0.2;
    }
}

The filename auto_install is not arbitrary, this causes the install script in bsd.rd to start the automated install by pulling configuration over HTTP. The complete layout for /tftpboot might look like this:

drwxr-xr-x   2 root  eradman      512 May 16 15:54 .
drwxr-xr-x  16 root  wheel       1024 May 13 01:26 ..
lrwxr-xr-x   1 root  eradman       13 May 16 15:52 auto_install -> pxeboot.amd64
lrwxr-xr-x   1 root  eradman       12 May 16 15:54 bsd -> bsd.rd.amd64
-rw-r--r--   1 root  eradman  7763412 May 13 10:07 bsd.rd.amd64
-rw-r--r--   1 root  eradman    82300 May 13 10:07 pxeboot.amd64

Per-Host Install Options

The next-server entry specified by the DHCP server points to the path where answers file can be found:

default 192.168.1.37 - - [16/May/2016:15:55:54 -0400]
  "GET /00:1e:c9:4c:69:59-install.conf?path=5.9/amd64 HTTP/1.0" 200 314

The answers file contains strings which match the questions from the installer

# /var/www/htdocs/00:1e:c9:4c:69:59-install.conf
System hostname = openbsd1
Password for root = 123456
Network interfaces = run0
IPv4 address for run0 = dhcp
Setup a user = eradman
Password for user eradman = zzzzzz
Public ssh key for user = ssh-ed25519 XYZ123... eradman@t420s.eradman.com
Which disk is the root disk = sd0
What timezone are you in = US/Eastern
Unable to connect using https. Use http instead = yes
Location of sets = http
Server = 172.16.0.1
Set name(s) = -all bsd* base* etc* man* site* comp*

If you don't specify a line then a default will be used. If more options are

Partitioning

Custom partition layouts are documented under the disklabel(8) man page. This is all we need to add to the autoinstall script:

URL to autopartitioning template for disklabel = http://172.16.0.1/openbsd-pgdb.disklabel

Sane mount options are selected automatically. The minimum in a range is selected for each file system, and remaining space is split between the partitions based on the percentage:

/           250M
swap        80M-256M  10%
/tmp        120M-4G    8%
/var        80M-4G    13%
/usr        900M-2G    5%
/usr/local  2G-10G    10%
/pg_data    1G-*      45%

Hotplug

To make this mechanism portable I run these services from my laptop. First I enable the hotplug daemon

# rcctl enable hotplugd

Next create /etc/hotplug/attach to add any USB-to-Ethernet adapter to a local bridge that is serving DHCP requests

#!/bin/sh

DEVCLASS=$1
DEVNAME=$2

case $DEVCLASS in
3)
    ifconfig bridge0 add $DEVNAME
    ;;
esac

Where DEVCLASS 3 is a network interface. Similarly, /etc/hotplug/detach disables these services using the opposite actions.

Custom Sets

OpenBSD allows for custom software to be installed by adding a site-specific tgz file. If index.txt includs the new file it will appear in the menu; we only need to select the new package in *install.conf

Set name(s) = site59.tgz

To make this easy I am allowing for this one package to be installed without being signed.

Checksum test for site62.tgz failed. Continue anyway = yes
Unverified sets: site62.tgz. Continue without verification = yes

rc.firsttime

One of the most interesting files that can be installed with siteNN.tgz is /etc/rc.firsttime. This is executed the first time a system boots up in multi-user mode, and is a very convenient way to make sure some bits of essential post-install configuration occur. This example fetches and installs ports on first boot

#!/bin/sh
ftp -o - http://192.168.1.1/pub/OpenBSD/5.9/ports.tar.gz | tar -zxf - -C /usr

Even better, use this to get a Salt minion running

#!/bin/sh
master=172.16.0.1
ftp -o /tmp/bootstrap-salt.sh http://$master/bootstrap-salt.sh
sh /tmp/bootstrap-salt.sh -r -A $master

Where -r instructs the salt-boot strap script not to autoselect a mirror (preserving /etc/installurl on OpenBSD).

The autoinstaller allows you to skip checkums and signatures for the package, but it has to be listed in index.txt

#!/bia/sh
(cd site62; tar -czvf ../amd64/site62.tgz .)
(cd amd64; ls -l > index.txt)

Another useful item to put in siteXX.tgz is the public signify(1) key. More on that later...

Autoinstalling OpenBSD on VMM

In addition to PXE-booting a bare-metal system we can install local VMs in almost the exact same manner. The first step is to define the VMs.

#/etc/vm.conf
switch "local" {
    add vether0
    up
}

vm "db1" {
    disable
    memory 512M
    boot "/bsd.db1"
    disk "/home/eradman/vm/db1.img"
    interface {
        switch "local"
        lladdr fe:e1:bb:d1:cd:29
    }
}

vm "db2" {
    disable
    memory 512M
    boot "/bsd.db2"
    disk "/home/eradman/vm/db2.img"
    interface {
        switch "local"
        lladdr fe:e1:bb:d1:cd:30
    }
}

VMM's boot option can be a BIOS or a kernel image. The trick to auto-installing a VM is to temporarily swap in bsd.rd in order to get to the installer. Now you can type vmctl start db1 -c and press A. We can have tmux automate this for us

#!/bin/sh -ex
# reinstall-all.sh
# - Delete master salt keys
# - Reset all VMs
# - Start each VM and select Autoinstall
# - Set the boot kernel to bsd.sp
vms="
    db1
    db2
"
salt-key -Dy
rcctl restart vmd

for vm in $vms; do
    ln -sf /bsd.rd /bsd.$vm
done

for vm in $vms; do
    tmux new-session -s autoinstall -d
    tmux send-keys -t autoinstall:0 "doas vmctl start $vm -c" C-m
    sleep 10
    tmux send-keys -t autoinstall:0 "A" C-m
    sleep 2
    tmux kill-session -t autoinstall
    ln -sf /bsd.sp /bsd.$vm
done
doas vmctl status

Signing Custom Packages

Once we can spin up fresh VMs or bare-metal installations we can build custom packages without having to run pkg_add with -Dunsigned. To do this we need to generate a signing key

signify -G -n -s /etc/signify/custombuild-pkg.sec -p /etc/signify/custombuild-pkg.pub

After building our custom packages sign each of them

pkg_sign -C -v \
  -s signify2 -s /etc/signify/custombuild-pkg.sec \
  -S /usr/ports/packages/amd64/all \
  -o /var/www/htdocs/pub/OpenBSD/6.2/packages/amd64

Adding -C to pkg_sign will take care of updating the checksum file (SHA256) in the target directory.

Last updated on December 07, 2017