OpenBSD: setup a local auto-installation server

Last modification on

This guide describes how to setup a local mirror and installation/upgrade server that requires little or no input interaction.

Setup a local HTTP mirror

The HTTP mirror will be used to fetch the base sets and (optional) custom sets. In this guide we will assume 192.168.0.2 is the local installation server and mirror, the CPU architecture is amd64 and the OpenBSD release version is 6.5. We will store the files in the directory with the structure:

http://192.168.0.2/pub/OpenBSD/6.5/amd64/

Create the www serve directory and fetch all sets and install files (if needed to save space *.iso and install65.fs can be skipped):

$ cd /var/www/htdocs
$ mkdir -p pub/OpenBSD/6.5/amd64/
$ cd pub/OpenBSD/6.5/amd64/
$ ftp 'ftp://ftp.nluug.nl/pub/OpenBSD/6.5/amd64/*'

Verify signature and check some checksums:

$ signify -C -p /etc/signify/openbsd-65-base.pub -x SHA256.sig

Setup httpd(8) for simple file serving:

# $FAVORITE_EDITOR /etc/httpd.conf

A minimal example config for httpd.conf(5):

server "*" {
	listen on * port 80
}

The default www root directory is: /var/www/htdocs/

Enable the httpd daemon to start by default and start it now:

# rcctl enable httpd
# rcctl start httpd

Creating an installation response/answer file

The installer supports loading responses to the installation/upgrade questions from a simple text file. We can do a regular installation and copy the answers from the saved file to make an automated version of it.

Do a test installation, at the end of the installation or upgrade when asked the question:

Exit to (S)hell, (H)alt or (R)eboot?

Type S to go to the shell. Find the response file for an installation and copy it to some USB stick or write down the response answers:

cp /tmp/i/install.resp /mnt/usbstick/

A response file could be for example:

System hostname = testvm
Which network interface do you wish to configure = em0
IPv4 address for em0 = dhcp
IPv6 address for em0 = none
Which network interface do you wish to configure = done
Password for root account = $2b$10$IqI43aXjgD55Q3nLbRakRO/UAG6SAClL9pyk0vIUpHZSAcLx8fWk.
Password for user testuser = $2b$10$IqI43aXjgD55Q3nLbRakRO/UAG6SAClL9pyk0vIUpHZSAcLx8fWk.
Start sshd(8) by default = no
Do you expect to run the X Window System = no
Setup a user = testuser
Full name for user testuser = testuser
What timezone are you in = Europe/Amsterdam
Which disk is the root disk = wd0
Use (W)hole disk MBR, whole disk (G)PT, (O)penBSD area or (E)dit = OpenBSD
Use (A)uto layout, (E)dit auto layout, or create (C)ustom layout = a
Location of sets = http
HTTP proxy URL = none
HTTP Server = 192.168.0.2
Server directory = pub/OpenBSD/6.5/amd64
Unable to connect using https. Use http instead = yes
Location of sets = http
Set name(s) = done
Location of sets = done
Exit to (S)hell, (H)alt or (R)eboot = R

Get custom encrypted password for response file:

$ printf '%s' 'yourpassword' | encrypt

Changing the RAMDISK kernel disk image

rdsetroot(8) is publicly exposed now in base since 6.5. Before 6.5 it is available in the /usr/src/ tree as elfrdsetroot, see also the rd(4) man page.

$ mkdir auto
$ cd auto
$ cp pubdir/bsd.rd .
$ rdsetroot -x bsd.rd disk.fs
# vnconfig vnd0 disk.fs
# mkdir mount
# mount /dev/vnd0a mount

Copy the response file (install.resp) to: mount/auto_install.conf (installation) or mount/auto_upgrade.conf (upgrade), but not both. In this guide we will do an auto-installation.

Unmount, detach and patch RAMDISK:

# umount mount
# vnconfig -u vnd0
$ rdsetroot bsd.rd disk.fs

To test copy bsd.rd to the root of some testmachine like /bsd.test.rd then (re)boot and type:

boot /bsd.test.rd

In the future (6.5+) it will be possible to copy to a file named "/bsd.upgrade" in the root of a current system and automatically load the kernel: See the script bsd.upgrade in CVS. Of course this is possible with PXE boot or some custom USB/ISO also. As explained in the autoinstall(8) man page: create either an auto_upgrade.conf or an auto_install.conf, but not both.

Create bootable miniroot

In this example the miniroot will boot the custom kernel, but fetch all the sets from the local network.

We will base our miniroot of the official version: miniroot65.fs.

We will create a 16MB miniroot to boot from (in this guide it is assumed the original miniroot is about 4MB and the modified kernel image fits in the new allocated space):

$ dd if=/dev/zero of=new.fs bs=512 count=32768

Copy first part of the original image to the new disk (no truncation):

$ dd conv=notrunc if=miniroot65.fs of=new.fs
# vnconfig vnd0 new.fs

Expand disk OpenBSD boundaries:

# disklabel -E vnd0
> b
Starting sector: [1024]
Size ('*' for entire disk): [8576] *
> r
Total free sectors: 1168.
> c a
Partition a is currently 8576 sectors in size, and can have a maximum
size of 9744 sectors.
size: [8576] *
> w
> q

or:

# printf 'b\n\n*\nc a\n*\nw\n' | disklabel -E vnd0

Grow filesystem and check it and mark as clean:

# growfs -y /dev/vnd0a
# fsck -y /dev/vnd0a

Mount filesystem:

# mount /dev/vnd0a mount/

The kernel on the miniroot is GZIP compressed. Compress our modified bsd.rd and overwrite the original kernel:

# gzip -c9n bsd.rd > mount/bsd

Or to save space (+- 500KB) by stripping debug symbols, taken from bsd.gz target in this Makefile.

$ cp bsd.rd bsd.strip
$ strip bsd.strip
$ strip -R .comment -R .SUNW_ctf bsd.strip
$ gzip -c9n bsd.strip > bsd.gz
$ cp bsd.gz mount/bsd

Now unmount and detach:

# umount mount/
# vnconfig -u vnd0

Now you can dd(1) the image new.fs to your bootable (USB) medium.

Adding custom sets (optional)

For patching /etc/rc.firsttime and other system files it is useful to use a customized installation set like siteVERSION.tgz, for example: site65.tgz. The sets can even be specified per host/MAC address like siteVERSION-$(hostname -s).tgz so for example: site65-testvm.tgz

When the installer checks the base sets of the mirror it looks for a file index.txt. To add custom sets the site entries have to be added.

For example:

-rw-r--r--  1 1001  0    4538975 Oct 11 13:58:26 2018 site65-testvm.tgz

The filesize, permissions etc do not matter and are not checked by the installer. Only the filename is matched by a regular expression.

Sign custom site* tarball sets (optional)

If you have custom sets without creating a signed custom release you will be prompted for the messages:

checksum test failed

and:

unverified sets: continue without verification

OpenBSD uses the program signify(1) to cryptographically sign and verify filesets.

To create a custom public/private keypair (ofcourse make sure to store the private key privately):

$ signify -G -n -c "Custom 6.5 install" -p custom-65-base.pub -s custom-65-base.sec

Create new checksum file with filelist of the current directory (except SHA256* files):

$ printf '%s\n' * | grep -v SHA256 | xargs sha256 > SHA256

Sign SHA256 and store as SHA256.sig, embed signature:

$ signify -S -e -s /privatedir/custom-65-base.sec -m SHA256 -x SHA256.sig

Verify the created signature and data is correct:

$ signify -C -p /somelocation/custom-65-base.pub -x SHA256.sig

Copy only the public key to the RAMDISK:

$ cp custom-65-base.pub mount/etc/signify/custom-65-base.pub

Now we have to patch the install.sub file to check our public key. If you know a better way without having to patch this script, please let me know.

Change the variable PUB_KEY in the shellscript mount/install.sub from:

PUB_KEY=/etc/signify/openbsd-${VERSION}-base.pub

To:

PUB_KEY=/etc/signify/custom-${VERSION}-base.pub

And for upgrades from:

$UPGRADE_BSDRD &&
	PUB_KEY=/mnt/etc/signify/openbsd-$((VERSION + 1))-base.pub

To:

$UPGRADE_BSDRD &&
	PUB_KEY=/mnt/etc/signify/custom-$((VERSION + 1))-base.pub

Ideas

  • Patch rc.firsttime(8): and run syspatch, add ports, setup xenodm etc.
  • Custom partitioning scheme, see autoinstall(8) "URL to autopartitioning template for disklabel = url".
  • Setup pxeboot(8) to boot and install over the network using dhcpd(8) and tftpd(8) then not even some USB stick is required.

References