Seamless login/logout with ZFS Encrypted Home Directories

Installing FreeBSD from scratch is a rare occurrence for me. The installation on my daily driver laptop, a Lenovo X220, might be pushing 10 years. So, when I got a new Framework 16, it was a nice opportunity to start from scratch and try some new things. One of those new things was a native ZFS encrypted home directory. The installer, while a little rough around the edges, makes this relatively straightforward. What does take some manual intervention now, though, is mounting and unmounting the encrypted home directory.

It's inconvenient to do this every time before logging in to the system:
# zfs load-key zroot/home/jrm
# zfs mount /home/jrm

What I was hoping to accomplish was the same experience as with an unencrypted home directory. That is, at my login manager prompt, I simply enter my username and password and I'm logged in and ready to use the system. Fortunately, ZFS has a pam module called pam_zfs_key. From the man page, "pam_zfs_key is a PAM module that automatically manages encryption keys for ZFS datasets during user authentication and session management. When a user logs in, the module uses their password to unlock their encrypted home directory. When the last session closes, the module unmounts the dataset and unloads the key."

ZFS native encryption supports keyformat=passphrase, where the dataset is protected by a passphrase you supply. pam_zfs_key hooks into PAM and it uses your password to load the ZFS encryption key and mount your home dataset. On logout (when the last session closes), it unmounts the dataset and unloads the key. It uses reference counting under /var/run/pam_zfs_key/ to handle multiple simultaneous logins from the same user gracefully. The key constraint is that your login password must match your ZFS dataset passphrase. The module only works with keyformat=passphrase and keylocation=prompt datasets.

I've been using xdm for decades now, but after an hour of so of tinkering, I couldn't get it working with PAM, so I tried a few other login managers. I settled on LightDM because it just worked.

The module needs two entries in your PAM service file: one for auth to attempt key loading during authentication, and one for session to handle mounting on open and unmounting on close.

LightDM (/usr/local/etc/pam.d/lightdm)
# auth
auth        sufficient    pam_zfs_key.so
auth        include       system

# account
account     requisite     pam_securetty.so
account     required      pam_nologin.so
account     include       system

# session
session     include       system
session     optional      pam_zfs_key.so    mount_recursively forceunmount

# password
password    include       system

The auth sufficient line means a successful ZFS key load satisfies authentication; if it fails (e.g., the dataset doesn't exist for that user), PAM falls through to the normal system auth stack. The session line handles mount/unmount with mount_recursively (for hierarchical datasets) and forceunmount (to unmount even if something is holding the filesystem busy at logout).

I also added similar configuration to a few other PAM-aware services.

SSH (/etc/pam.d/sshd)
# auth
auth		sufficient	pam_zfs_key.so
auth		required	pam_unix.so		no_warn try_first_pass

# account
account		required	pam_nologin.so
account		required	pam_login_access.so
account		required	pam_unix.so

# session
session		required	pam_permit.so
session		optional	pam_zfs_key.so		mount_recursively forceunmount

# password
password	required	pam_unix.so		no_warn try_first_pass
su (/etc/pam.d/su)
# auth
auth		sufficient	pam_rootok.so		no_warn
auth		sufficient	pam_self.so		no_warn
auth		requisite	pam_group.so		no_warn group=wheel root_only fail_safe ruser
auth		sufficient	pam_zfs_key.so
auth		include		system

# account
account		include		system

# session
session		required	pam_permit.so
session		optional	pam_zfs_key.so		mount_recursively forceunmount
passwd (/etc/pam.d/passwd)
password	optional	pam_zfs_key.so
password	required	pam_unix.so		no_warn try_first_pass nullok

The password stack in PAM handles password change operations. Adding pam_zfs_key.so there means that when a user changes their login password, the module automatically re-keys the ZFS dataset with the new passphrase, keeping the two in sync automatically. Without it, you'd need to manually run zfs change-key every time you changed your login password, and if you forgot, the automatic unlock would silently stop working at next login.

Any login manager or service that goes through PAM should be able to use pam_zfs_key. It's transparent to the manager itself. What matters is that the service has a corresponding PAM configuration file.

A limitation to watch for with display managers is that the session close hook fires when the display manager's session ends, so any background processes still running in your home directory at logout may prevent an unmount. The forceunmount option handles this, but it's worth being aware of.

pam_zfs_key is a clean solution that requires no additional daemons or wrappers, just PAM configuration and properly prepared datasets. Once it's set up, the encryption is completely transparent in that your home directory appears decrypted the moment you log in and is locked again the moment your last session closes.

Posted 2025-12-28 13:51 | Comments

Real protection is missing in the Nova Scotia Crown Lands Act

The purpose of the Nova Scotia Crown Lands Act [1], as defined in the Act, is to

provide the legislative and regulatory framework that will ensure Crown lands are sustainably used, protected, and managed to maintain and enhance biodiversity and considers climate change and for purposes that include wilderness conservation, recreation, economic opportunity in forestry, tourism and other sectors, community development, and for the cultural, social and aesthetic enjoyment of Nova Scotians.

Reading that, one would feel confident that Nova Scotia Crown land is safe from the secret sale to private interests. However, given the events at Owl's Head and the Eisner Cove Wetland, the assumption is clearly incorrect. This legislation describing how and when Crown land in Nova Scotia may be sold is summarized in a policy document released by the Department of Natural Resources and Renewables [2].

Nova Scotia has a limited amount of Crown land. Only 35% of the Nova Scotia landmass is owned and administered by the province, compared to 50-90% in other provinces and territories in Canada.

Because of the limited amount of Crown land and existing commitments on Crown land - such as forestry licenses, parks, trails, and leases - we rarely offer Crown land for sale.

Nova Scotia's Sale of Crown Land Policy sets out the circumstances in which Crown land may be sold. For example, Crown land may be sold:

  • to a municipality, agency, non-profit group, or community organization when a public benefit can be demonstrated
  • to support or promote economic activity when all other reasonable alternatives have been canvassed by the applicant
  • to alleviate undue hardship or in extenuating circumstances, when in the best interests of the province.

The policy document also certainly seems to indicate that we aspire to protect our Crown land, but both documents provide loopholes for the provincial government to do as they please with Crown land when it is "in the best interests of the province".

If you want to "ensure Crown lands are sustainably used, protected, and managed to maintain and enhance biodiversity" and if you care about climate change, you should be deeply concerned with the current Act. Until the loopholes are removed, provincial ministers can use spurious arguments to do as they please with Crown land.

Posted 2022-10-13 22:12 | Comments

Emacs as an X Clipboard Manager, take 2

I am no longer using any of the solutions from my first Emacs as an X Clipboard Manager post. I am now using an automatic, event-driven solution that is just a simple shell script. This makes the Emacs kill-ring an X clipboard manager without requiring the user to run some yank operation to pull content from the X clipboard. The shell script depends on clipnotify.

#!/bin/sh

id=$(id -u)

while clipnotify; do
  if pgrep -q -u "$id" -f 'emacs --daemon'; then
    emacsclient -n -e "(current-kill 0)"
  fi
done

If you want to save the kill ring between Emacs sessions, add kill-ring to savehist-additional-variables.

If your terminal emulator supports the OSC 52 ANSI escape sequence, then terminal applications (running on remote hosts) can copy to the X clipboard (without X forwarding) as well.

Posted 2021-05-10 16:40 | Comments

EuroBSDCon 2018 Trip Report

The developer summit began on Thursday morning with an unconference feel. After introductions and greetings, we got down to brass tacks by hacking in smaller groups. I took the opportunity to work with dch@ on a review, catch up on some core@ email, and work on a few port updates.

Eventually seanc@ stepped up to lead a room-wide discussion. Some of the topics were:

For me, the most notable point from the 'Core.10 expectations' discussion was that Core.10 is planning a developer survey. To use Sean's phrase, "We are flying blindly". With a well-crafted survey, we can begin to make decisions based on what people really want. If you have questions that you think should be added to the survey, please discuss them on freebsd-arch@. Once the survey of developers is complete, the plan is to send out a version to the wider community. I have high expectations for the surveys. They have the potential to shape the direction of the project for years to come. On a related note, there were rumblings about either revitalizing or coming up with something similar to the BSDstats project, so we can get good system data to direct development. It is a waste of time and energy, for example, to maintain drivers that no one is using and conversely, it would be a shame to remove parts of the OS that are still useful to some number of users.

During the 'Workflow and version control' discussion, I sensed that we (mostly) agreed and reaffirmed that we want: I am not sure that our attempt to approach this topic with an open mind was successful. There were undertones of "switching to Git is inevitable". When Sean asked for a show of hands to determine who already uses Git, almost everyone in the room lifted a hand. If I recall correctly, the second question was something along the lines of "Do you choose to use Git?" and the result was the about the same. It was noted that this likely does not represent the developer community as a whole, but it was compelling anecdotal support for a switch. However, there are major challenges involved with switching:

Later on Twitter, des@ reminded us of a post by phk@ in one of our 'Git versus Subversion' threads of pain. In it, phk@ argues that, in some respects, comparing Subversion and Git is dubious. Subversion supports the traditional features of a version control system, whereas Git is different. Git is more of a collaboration tool that can mostly substitute as a version control system. It is a worthwhile (re-)read for anyone interested in this topic.

Considering the contentious nature of the topic, the discussions were civil and technical and even though it has been discussed a few times in the past, some new, important technical questions were raised. Hopefully the developer survey will help us clarify one important point: What proportion of developers feel that the benefits of switching will outweigh the costs?

The most discussion time was dedicated to 'Goals for FreeBSD 13'. Since there are notes, I will only give an overview of what I found most interesting We want:

dch@ gave a short talk/demo about an interesting idea to revive something similar to RedPorts, which offered package building and testing as a service. Hopefully this will get more people interested in helping out with ports. Given the many combinations of supported versions and architectures, it can be difficult to tests all those combinations on a desktop or laptop.

The conference proper started on Saturday with a keynote talk by Costin Raiciu. In a nutshell, he is working on something he calls Unikraft, which reduces the time and effort required to strip down a Linux kernel, so that it only includes the components necessary for specific target applications. I learned that these stripped down kernels are referred to as unikernels. He also described other unikernel work designed to improve the efficiency of lightweight virtual machines (VM), so that they approach the speed and size of containers. An example use-case involved creating ephemeral VM instances whenever requests came in from mobile devices. The motivation for doing this is to ensure that each request is run with a high level of isolation. When there are thousands of such instances running simultaneously, efficiency is a priority.

A few talks that stood out for me were Advances in OpenBSD packages: https is a lie, by Marc Espie, Using Boot Environments at Scale by Allan Jude, The End of DNS as we know it, by Carsten Strotmann, and FreeBSD Graphics by Niclas Zeising.

In Marc's talk he described work to 1. securely and efficiently fetch packages with OpenBSD's pkg_add and 2. separate privileges when building packages on OpenBSD. Marc is an enthusiastic and opinionated speaker, which I like (within limits) to keep my attention. There was also some good back-and-forth with the audience to tease out a few technical questions, which kept things lively. Could TLS 1.3 solve some of the challenges with session resumption?

The piece of mind ZFS boot environments offer when making changes to the OS have been well publicized. Allan described his efforts to enhance this work by wrangling boot environments and poudriere image into something that could be used to install and upgrade many of his company's servers without having to deal with mergemaster or etcupdate. A few other advantages to the approach are: 1. no need for console access, 2. failures are handled gracefully, 3. less manual intervention, and 4. releases can be created without compiling. A nice part of the talk was when Allan described the pitfalls he faced along the way. For example, using a separate filesystem for /etc, so that it can persist through an upgrade sounds like a good idea, but when /etc/fstab or /etc/rc are not available at the right time, bad things happen. Contact Allan to help with ZFS support for poudriere image or to report bugs and request features.

The End of DNS as we know it was a fantastic talk that sparked lots of interesting comments from a knowledgeable audience. Carsten did a nice job finding a good technical balance. The talk was not light on technical information, but did not venture too far into the weeds. It was interesting to hear a comprehensive review of where we are headed with such a fundamental Internet protocol.

If you run FreeBSD on your desktop or laptop, then Niclas's FreeBSD Graphics talk would have helped you appreciate the work the small graphics team does so that we can run our favourite window manager. There are different parts the team takes responsibility for: drivers, libraries, the X server itself, and a variety of other applications that sum up to about 300 ports. After giving an overview of these different parts, Niclas described some of the challenges and future work. If you would like to get in touch with the Graphics team, contact them at x11@FreeBSD.org, https://gitter.im/FreeBSDDesktop/Lobby, #freebsd-xorg on efnet, or on the FreeBSD Desktop GitHub page.

Monday was secretary-tourist-day as René Laden and I set out to play tourists. Unfortunately, Mondays are less than ideal for tourist activities, since many of the museums and other attractions are closed, so we decided to have a free walk around the city without much of a plan. Some highlights were the Palace of the Parliament (which apparently is the second largest administrative building and the heaviest building in the world), our stroll along the Dambovita River, and the city parks.

We discussed the developer summit, our roles as secretaries, and the challenges and opportunities facing the project. In one of our personal conversations, which René permitted me to share, he described some of his Autism-related challenges, in particular his difficulties with communication. On the one hand, this was surprising, because René's reports and other writings are high quality. On the other hand, the clarity of his speech can vary, but luckily René does not mind repeating himself if something is unclear the first time around. He also explained how his speech depends on how comfortable he feels in a social situation. I saw evidence of this, especially in the relaxed atmosphere of a quiet café, because our discussions were natural, stimulating and a high point of my trip.

Indeed, the talks and the developer summit were interesting, but a meaningful aspect of a conference like this for me is to step away from the comfort of the Irc/mail client and engage in face-to-face discussions with others. We spend significant amounts of time working on a common goal, so it is especially rewarding for me to make a connection with other members of the team.

Thank you to the FreeBSD Foundation for supporting my trip to EuroBSDCon 2018.

Posted 2019-02-16 20:52 | Comments

Upgrading Mastodon on FreeBSD

This post is now out of date. The FreeBSD port/package of Mastodon, net-im/mastodon, is no longer available. To install Mastodon on FreeBSD, refer to the Mastodon Production Guide. To upgrade Mastodon, refer to the Mastodon release notes.

Stop the three Mastodon servers.
# service mastodon_web stop
# service mastodon_workers stop
# service mastodon_stream stop
Upgrade your packages.
# pkg upgrade

Check the Mastodon release notes to see if any special scripts need to be run. For example, if a data migration is required, run the commands below, but do not prepend commands with `bundle exec` and never run `bundle install`, `yarn install`, or `assets:precompile`.

# su - mastodon
% echo -n > Gemfile.lock
% RAILS_ENV=production rails db:migrate

Do a diff between nginx-include.conf.sample and nginx-include.conf to check for changes. Update nginx-include.conf if necessary.

Return to root privileges.

If you made changes to nginx-include.conf, reload the nginx configuration.
# service nginx reload
Finally, restart the Mastodon daemons.
# service mastodon_web start
# service mastodon_workers start
# service mastodon_stream start

Posted 2017-04-27 12:55 | Comments

Recent posts

Monthly Archives

Yearly Archives


RSS