AuxPower1U: Features

This is post 2 in the series (next: Case Selection, previous: Requirements).


In my previous post, I split all my desires into two categories: requirements and features; requirements being something mandatory while features being something that is nice to have. In reality, the line between them isn’t really as clear so you can view this just as an addition to the first post.

When it comes to features, the main externally visible functionality of my consolidated power supply box will be the ability to reset devices from the front as I currently have with ResetBox.

While this is a requirement, the exact button count is a bit more flexible since the primary function of them is to provide a quick reset of the Internet for my family when I’m not around. Considering that, the minimum would be something like this:

  • Button 1: Arris SURFboard Modem
  • Button 2: Mikrotik hAP ac, Mikrotik Audience, and Mikrotik hAP ax3

However, while I have other means of resetting devices, I also like buttons so my desired setup might be something like this:

  • Button 1: Arris SURFboard Modem
  • Button 2: Mikrotik hAP ac, Mikrotik Audience, and Mikrotik hAP ax3
  • Button 3: Dell OptiPlex 3050 Micro
  • Button 4: Intel NUC

Let’s add one more button to account for possible future needs, and this brings us up to a total of 5. Please note there is no requirement that the button count matches the physical layout (i.e., one button could reset two different outputs), but I like to have it setup like that nonetheless. It just simplifies configuration immensely if I keep that link.

Functionality of buttons is to remain the same as it is for ResetBox; i.e., they should handle brief touches without any action, and reset should be “hidden” behind a longer press. Also, if one keps pressing button for a long duration (e.g., more than 10 seconds), the reset should be cancelled.

I might as well try to include some monitoring for both voltage and current. This is easy enough to implement, and it would be a shame to miss such opportunity. As not to go overboard, just monitoring output lines will be sufficient since I can already see that monitoring both input and output would take a lot of board space.

In order to display that data, I would definitely like to go with OLED, albeit I can already see mounting it on the front will be a challenge no matter which case I select. Thus, I might not bother making it user-visible, but I would still like to have it on board so my development and troubleshooting can benefit from it. I trust 128x32 will do.

For real-time monitoring, I would like to have either UART, RS-232, RS-485, or CAN-bus output. Since each of those requires different components, I would need to make that decision eventually but not necessarily now. I am tentatively leaning toward CAN-bus due to its resilience; but let’s hold the final decision for now.

Lastly, having an idea of the temperature might be beneficial. While precision is not really important, there should be a sensor somewhere on the board that will give us a ballpark figure of how hot we’re running.

That’s all I really want from this project. Now, onto figuring out the details.

AuxPower1U: Requirements

This is post 1 in the series (next: Features).


As part of my home server setup, I have a few devices that have “free-floating” power supplies. For example, my modem, my wireless PoE adapters, and a few test boxes all have their power supply pushed into rack’s nooks and crannies. Even worse, since I want to have the capability to reset them, they are connected via ResetBox (or its type-C variant) making cable situation even messier. It’s way beyond time to sort that out!

In this blog series, I will go over what’s needed to design a nice 1U box that can fit all (or as many as I can) different power supplies togethe. Their outputs should be resettable by physical buttons on the device’s front. Since I actually didn’t finish the project as I’m typing this, expect the series not only to last a few months while I gather all necessary equipment but also for my “specifications” to shift slightly as I discover new things or rethink my old ways.

At first, let’s look at what power supplies we have currently:

  • 12V 25W: Arris SURFboard Modem
  • 22V-57V 20W: Mikrotik hAP ac
  • 24-57V 30W: Mikrotik Audience
  • 12-28V 40W: Mikrotik hAP ax3
  • 19.5V 65W: Dell OptiPlex 3050 Micro
  • 12-19V 65W: Intel NUC
  • 54V 15W: Netgear GS305EPP (150W max)

If I squint hard enough, there are three distinct power supplies to use there; the first one being a simple 12V power supply for modem. Power usage will be really low on this one, so any hardware we place will run from it too.

To the second power supply, I had to give a bit more thought. Without question, it has to run of the UPS but its target voltage is a bit of an unknown. Currently, I am running my hAP ac and Audience from a 20V type-C power supply, and Audience doesn’t seem to love it as any minor transient causes reset. And yes, officially Audience doesn’t run that low, but I got lucky, I guess. Previously, I was running my WiFi routers on 48V and both were fine with that; so there are my two daya points. Considering other devices, I was leaning toward selecting 24V as a second power supply.

However, that leaves my NetGear PoE switch a bit of a loner and outside of The Box. For it, I would need to provide a proper 55V PoE power supply or at least 48V if stars align. The downside of this approach is that it leaves my Mikrotik hAP ax3 either on 12V or for the last, non-UPS power supply. But the upside is that it allows for a bit of future-proofing.

That leaves 2 non-UPS computers. Why are they not using UPS? Well, in my setup, UPS power is really limited and is reserved for only two categories: my main server and my Internet delivery devices (modem, router, WiFi APs). Anything else just needs to handle a power loss. These two computers fall into “anything else” category.

Intel NUC, we can already see, is flexible with a power supply specification. When it comes to standard industrial voltages, it can handle both 12V and 15V inputs. However, it Dell bretheren officially are not that flexible. While there is a possibility my 3050 micro would work on 15V, anything higher is a no-go. And since Dell authenticates its chargers, to figure this out, I’d need to trick it first into accepting such voltage - all of which smells like another sub-project. :)

If Dell doesn’t want to cooperate, the only way forward would be to use one of many buck modules intended for RVs that brings 24V to 19V. I would really like to avoid this, if possible, because the last thing I need is yet another power supply. However, it’s good to have options. And yes, ideally, I would find a 19V power supply; but I have a feeling that finding one that can be properly mounted inside a 1U case is not going to happen.

This brings us to the following rough power supply distribution (with a bit of derating on power specification):

  • 12V 150W (100W if I kick out Mikrotik hAP ax3 from my network):

    • 25W: Arris SURFboard Modem
    • 25W: Control boards
    • 40W: Mikrotik hAP ax3
  • 15V/19V/24V 200W (150W should be realistically more than fine):

    • 65W: Dell OptiPlex 3050 Micro
    • 65W: Intel NUC
  • 48V/55V 100W (going higher than this might be good for the future PoE devices):

    • 20W: Mikrotik hAP ac
    • 30W: Mikrotik Audience
    • 15W: Netgear GS30s5EPP

There is an alternative at a bit lower voltage:

  • 12V 75W

    • 25W: Arris SURFboard Modem
    • 25W: Control boards
  • 15V/19V/24V 200W (150W should be realistically more than fine):

    • 65W: Dell OptiPlex 3050 Micro
    • 65W: Intel NUC
  • 24V 150W

    • 20W: Mikrotik hAP ac
    • 30W: Mikrotik Audience
    • 40W: Mikrotik hAP ax3
  • Out-of-scope

    • 15W: Netgear GS30s5EPP

I am strongly leaning toward option 1, but option 2 is a good alternative. And yes, Mikrotik is not as power hungry as it seems above; I’ve never seen it reach its maximum power usage. However, since I really love my network, I use those numbers to bring an additional margin to the dimensioning process. If I find a nice power supply that’s slightly below what I need, I will get it and not worry about it. But, before I get to that, this fudged accounting provides more visibility into what brings the most value.

For various protection circuits, I’, going to rely onto power supplies to protect themselves. Thus, at minimum, I expect any selected power supply to have over-voltage, over-current, short-circuit, and over-temperature protection built-in.

When it comes to controlling this, I would say that ability to reset my Modem and WiFi is a must. And these can be two buttons as I want to be able to separately restart modem. For the hAP ax3, I don’t care as much since it has “under test” status in my network at this time. But let’s argue that it needs to be a separate button. At this time, I do control each of my computers inside the rack via smart plug so moving them into The Box, I might want to see a button for each, but I wouldn’t really mind if both go down at the same time. This brings the total number of reset buttons to somewhere between a minimum of three and a maximum of seven.

Lastly, this blog post leaves us with the following action items I will probably get around to solving:

  • Chck can Netgear GS305EPP work on 48V
  • Make trigger board for Dell
  • Check does Dell Optiplex 3050 Micro work on 15V

Manually Installing Encrypted ZFS on Ubuntu 24.04

For my daily driver I’m fortunate enough to have mirrored ZFS setup, my secondary machine has only a single SSD slot. While that makes mirror setup unproductive, I still want to use ZFS. Yes, it cannot automatically correct errors but it can at least help me know about them. And that’s before considering datasets, quotas, and beautiful snapshots.

For this setup I will use LUKS instead of the native ZFS encryption. Computer is fast enough that I don’t notice difference during daily work and total encryption is worth it for me.

As before, if you are beginner with ZFS, you might want to use Ubuntu’s built-in ZFS installatiion method. It will result in similar enough setup but without all these manual steps.

Why do I use this setup? Well, I like to setup my own partitions and I definitely love setting up my own datasets. In addition, this is the only way to make Ubuntu do a very minimal install. Lastly, I like to use hibernation with my computers and setting this during manual installation is often easier than sorting issues later.

With preamble out of the way, let’s go over all the steps to make it happen.

The first step is to boot into the USB installation and use “Try Ubuntu” option. Once on the a desktop, we want to open a terminal, and, since all further commands are going to need root access, we can start with that.

sudo -i

Next step should be setting up a few variables - disk, hostname, and username. This way we can use them going forward and avoid accidental mistakes. Just make sure to replace these values with ones appropriate for your system.

DISK=/dev/disk/by-id/<whatever>
HOST=radagast
USERNAME=josip

I want to partition disk into 4 partitions. The first two partitions are unencrypted and in charge of booting (boot + EFI). While I love encryption, I almost never encrypt the boot partitions in order to make my life easier as you cannot seamlessly integrate the boot partition password prompt with the later password prompt. Thus encrypted boot would require you to type the password twice (or thrice if you decide to use native ZFS encryption on top of that). Third partition is largest and will contain all user data. The last partition is actually swap that we need for hibernation support. Since we’re using SSD, its position doesn’t matter (on the hard drive you definitely want it way ahead) so I put it these days at the end to match my mirrored ZFS setup.

All these requirements come in the following few partitioning commands:

DISK_LASTSECTOR=$(( `blockdev --getsz $DISK` / 2048 * 2048 - 2048 - 1 ))
DISK_SWAPSECTOR=$(( DISK_LASTSECTOR - 64 * 1024 * 1024 * 1024 / 512 ))

blkdiscard -f $DISK 2>/dev/null
sgdisk --zap-all                                $DISK
sgdisk -n1:1M:+255M           -t1:EF00 -c1:EFI  $DISK
sgdisk -n2:0:+1792M           -t2:8300 -c2:Boot $DISK
sgdisk -n3:0:$DISK_SWAPSECTOR -t3:8309 -c3:LUKS $DISK
sgdisk -n4:0:$DISK_LASTSECTOR -t4:8200 -c4:Swap $DISK
sgdisk --print                                  $DISK

The next step is to setup all LUKS partitions. If you paid attention, that means we need to repeat formatting a total of 2 times. Unless you want to deal with multiple password prompts, make sure to use the same password for each:

cryptsetup luksFormat -q --type luks2 \
    --sector-size 4096 \
    --cipher aes-xts-plain64 --key-size 256 \
    --pbkdf argon2i $DISK-part3

cryptsetup luksFormat -q --type luks2 \
    --sector-size 4096 \
    --cipher aes-xts-plain64 --key-size 256 \
    --pbkdf argon2i $DISK-part4

Since creating encrypted partitions doesn’t mount them, we do need this as a separate step. I like to name my LUKS devices based on partition names so we can recognize them more easily:

cryptsetup luksOpen \
    --persistent --allow-discards \
    --perf-no_write_workqueue --perf-no_read_workqueue \
    $DISK-part3 ${DISK##*/}-part3

cryptsetup luksOpen \
    --persistent --allow-discards \
    --perf-no_write_workqueue --perf-no_read_workqueue \
    $DISK-part4 ${DISK##*/}-part4

Finally, we can set up our ZFS pool with an optional step of setting quota to roughly 85% of disk capacity. Since we’re using LUKS, there’s no need to setup any ZFS keys. Name of the pool will match name of the host and it will contain several datasets to start with. Most of my stuff goes to either Data dataset for general use or to VirtualBox dataset for virtual machines. Consider this just a suggestion and a good starting point, adjust as needed:

zpool create -o ashift=12 -o autotrim=on \
    -O compression=lz4 -O normalization=formD \
    -O acltype=posixacl -O xattr=sa -O dnodesize=auto -O atime=off \
    -O quota=1600G \
    -O canmount=off -O mountpoint=none -R /mnt/install \
    ${HOST^} /dev/mapper/${DISK##*/}-part3

zfs create -o canmount=noauto -o mountpoint=/ \
    -o reservation=100G \
    ${HOST^}/System
zfs mount ${HOST^}/System

zfs create -o canmount=noauto -o mountpoint=/home \
           ${HOST^}/Home
zfs mount ${HOST^}/Home
zfs set canmount=on ${HOST^}/Home

zfs create -o canmount=noauto -o mountpoint=/Data \
           -o 28433:snapshot=360 \
           ${HOST^}/Data
zfs set canmount=on ${HOST^}/Data

zfs create -o canmount=noauto -o mountpoint=/VirtualBox \
           -o recordsize=32K \
           ${HOST^}/VirtualBox
zfs set canmount=on ${HOST^}/VirtualBox

zfs set devices=off ${HOST^}

With ZFS done, we might as well setup boot, EFI, and swap partitions too:

yes | mkfs.ext4 $DISK-part2
mkdir /mnt/install/boot
mount $DISK-part2 /mnt/install/boot/

mkfs.msdos -F 32 -n EFI -i 4d65646f $DISK-part1
mkdir /mnt/install/boot/efi
mount $DISK-part1 /mnt/install/boot/efi

mkswap /dev/mapper/${DISK##*/}-part4

At this time, I also often disable IPv6 as I’ve noticed that on some misconfigured IPv6 networks it takes ages to download packages. This step is both temporary (i.e., IPv6 is disabled only during installation) and fully optional:

sysctl -w net.ipv6.conf.all.disable_ipv6=1
sysctl -w net.ipv6.conf.default.disable_ipv6=1
sysctl -w net.ipv6.conf.lo.disable_ipv6=1

To start the fun we need to debootstrap our OS. As of this step, you must be connected to the Internet:

apt update
apt dist-upgrade --yes
apt install --yes debootstrap
debootstrap noble /mnt/install/

We can use our live system to update a few files on our new installation:

echo $HOST > /mnt/install/etc/hostname
sed "s/ubuntu/$HOST/" /etc/hosts > /mnt/install/etc/hosts
rm /mnt/install/etc/apt/sources.list
cp /etc/apt/sources.list.d/ubuntu.sources /mnt/install/etc/apt/sources.list.d/ubuntu.sources
cp /etc/netplan/*.yaml /mnt/install/etc/netplan/

At last, we’re ready to chroot into our new system.

mount --rbind /dev  /mnt/install/dev
mount --rbind /proc /mnt/install/proc
mount --rbind /sys  /mnt/install/sys
chroot /mnt/install /usr/bin/env \
    DISK=$DISK HOST=$HOST USERNAME=$USERNAME USERID=$USERID \
    bash --login

With our newly installed system running, let’s not forget to set up locale and time zone:

locale-gen --purge "en_US.UTF-8"
update-locale LANG=en_US.UTF-8 LANGUAGE=en_US
dpkg-reconfigure --frontend noninteractive locales

dpkg-reconfigure tzdata

Now we’re ready to onboard the latest Linux kernel. I find that hwe is a nice compromise between using generic and oem but you can use whichever you or your hardware prefers:

apt update
apt install --yes --no-install-recommends linux-generic-hwe-24.04 linux-headers-generic-hwe-24.04

Now we set up crypttab so our encrypted partitions are decrypted on boot:

echo "${DISK##*/}-part3 $DISK-part3 none \
      luks,discard,initramfs,keyscript=decrypt_keyctl" >> /etc/crypttab

echo "${DISK##*/}-part4 $DISK-part4 none \
      luks,discard,initramfs,keyscript=decrypt_keyctl" >> /etc/crypttab

cat /etc/crypttab

To mount all those partitions, we also need some fstab entries too. ZFS entries are not strictly needed. I just like to add them in order to hide our LUKS encrypted ZFS from the file manager:

echo "PARTUUID=$(blkid -s PARTUUID -o value $DISK-part2) \
    /boot ext4 noatime,nofail,x-systemd.device-timeout=3s 0 1" >> /etc/fstab
echo "PARTUUID=$(blkid -s PARTUUID -o value $DISK-part1) \
    /boot/efi vfat noatime,nofail,x-systemd.device-timeout=3s 0 1" >> /etc/fstab

echo "/dev/mapper/${DISK##*/}-part3 \
    none auto nofail,nosuid,nodev,noauto 0 0" >> /etc/fstab

echo "/dev/mapper/${DISK##*/}-part4 \
    swap swap nofail 0 0" >> /etc/fstab

cat /etc/fstab

On systems with a lot of RAM, I like to adjust memory settings a bit. This is inconsequential in the grand scheme of things, but I like to do it anyway. Think of it as wearing “lucky” socks:

echo "vm.swappiness=10" >> /etc/sysctl.conf
echo "vm.min_free_kbytes=1048576" >> /etc/sysctl.conf

Now we can create the boot environment:

apt install --yes zfs-initramfs cryptsetup keyutils \
                  grub-efi-amd64-signed shim-signed

update-initramfs -c -k all

And then, we can get grub going. Do note we also set up booting from swap (needed for hibernation) here too. If you’re using secure boot, bootloaded-id HAS to be Ubuntu:

apt install --yes grub-efi-amd64-signed shim-signed
sed -i "s/^GRUB_CMDLINE_LINUX_DEFAULT.*/GRUB_CMDLINE_LINUX_DEFAULT=\"quiet splash \
    RESUME=UUID=$(blkid -s UUID -o value /dev/mapper/${DISK##*/}-part4)\"/" \
    /etc/default/grub

update-grub

grub-install --target=x86_64-efi --efi-directory=/boot/efi \
             --bootloader-id=Ubuntu --recheck --no-floppy

I don’t like snap so I preemptively banish it from ever being installed:

apt remove --yes snapd 2>/dev/null
echo 'Package: snapd'    > /etc/apt/preferences.d/snapd
echo 'Pin: release *'   >> /etc/apt/preferences.d/snapd
echo 'Pin-Priority: -1' >> /etc/apt/preferences.d/snapd
apt update

And now, finally, we can install our minimal desktop environment:

apt install --yes ubuntu-desktop-minimal man

Since Firefox is a snapd package (banished), we can install it manually:

add-apt-repository --yes ppa:mozillateam/ppa
cat << 'EOF' | sed 's/^    //' | tee /etc/apt/preferences.d/mozillateamppa
    Package: firefox*
    Pin: release o=LP-PPA-mozillateam
    Pin-Priority: 501
EOF
apt update && apt install --yes firefox

Chrome aficionados, can install it too:

pushd /tmp
wget --inet4-only https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
apt install ./google-chrome-stable_current_amd64.deb
popd

For hibernation, I like to change sleep settings so that hibernation kicks in after 13 minutes of sleep:

sed -i 's/.*AllowSuspend=.*/AllowSuspend=yes/' \
    /etc/systemd/sleep.conf
sed -i 's/.*AllowHibernation=.*/AllowHibernation=yes/' \
    /etc/systemd/sleep.conf
sed -i 's/.*AllowSuspendThenHibernate=.*/AllowSuspendThenHibernate=yes/' \
    /etc/systemd/sleep.conf
sed -i 's/.*HibernateDelaySec=.*/HibernateDelaySec=13min/' \
    /etc/systemd/sleep.conf

For that we also need to do a minor lid switch configuration adjustment:

apt install -y pm-utils

sed -i 's/.*HandlePowerKey=.*/HandlePowerKey=hibernate/' \
    /etc/systemd/logind.conf
sed -i 's/.*HandleLidSwitch=.*/HandleLidSwitch=suspend-then-hibernate/' \
    /etc/systemd/logind.conf
sed -i 's/.*HandleLidSwitchExternalPower=.*/HandleLidSwitchExternalPower=ignore/' \
    /etc/systemd/logind.conf

Lastly, we need to have a user too.

adduser --disabled-password --gecos '' $USERNAME
usermod -a -G adm,cdrom,dialout,dip,lpadmin,plugdev,sudo,tty $USERNAME
echo "$USERNAME ALL=NOPASSWD:ALL" > /etc/sudoers.d/$USERNAME
passwd $USERNAME

It took a while, but we can finally exit our debootstrap environment:

exit

Let’s clean all mounted partitions and get ZFS ready for next boot:

sync
umount /mnt/install/boot/efi
umount /mnt/install/boot
mount | grep -v zfs | tac | awk '/\/mnt/ {print $3}' | xargs -i{} umount -lf {}
zpool export -a

After reboot, we should be done and our new system should boot with a password prompt.

reboot

Our newly installed system should boot now.


[2024-08-22: Automatically determining the last sector in DISK_LASTSECTOR variable] [2024-08-28: Removed manual user ID assignment]