KDE FreeBSD in my KDE FreeBSD

Yo dawg, I heard you like FreeBSDYeah, dogg. I hear ya.

This post carries on a bit about the way I build and test packages for FreeBSD. I only have one desktop machine, running FreeBSD amd64, and it needs to function as a desktop even while building and testing packages. Elsewhere, things like Project Neon and the OpenSUSE build service do something similar, on a much larger scale: building packages from various stages of development and delivering them to users. Here, though, I’m concentrating on end-to-end ports and packages testing for FreeBSD for a single computer and user.

To do this, I’m using both FreeBSD jails (containers, if you will) and VirtualBox VMs. This means everything runs on one host (although really it doesn’t have to) and one monitor. The process looks like this:

Diagram of build steps and repositories

Components of the build process with poudriere + VMs.

Very little of this is my original work. Previously I pointed to some poudriere tutorials; that was the nucleus of this build setup.

Build Configurations: poudriere uses ZFS in FreeBSD to manage the disk space used by the different build configurations. This makes a couple of things easy: creating and destroying configurations and keeping state or rolling back to previous known-good configurations. This means you can have several ports trees — I’ve illustrated the default ports tree (which as of this writing is Qt 5.4.1, and KDE SC 4.14.3), area51 (which is Qt 5.4.1 and KDE Frameworks) and a plasma5 (which is Qt 5.5 and KDE Frameworks plus Plasma 5 Desktop).

Each ports tree is created with poudriere ports -c -p , which fetches the default ports tree. After that, I customize the tree by futzing directly with the filesystem. That can mean kdemerge (from area51), or editing ports files by hand. If needed, I can use ZFS snapshots to keep a known-good configuration around.

Each build jail is also created by poudriere, with poudriere jail -c -j . I show an amd64 and an armv6 jail here, since those are the architectures I build for. The armv6 jail takes a little more effort to set up, but that’s described in the poudriere tutorial. These jails live on a ZFS filesystem too, so I can snapshot them and update if needed, or clone them cheaply.

Doing a build takes one build jail — say amd64 — and one ports tree — say area51 — and builds a particular set of packages for that combination, with poudriere bulk -f -j -p . All the resulting packages are dropped into yet another filesystem (easy to clean!); each set of packages is named for the two ingredients that went into it. The packagelist-file can be really simple: if can be a single line x11/plasma5-plasma-desktop to build the KDE Plasma 5 Desktop.

You can use any kind of web server to serve up the packages. I followed the straightforward nginx setup described in the tutorial.

But the upshot of all this is that I can grind out packages in a bunch of different configurations in a pretty straightforward fashion. Per package set the automation might be a little different, but as an example, amd64-area51 packages (preparing for the first KF5 packages on FreeBSD) go like this:

  1. rollback the area51 ports tree to a clean state
  2. update the base ports tree in the snapshot, reset snapshot to new clean state
  3. update area51
  4. merge area51 with the ports tree
  5. kick off a build

With the current gaggle of packages, this takes about 4 hours on my machine, and then I’ve got a new set of packages I can test.

Test Machines: that second half of the setup is doing actual testing of the packages; e.g. installing them and running stuff. For this, I use VirtualBox — at least for the amd64 packages. It’s cheap and simple and gives me a standard platform that I can recycle and use for subsequent tests.

I have one disk image, which I snapshot to a known good state. I find it vaguely amusing that I can use ZFS snapshots inside the VM (on the virtual disk), or snapshots in VirtualBox, or snapshot the disk image in its own ZFS filesystem on the host system. So many ways to achieve the same thing.

My basic disk image has XOrg server installed, twm, xterm, and has a user account configured for ssh access. The point is to have a usable system (yay twm!) on which I can configure and run the packages just built elsewhere. To try a new package set, I clone the existing machine, start it up, set the pkg(8) repository to the right URL (which is on the host machine of the VM), then snapshot it and pkg install qt5 (or whatever packages I’m interested in at the time).

Since my area51 builds only build Qt5, KDE Frameworks 5 and KDE Plasma 5 Desktop (plus dependencies that aren’t already installed), it’s possible that the VM needs packages that aren’t built by poudriere in the build jails. So I have two pkg(8) configuration files in /usr/local/etc/pkg/repos, one to pull from the official packages repository, and one for my test repo.

[adridg@kafer ~]$ cat /usr/local/etc/pkg/repos/FreeBSD.conf 
FreeBSD: {enabled: yes}

[adridg@kafer ~]$ cat /usr/local/etc/pkg/repos/beastie.conf 
beastie: {
url: "pkg+http://beastie:8080/repo/101amd64-area51/.latest",
mirror_type: "srv",
enabled: yes
priority: 10
}

Aside from the URL — it uses my in-house DNS to resolve the hostname of the poudriere build-machine — the important thing in the second file is the priority: 10 line, which ensures that packages that are available from beastie will be preferred over packages from the official FreeBSD packages repository. As long as my build machine stays reasonably up-to-date, that’s fine.

The standard snapshot for my VM uses only the official FreeBSD packages, so I can also do this:

  1. rollback the VM snapshot to a known-good state
  2. update all the packages (from the official packages), reset snapshot to new clean state
  3. install packages from area51 (e.g. pkg install plasma5-plasma-desktop)
  4. startx. Yay twm!
  5. futz around; I generally run startkde by hand inside the X11 session, since I don’t know beforehand if it’s going to work, nor if the fallback behavior is sensible.

And with all that, I can say here’s KDE in FreeBSD in KDE in FreeBSD (with gobs of error messages from plasmashell, and I forgot to minimize some of the xterms so you could see the background; but the hamburger is there and KWin (X11) runs.):

Side-by-side screenshots

TWM on the left, KDE Plasma 5 Desktop on the right, on a KDE 4 desktop all running FreeBSD 10-STABLE.

PS. Now is the time to start with a FreeBSD-themed splash or default background. I think Beastie could very well be turned to glass and smashed into triangular bits to fit into the look. Any interested artists?

FreeBSD Plasma 5.4.1 and Frameworks 5.14.

Just a quick note that the KDE-FreeBSD “bleeding edge” ports repository area51 has been updated to the most-recently-released KDE Frameworks 5.14.0 and Plasma Desktop 5.4.1. These packages have been poudriere-built on 9.3 x86, 10.2 amd64 and 11 amd64 (no, I’m not building Plasma Desktop for Beagle Bone just yet). Information on area51 is on the K-F site, although you’ll want the plasma5-branch, which is at http://area51.pcbsd.org/branches/plasma5.

FreeBSD on Beagle Bone Black (with networking)

I set out to run FreeBSD on my Beagle Bone Black (now dubbed “smurf” by the kids on account of it’s small and blue), for network services. My DSL modem is a crappy under-configurable thing, but I don’t dare to start hacking on it directly because it runs the telephony side of things, too. So I decided to use the Beagle Bone Black to take control of my home network.

The modem is, of course, responsible for the actual internet connection. Its internal IPv4 address is 192.168.0.1, and it functions as the router for the whole house. Smurf is wired to one of the LAN ports on the modem, and it lives at 192.168.0.2. There’s a switch wired to another LAN port on the modem, and the rest of the wired network lives behind that switch. Wifi (still) goes through the modem. I might change that too, since the range is lousy compared to my old WRT 54GL. It looks something like this:

Network diagram described in text

Network diagram showing services and hostnames

Inside the network, there are a couple of machines with specific roles. There’s the printer, and the media server, and the NAS, and the poudriere builder, and a couple more. Most of these machines run services with some form of autodiscovery — like the printer and the media server — but not all of them. And there’s the configuration aspect of each: not only do I need to be able to find the printer’s IPP service, I may also need to reach the printer’s configuration webpage. It’s most convenient when I can put http://printer/ into a browser anywhere in the house and get the webpage for exactly that.

There’s probably plenty of solutions for this, but the one I settled on is somewhat old-school: control over DHCP and over DNS, with DHCP handing out fixed addresses in-house to the machines that need it (e.g. the printer is always 192.168.0.10) and DNS set to something I control that can respond with printer.example.com. 3600 IN A 192.168.0.10. For the non-special machines — random desktops, laptops, phones — I don’t need any kind of in-house naming, those machines just need an address.

Starting DHCP. On the modem side, I had to change two settings: DHCP server off, and DHCP relay on (to the fixed IP address for smurf). Until I got DHCP up and running on smurf, this effectively killed new wireless connections and wired connections that weren’t set to a fixed address, (this was mostly annoying for the kids on their phones).

Smurf is configured with a fixed address, and fixed routing to the outside world, and nameservers as provided by my ISP (I suppose I could add some public DNS there too — in the example configuration below, that’s Google). I used the isc-dhcp41-server package that I’d previously built with poudriere. Basic configuration was really simple (in /usr/local/etc/dhcpd.conf):

option domain-name "example.com";
option domain-name-servers 8.8.8.8;
authoritative;
subnet 192.168.0.0 netmask 255.255.255.0 {
  range 192.168.0.128 192.168.0.250;
  option routers 192.168.0.1;
}

A little testing shows that yes, the DHCP server is handing out addresses. I left a non-daemonized, verbose logging version of the server running while testing the rest.

Fixed DHCP. Next up is assigning fixed addresses to my special machines. Before starting on this, I had them all configured with fixed IPv4 addresses, so they all need to be changed to use DHCP, and on the DHCP side of things, their MAC addresses need to be associated with a fixed address. The easiest way to gather the necessary information seemed to me to be a combination of nmap(1) and arp(8) — use nmap to ping the whole network, then get the MAC addresses out of the ARP cache:

$ nmap -sn 192.168.0.0/24
$ arp -a

For each special host, I added a host block to the DHCP server configuration, like so:

host printer {
  hardware ethernet e8:9a:db:db:db:db;
  fixed-address printer.example.com;
}

Note that I’m using a hostname in this configuration, so I added a line to /etc/hosts as well to fix the IPv4 address for that name. That’s needed as long as I don’t have DNS set up to resolve hostnames “printer” and “printer.example.com” to the desired address.

Starting DNS. FreeBSD has recently switched to shipping unbound(8) with the base system. Previously you could use BIND(8) or unbound(8) from ports. Unbound is much smaller and lighter, especially for the home-network situation. I can use it as DNS for the whole home network, and it will cache requests — but it also gives me control over the naming inside the house.

I followed an Unbound DNS tutorial, which was pretty comprehensive. On FreeBSD it’s even easier: set up the right nameservers in /etc/resolv.conf, then add local_unbound_enable="YES" to /etc/rc.conf, and start the local_unbound service. A configuration file is generated and unbound(8) is started. The unbound.conf example under Authoritative, validating, recursive caching DNS setup and install in that tutorial is pretty much what I ended up using.

Unfortunately, the auto-setup of unbound(8) seems to leave two things out: it runs unbound(8) in a chroot where there’s no /dev/random, and then everything comes to a DNS-crashing halt. It took me a while to figure that out even though it’s mentioned in the documentation. I ended up just adding it to /etc/fstab:

devfs   /etc/unbound/dev        devfs   rw      0       0

After that, unbound started complaining about its DNSSEC trust root file. I couldn’t quickly figure out what that was about, so I ended up just disabling the auto-trust-anchor-file in the configuration. The config files are spread out a bit, but here’s the most important bits:

# Server listens on the local network and allows all queries
server:
        interface: 127.0.0.1
        interface: 192.168.0.2
        access-control: 127.0.0.0/8 allow
        access-control: 192.168.0.0/24 allow
        unblock-lan-zones: yes
        domain-insecure: 168.192.in-addr.arpa.
        domain-insecure: 127.in-addr.arpa.
# Forward to Google's public DNS
forward-zone:
        name: "."
        forward-addr: 8.8.8.8

After a few tests with host(1) — which initially all returned SERVFAIL until I had the random-number thing sorted out — I was confident I could use this from smurf and from hosts in the local network as DNS. So the next step was to switch the option “domain-name-servers” in the DHCP configuration over to unbound(8) running on smurf.

Fixed DNS. The last step in this setup is to associate the names for machines on the local network with the local IPv4 addresses I’ve chosen for them. This is done through local-data and local-data-ptr records in the unbound(8) configuration. FreeBSD has an /etc/unbound/conf.d/ directory to drop configuration files into, so I added a local.conf with the local network definitions:

server:
        local-zone: "example.com" static
        local-data: "smurf.example.com.     IN A 192.168.0.2"
        local-data: "printer.example.com.   IN A 192.168.0.10"
        local-data-ptr: "192.168.0.2  smurf.example.com"
        local-data-ptr: "192.168.0.10  printer.example.com"

Once that is done, then all the hosts in my local network that use DNS from smurf — and that means all of them that use DHCP — can use “smurf” as a hostname, and they’ll resolve 192.168.0.2, and reach the Beagle Bone Black.

With these two services in place, I can take each machine in the house and switch it to use DHCP, knowing that the ones that need a fixed address will still get one — and better yet, that they now have a useful name from all over the local network — and that the rest will have the names of the fixed-address machines available.

Musings. Conceptually, I’m working with configuration triplets: a particular MAC address has a particular name and designated IPv4 address. In the configuration, though, the triplet gets scattered across a number of configuration files, each in a different format. The issue expands with DNS, since then I have to repeat some of the information from /etc/hosts. Let’s call the elements of these triplets (M, N, A). I could make a small table of them. But getting that information into the right places is annoying, since the data gets spread out like this (with a bunch of hand-wavey notation):

  • N, A -> /etc/hosts format “${A} ${N} ${N}.example.com”,
  • N, A -> /etc/unbound/conf/local.conf format “local-data: ‘${N}.example.com. IN A ${A}” (but it has to be at the right spot in the file),
  • N, A -> /etc/unbound/conf/local.conf for the local-data-ptr line,
  • M, N -> /usr/local/etc/dhcpd.conf for the MAC-to-IPv4 mapping.

For my small home network, as long as I don’t go crazy buying devices that need a fixed address and name, the management burden is small. But it cries out for some kind of automation, so that I only need to write down the essentials somewhere once.