%h1 Automated OpenBSD Installation %p OpenBSD has a very good story for automated installations, which includes features such as: %ul %li Boot parameters set on boot (example: switch to serial port) %li Configuration of network interfaces %li Partitioning & adaptive disklabels %li Selection and installation of base sets %li Custom site package that can contain arbitrary content %li Installation of non-root user with SSH public key %li Applying patches on first boot %h2 Mirroring Sets %p OpenBSD project kindly makes %a{:href=>"https://www.openbsd.org/ftp.html#rsync"} rsync mirrors available aid in mainting a local mirror :codeblock :::sh #!/bin/sh cd /var/www/htdocs/pub/OpenBSD mkdir -p 7.4/amd64 syspatch/7.4/amd64 cd /var/www/htdocs/pub/OpenBSD openrsync -rv rsync://ftp4.usa.openbsd.org/sevenone/amd64 7.4/ openrsync -rv rsync://ftp4.usa.openbsd.org/ftp/syspatch/7.4/amd64 syspatch/7.4/ %h2 TFTP Boot %p The first task of booting via PXE is to hand out an address and filename to fetch over TFTP :codeblock :::pf # /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; host db1 { hardware ethernet fe:e1:bb:d1:cd:29; filename "auto_install"; next-server 172.16.0.1; option host-name "db1"; fixed-address 172.16.0.2; } host t460s { hardware ethernet c8:5b:76:0d:1c:f3; filename "efi/auto_install"; next-server 172.16.0.1; option host-name "t460s"; fixed-address 172.16.0.10; } } %p A basic layout for %code /tftpboot may look like this: :codeblock |-- auto_install -> pxeboot |-- bsd -> bsd.rd |-- bsd.rd |-- efi | |-- BOOTX64.EFI | |-- auto_install -> BOOTX64.EFI `-- pxeboot %p Where %code pxeboot is used for a BIOS boot, and %code BOOTX64.EFI is used for UEFI. %p %code auto_install is not an arbitrary filename: this triggers to the install in %code bsd.rd to start the automated install by pulling configuration over HTTP. %p If the system is headless, console parameters may be provided in %code /tftpboot/etc/boot.conf :codeblock stty com0 115200 set tty com0 %h2 Per-Host Install Options %p The %code next-server entry specified by the DHCP server points to the path where answers file can be found. This file contains strings which match the questions from the installer :codeblock :::cfg # /var/www/htdocs/fe:e1:bb:d1:cd:29-install.conf System hostname = db1 Password for root = 123456 Network interfaces = run0 IPv4 address for run0 = dhcp Setup a user = eradman Password for user eradman = 123456 Public ssh key for user = ssh-ed25519 XYZ123... eradman@t430s.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* %p Plain text passwords are accepted, or generate encrypted passwords using :codeblock :::sh printf "123456" | encrypt %h2 Partitioning %p Custom partition layouts are documented under the %a{:href=>'https://man.openbsd.org/disklabel#AUTOMATIC_DISK_ALLOCATION'} disklabel(8) man page. This is all we need to add to the autoinstall script: :codeblock :::cfg URL to autopartitioning template for disklabel = http://172.16.0.1/openbsd-pgdb.disklabel %p Disklabel slices are calculated based on a minimum size within each range, then remaining space is split between the partitions based on a percentage: :codeblock / 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% %h2 Hotplug %p To make this mechanism portable I run these services from my laptop. First I enable the hotplug daemon :codeblock # rcctl enable hotplugd %p Next create %code /etc/hotplug/attach to add any USB-to-Ethernet adapter to a local bridge that is serving DHCP requests :codeblock :::sh #!/bin/sh DEVCLASS=$1 DEVNAME=$2 case $DEVCLASS in 3) ifconfig $DEVNAME up ifconfig bridge0 add $DEVNAME ;; esac %p Where %code DEVCLASS 3 is a network interface. Similarly, %code /etc/hotplug/detach disables these services using the opposite actions. %h2 Custom Sets %p OpenBSD allows for custom software to be installed by adding a = succeed '.' do %a{:href=>"http://www.openbsd.org/faq/faq4.html#site"} site-specific tgz file If %code index.txt includs the new file it will appear in the menu. %p To make this easy, verification for %code siteNN.tgz may be skipped :codeblock :::cfg Checksum test for site71.tgz failed. Continue anyway = yes Unverified sets: site71.tgz. Continue without verification = yes %p One of the most interesting files that can be installed with %code siteNN.tgz is = succeed '.' do %code /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. %h2 Autoinstalling OpenBSD on VMM %p 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. :codeblock :::cfg #/etc/vm.conf switch "local" { add vether0 up } vm "db1" { disable memory 512M boot "/bsd.db1" disk "/vm/db1.img" interface { switch "local" lladdr fe:e1:bb:d1:cd:29 } } vm "db2" { disable memory 512M boot "/bsd.db2" disk "/vm/db2.img" interface { switch "local" lladdr fe:e1:bb:d1:cd:30 } } %p VMM's %code boot option can be a BIOS or a kernel image. The trick to auto-installing a VM is to temporarily swap in %code bsd.rd to get to the installer. Now use %code vmctl start db1 -c and press = succeed '.' do %code A We can have tmux automate this for us :codeblock :::sh #!/bin/sh -ex # reinstall-all.sh # - Reset all VMs # - Start each VM and select Autoinstall # - Set the boot kernel to bsd.sp for vm in $*; do ln -f /bsd.rd /bsd.$vm done rcctl restart vmd for vm in $*; 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 -f /bsd.sp /bsd.$vm done doas vmctl status %h2 Signing Custom Packages %p Once we can spin up fresh VMs or bare-metal installations we can build custom packages without having to run %code pkg_add with = succeed '.' do %code -Dunsigned To do this we need to generate a signing key :codeblock signify -G -n -s /etc/signify/custombuild-pkg.sec -p /etc/signify/custombuild-pkg.pub %p After building our custom packages sign each of them :codeblock pkg_sign -C -v \ -s signify2 -s /etc/signify/custombuild-pkg.sec \ -S /usr/ports/packages/amd64/all \ -o /var/www/htdocs/pub/OpenBSD/7.4/packages/amd64 %p Adding %code -C to %code pkg_sign will take care of updating the checksum file (SHA256) in the target directory.