Category Archives for "Thomas Habets blog"

Dropping privileges

If you’re writing a tool that takes untrusted input, and you should treat almost all input as untrusted, then it’s a good idea to add a layer of defense against bugs in your code.

What good is a buffer overflow, if the process is fully sandboxed?

This applies to both processes running as root, and as normal users. Though there are some differences.

Standard POSIX

In POSIX you can only sandbox if you are root. The filesystem can be hidden with chroot(), and you can then change user to be non-root using setuid() and setgid().

There have been ways to break out of a chroot() jail, but if you make sure to drop root privileges then chroot() is pretty effective at preventing opening new files and running any new programs.

But which directory? Ideally you want it to be:

  • read-only by the process (after dropping root)
  • empty
  • not shared by any other process that might write to it

The best way no ensure this is probably to create a temporary directory yourself, owned by root.

This is pretty tricky to do, though:

// Return 0 on success.
int do_chroot()
  const char* tmpdir = getenv("TMPDIR");
  if (tmpdir == NULL)  Continue reading

seccomp — Unsafe at any speed

I’ll just assert that there’s no way to use seccomp() correctly. Just like how there’s no way to use gets() correctly, causing it to eventually be removed from the C and C++ standards.

seccomp, briefly

seccomp allows you to filter syscalls with a ruleset.

The obvious thing is to filter anything your program isn’t supposed to be doing. If it doesn’t do file IO, don’t let it open files. If it’s not supposed to execute anything, don’t let it do that.

But whether you use a whitelist (e.g. only allow working with already open file descriptors), or a blacklist (e.g. don’t allow it to open these files), it’s fundamentally flawed.

1. Syscalls change. Sometimes without even recompiling

open() in your code actually becomes the openat syscall. Maybe. At least today. At least on my machine, today.

select() actually becomes pselect6. At least on Fridays.

If you upgrade libc or distribute a binary to other systems, this may start to fail.

2. Surprising syscalls

Calling printf() will call the syscall newfstatat, a syscall hard to even parse into words. But only the first time you call it! So after your first printf() you can block newfstatat.

Maybe Continue reading

AX.25 over D-Star

Setting up AX.25 over 1200bps was easy enough. For 9600 I got kernel panics on the raspberry pi, so I wrote my own AX.25 stack.

But I also want to try to run AX.25 over D-Star. Why? Because then I can use radios not capable of 9600 AX.25, and because it’s fun.

It seems that radios (at least the two I’ve been working with) expose the D-Star data channel as a byte stream coming over a serial connection. Unlike working with a TNC you don’t have to talk KISS to turn the byte stream into packets, and vice versa.

IC9700 setup

The first hurdle to overcome, because we want to send binary data, is to escape the XON/XOFF flow control characters that the IC9700 mandates. Otherwise we won’t be able to send 0x13 or 0x11. Other bytes seem to go through just fine.

So I wrote a wrapper for that, taking /dev/ttyUSB1 on one side, and turning it into (e.g.) /dev/pts/20 for use with kissattach.

$ ./dsax /dev/ttyUSB1
$ kissattach /dev/pts/20 radio
$ kissattach -p radio -c 2     # See below

Set Menu>Set>DV/DD Set>DV Data TX to Auto, for “automatic PTT”. As Continue reading

Localisation isn’t translation

If you only have your app in English then you’ll still be understood[1] by the new market whose official language isn’t English.

If you show farenheit (a word I can’t even spell), then 96% of the world cannot understand your app. At all.

For most of the west I would argue that translation doesn’t even matter at all, but you cannot have your app start your weeks on Sunday, you cannot show fahrenheit, or feet, or furlongs, or cubits or whatever US-only units exist. And you cannot use MM/DD/YY.

NONE of these things are tied to language. Most users of English don’t want any of this US-only failure to communicate.

[1] While most of the world doesn’t speak English fluently, they may know words. And they can look up words. You cannot “look up” understanding fahrenheit or US-only date formats.

Go programs are not portable

A while ago I was asked why I wrote Sim in C++ instead of Go. I stumbled upon my answer again and realized it could be a blog post.

So here’s what I wrote then. I think I stand by it still, and I don’t think the situation has improved.

Why not write portable system tools in Go

My previous experience with “low level” things in Go (being very careful about which syscalls are used, and in which order) has had some frustrations in Go. Especially with portability. E.g. different definitions of syscall.Select between BSDs and Linux, making me have to use reflection at some points. (e.g. see this Go bug.

And to work around those things Go unfortunately uses the antipattern of (essentially) #ifdef __OpenBSD__, which we’ve known for decades is vastly inferior to checking for specific capabilities.

To me the Go proverb “Syscall must always be guarded with build tags” essentially means “Go is not an option for any program that needs to be portable and may at some point in the future require the syscalls package”. And since this tool is meant to be portable, and calls what would be syscall.Setresuid, Continue reading

SSH over bluetooth – cleanly

In my previous two posts I set up a login prompt on a bluetooth serial port and then switched to running SSH on it.

I explicitly did not set up an IP network over bluetooth as I want to minimize the number of configurations (e.g. IP address) and increase the chance of it working when needed.

E.g. firewall misconfiguration or Linux’s various “clever” network managers that tend to wipe out network interface configs would have more of a shared fate with the primary access method (SSH over normal network).

This post is about how to accomplish this more properly.

The problems now being solved are:

  • It wasn’t entirely reliable. The rfcomm tool is pretty buggy.

  • There was no authentication of the Bluetooth channel. Not as much a problem when doing SSH, but if there are passwords then there could be a man-in-the-middle attack.

  • The server side had to remain discoverable forever. So anyone who scans for nearby bluetooth devices would see your servers, and would be able to connect, possibly brute forcing passwords. Not as much of a problem if running SSH with password authentication turned off, but why broadcast the name of a server if you don’t Continue reading

SSH over bluetooth

Yesterday I set up a simple serial console over bluetooth as a backup console.

Today I’m running SSH over bluetooth. Raw SSH, no IP. I only use IP on the two ends to talk to the SSH client and server. It doesn’t actually go over the bluetooth.

This fixes the security aspects with the previous solution. As long as you make sure to check the host key signature it’ll be perfectly secure.

No need for one-time passwords. You can even use SSH pubkey auth.

Connect to the system SSH


rfcomm watch hci0 2 socat TCP: file:/proc/self/fd/6,b115200,raw,echo=0


sudo rfcomm bind rfcomm2 AA:BB:CC:XX:YY:ZZ 2
ssh -oProxyCommand="socat - file:/dev/rfcomm2,b115200,raw,echo=0" dummy-hostname

A backup SSH

If you’re messing around with an OpenSSH config then it may be a good idea to set up a minimal config on another port. Maybe port 23. Not like that port is used for anything else anymore.

Raspberry Pi bluetooth console

Sometimes you want to connect to a bluetooth on the console. Likely because you screwed something up with the network or filewall settings.

You could plug in a screen and keyboard, but that’s a hassle. And maybe you didn’t prepare the Pi to force the monitor to be on even if it’s not connected at boot. Then it just doesn’t work.

Even more of a hassle is to plug in a serial console cable into the GPIO pins.

But modern Raspberry Pi’s have bluetooth. So let’s use that!

Setting up the service on the raspberry pi

Create /etc/systemd/system/bluetooth-console.service with this content:

Description=Bluetooth console

ExecStart=/usr/bin/rfcomm watch hci0 1 getty rfcomm0 115200 vt100


This sets up a console on bluetooth channel 1 with a login prompt. But it doesn’t work yet. Apparently setting After, Required, and even Requisite doesn’t prevent systemd from running this before setting up bluetooth (timestamps in the logs don’t lie). Hence the restart stuff.

I also tried setting ExecStartPre / ExecStartPost there to enable Bluetooth discoverability, since something else in the boot process seems to turn it back off if I set it Continue reading

Virtual audio cables

This is another post about the mess that is Linux audio. To follow along you may want to read the previous one first.

The goal this time

This time I want to create a virtual audio cable. That is, I want one application to be able to select a “speaker”, which then another application can use as a “microphone”.

The reason for this is that I want to use GNURadio to decode multiple channels at the same time, and route the audio from the channels differently. Specifically my goal is to usy my ICom 7300 in IF mode (which gives me 12kHz of audio bandwidth) tuned to both the FT8 and JS8 HF frequencies, and then let wsjtx listen on a virtual sound card carrying FT8, and JS8Call listen to a virtual sound card carrying JS8.

Creating virtual cables

We could use modprobe snd_aloop to create loopback ALSA devices in the kernel. But I’ve found that to be counter intuitive, buggy, and incompatible (not everything application supports the idea of subdevices). It also requires root, obviously. So this is best solved in user space, since it turns out it’s actually possible to do so.

Another way to say this is Continue reading

Linux sound devices are a mess

It started with a pretty simple requirement: I just want to know which sound card is which.

Background about the setup

I sometimes play around with amateur radios. Very often I connect them to computers to play around. E.g. JS8Call, FT8, SSTV, AX.25, and some other things.

This normally works very well. I just connect radio control over a serial port, and the audio using a cheap USB audio dongle. Sometimes the radio has USB support and delivers both a serial control port and an audio interface over the same cable.

The problem

So what if I connect two radios at the same time? How do I know which sound card, and which serial port, is which?

Both serial ports (/dev/ttyUSB<n>) and audio device numbers and names depend on the order that the devices were detected, or plugged in, which is not stable.

The fix for serial ports

Serial ports are relatively easy. You just tell udev to create some consistent symlinks based on the serial number of the USB device.

For example here’s the setup for a raspberry pi that sees various radios at various times (with some serial numbers obscured) Continue reading

Unifi docker upgrade

This post is mostly a note to self for when I need to upgrade next time.

Because of the recent bug in log4j, which also affected the Unifi controller, I decided to finally upgrade the controller software.

Some background: There a few different ways to run the controller. You can use “the cloud”, run it yourself on some PC or raspberry pi, or you can buy their appliance.

I run it myself, because I already have a raspberry pi 4 running, which is cheaper than the appliance, and gives me control of my data and works during an ISP outage.

I thought it’d be a good opportunity to play with docker, too.

How to upgrade

Turns out I’d saved the command I used to create the original docker image. Good thing too, because it seems that upgrading is basically delete the old, install the new.

  1. Take a backup from the UI.
  2. Stop the old instance (docker stop <old-name-here>).
  3. Take a backup of the state directory.
  4. Make sure the old instance doesn’t restart (docker update --restart=no <old-name-here>).
  5. Create a new instance with the same state directory.
  6. Wait a long time (at least on Raspberry Pi), like Continue reading

AX.25 in user space

The Linux kernel AX.25 implementation (and userspace) is pretty poor. I’ve encountered many problems. E.g.:

  • you can’t read() and write() from the same socket at the same time

  • DGRAM receiving just plain doesn’t work.

  • CRC settings default such that at least all my radios (and direwolf) drop the first two packets sent. (fix with kissparms radio -c 1)

  • Setting CRC mode resets all other settings.

  • On 64bit Raspberry Pi OS setsockopt for some flags don’t take effect at all (e.g. setting AX25_EXTSEQ), and treat other obvious correct ones as invalid (e.g. can’t set AX25_WINDOW to any value at all).

  • I also get kernel null pointer dereferences on 32bit Raspberry Pi OS when testing AX.25. Not exactly comforting.

  • Other OSs don’t have AX.25 socket support. E.g. OpenBSD. And it’s not obvious to me that this is best solved in kernel space.

  • It doesn’t seem clear to anyone how the AX.25 stack in the kernel is supposed to work. E.g. should axparms -assoc be an enforcing ACL? It’s not, but is it supposed to be?

  • I’ve also seen suggestions that AX.25 should be ripped out of the Linux kernel. Continue reading

The uselessness of bash

The way I write automation for personal projects nowadays seems to follow a common pattern:

  1. A command line, that’s getting a bit long
  2. A bash script
  3. Rewrite in Go

Occasionally I add a step between 2 and 3 where I write it in Python, but it’s generally not actually gaining me anything. Python’s concurrency primitives are pretty bad, and it’s pretty wasteful.

Maybe there’s an actually good scripting language somewhere.

I should remember that writing a bash script (step 2) seems to almost never be worth it. If it’s so complicated that it doesn’t fit on one line, then it’ll become complicated enough to not work with bash.

There are two main things that don’t work well. Maybe there are good solutions to these problems, but I’ve not found them.

1. Concurrency

There are no good primitives. Basically only xargs -P and &. It’s annonying when you have an embarrassingly parallelizable problem where you want to run exactly nproc in parallel.

Especially error handling becomes terrible here.

2. Error handling

You can handle errors in bash scripts in various ways:

  1. || operator. E.g. gzip -9 < a > a.gz || (echo "handling error…")
  2. set -e at the top Continue reading

More FT8 propagation

Last month I graphed the distance to remote stations as a function of time of day.

Today I plotted the gridsquare locations on a world map:

Grid squares heard

Ignore the top right one. That’s “RR73”, and not a real grid square. The rest should be accurate.

More that can be done (more interesting with more data than I can get, though):

  • also take into account the received signal strength
  • …and number of unique callsigns per grid square
  • create animations over time

If I had access to the data from pskreporter I could even, instead of using just a callsign as input data, use a grid square as input.

So for example I could create an animation to show what the propagation was over the last week from any given gridsquare, and generate them on-demand.

Like last time the scripts are pretty hacky proof of concepts. But they work.

Measuring propagation using FT8

One obvious thing that you can do after putting up an amateur radio antenna is to operate a bit on FT8, to see how the propagation goes. Just transmit on all bands and see how for you get.

E.g. this map on with 10W on my EFHW:

10W EFHW propagation

You can also use the [reverse beacon network][rev] with morse code:

Reverse beacon network for M0THC

But that’s just a few samples. What about more statistical data? And propagation over time? I don’t have access to the raw data from, and even if I did I can’t just set up an automatic beacon tx round the clock every day without requesting a Notice of Variation.

I may do that some day, but it’s a project for another time.

For this post what I want to know is if my antenna setup is better for 20m or 40m. Subjectively it seems like more is trickling in on 40m. And when they say that 40m is better “at night”, what time exactly do they mean?

For passive listening my data will, of course, be heavily skewed by when people are awake and active. But that means it’s skewed towards representing “if I call CQ, how Continue reading

Unifi controller with a real cert

I finally got sick of seeing a certificate error when connecting to my Ubuiquiti Unifi WiFi controller.

There are a bunch of shitty howtos describing how to install a cert, and one good one. But in order to make it more copy-paste for future me when the certificate needs renewing, and because the paths are not quite the same since I run the controller in a Docker container on a raspberry pi, here are the commands (after copying fullchain.pem and privkey.pem into the stateful data dir):

host$ docker ps  # make note of the docker ID
host$ docker exec ID_HERE -ti bash
docker$ openssl pkcs12 \
        -export \
        -inkey privkey.pem \
        -in fullchain.pem \
        -out cert.p12 \
        -name unifi \
        -password pass:secret
docker$ keytool \
        -importkeystore \
        -deststorepass aircontrolenterprise \
        -destkeypass aircontrolenterprise \
        -destkeystore /usr/lib/unifi/data/keystore \
        -srckeystore cert.p12 \
        -srcstorepass secret \
        -alias unifi \
docker$ exit
host$ docker stop ID_HERE
host$ docker start ID_HERE

I’m mostly happy with the Ubiquiti access points. I have an AP-AC-LR and an AP-M. My complaints are:

  • When I reported a bug about access to SSH on non-management interfaces, they responded by turning off management over IPv6 Continue reading

Tiling window manager

A couple of months ago it occurred to me that I’ve been manually tiling my windows. That is, I use all the screen real estate, and don’t have windows overlapping each other.

In various window manages (and on Windows) I have used Super+Left and Super+Right to divide the screen 50/50.

So why am I not running a tiling window manager? That’s literally what they do, and they allow more flexibility in how to tile, without wasting space.

Switching to tiling

A quick googling says that i3 is what I want. Fast, small, efficient. No bells and whistles.

I used it for a little while, but then because I wanted to make it even harder on myself, err… I mean to join the 21st century, I thought I’d switch from X11 to Wayland, too. Luckily there’s a Wayland Compositor that’s equilavent to the i3 Window Manager called Sway.

It’s great! I knew X11 and Gnome had issues, but I didn’t realize just how much better I feel when I don’t have to deal with their deficiencies.


  • screen tearing when scrolling in terminal windows
  • changing focus can take up to a second, sometimes
  • X11 resets keyboard settings when it bloody feels Continue reading

Bypassing safety check for an obviously safe change

This is less concrete technical than my usual blog post.

For every 100 changes we’re 99% sure won’t cause an outage, one will

It’s actually hard to be 99% sure of anything. I’m not 99% sure today’s Thursday. I say that because more often than one day in a hundred, I’ll think “hmm… feels like Wednesday” when it’s not.

I just closed my eyes and tried to remember what time it is. I don’t think I can guess with 99% accuracy what hour I’m in. (but to be fair, it’s de-facto Friday afternoon today, as I’m off tomorrow).

Anyway… the reason I say this is that this should be kept in mind every time someone comes and says they want to circumvent some process for a change that they are absolutely sure won’t cause an outage, that can actually be put into numbers. And those numbers are “you are not 100% sure of anything”.

By saying you are 99% sure this won’t cause an outage (and are you right about that?) you are saying that for every 100 requests like yours that will bypass normal checks, there will be an outage. You are taking on an amortized 1% of Continue reading

BPF: The future of configs

BPF has some wow-presentations, showing how it enables new performance measuring and tracing. Brendan Gregg has a whole bunch, for example. But I don’t think’s it’s very well explained just why BPF is such a big deal.

Most of the demos are essentially cool and useful looking tools, with an “oh by the way BPF made this happen”. Similar to how it’s common to see announcements about some software, where the very title of the announcement ends with “written in Go”. It gives a vibe of “so what?”.

If you’re interested in system tooling and configuration, and aren’t already aware of BPF, then this is for you.

I’m not an expert on BPF, but this will hopefully help someone else bootstrap faster.


bpftrace is really cool. Clearly it’s inspired by dtrace. But one should not mistake bpftrace for BPF. bpftrace is only yet another tool that uses BPF, albeit one that allows you to create trace points in a domain specific language.

This is not the full power of BPF. It’s not at all the big picture.

BPF and configs

Let’s take packet filtering as an example. Once upon a time in Linux there was ipfwadm. I Continue reading

A smarter emacs

I’ve been running Emacs for like 25 years. But I’ve never really configured it with anything fancy.

Sure, I’ve set some shortcut keys, and enabled global-font-lock-mode and set indent size, but that’s almost it.

All my coding is done in tmux&Emacs. One project gets exactly one tmux session. Window 0 is emacs. Window 1 is make && ./a.out (sometimes split panes to tail logs or run both server and client), and to run git commands. The remaining windows are used for various things like reading manpages etc….

I have that same workflow whether I’m editing a blog post or doing kernel programming.

This way I can work at my desk with large and plentiful screens, and then move to my laptop and everything continues working exactly the same.

tmux I’ve customized, but not that much with Emacs.

So, step one to get my coding environment to be less 1995, and more 2020: make my editor understand my code, and show me stuff about it.

I’m learning as I’m going, and writing what I’m learning. As always if you see something wrong then please leave a comment.

Code annotations and other semantic understanding

The way to do this is Continue reading

1 2 3 5