Automated FreeBSD Install
For FreeBSD, the ability to PXE boot BIOS and UEFI systems is not new, but documentation frequently assumes an NFS mounted root file system. In my view NFS is unworkable since file access is not logged, and a hung mount is difficult to diagnose.
By using TFTP to pivot to a minroot, then switching to HTTP the entire process can be audited and tailored for individual hosts. This methodology aims to borrow techniques used by an automated install for OpenBSD and Red Hat Linux.
DHCP Services
To PXE-boot FreeBSD there are three components we will need to three components
-
filename
to be loaded by PXE -
root-path
to be used by the loader -
next-server
for the custom installation script
# /etc/dhcpd.conf subnet 192.168.0.0 netmask 255.255.255.0 { option routers 192.168.0.7; option domain-name-servers 192.168.0.3; range 192.168.0.64 192.168.0.254; next-server 192.168.2.20; host T460S { hardware ethernet c8:5b:76:0d:1c:f3; fixed-address 192.168.0.11; filename "loader.efi"; option root-path "tftp://192.168.2.20/freebsd14"; option host-name "t460s"; } }
HTTP Services
The install media can be copied, or mount the ISO as a read-only file system
vnconfig vnd1 /var/www/htdocs/FreeBSD-14.1-RELEASE-amd64-disc1.iso mount_cd9660 /dev/vnd1c /var/www/htdocs/pub/freebsd14
Distribution files can now be pulled from this
http://${next_server}/pub/freebsd14/usr/freebsd-dist/
.
Copy
loader.efi
to
/tftpboot/
.
Memory File System
Sadly, FreeBSD does not ship a minroot, but we can fetch an image from the mfsBSD project. Mount this ISO in a location where the loader and access boot config and minroot
vnconfig vnd0 /var/www/htdocs/mfsbsd-14.1-STABLE-amd64.iso mount_cd9660 /dev/vnd0c /tftpboot/freebsd14
This provides everything required to boot
|-- boot | |-- defaults | | `-- loader.conf | |-- device.hints | |-- kernel | | |-- acpi_*.ko | | |-- ahci.ko | | |-- kernel.gz | | `-- linker.hints | |-- loader | |-- loader.conf | `-- lua | |-- cli.lua | |-- color.lua | |-- config.lua | |-- core.lua | |-- drawer.lua | |-- gfx-*.lua | |-- hook.lua | |-- loader.lua | |-- menu.lua | |-- password.lua | `-- screen.lua |-- boot.config `-- mfsroot.gz
boot/loader.conf
provides the configuration for switching to a memory file system
mfs_load="YES" mfs_type="mfs_root" mfs_name="/mfsroot" ahci_load="YES" vfs.root.mountfrom="ufs:/dev/md0"
For a standard build
mfsroot.gz
is about 70MB.
Building a Custom Miniroot
The mfsBSD root image can be modifed to kick off a custom install script. Get a copy of the latest build
fetch https://github.com/mmatuska/mfsbsd/archive/refs/heads/master.tar.gz tar -zxf master.tar.gz cd mfsbsd-master
Then edit
conf/rc.local.sample
# sample rc.local # export PATH=/usr/local/bin:$PATH # Find http server and host name from dhclient leases for op in `awk '/^( | option )(next-server|host-name|domain-name) / { gsub("-", "_", $(NF-1)); print $(NF-1)"="$NF }' /var/db/dhclient.leases.*` do eval "$op" done # Fetch sources mkdir /usr/freebsd-dist cd /usr/freebsd-dist for dist in MANIFEST kernel.txz base.txz; do fetch http://${next_server}/pub/freebsd14/usr/freebsd-dist/${dist} done cd - # Get bsdinstall(8) configuration by hostname and launch in a tmux session fetch http://${next_server}/bsdinstall/${host_name}.cfg echo "Starting autoinstall" tmux new-session -s autoinstall -d tmux send-keys -t autoinstall:0 "bsdinstall script ${host_name}.cfg" C-m
Mount a copy of the install image
mdconfig -f FreeBSD-14.1-RELEASE-amd64-disc1.iso mkdir $HOME/14.1-RELEASE mount -t cd9660 /dev/md0 $HOME/14.1-RELEASE
To build:
cd mfsbsd-master make clean-all BASE=$HOME/14.1-RELEASE/usr/freebsd-dist RELEASE=14.1 make BASE=$HOME/14.1-RELEASE/usr/freebsd-dist RELEASE=14.1 make iso BASE=$HOME/14.1-RELEASE/usr/freebsd-dist RELEASE=14.1
Host Configuration
The installation bootstrap is based into the custom miniroot, but the host configuration is fetched from the installer set by the DHCP option
PARTITIONS="nda0 { 64G freebsd-swap, auto freebsd-ufs / }" export BSDINSTALL_DISTSITE="http://172.16.0.1/pub/freebsd14/usr/freebsd-dist/" export BSDINSTALL_DISTDIR="/usr/freebsd-dist" #!/bin/sh cat >> /etc/rc.conf <<CONF ifconfig_DEFAULT=DHCP sshd_enable=YES ntpd_enable=YES CONF # set timezone ln -s /usr/share/zoneinfo/US/Eastern /etc/localtime # install packages pkg install -y doas echo "permit keepenv nopass :wheel" > /usr/local/etc/doas.conf chmod 600 /usr/local/etc/doas.conf # add users pw useradd eradman -m -u 1000 -s /bin/sh -G wheel install -d -o eradman -g eradman -m 700 /home/eradman/.ssh echo "ssh-ed25519 AAAA... eradman@t470s.eradman.com" > /home/eradman/.ssh/authorized_keys chown eradman:eradman /home/eradman/.ssh/authorized_keys # set passwords sed -i -e 's/:passwd_format=sha512:/:passwd_format=blf:/g' /etc/login.conf chpass -p '$2b$ ...' root chpass -p '$2b$ ...' eradman # activate FreeBSD boot option efibootmgr | awk '/Boot.+ FreeBSD/ { print substr($1, 5, 4) }' | xargs -n1 efibootmgr -o echo "rebooting in 10 seconds" sleep 10 reboot