Ben Gregory's Blog

My FreeBSD Setup (for Desktop Use)

Many of the personal FreeBSD blogs that I’ve read online seem to extol the design, utilities (bhyve, jails, etc.), and rock-solid stability of FreeBSD. My reasons aren’t nearly as well-founded; I simply wanted a systemd-free OS with minimal processes running.

I actually would prefer to use the Linux kernel as I find the support for hardware and gaming much better, but I can’t find a distribution that I’m 100% satisfied with in daily use. For the most part I like Debian, but having systemd running in the background really bothers me at some subconscious level. I’m not opposed to the idea of systemd. In fact I think it’s probably the best thing to happen to Linux in terms of making the system easier to approach for your average user or developer. However, I have some deep-seated desire to fully understand everything running on my system, even at the expense of usability.

Yes, WiFi speed is an issue on FreeBSD (although I’m usually hardwired so this doesn’t bother me too much). Yes, most open source tools are designed for Linux and require weird or hacky workarounds in FreeBSD. Yes, hardware isn’t nearly as well supported as it is Linux. But all that aside, I’ve found the system to be much easier to understand. Applications and files are stored in logical areas of the system and don’t deviate (as far as I can tell). The amount of processes running when I look at htop are manageable and make sense (i.e. only things I started, or that were started as a direct result of something I started). These things are important to me, and I’m willing to trade a lot for the peace of mind that comes with a system I can understand.

There are plenty of systemd-free Linux distributions, and I must admit I’ve tried almost all of them. If I’m being honest they all worked great; I easily could have stayed on Void Linux. The only thing that steered me to FreeBSD was the community size and the backing/support. FreeBSD is not a small project, despite its desktop usage being much smaller when compared to Linux. I trust the FreeBSD development team and package maintainers to vet changes much more than I do a smaller Linux distribution. This isn’t meant to bash the Linux distributions, it’s simply just a matter of who has the most funding and the most dedicated resources to handle something as taxing as package maintenance. But enough about my reasons for switching to FreeBSD (I’ll cover this in another post), let’s get to some of the good stuff.


Overview

I won’t bore you any longer with walls of text. The following sections are areas that I needed to research or tinker with in order to get a similar workstation experience that I had with Linux distributions. I thought documenting the steps I had to take would benefit others just as much as my future self.

Firewall

NOTE: All of this information can be found in the handbook. If you are unsure about the commands or want to get more information, please check there.

If you’re a firewall expert or somebody with prior knowledge in setting up a firewall manually, you’ll probably find FreeBSD’s firewall implementations straightforward and easy to work with. This section is not for you. If you’re like me and used to rely on a apt install ufw;systemctl enable ufw, then continue reading.

FreeBSD includes 3 firewalls as part of a standard install: PF, IPFW, and IPFILTER. I’m sure they’re all great in their own ways, but I found PF to have the most documentation and also the easiest to work with. From what I’ve gathered, the FreeBSD version of PF is a fork of OpenBSD PF, and has diverged enough over the years that it’s just different enough to be worth a mention. For my simple usecases I didn’t run into any issues referencing OpenBSD documentation for PF, but if you’re doing something more advanced then keep this in mind.

Simple PF workstation rules

To use PF as your firewall, create the file /etc/pf.conf with the following contents:

set skip on lo0

block in all
pass out all keep state

set skip on lo0 is considered best practice in the OpenBSD PF docs. Without this, you won’t be able to send traffic through your loopback interface. This means things like running a test server through localhost:[PORT] will hang/fail.

block in all is also a recommended best practice in the OpenBSD PF docs. This statement simply denies all incoming traffic by default, allowing you to write your rules as explicit allow statements, which is much easier than trying to do the inverse.

pass out all keep state allows your machine to send traffic out while also retaining state information. My loose understanding of this is that the state information is stored in a state table during the initial connection, which allows return traffic to skip over any additional rule processing. This improves packet filtering performance and allows you to write simpler rules.

Next enable the firewall and start it:

sysrc pf_enable=yes
service pf start

That’s it! You have a simple workstation firewall configured and you should be good to go. I probably shouldn’t have to say this, but please…please make sure you double check what I’ve posted before you run this on your own system. I’m pretty confident these steps will produce a secure firewall for a simple workstation, but always do your own research for security related settings.

See the handbook for more information.

Cursor themes

I’m not sure if it’s just my specific desktop configuration or an obscure bug, but getting i3wm to respect my .Xresources file’s cursor size was a major pain. No matter what I tried, i3wm would not register the Xcursor.size: 16 setting in my .Xresources file. The cursor size would be correct within GTK and QT applications (Firefox, etc.), but not on any i3wm specific resources (desktop, status bar, etc.).

After trying everything in the arch wiki related to setting up cursor themes, I finally found an obscure Reddit comment that led me to the solution:

mkdir ~/.icons
ln -s /usr/local/share/icons/Adwaita ~/.icons/default

For some unknown reason, storing the cursor theme settings in ~/.icons/default/index.theme, ~/.local/share/icons/default/index.theme, or /usr/local/share/icons/default/index.theme failed to register. Only when symlinking to the cursor theme from a ~/.icons folder was the ~/.Xresources file respected.

NOTE: Even a symlink to ~/.local/share/icons/default failed to register. The symlink can only exist under ~/.icons.

YubiKey setup

I use a YubiKey for every online service that supports it. In almost every Linux distribution that I’ve tried, my YubiKey was detected and supported by Firefox out of the box through udev rules which were usually provided by a package such as libfido2. Most Linux distributions package this library with Firefox, so manually installing it is almost never required. This is not the case with FreeBSD.

Years ago I remember struggling with setting up a YubiKey on FreeBSD, but luckily somebody has taken the time to document the steps required for almost every configuration you may be interested in for using your YubiKey on FreeBSD. The GitHub link can be found here. If all you’re interested in doing is getting your YubiKey to work with Firefox, the steps required are found here. In short, perform the following actions:

  1. Install libu2f-host (the YubiCo C library) and u2f-devd (the FreeBSD specific devd rules for U2F devices):
pkg install libu2f-host u2f-devd
  1. Add your user to the u2f group:
pw group mod u2f -m [USER]
  1. Reboot your machine. In theory you should be able to just restart the devd service, but this didn’t work for me until I fully rebooted.

Suspend after time

Like Linux, FreeBSD has awesome support for Thinkpad laptops. I personally use a t480 and only have minor issues (namely audio through HDMI). If you’re used to an operating system that has systemd available (or the alternative elogind), you’ve probably never had to worry about issuing a suspend command as a non-privileged user, however things are a bit different in FreeBSD.

If all you want to do is poweroff or reboot, just ensure your user is a member of the operator group. Please take care to read the manual, as there is a specific note about the security implications of adding users to operator.

If you want to suspend your machine in S3 state, you can run the command acpiconf -s 3, which will require sudo privileges due to the commands issued as part of the suspend process (acpiconf itself doesn’t require additional privileges). If that’s too much typing, the /usr/sbin/zzz utility is available which simplifies the suspend command (it does other things as well, so please dig into it more if you’re curious). Binding either of the above commands to a key in something like i3wm is simple enough, but what if you wanted to sleep after a specified amount of time without needing to provide a password for sudo?

This is less of a FreeBSD workflow and more of a “platform agnostic” way of doing things, but I use 2 tools to lock and suspend my machine: xss-lock and xautolock. These tools are available on every *nix based platform I’ve used, and that has kept my workflow largely the same. Here is an example of how I use these tools in my i3wm config file:

# screen locking/timeout
## NOTE: If we let xautolock handle both the locking and suspend actions, it
## appears that the suspend (killtime) timer will begin counting even if the
## lock was initiated manually. For this reason, we're going to run 2 timers -
## xss-lock for the lock timer, and xautolock for the suspend timer

## lock after 45 minutes - time is determined by DPMS in .xinitrc. xss-lock will
## also lock on systemd events (such as suspend)
exec --no-startup-id xss-lock --transfer-sleep-lock -- i3lock --nofork
## suspend after 1 hour
exec --no-startup-id xautolock -time 60 -locker "sudo zzz" -detectsleep
## manually lock the screen
set $lock exec --no-startup-id i3lock -n
bindsym $mod+Escape $lock

There’s quite a bit going on here, but high-level this is using xss-lock to lock on either systemd events (not relevant here) or DPMS screensaver events (which I set in my .xinitrc). If you’re not familiar with DPMS or how to change the screensaver timeout settings, take a look at the arch wiki (specifically the runtime settings with xset s [TIMEOUT]). A separate timer is also spun up using xautolock which handles running the suspend command. In this case, the suspend command is sudo zzz (as described above), but this exact same workflow can be used on a systemd system by running something like systemctl suspend.

We’re closer, but we still have an issue - sudo zzz will ask for a password. Although the following workaround is not my favorite solution, in the absence of systemd or a desktop environment leveraging polkit, we have pretty limited options to execute commands that require escalated privileges. I didn’t try, but I saw where others reported even adding your user account to the operator group didn’t provide access to the suspend subset of commands, so even that approach was out.

Using the linked mailing list thread above as reference, the only suitable approach I could come up with was to grant NOPASSWD access through sudo to the zzz command. To do this, I created an additional sudoers file under /usr/local/etc/sudoers.d/ named zzz using the following command:

sudo visudo -f /usr/local/etc/sudoers.d/zzz

The contents of that file looks like this:

%wheel ALL=(ALL:ALL) NOPASSWD: /usr/sbin/zzz

If you’re not familiar with the format of the sudoers file, I encourage you to look into this on your own. For something as important as your sudo configuration, please do not get in the habit of just blindly copying commands. :)

Now to verify that we can run sudo zzz without needing to provide a password, let’s check our sudo permissions with the following command:

me@dev:~$ sudo -l
User me may run the following commands on dev:
	(ALL : ALL) ALL
	(ALL : ALL) NOPASSWD: /usr/sbin/zzz

Nice! Our new sudoer file seems to have taken effect. At this point, the command issued after the time expires in xautolock should now execute without prompting us for a password. What should happen is that xss-lock will lock the screen after 45 minutes of inactivity (defined by DPMS), and then xautolock will suspend the machine after 1 hour of inactivity using the sudo zzz command (15 minutes after the screen locks).

This workflow works flawlessly on Linux distributions as well. Aside from switching out the command xautolock calls with a systemd variant, everything can pretty much stay the same. Although just my personal opinion, I highly recommend adopting a workflow like this if you find yourself bouncing from machine to machine or distribution to distribution. Not tying yourself to a specific DE is one of the most freeing things you can experience in *nix land!


#Tech #OS

Get in touch with me at dev@mailcd.com!