Replace USB boot key on FreeNAS

You don’t really install FreeNAS as you do with some other OS. Instead you have a device (often an USB key) that boots FreeNAS which itself manages your data storage. Indeed why wasting some precious SATA port for a boot device when all you need is a little USB key? Especially when thits boot device is less than 2GB in size.

The problem with USB keys is that they wear out over time. So it inevitably comes a time when it must be replaced. This is what happened to me a few days ago.

The freenas-boot pool switched to a DEGRADED state because checksum error happened too frequently. Fortunately a zpool scrub freenas-boot detected no error, and it seems that zfs was still able to correct those. So instead of reflashing a new USB key and restoring my config backup, I could from FreeNAS itself create a new bootable USB key and replace replace the faulty device in the freenas-boot pool.

Let’s login on FreeNAS and plug in your new USB key. We first find and verify what is the device name for the USB key, in my case it’s da2:

$ gpart part show da2
=>       1  60088319  da2  MBR  (29G)
         1        31       - free -  (16K)
        32  60088288    1  fat32lba  (29G)

You see that MBR table and a FAT32 partition, in my case it can only be the new USB key. We will replace that with GPT partitions. This will contain a boot partition (MBR boot or EFI boot), and a ZFS partition. As root:

# Clean that MBR and create GPT partition table
gpart destroy -F da2
gpart create -s GPT da2

# If you use MBR boot
gpart add -s 64K -t freebsd-boot da2
gpart bootcode -b /boot/pmbr -p /boot/gptzfsboot -i 1 da2

# If you use EFI boot
gpart add -s 128M -t efi da2
gpart bootcode -p /boot/boot1.efifat -i 1 da2

# Create the ZFS partition
gpart add -t freebsd-zfs da2

Now it’s time to replace the device:

$ zpool replace -f freenas-boot da1p2 da2p2
cannot replace da1p2 with da2p2: device is too small
# OOPS! :(

If you got the message device is too small, that is because you’ve been trying to replace a device with another one that is smaller than the original (in term of ZFS partition size I suppose). ZFS requires that you replace a device with another with a size greater or equal to the old one. This can be a problem especially if you have a ZFS partition along with others in the partition table like here, or if the vendor announces a certain disk size but only within a certain approximation. You could result in a disk announced to be the exact same size ending up to be very slighty shorter than your original disk. And as a result you won’t be able to use it to replace any disk in your ZFS pool/vdev. That’s why some recommend to create ZFS filesystems several percents less than the available disk size.

But since we messed up, instead we will create a pool on the other USB key and use ZFS send/recv to migrate the files.

# Create the pool on the new USB key.
# Note that we have to call it freenas-boot2
# because freenas-boot already exists.
zpool create freenas-boot2 da2p2 

# Create a recursive snapshot of the original pool
# that we will use to restore the backup. 
zfs snapshot -r freenas-boot@restore

# Send the snapshot from the old to the new USB key.
zfs send -R freenas-boot@restore | zfs recv -F freenas-boot2

We also need to save the original setting for bootfs because FreeNAS has its own way of managing the boot pool. We will set this value later on the new pool.

$ zpool get bootfs
NAME          PROPERTY  VALUE                        SOURCE
freenas-boot  bootfs    freenas-boot/ROOT/11.3-U4.1  local

We still have to change that name from freenas-boot2 to freenas-boot. However we cannot do so on the running instance of FreeNAS because it already has a pool named freenas-boot. So, we will import it on another ZFS capable machine and fix its name.

(but if anyone knows how to do this live, I’m interested)

# On some other ZFS capable host (FreeBSD for instance ;) )
# import the pool and change its name back to freenas-boot.
# The -f is required because it comes from another host.
zpool import -f freenas-boot2 freenas-boot

# Configure default boot filesystem.
# Remember the value of bootfs we found before on the original pool.
zpool set bootfs=freenas-boot/ROOT/11.3-U4.1 freenas-boot

# Export the pool again.
zpool export freenas-boot

Finally you can clear the old snapshot:

zfs destroy -R freebsd-boot@restore

Hide logs from wheel users

If you have grown accustomed to FreeBSD administration, you’ve probably learned that users need to be member of the wheel group to be able to use the su command. Some in the land of GNU don’t agree so much with this way of doing and firmly believe that wheel is an instrument of power (which is true in a literal sens) but that’s another story.

In fact I am perfectly fine with this save for one little detail. By default most log files are owned by root:wheel. Altough while some of them have permission 600, a lot of them are 640 which means that members of the wheel group will be able to read them. We have basically two solutions to fix this:

  • Fix permissions in /etc/newsyslog.conf.
  • Use another group instead of wheel for the su command.

Fixing newsyslog.conf is quite easy, just replace the mode column with any permission you fancy (in our case 600). Don’t forget to restart newsyslog and fix existing permissions with find /var/log -type f -exec chmod 600 {} \;.

However that might not be enough. You see on most BSD wheel has gid 0, whereas on Linux it is root that has gid 0. Nobody is supposed to be a member of root, but it serves as a general purpose group for anything owned solely by root. As such you can often use chown 0:0 as a synonym of owned by root.

But root:root on Linux would not have the same meaning as root:wheel on BSD. In particular you can generally suppose that files owned by root:root with permission 640 on Linux are only readable by the root user but the same supposition doesn’t translate so well for us BSD users.

While I’m not keen of importing such kind of Linuxism into FreeBSD, one way to deal with it would be to use another group for su users. For this we would:

  • Create the su system group.
  • Move all members of wheel to this group.
  • Modify /etc/pam.d/su to use group=su instead of group=wheel.

Now I don’t personally do that, but I guess what you do it’s your business.

Dedibox serial shell access

If you cannot access the shell on a FreeBSD dedibox from the online.net console, here is a quick tip. Create an alternate root account with csh or tcsh and access ttyu1 on this account instead. That’s almost precisely what the toor account is made for, except that we generally leave the default shell on root.

No idea why sh, bash and zsh are not working through the serial connection, more doesn’t work either, but vi does. Probably a termcap thingy ? If anyone has a clue…

Use notify-send as root

The automount script is a neat devd based automounter for FreeBSD. Just pkg install automount and all your removable media will mount themselves automatically in /media when you plug them in. It’s very clean. You may also check vumount, a short script that I made to list all removable media and remove the mount point when you unmount them.

It’s possible to configure automount to send a notification to your desktop using notify-send from libnotify. What it does exactly is (as root):

env DISPLAY=:0 notify-send automount "Device '${1}' mounted on '${3}' directory."

Except that it doesn’t work… I started dbus-monitor and tried notify-send as root (from ttyv2) but didn’t receive anything and notify-send did not complain. So I tried to start dbus-monitor from root instead, in the hope that it would be a bit more verbose than notify-send. I got this error message:

Failed to open connection to session message bus: Did not receive a reply. Possible causes include: the remote application did not send a reply, the message bus security policy blocked the reply, the reply timeout expired, or the network connection was broken.

By default DBus sessions are private and don’t accept connections from other users than the one that own the bus, even root. The solution is to configure DBus to allow root on the session bus. Edit /usr/local/etc/dbus-1/session.conf and add this line to the default policy:

<allow user="root"/>

But you still need tell DBus how to connect to the session bus. To do so you have to specify the session bus address in DBUS_SESSION_BUS_ADDRESS. Fortunately automount is a shell script, so you can fetch and export the bus address from its configuration file. Just add this to /usr/local/etc/automount.conf:

# Load DBus session bus address
DBUS_USER=your-user
if [ -d /home/$DBUS_USER/.dbus/session-bus ]
then
  dbus_file=$(ls -t1 /home/$DBUS_USER/.dbus/session-bus | head -n1)
  export DBUS_SESSION_BUS_ADDRESS=$(cat /home/$DBUS_USER/.dbus/session-bus/$dbus_file | \
                                    grep "DBUS_SESSION_BUS_ADDRESS=" | \
                                    sed 's/[A-Z_]*=//')
fi

Ext4 on root filesystem

Since a few days the last version of squeeze’s debian installer (which runs sid I think) is able to install on an ext4 filesystem. Which means that the ext4 filesystem could now easily be used as root filesystem.
When the installation is completed a problem might appear at the first boot. The kernel will simply panic complaining that he cannot mount the root filesystem. Actually the squeeze’s default kernel is currently 2.6.26 and this version does not fully support ext4.
The quick fix is to use the rescue part of the debian installer which will chroot you in your root partition and let you temporarily change your apt’s sources from squeeze to unstable so that you can install the last unstable kernel (>= 2.6.28) wich is ext4 capable.
If you try to install with the stable debian installer or any GNU/Linux distribution which is not ext4 capable here is a trick to use ext4 as your root filesystem.
First install your system with an ext3 filesystem and let /dev/sda1 be that partition. Then install grub2 and make sure it works perfectly. Then boot on a live system which is ext4 capable, backup all files so you can create an ext4 filesystem on /dev/sda1, put your backup back in it. Then change fstab in /dev/sda1 and replace ext3 with ext4 for the root filesystem. Check that grub.cfg in /dev/sda1 doesn’t use UUID to pass the root parameter to the kernel (actually they’ll change since you made a new filesystem) and use root=/dev/sda1 instead. Now reboot and your system should be running ext4 on the root filesystem.