Skip to content

Instantly share code, notes, and snippets.

@ctsrc
Last active May 1, 2024 04:21
Show Gist options
  • Star 16 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ctsrc/9a72bc9a0229496aab5e4d3745af0bb9 to your computer and use it in GitHub Desktop.
Save ctsrc/9a72bc9a0229496aab5e4d3745af0bb9 to your computer and use it in GitHub Desktop.
Install FreeBSD 14.0 on Hetzner

Install FreeBSD 14.0 on Hetzner server

Hetzner no longer offers direct install of FreeBSD, but we can do it ourselves. Here is how :)

Boot the hetzner server in Hetnzer Debain based rescue mode. ssh into it. then:

wget https://mfsbsd.vx.sk/files/iso/14/amd64/mfsbsd-14.0-RELEASE-amd64.iso

qemu-system-x86_64 \
    -cdrom mfsbsd-14.0-RELEASE-amd64.iso \
    \
    -drive format=raw,file=/dev/sda,if=virtio \
    -drive format=raw,file=/dev/sdb,if=virtio \
    -drive format=raw,file=/dev/sdc,if=virtio \
    -drive format=raw,file=/dev/sdd,if=virtio \
    -drive format=raw,file=/dev/sde,if=virtio \
    \
    -drive format=raw,file=/dev/sdf,if=virtio \
    -drive format=raw,file=/dev/sdg,if=virtio \
    -drive format=raw,file=/dev/sdh,if=virtio \
    -drive format=raw,file=/dev/sdi,if=virtio \
    -drive format=raw,file=/dev/sdj,if=virtio \
    \
    -drive format=raw,file=/dev/sdk,if=virtio \
    -drive format=raw,file=/dev/sdl,if=virtio \
    -drive format=raw,file=/dev/sdm,if=virtio \
    -drive format=raw,file=/dev/sdn,if=virtio \
    -drive format=raw,file=/dev/sdo,if=virtio \
    \
    -display curses \
    -boot d \
    -m 8G

basically have a mini VPS with mfsbsd running with real disk passthrough and console access, just like a KVM, so I can install as usual - and then I can even test my installation directly by booting from it in the same way! Then when it works I just boot the server normal (ie directly into FreeBSD) and if I ever b0rk something up I boot the Linux rescue image and run mfsbsd again!

Source: https://www.reddit.com/r/freebsd/comments/wf7h34/hetzner_has_silently_dropped_support_for_freebsd/ijcxgvb/

Start install

Log in from the console

  • login: root
  • password: mfsroot

Proceed to either of the following:

  • Perform a standard install of FreeBSD as described in 01_standard_install.md below, or
  • make a custom install of FreeBSD as described in 02_custom_install.md below

Standard install of FreeBSD

Start the FreeBSD installer

bsdinstall

Proceed with installation. When done, "power off" the qemu VM

poweroff

Check that it works

Now boot the physical disks in qemu without having the CD ISO attached.

qemu-system-x86_64 \
    \
    -drive format=raw,file=/dev/sda,if=virtio \
    -drive format=raw,file=/dev/sdb,if=virtio \
    -drive format=raw,file=/dev/sdc,if=virtio \
    -drive format=raw,file=/dev/sdd,if=virtio \
    -drive format=raw,file=/dev/sde,if=virtio \
    \
    -drive format=raw,file=/dev/sdf,if=virtio \
    -drive format=raw,file=/dev/sdg,if=virtio \
    -drive format=raw,file=/dev/sdh,if=virtio \
    -drive format=raw,file=/dev/sdi,if=virtio \
    -drive format=raw,file=/dev/sdj,if=virtio \
    \
    -drive format=raw,file=/dev/sdk,if=virtio \
    -drive format=raw,file=/dev/sdl,if=virtio \
    -drive format=raw,file=/dev/sdm,if=virtio \
    -drive format=raw,file=/dev/sdn,if=virtio \
    -drive format=raw,file=/dev/sdo,if=virtio \
    \
    -nic user,hostfwd=tcp::2222-:22 \
    -display curses \
    -boot d \
    -m 8G

Before you reboot the host machine

Qemu provides an emulated NIC to the VM. So if the physical network in the host uses a NIC that needs a different driver, the NIC name will be different in the VM from what it will be when running FreeBSD on the hardware.

The Qemu NIC will appear as em0.

However in my case the physical NIC in the machine uses a different driver and appears as igb0 when running FreeBSD on the hardware.

The Hetzner Debian based rescue system will give you a minimal description of the NIC in the machine when you ssh into it. Make note of that. If it's intel, you can put and entry for both igb0 in addtion to em0 in your /etc/rc.conf and then when you boot and ssh into the machine you will see which one was used and then you can update your /etc/rc.conf accordingly.

If the NIC is not Intel, you have to find out what Linux commands to use in the Hetzner Debian based rescue system to show more details about your NIC, and then you need to figure out which FreeBSD NIC driver is correct for that one and edit your /etc/rc.conf accordingly.

For reference, here is what the complete /etc/rc.conf from one of my Hetzner servers looks like currently:

clear_tmp_enable="YES"
syslogd_flags="-ss"
hostname="de2"

# Used when booting in Qemu
ifconfig_em0="DHCP"
ifconfig_em0_ipv6="inet6 accept_rtadv"

# Used when booting on hardware
ifconfig_igb0_name="extif"
ifconfig_extif="DHCP"
ifconfig_extif_ipv6="inet6 2a01:4f9:5a:16cb:876a:bce7:b3c8:118a prefixlen 80"
ipv6_defaultrouter="fe80::1%extif"

local_unbound_enable="YES"

sshd_enable="YES"

ntpd_enable="YES"
ntpd_sync_on_start="YES"

moused_nondefault_enable="NO"

# Set dumpdev to "AUTO" to enable crash dumps, "NO" to disable
dumpdev="AUTO"

zfs_enable="YES"

wireguard_enable="YES"
wireguard_interfaces="wg0"

jail_enable="YES"

Moment of truth

Reboot the host machine. All goes well, you'll be able to ssh into it and find a running FreeBSD system :D

That's it, you're done!

Custom install of FreeBSD

For many (most?) purposes, the standard install described above is sufficient. It's straight forward, and easy to fix if when something breaks.

The standard install described above however does not encrypt any parts of the system, not even the home directories of users. And while you can add additional individual encrypted datasets to your ZFS pool even with a standard install, you will not be able to turn on encryption for any of the ZFS datasets that have been created by the installer. Wouldn't it be nice if we could reduce the amount of data that is kept unencrypted at rest at least a bit? One of the motivations of the custom install described here is to do exactly that.

Defining our goals

For my server there are some specific things I am interested in achieving:

  • Keep as much of the system as possible encrypted at rest. With data encrypted at rest, and the keys to decrypt that data kept separate, we can recycle the harddrives in the future without needing to do overwrites of the disks first. This is desirable for multiple reasons:
    • Big disks take a long time to fully overwrite. Especially so when you do one pass of writing zeros followed by one or more passes of writing random data to completelly cover the disks.
    • Hardware failures can leave us unable to fully or even partially being able to overwriting the data, meaning that safe disposal will hinge on being able to sufficiently physically destroying the drives.
  • The base system should be possible to throw away and set up again quickly and easily.
    • Corollary: None of the system directory trees should be included in backups. Not even /usr/home as a whole. We'll get back to this.
  • Anything that is important should live in jails, with their own ZFS datasets.
    • This way, we can back up as well as restore or rollback to past versions of those "things" mostly independently of the host system itself.

Initial install

We will start off with a standard install, because getting the EFI boot working when I try to manually partition and copy boot loader files is not working for me.

This will form the basis for our "outer" base system. We will this one to boot the server into a state where we can ssh into it to unlock our remaining datasets, from which we can then reboot into our "inner" base system.

It'll work similar to how it's done in https://github.com/emtiu/freebsd-outerbase

We have 15 disks total, which we plan to add as three vdevs in a single pool. Initially we will set things up with one vdev having 5 of the disks.

Run

bsdinstall

Performing the install:

  • For hostname I choose stage4, because the normal boot itself has 3 stages and this will be our fourth stage of sort of booting.
  • At the partitioning step we do guided root on ZFS, and we select:
    • Pool type/disks to consist of a single vdev raidz3 with 5 disks (we'll manually add the other vdevs later)
    • Partition scheme "GPT (UEFI)"
  • At the user creation step, after you've created a password for root, create a user that has "boot" as part of its name, to distinguish it from the kinds of users you normally make on your servers. For example I usually make my user named "erikn" but here I name it erikboot. When asked if you want to add the user to any additional groups, make sure to add the user to the wheel group.
  • Keep ssh selected as a service to run.
  • For all other steps make whatever choices you'd normally make according to your preference.

Finish initial steps

Export the zpool and then power off the VM.

zpool export zroot
poweroff

Check that it works so far

Now boot the VM again but without the mfsbsd media.

In order to boot EFI in QEMU we need some extra files from https://www.kraxel.org/repos/jenkins/edk2/ as mentioned at https://wiki.freebsd.org/UEFI and also https://joonas.fi/2021/02/uefi-pc-boot-process-and-uefi-with-qemu/

wget https://www.kraxel.org/repos/jenkins/edk2/edk2.git-ovmf-x64-0-20220719.209.gf0064ac3af.EOL.no.nore.updates.noarch.rpm
sha256sum edk2.git-ovmf-x64-0-20220719.209.gf0064ac3af.EOL.no.nore.updates.noarch.rpm
bc42937c5c50b552dd7cd05ed535ed2b8aed30b04060032b7648ffeee2defb8e  edk2.git-ovmf-x64-0-20220719.209.gf0064ac3af.EOL.no.nore.updates.noarch.rpm

Extract.

apt install rpm2cpio
rpm2cpio edk2.git-ovmf-x64-0-20220719.209.gf0064ac3af.EOL.no.nore.updates.noarch.rpm | cpio -idmv
./usr/share/doc/edk2.git-ovmf-x64
./usr/share/doc/edk2.git-ovmf-x64/README
./usr/share/edk2.git
./usr/share/edk2.git/ovmf-x64
./usr/share/edk2.git/ovmf-x64/MICROVM.fd
./usr/share/edk2.git/ovmf-x64/OVMF-need-smm.fd
./usr/share/edk2.git/ovmf-x64/OVMF-pure-efi.fd
./usr/share/edk2.git/ovmf-x64/OVMF-with-csm.fd
./usr/share/edk2.git/ovmf-x64/OVMF_CODE-need-smm.fd
./usr/share/edk2.git/ovmf-x64/OVMF_CODE-pure-efi.fd
./usr/share/edk2.git/ovmf-x64/OVMF_CODE-with-csm.fd
./usr/share/edk2.git/ovmf-x64/OVMF_VARS-need-smm.fd
./usr/share/edk2.git/ovmf-x64/OVMF_VARS-pure-efi.fd
./usr/share/edk2.git/ovmf-x64/OVMF_VARS-with-csm.fd
./usr/share/edk2.git/ovmf-x64/UefiShell.iso
./usr/share/qemu/firmware/80-ovmf-x64-git-need-smm.json
./usr/share/qemu/firmware/81-ovmf-x64-git-pure-efi.json
./usr/share/qemu/firmware/82-ovmf-x64-git-with-csm.json
37888 blocks

Boot

qemu-system-x86_64 \
    \
    -drive if=pflash,format=raw,unit=0,readonly=on,file=usr/share/edk2.git/ovmf-x64/OVMF_CODE-pure-efi.fd \
    -drive if=pflash,format=raw,unit=1,file=usr/share/edk2.git/ovmf-x64/OVMF_VARS-pure-efi.fd \
    \
    -drive format=raw,file=/dev/sda,if=virtio \
    -drive format=raw,file=/dev/sdb,if=virtio \
    -drive format=raw,file=/dev/sdc,if=virtio \
    -drive format=raw,file=/dev/sdd,if=virtio \
    -drive format=raw,file=/dev/sde,if=virtio \
    \
    -drive format=raw,file=/dev/sdf,if=virtio \
    -drive format=raw,file=/dev/sdg,if=virtio \
    -drive format=raw,file=/dev/sdh,if=virtio \
    -drive format=raw,file=/dev/sdi,if=virtio \
    -drive format=raw,file=/dev/sdj,if=virtio \
    \
    -drive format=raw,file=/dev/sdk,if=virtio \
    -drive format=raw,file=/dev/sdl,if=virtio \
    -drive format=raw,file=/dev/sdm,if=virtio \
    -drive format=raw,file=/dev/sdn,if=virtio \
    -drive format=raw,file=/dev/sdo,if=virtio \
    \
    -nic user,hostfwd=tcp::2222-:22 \
    -vnc 127.0.0.1:1,password=on -k en-us -monitor stdio \
    -boot d \
    -m 8G

From the qemu console, use command change vnc password to set VNC password as per https://wiki.archlinux.org/title/QEMU#VNC

Then forward port 5901 from the server to your machine over SSH and then connect to VNC over the forwarded port.

Run this command in a new terminal on your computer to forward the port:

ssh -L 25901:127.0.0.1:5901 yourserver.example.com

(Subsitute your actual server DNS name or IP address in place of yourserver.example.com)

Then connect to VNC from your machine using the forwarded port 127.0.0.1:25901.

VNC should show the FreeBSD console login prompt. Log in.

Check the pool info and the datasets that have been created so far.

zpool status
  pool: zroot
 state: ONLINE
config:

        NAME         STATE     READ WRITE CKSUM
        zroot        ONLINE       0     0     0
          raidz3-0   ONLINE       0     0     0
            vtbd0p2  ONLINE       0     0     0
            vtbd1p2  ONLINE       0     0     0
            vtbd2p2  ONLINE       0     0     0
            vtbd3p2  ONLINE       0     0     0
            vtbd4p2  ONLINE       0     0     0
zfs list
NAME                 USED  AVAIL  REFER  MOUNTPOINT
zroot                853M  10.7T   153K  /zroot
zroot/ROOT           849M  10.7T   153K  none
zroot/ROOT/default   849M  10.7T   849M  /
zroot/home           204K  10.7T   204K  /home
zroot/tmp            153K  10.7T   153K  /tmp
zroot/usr            459K  10.7T   153K  /usr
zroot/usr/ports      153K  10.7T   153K  /usr/ports
zroot/usr/src        153K  10.7T   153K  /usr/src
zroot/var            988K  10.7T   153K  /var
zroot/var/audit      153K  10.7T   153K  /var/audit
zroot/var/crash      153K  10.7T   153K  /var/crash
zroot/var/log        223K  10.7T   223K  /var/log
zroot/var/mail       153K  10.7T   153K  /var/mail
zroot/var/tmp        153K  10.7T   153K  /var/tmp

Export the zpool and shutdown the machine VM. Boot with mfsBSD media again.

zpool export zroot
poweroff

Boot with mfsBSD again

qemu-system-x86_64 \
    -cdrom mfsbsd-14.0-RELEASE-amd64.iso \
    \
    -drive format=raw,file=/dev/sda,if=virtio \
    -drive format=raw,file=/dev/sdb,if=virtio \
    -drive format=raw,file=/dev/sdc,if=virtio \
    -drive format=raw,file=/dev/sdd,if=virtio \
    -drive format=raw,file=/dev/sde,if=virtio \
    \
    -drive format=raw,file=/dev/sdf,if=virtio \
    -drive format=raw,file=/dev/sdg,if=virtio \
    -drive format=raw,file=/dev/sdh,if=virtio \
    -drive format=raw,file=/dev/sdi,if=virtio \
    -drive format=raw,file=/dev/sdj,if=virtio \
    \
    -drive format=raw,file=/dev/sdk,if=virtio \
    -drive format=raw,file=/dev/sdl,if=virtio \
    -drive format=raw,file=/dev/sdm,if=virtio \
    -drive format=raw,file=/dev/sdn,if=virtio \
    -drive format=raw,file=/dev/sdo,if=virtio \
    \
    -display curses \
    -boot d \
    -m 8G

Initial ZFS datasets

Import pool

zpool import -o altroot=/mnt -f zroot

Get rid of the datasets that we don't want

zfs destroy zroot/var/tmp
zfs destroy zroot/var/mail
zfs destroy zroot/var/log
zfs destroy zroot/var/crash
zfs destroy zroot/var/audit
zfs destroy zroot/var
zfs destroy zroot/usr/src
zfs destroy zroot/usr/ports
zfs destroy zroot/usr
zfs destroy zroot/tmp
zfs destroy zroot/home

Now we are left with only the datasets we want to have

zfs list
NAME                 USED  AVAIL  REFER  MOUNTPOINT
zroot                852M  10.7T   153K  /mnt/zroot
zroot/ROOT           849M  10.7T   153K  none
zroot/ROOT/default   849M  10.7T   849M  /mnt

Unmount the zroot dataset.

mount
/dev/md0 on / (ufs, local, read-only)
devfs on /dev (devfs)
tmpfs on /rw (tmpfs, local)
devfs on /rw/dev (devfs)
zroot on /rw/mnt/zroot (zfs, local, noatime, nfsv4acls)
zfs umount zroot

Of course, now there are a bunch of files that we want to have which we no longer have, since they were put on those other datasets by bsdinstall.

Let's fix that.

Restore the files we want to keep

zfs mount zroot/ROOT/default
cd /mnt/tmp/
fetch https://download.freebsd.org/releases/amd64/14.0-RELEASE/base.txz
sha256sum base.txz
05a7c268aa4bdc5eb178f4611875acaaee5b210b7b57ecc2445345ac839b7cb8  base.txz
cd /mnt/
tar xv --keep-old-files -f /mnt/tmp/base.txz
cd /
chroot /mnt/
getent passwd
[...]
erikboot:[...]:1001:1001:Boot user:/home/erikboot:/bin/sh
mkdir /home/erikboot
chown erikboot:erikboot /home/erikboot
chmod 751 /home/erikboot
chmod 1777 /tmp
exit

Prepare system

Boot into VM without mfsBSD

qemu-system-x86_64 \
    \
    -drive if=pflash,format=raw,unit=0,readonly=on,file=usr/share/edk2.git/ovmf-x64/OVMF_CODE-pure-efi.fd \
    -drive if=pflash,format=raw,unit=1,file=usr/share/edk2.git/ovmf-x64/OVMF_VARS-pure-efi.fd \
    \
    -drive format=raw,file=/dev/sda,if=virtio \
    -drive format=raw,file=/dev/sdb,if=virtio \
    -drive format=raw,file=/dev/sdc,if=virtio \
    -drive format=raw,file=/dev/sdd,if=virtio \
    -drive format=raw,file=/dev/sde,if=virtio \
    \
    -drive format=raw,file=/dev/sdf,if=virtio \
    -drive format=raw,file=/dev/sdg,if=virtio \
    -drive format=raw,file=/dev/sdh,if=virtio \
    -drive format=raw,file=/dev/sdi,if=virtio \
    -drive format=raw,file=/dev/sdj,if=virtio \
    \
    -drive format=raw,file=/dev/sdk,if=virtio \
    -drive format=raw,file=/dev/sdl,if=virtio \
    -drive format=raw,file=/dev/sdm,if=virtio \
    -drive format=raw,file=/dev/sdn,if=virtio \
    -drive format=raw,file=/dev/sdo,if=virtio \
    \
    -nic user,hostfwd=tcp::2222-:22 \
    -vnc 127.0.0.1:1,password=on -k en-us -monitor stdio \
    -boot d \
    -m 8G

Log in as root over forwarded VNC as before.

Install some packages.

pkg install doas tree neovim zsh tmux

Create config file for doas

cat > /usr/local/etc/doas.conf <<EOF
permit nopass :wheel
EOF

Disallow password login over ssh by setting KbdInteractiveAuthentication to no in /etc/ssh/sshd_config.

KbdInteractiveAuthentication no

Restart sshd

service sshd restart

Create ssh authorized keys file for non-root user

su -
mkdir .ssh
chmod 700 .ssh
cat > .ssh/authorized_keys <<EOF
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBh5RdK5ZAwRUfnV09LdkCUq+uNGK/kolX9hRx4ZShxT erikn@milkyway
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGuGKOxxMAZh/SgLpjqv7MF6Ix6GM7MJ/dRs/Z1BUgLO erikn@nova.local
EOF
exit

Partition remaining disks

zpool status
  pool: zroot
 state: ONLINE
config:

	NAME         STATE     READ WRITE CKSUM
	zroot        ONLINE       0     0     0
	  raidz3-0   ONLINE       0     0     0
	    vtbd0p2  ONLINE       0     0     0
	    vtbd1p2  ONLINE       0     0     0
	    vtbd2p2  ONLINE       0     0     0
	    vtbd3p2  ONLINE       0     0     0
	    vtbd4p2  ONLINE       0     0     0

errors: No known data errors
geom disk list
Geom name: vtbd0
Providers:
1. Name: vtbd0
   Mediasize: 6001175126016 (5.5T)
   Sectorsize: 512
   Mode: r2w2e5
   descr: (null)
   ident: (null)
   rotationrate: unknown
   fwsectors: 63
   fwheads: 16

Geom name: vtbd1
Providers:
1. Name: vtbd1
   Mediasize: 6001175126016 (5.5T)
   Sectorsize: 512
   Mode: r1w1e2
   descr: (null)
   ident: (null)
   rotationrate: unknown
   fwsectors: 63
   fwheads: 16

Geom name: vtbd2
Providers:
1. Name: vtbd2
   Mediasize: 6001175126016 (5.5T)
   Sectorsize: 512
   Mode: r1w1e2
   descr: (null)
   ident: (null)
   rotationrate: unknown
   fwsectors: 63
   fwheads: 16

Geom name: vtbd3
Providers:
1. Name: vtbd3
   Mediasize: 6001175126016 (5.5T)
   Sectorsize: 512
   Mode: r1w1e2
   descr: (null)
   ident: (null)
   rotationrate: unknown
   fwsectors: 63
   fwheads: 16

Geom name: vtbd4
Providers:
1. Name: vtbd4
   Mediasize: 6001175126016 (5.5T)
   Sectorsize: 512
   Mode: r1w1e2
   descr: (null)
   ident: (null)
   rotationrate: unknown
   fwsectors: 63
   fwheads: 16

Geom name: vtbd5
Providers:
1. Name: vtbd5
   Mediasize: 6001175126016 (5.5T)
   Sectorsize: 512
   Mode: r0w0e0
   descr: (null)
   ident: (null)
   rotationrate: unknown
   fwsectors: 63
   fwheads: 16

Geom name: vtbd6
Providers:
1. Name: vtbd6
   Mediasize: 6001175126016 (5.5T)
   Sectorsize: 512
   Mode: r0w0e0
   descr: (null)
   ident: (null)
   rotationrate: unknown
   fwsectors: 63
   fwheads: 16

Geom name: vtbd7
Providers:
1. Name: vtbd7
   Mediasize: 6001175126016 (5.5T)
   Sectorsize: 512
   Mode: r0w0e0
   descr: (null)
   ident: (null)
   rotationrate: unknown
   fwsectors: 63
   fwheads: 16

Geom name: vtbd8
Providers:
1. Name: vtbd8
   Mediasize: 6001175126016 (5.5T)
   Sectorsize: 512
   Mode: r0w0e0
   descr: (null)
   ident: (null)
   rotationrate: unknown
   fwsectors: 63
   fwheads: 16

Geom name: vtbd9
Providers:
1. Name: vtbd9
   Mediasize: 6001175126016 (5.5T)
   Sectorsize: 512
   Mode: r0w0e0
   descr: (null)
   ident: (null)
   rotationrate: unknown
   fwsectors: 63
   fwheads: 16

Geom name: vtbd10
Providers:
1. Name: vtbd10
   Mediasize: 6001175126016 (5.5T)
   Sectorsize: 512
   Mode: r0w0e0
   descr: (null)
   ident: (null)
   rotationrate: unknown
   fwsectors: 63
   fwheads: 16

Geom name: vtbd11
Providers:
1. Name: vtbd11
   Mediasize: 6001175126016 (5.5T)
   Sectorsize: 512
   Mode: r0w0e0
   descr: (null)
   ident: (null)
   rotationrate: unknown
   fwsectors: 63
   fwheads: 16

Geom name: vtbd12
Providers:
1. Name: vtbd12
   Mediasize: 6001175126016 (5.5T)
   Sectorsize: 512
   Mode: r0w0e0
   descr: (null)
   ident: (null)
   rotationrate: unknown
   fwsectors: 63
   fwheads: 16

Geom name: vtbd13
Providers:
1. Name: vtbd13
   Mediasize: 6001175126016 (5.5T)
   Sectorsize: 512
   Mode: r0w0e0
   descr: (null)
   ident: (null)
   rotationrate: unknown
   fwsectors: 63
   fwheads: 16

Geom name: vtbd14
Providers:
1. Name: vtbd14
   Mediasize: 6001175126016 (5.5T)
   Sectorsize: 512
   Mode: r0w0e0
   descr: (null)
   ident: (null)
   rotationrate: unknown
   fwsectors: 63
   fwheads: 16

Geom name: cd0
Providers:
1. Name: cd0
   Mediasize: 0 (0B)
   Sectorsize: 2048
   Mode: r0w0e0
   descr: QEMU QEMU DVD-ROM
   ident: (null)
   rotationrate: unknown
   fwsectors: 0
   fwheads: 0
gpart show vtbd0
=>         40  11721045088  vtbd0  GPT  (5.5T)
           40       532480      1  efi  (260M)
       532520         2008         - free -  (1.0M)
       534528  11720509440      2  freebsd-zfs  (5.5T)
  11721043968         1160         - free -  (580K)
doas gpart create -s gpt vtbd5
vtbd5 created

...

doas gpart create -s gpt vtbd14
vtbd14 created

Partition vtbd14

doas gpart add -t efi -s 532480 vtbd14
vtbd14p1 added
gpart show vtbd14
=>         40  11721045088  vtbd14  GPT  (5.5T)
           40       532480       1  efi  (260M)
       532520  11720512608          - free -  (5.5T)
doas gpart add -t freebsd-zfs -a 1M vtbd14
vtbd14p2 added
gpart show vtbd14
=>         40  11721045088  vtbd14  GPT  (5.5T)
           40       532480       1  efi  (260M)
       532520         2008          - free -  (1.0M)
       534528  11720509440       2  freebsd-zfs  (5.5T)
  11721043968         1160          - free -  (580K)

Then create EFI partition on vtbd13 to vtbd5

doas gpart add -t efi -s 532480 vtbd13
vtbd13p1 added

...

doas gpart add -t efi -s 532480 vtbd5
vtbd5p1 added

And then freebsd-zfs partitions for vtbd13 to vtbd5

doas gpart add -t freebsd-zfs -a 1M vtbd13
vtbd13p2 added

...

doas gpart add -t freebsd-zfs -a 1M vtbd5
vtbd5p2 added

Now we have all the partitions

gpart show
=>         40  11721045088  vtbd0  GPT  (5.5T)
           40       532480      1  efi  (260M)
       532520         2008         - free -  (1.0M)
       534528  11720509440      2  freebsd-zfs  (5.5T)
  11721043968         1160         - free -  (580K)

=>         40  11721045088  vtbd1  GPT  (5.5T)
           40       532480      1  efi  (260M)
       532520         2008         - free -  (1.0M)
       534528  11720509440      2  freebsd-zfs  (5.5T)
  11721043968         1160         - free -  (580K)

=>         40  11721045088  vtbd2  GPT  (5.5T)
           40       532480      1  efi  (260M)
       532520         2008         - free -  (1.0M)
       534528  11720509440      2  freebsd-zfs  (5.5T)
  11721043968         1160         - free -  (580K)

=>         40  11721045088  vtbd3  GPT  (5.5T)
           40       532480      1  efi  (260M)
       532520         2008         - free -  (1.0M)
       534528  11720509440      2  freebsd-zfs  (5.5T)
  11721043968         1160         - free -  (580K)

=>         40  11721045088  vtbd4  GPT  (5.5T)
           40       532480      1  efi  (260M)
       532520         2008         - free -  (1.0M)
       534528  11720509440      2  freebsd-zfs  (5.5T)
  11721043968         1160         - free -  (580K)

=>         40  11721045088  vtbd5  GPT  (5.5T)
           40       532480      1  efi  (260M)
       532520         2008         - free -  (1.0M)
       534528  11720509440      2  freebsd-zfs  (5.5T)
  11721043968         1160         - free -  (580K)

=>         40  11721045088  vtbd6  GPT  (5.5T)
           40       532480      1  efi  (260M)
       532520         2008         - free -  (1.0M)
       534528  11720509440      2  freebsd-zfs  (5.5T)
  11721043968         1160         - free -  (580K)

=>         40  11721045088  vtbd7  GPT  (5.5T)
           40       532480      1  efi  (260M)
       532520         2008         - free -  (1.0M)
       534528  11720509440      2  freebsd-zfs  (5.5T)
  11721043968         1160         - free -  (580K)

=>         40  11721045088  vtbd8  GPT  (5.5T)
           40       532480      1  efi  (260M)
       532520         2008         - free -  (1.0M)
       534528  11720509440      2  freebsd-zfs  (5.5T)
  11721043968         1160         - free -  (580K)

=>         40  11721045088  vtbd9  GPT  (5.5T)
           40       532480      1  efi  (260M)
       532520         2008         - free -  (1.0M)
       534528  11720509440      2  freebsd-zfs  (5.5T)
  11721043968         1160         - free -  (580K)

=>         40  11721045088  vtbd10  GPT  (5.5T)
           40       532480       1  efi  (260M)
       532520         2008          - free -  (1.0M)
       534528  11720509440       2  freebsd-zfs  (5.5T)
  11721043968         1160          - free -  (580K)

=>         40  11721045088  vtbd11  GPT  (5.5T)
           40       532480       1  efi  (260M)
       532520         2008          - free -  (1.0M)
       534528  11720509440       2  freebsd-zfs  (5.5T)
  11721043968         1160          - free -  (580K)

=>         40  11721045088  vtbd12  GPT  (5.5T)
           40       532480       1  efi  (260M)
       532520         2008          - free -  (1.0M)
       534528  11720509440       2  freebsd-zfs  (5.5T)
  11721043968         1160          - free -  (580K)

=>         40  11721045088  vtbd13  GPT  (5.5T)
           40       532480       1  efi  (260M)
       532520         2008          - free -  (1.0M)
       534528  11720509440       2  freebsd-zfs  (5.5T)
  11721043968         1160          - free -  (580K)

=>         40  11721045088  vtbd14  GPT  (5.5T)
           40       532480       1  efi  (260M)
       532520         2008          - free -  (1.0M)
       534528  11720509440       2  freebsd-zfs  (5.5T)
  11721043968         1160          - free -  (580K)

Add remaining vdevs to pool

doas zpool add -f zroot raidz3 vtbd5p2 vtbd6p2 vtbd7p2 vtbd8p2 vtbd9p2
zpool status
  pool: zroot
 state: ONLINE
config:

	NAME         STATE     READ WRITE CKSUM
	zroot        ONLINE       0     0     0
	  raidz3-0   ONLINE       0     0     0
	    vtbd0p2  ONLINE       0     0     0
	    vtbd1p2  ONLINE       0     0     0
	    vtbd2p2  ONLINE       0     0     0
	    vtbd3p2  ONLINE       0     0     0
	    vtbd4p2  ONLINE       0     0     0
	  raidz3-1   ONLINE       0     0     0
	    vtbd5p2  ONLINE       0     0     0
	    vtbd6p2  ONLINE       0     0     0
	    vtbd7p2  ONLINE       0     0     0
	    vtbd8p2  ONLINE       0     0     0
	    vtbd9p2  ONLINE       0     0     0
doas zpool add -f zroot raidz3 vtbd10p2 vtbd11p2 vtbd12p2 vtbd13p2 vtbd14p2
zpool status
  pool: zroot
 state: ONLINE
config:

	NAME          STATE     READ WRITE CKSUM
	zroot         ONLINE       0     0     0
	  raidz3-0    ONLINE       0     0     0
	    vtbd0p2   ONLINE       0     0     0
	    vtbd1p2   ONLINE       0     0     0
	    vtbd2p2   ONLINE       0     0     0
	    vtbd3p2   ONLINE       0     0     0
	    vtbd4p2   ONLINE       0     0     0
	  raidz3-1    ONLINE       0     0     0
	    vtbd5p2   ONLINE       0     0     0
	    vtbd6p2   ONLINE       0     0     0
	    vtbd7p2   ONLINE       0     0     0
	    vtbd8p2   ONLINE       0     0     0
	    vtbd9p2   ONLINE       0     0     0
	  raidz3-2    ONLINE       0     0     0
	    vtbd10p2  ONLINE       0     0     0
	    vtbd11p2  ONLINE       0     0     0
	    vtbd12p2  ONLINE       0     0     0
	    vtbd13p2  ONLINE       0     0     0
	    vtbd14p2  ONLINE       0     0     0
zpool list
NAME    SIZE  ALLOC   FREE  CKPOINT  EXPANDSZ   FRAG    CAP  DEDUP    HEALTH  ALTROOT
zroot  81.8T  2.42G  81.8T        -         -     0%     0%  1.00x    ONLINE  -
zfs list
NAME                 USED  AVAIL  REFER  MOUNTPOINT
zroot                986M  32.5T   153K  /zroot
zroot/ROOT           984M  32.5T   153K  none
zroot/ROOT/default   983M  32.5T   983M  /

Reservation

Create a dataset that will reserve 20% of the capacity of the pool, as per recommendation from Michael W Lucas in the book FreeBSD Mastery: Advanced ZFS.

doas zfs create -o refreservation=6.5T -o canmount=off -o readonly=on -o mountpoint=none zroot/reservation
zfs list
NAME                 USED  AVAIL  REFER  MOUNTPOINT
zroot               6.50T  26.0T   153K  /zroot
zroot/ROOT           984M  26.0T   153K  none
zroot/ROOT/default   983M  26.0T   983M  /
zroot/reservation   6.50T  32.5T   153K  none

Prepare and mount encrypted dataset for "inner"

doas zfs create -o mountpoint=none -o encryption=on -o keyformat=passphrase zroot/IROOT
Enter new passphrase:
Re-enter new passphrase:
doas zfs create -o mountpoint=none zroot/IROOT/default
zfs list -o name,used,avail,refer,mountpoint,encryption,keyformat
NAME                  USED  AVAIL  REFER  MOUNTPOINT  ENCRYPTION   KEYFORMAT
zroot                6.50T  26.0T   153K  /zroot      off          none
zroot/IROOT           586K  26.0T   293K  none        aes-256-gcm  passphrase
zroot/IROOT/default   293K  26.0T   293K  none        aes-256-gcm  passphrase
zroot/ROOT            984M  26.0T   153K  none        off          none
zroot/ROOT/default    983M  26.0T   983M  /           off          none
zroot/reservation    6.50T  32.5T   153K  none        off          none
doas zfs set -u mountpoint=/mnt zroot/IROOT/default
doas zfs mount zroot/IROOT/default
mount
zroot/ROOT/default on / (zfs, local, noatime, nfsv4acls)
devfs on /dev (devfs)
/dev/gpt/efiboot0 on /boot/efi (msdosfs, local)
zroot on /zroot (zfs, local, noatime, nfsv4acls)
zroot/IROOT/default on /mnt (zfs, local, noatime, nfsv4acls)

Install "inner"

doas bsdinstall

Choose hostname as inner.

On the partitioning step, choose "Shell" ("Open a shell and partition by hand"). We've already done the partitioning and mounted the target so proceed to exit.

exit

The installer will now extract the system.

After it finishes, exit the installer and have a look at the extracted files.

ls -al /mnt
drwxr-xr-x  19 root wheel   24 May  1 03:11 .
drwxr-xr-x  20 root wheel   25 May  1 02:45 ..
-rw-r--r--   2 root wheel 1011 Nov 10 09:11 .cshrc
-rw-r--r--   2 root wheel  495 Nov 10 09:11 .profile
-r--r--r--   1 root wheel 6109 Nov 10 09:49 COPYRIGHT
drwxr-xr-x   2 root wheel   49 Nov 10 09:11 bin
drwxr-xr-x  14 root wheel   70 May  1 03:11 boot
dr-xr-xr-x   2 root wheel    3 May  1 03:10 dev
-rw-------   1 root wheel 4096 May  1 03:11 entropy
drwxr-xr-x  30 root wheel  107 May  1 03:11 etc
drwxr-xr-x   3 root wheel    3 May  1 03:11 home
drwxr-xr-x   4 root wheel   78 Nov 10 09:17 lib
drwxr-xr-x   3 root wheel    5 Nov 10 09:11 libexec
drwxr-xr-x   2 root wheel    2 Nov 10 08:48 media
drwxr-xr-x   2 root wheel    2 Nov 10 08:48 mnt
drwxr-xr-x   2 root wheel    2 Nov 10 08:48 net
dr-xr-xr-x   2 root wheel    2 Nov 10 08:48 proc
drwxr-xr-x   2 root wheel  150 Nov 10 09:15 rescue
drwxr-x---   2 root wheel    7 Nov 10 09:49 root
drwxr-xr-x   2 root wheel  150 Nov 10 09:44 sbin
lrwxr-xr-x   1 root wheel   11 Nov 10 08:48 sys -> usr/src/sys
drwxrwxrwt   2 root wheel    2 Nov 10 08:48 tmp
drwxr-xr-x  14 root wheel   14 Nov 10 10:02 usr
drwxr-xr-x  24 root wheel   24 Nov 10 08:48 var

Give same hostid to inner as outer has, so that zpool import will think pool has not been used by a different system.

doas cp /etc/hostid /mnt/etc/hostid

And copy the authorized keys to the inner user.

mkdir /mnt/home/erikn/.ssh/
chown 1001:1001 /mnt/home/erikn/.ssh/
chmod 700 /mnt/home/erikn/.ssh/
cp ~erikboot/.ssh/authorized_keys /mnt/home/erikn/.ssh/authorized_keys

Edit rc conf files of outer and inner.

doas vim /etc/rc.conf
clear_tmp_enable="YES"
syslogd_flags="-ss"
hostname="stage4"

# Used when booting in Qemu
ifconfig_em0="DHCP"
ifconfig_em0_ipv6="inet6 accept_rtadv"

# Used when booting on hardware
ifconfig_igb0_name="extif"
ifconfig_extif="DHCP"
ifconfig_extif_ipv6="inet6 2f00::ba22 prefixlen 80"
ipv6_defaultrouter="fe80::1%extif"

local_unbound_enable="YES"

sshd_enable="YES"

ntpd_enable="YES"
ntpd_sync_on_start="YES"

moused_nondefault_enable="NO"

# Set dumpdev to "AUTO" to enable crash dumps, "NO" to disable
dumpdev="AUTO"

zfs_enable="YES"
doas vim /mnt/etc/rc.conf
clear_tmp_enable="YES"
syslogd_flags="-ss"
hostname="inner"

# Used when booting in Qemu
ifconfig_em0="DHCP"
ifconfig_em0_ipv6="inet6 accept_rtadv"

# Used when booting on hardware
ifconfig_igb0_name="extif"
ifconfig_extif="DHCP"
ifconfig_extif_ipv6="inet6 2f00::1279:9d43 prefixlen 80"
ipv6_defaultrouter="fe80::1%extif"

local_unbound_enable="YES"

sshd_enable="YES"

ntpd_enable="YES"
ntpd_sync_on_start="YES"

moused_nondefault_enable="NO"

# Set dumpdev to "AUTO" to enable crash dumps, "NO" to disable
dumpdev="AUTO"

zfs_enable="YES"

wireguard_enable="YES"
wireguard_interfaces="wg0"

jail_enable="YES"

Power off the VM, and then power it on and ssh into it again.

Then, unset mountpoint for inner

doas zfs set mountpoint=none zroot/IROOT/default

Decrypt it

doas zfs load-key zroot/IROOT
Enter passphrase for 'zroot/IROOT':

And attempt to reboot into it

doas kenv vfs.root.mountfrom="zfs:zroot/IROOT/default"
doas reboot -r

If you're watching on VNC you'll see that it says

Trying to mount root from zfs:zroot/IROOT/default []...

and after a little bit of time you should see that it gives the login prompt with the hostname of the inner system

FreeBSD/amd64 (inner) (ttyv0)

login:

If you now try to ssh into it, you'll be met with an expected warning that host identification has changed.

Accept the modified id (keep in mind that the outer will then later be warned about too until we do something about that.)

Disallow password login over ssh by setting KbdInteractiveAuthentication to no in /etc/ssh/sshd_config on the inner system.

KbdInteractiveAuthentication no

Moment of truth

Reboot the host machine and you should be able to ssh into outer system.

Doesn't boot?

Mine didn't boot.

Maybe EFI boot is not enabled?

https://docs.hetzner.com/robot/dedicated-server/operating-systems/uefi/

I sent a support ticket. Waiting to see if they can turn it on for me.

But I should have checked the output of efibootmgr first, from the Hetznet Rescue System.

Success ✌🏻

Hetzner enabled UEFI on my server and after a reboot it works flawlessly 😎

Fixing problems

If problems arise booting into the system, for example after a system upgrade, boot the server into rescue mode again and ssh into it. Then

wget https://mfsbsd.vx.sk/files/iso/14/amd64/mfsbsd-14.0-RELEASE-amd64.iso

qemu-system-x86_64 \
    -cdrom mfsbsd-14.0-RELEASE-amd64.iso \
    \
    -drive format=raw,file=/dev/sda,if=virtio \
    -drive format=raw,file=/dev/sdb,if=virtio \
    -drive format=raw,file=/dev/sdc,if=virtio \
    -drive format=raw,file=/dev/sdd,if=virtio \
    -drive format=raw,file=/dev/sde,if=virtio \
    \
    -drive format=raw,file=/dev/sdf,if=virtio \
    -drive format=raw,file=/dev/sdg,if=virtio \
    -drive format=raw,file=/dev/sdh,if=virtio \
    -drive format=raw,file=/dev/sdi,if=virtio \
    -drive format=raw,file=/dev/sdj,if=virtio \
    \
    -drive format=raw,file=/dev/sdk,if=virtio \
    -drive format=raw,file=/dev/sdl,if=virtio \
    -drive format=raw,file=/dev/sdm,if=virtio \
    -drive format=raw,file=/dev/sdn,if=virtio \
    -drive format=raw,file=/dev/sdo,if=virtio \
    \
    -display curses \
    -boot d \
    -m 8G

And then once inside the VM, import the ZFS pool with altroot specified

zpool import -o altroot=/mnt -f zroot

Then take it from there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment