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:
- Install
libu2f-host
(the YubiCo C library) andu2f-devd
(the FreeBSD specificdevd
rules for U2F devices):
pkg install libu2f-host u2f-devd
- Add your user to the
u2f
group:
pw group mod u2f -m [USER]
- 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!
Get in touch with me at dev@mailcd.com!