Fully Automated OpenBSD Installation Using PXE

Based on a specification by Don Jackson and a pxeboot diff from Chris Kuethe
posted to misc@ (see http://marc.info/?t=121511657000006&r=1&w=2) I have
implemented a fully automated PXE based installation capability. The changes
required to implement the installation hooks are relatively small - the diff
to current is here and a diff for the FAQ is here. The installation scripts
and configuration described below represent one possible way to use the hooks
to achieve a no touch installation. This note with links to related files can
be found at http://nbender.com/install.netboot/install.html.

I have tested this for i386 (32 bit) and amd64 (64 bit) using VMWare Fusion
running under OS X. The virtual machines are configured with one network
interface and one 20GB hard drive. The only configuration required for the
VMs is to set up the PXE boot host in the bios with PXE as the last boot
option. At initial power on the hard drive is uninitialized so the PXE boot
option is used. At the end of the install the VM is restarted and boots off
the hard drive installation.

The resulting install has a 4GB root partition and 1GB swap with the rest of
the drive unallocated. The root password entry is set to "nopass" so password
based login is not available. The installation script writes a public key to
/root/.ssh/known_hosts so access is available via ssh after the install.

The changes to the regular install are minimal. There is a test added in
dot.profile to check for the existance of the install.netboot script:

  if [ -f /install.netboot ]; then
      . /install.netboot
  fi

Due to space considerations the install.netboot script is only added to
the CD version of bsd.rd.

Within install.netboot there is a test for network booting:

  echo "Checking for netboot..."
  netboot_if=`ifconfig netboot | sed -n 's/:.*//;p;q'`

  if [ "X$netboot_if" != "X" ]; then
      echo "netboot detected on $netboot_if"
      ....

For i386 and amd64 the PXE interface will be in the netboot interface group.
This method should allow other architectures to take advantage of the new
installation hook.

At this point the PXE interface is started using dhclient with a configuration
that requests and requires dhcp option-255. If option-255 is not found then
install.pxe cleans up and returns control to the normal install process. If it
is found then the value passed to ftp to fetch the install script. If it is
retrieved successfuly the script is saved as /bootscript and invoked as:

  /bootscript <interface> <script_url> <os_version> <os_arch>

where <interface> is the boot network interface, <script_url> is the path
where the boot script was found, and <os_version> and <os_arch> are for
example 4.4 and amd64 respectively.

The boot.conf and install script files are searched for first with the mac
address (without colons) appended, then the os version and architecture
(.OpenBSD-4.4-amd64) appended, and then just with the base path. For testing
I used the following tftp hierarchy:

  /tftpboot:
    pxeboot.amd64
    bsd.rd.amd64
    pxeboot.i386
    bsd.rd.i386
    install
    install.auto
    install.disklabel
    install.input
    etc: 
      boot.conf.OpenBSD-4.4-amd64
      boot.conf.000c29c3c2a1

Note that the linked ramdisk files are gzipped and must be uncompressed
before use.

The install, install.auto, install.disklabel, and install.input implement the
automated install. My initial approach was to construct an input file that
would feed answers to the standard install.sh script. This was problematic
because the disklabel program apparently drains stdin before it exits leaving
nothing for the rest of the installation. To work around this I made two
modfications to install.sh and created install.auto. First I redefined
md_prep_disklable to run install.disklabel which  feeds the relevant inputs
to disklabel. Second I redefined the ask routine to echo arguments and results
to aid in constructing the proper input sequence. This is also where I made
the change to install a public key under /root/.ssh on the install disk.
Output from the install process is captured and copied to /var/log/install.log
on the installation disk. The log file for the 32 bit install is here.

For the dhcp server I used the following dhcpd.conf file:

  shared-network LOCAL-NET {
    option  domain-name "my.domain";
    option  domain-name-servers 172.16.134.1;

    subnet 172.16.28.0 netmask 255.255.255.0 {
        option routers 172.16.28.1;
        range 172.16.28.60 172.16.28.70;
        option option-225 "http://172.16.28.130/install";
    }

    group {
        use-host-decl-names on;
        filename "pxeboot.i386";
        host pxe32 { hardware ethernet 00:0c:29:c3:c2:a1; }
    }

    group {
        use-host-decl-names on;
        filename "pxeboot.amd64";
        host pxe64 { hardware ethernet 00:0c:29:00:9b:91; }
    }
  }

Note that if you use the stock dhcpd you need to be running a version built
after 10-sep-2008 as there was a bug in dhcpd which caused unreliable handling
of dhcp option numbers greater than 63.