It's summer, and that means the attic gets hot. I'm looking to get some kind of easy way to check the attic temperature from afar, so that I can keep tabs on it, and so that I can adjust the fans and the windows appropriately. This is really basic telemetry, a task that has been done many times before, using nearly every network protocol ever invented.
I decided for 2021 that I wanted to do this monitoring with some old-fashioned protocols, so the query tool of choice is "finger". I found a very nice modern implementation called finger2020 from Michael Lazar (mozz), which is designed for single-user systems. This runs under systemd, which makes it easy to install.
A simple crontab pulls temperature data from wherever it can find it, which on a Linux system could be lots of things. From past experience I have found that simply tracking the /sys/class/thermal values from the kernel give you CPU temperature for whatever computer you're running on, and that if your computer is hot, the rest of your space is probably hot too. As a bonus it's easy to grab with no additional external sensors needed.
I'm running Tailscale, which means there's a trustable inside
network on tailscale0
, and an untrusted outside network on
ethernet or wifi or whatever interfaces are exposed. This led
to the question: can you deliver different content to the world
depending on which interface the traffic comes in on, without
putting any of that logic into the program serving up the data?
After some spelunking through the systemd documentation and not
a lot of searchable examples from the net, I came up with either
a brilliant hack or an awful kludge to make this work. systemd
has a BindToDevice
directive in its socket files that lets you
associate a socket with an interface (not just its address,
which could change, but the interface itself). So I took
the unit files from finger2020, replicated them three ways,
and for each one added an appropriate BindToDevice=tailscale0
or whatever interface I had and then created a service file
that pointed to that socket.
The slightly weird part is that if you are on a machine querying itself, even if you specify an IP address of an external interface, you'll get packets on the loopback (lo) interface. So one socket handles loopback, one handles eth0 (for queries on a public network), and one handles tailscale0 (for queries on the private VPN).
A more elegant approach if you want to change your server is to look up the address from Tailscale, using the function tailscale.WhoIs. An example of this logic is in the hello service run by Tailscale which switches on the results of the WhoIs call and lets you further serve individual content because you know not just the interface the user is coming in on but the user name! So very handy if you have more than one user on your network.
The BindToDevice approach should work for any VPN you are using, and it should also let you do deliver services that are reachable only from private addresses and that don't ever show up on the public Internet, even if the code you are running is ignorant of that distinction.
Brilliant hack or awful kludge? Hard to say yet.
Comments