It might be the season for issuing Let’s Encrypt certificates, in any case both Dan and I have (independently from each-other) being issuing certificates. I for one have been restructuring our OwnTracks servers, but that’s beside the point.

Case in point is a blog post Dan wrote. It starts off pretty badly, because amongst others he blames me for his misery, but I’ll survive. :-) One of the things Dan rightly points out in his piece is there’s a bit of manual work involved when configuring grant statements in BIND, and while that portion could be automated by generating includes or whatnot, it occurred to me that an external authenticator might do the trick.

I wrote about that years ago in the context of Kerberos, so I’ll revisit with newer software and a different use case.

Let’s assume we have a zone in which the ACME dns-01 challenges will be updated dynamically. We might have one or more keys we use centrally or decentrally to do that, and we want to ensure those updates are performed a) by a TSIG key we have issued and b) only for domains we’re currently certifying.

A BIND dynamic update policy can contain all manner of rules (there are 16 different ones in 9.17). One of these allows named to delegate the decision on whether to allow a particular update to an external process.

zone "certs.example." IN {
    type primary;
    file "tests/certs.example";

    update-policy {
        grant local-ddns zonesub ANY;
        grant "local:/tmp/auth.sock" external * TXT;

Focus on the second rule which uses the external rule type. This causes named to send a message to the specified Unix domain socket from which it also expects a 1 or 0 response to indicate whether the update should be granted or denied.


The program (daemon) which creates the Unix domain socket can do whatever it pleases to verify whether an update is permitted or not, for example query a database to see whether a particular certificate name being issues belongs to us or not. I’ve not really grown past using a copy of bin/tests/system/tsiggss/ from the BIND source tree, but it works fine for demonstration purposes. Meanwhile I’ve created a small C program which does the trick nicely. The program creates the Unix domain socket, starts listening for requests from named and returns true for all checks, but it could decide based on key, IP address, or requested name to deny (0) update-permission.

Update by acme key=acme/163/59864 for, type=TXT at address

The key string contains the actual TSIG keyname used, its algorithm (in this case is 163 (HMACSHA256)), the key_id which is pretty useless, together formatted as

snprintf(cp, size, "%s/%s/%d", namestr, algstr, dst_key_id(key));

As usual, named logs our update request:

... /key acme: updating zone 'certs.example/IN': adding an RR at '' TXT "hola"

If the authenticator program is not running or does not respond, named will deny the request, and if the authenticator grants the update but it’s not in the list of permitted RR types in the grant statement, named will reject it. Both of which I find very sane.

While this does mean an extra moving part on the DNS server, it should be something which can be implemented in a safe way, but the effort is likely only worthwhile for a large number of domains which need certificates; I’m not likely to do this to replace a few handfuls of grant statements.

Further reading:

DNS, ACME, and RFC2136 :: 20 Dec 2020 :: e-mail

If we’ve ever spoken about DNSSEC key rollovers you’ll recall that I typically respond with something along the lines of why would you want to do that? or when was the last time you rolled your car keys?

It’s against the recommendations to not roll keys, and I’m all for recommendations except when they recommend that I roll my keys. :-) Some of you might have been surprised then to see me write that I was in midst of a DNSSEC key rollover.

twitter announcement

To be honest, it’s the first time I do so for any of my domains which I originally signed with RSASHA256 (algorithm 8) on PowerDNS. A lot of time has passed since then, including a migration to a new BIND environment, and I exported the original keys and carried on using them on BIND. Why unnecessarily roll a key? The only reason I’m now rolling is because I’m changing the key algorithm to ECDSAP256SHA256 (algorithm 13).

As soon as these two rollovers are complete, I’m not likely to do another for my own zones. It’s nerve-racking. #justkidding

dnssec :: 14 Dec 2020 :: e-mail

Via Kristian came a link to two MQTT-capable and HTTP-enabled power socket switches by Delock: one goes by the model number 11826, and the second is called 11827 and includes energy monitoring. I ordered one of each as it’s been a while since I toyed with stuff, and after all it is almost christmas…

the Delock devices

The taller but slimmer plug on the left is the 11827, and the squatter square one on the right the 11826. They look rather nice, and both have a button inset next to a minuscule LED. A touch of the button flicks the switch itself and, depending on how long and how often it is pressed, can trigger Over The Air (OTA) updates of the firmware and reset the device to factory settings, and all manner of other things.

The included leaflet is sufficient to get started. Plugging the device into mains has it create a WiFi access point I connect to, and I can then configure it to connect to my home network. The Tasmota firmware spoke German to me all the time and the only reason I can think of, as my browsers are all set to English because the firmware was flashed that way. Ronald confirms that and explains I can flash the device with an en firmware from here (I pasted the link to tasmota.bin into the firmware update page).

initial Wifi configuration

Once I enter the information for my WiFi router, the device reboots. This is the moment I switch the phone back to my environment and access the device again via its new IP address.

From the device’s Web UI I can switch the power on or off.

main screen including on/off

I can also switch the load via HTTP (I think it’s the first time I see the word “command” abbreviated to “cmnd”, and this could have been a PUT or at least a POST request, but I suppose it doesn’t matter.)

$ curl ""

$ curl -d POWER=TOGGLE ""
{"WARNING":"Befehl eingeben cmnd="}

The documented “Extra feature” of using cmnd=Strom in the German language, doesn’t work.

other configuration

The next was setting up MQTT, pretty easy if you’ve done this before, which I have. :-) I configured host, port, username, and password, as well as clientID and topic branch, which ended up being lamp/11826/%prefix%/%topic%/; the device fills %prefix% and by default %topic% with the word “delock”. I found that bit of UI to be a bit confusing, but clarity ensued when I booted the device:

$ mosquitto_sub -v -t 'lamp/#'
lamp/11826/tele/delock/LWT Online
lamp/11826/cmnd/delock/POWER (null)
lamp/11826/tele/delock/INFO1 {"Module":"Delock 11826v1","Version":"6.7.1(basic)","FallbackTopic":"cmnd/DVES_D8E724_fb/","GroupTopic":"donoffs"}
lamp/11826/tele/delock/INFO2 {"WebServerMode":"Admin","Hostname":"delock-1828","IPAddress":""}
lamp/11826/tele/delock/INFO3 {"RestartReason":"Software/System restart"}
lamp/11826/stat/delock/RESULT {"POWER":"ON"}
lamp/11826/stat/delock/POWER ON

The LWT topic is retained and indicates whether the device is online; the actual last will and testament payload is the string Offline value when the device goes offline.

If I had actually read those messages I might have seen “GroupTopic”, but I’m not sure I would have actually understood what it does. Instead I resorted to setting the device to do syslog and found out which topic the device subscribes to (“abonniere” is “I’m subscribing” in German). I could also have seen the subscribes in the MQTT broker logs:

delock-1828 ESP-HTP: Web-Server aktiv bei delock-1828.local mit IP-Adresse
delock-1828 ESP-UPP: Multicast (wieder-)verbunden
delock-1828 ESP-NTP: Drift 0, (UTC) Sun Nov 22 13:10:55 2020, (DST) Sun Mar 29 02:00:00 2020, (STD) Sun Oct 25 03:00:00 2020
delock-1828 ESP-UPP: Multicast deaktiviert
delock-1828 ESP-MQT: Verbindungsversuch...
delock-1828 ESP-MQT: verbunden
delock-1828 ESP-MQT: lamp/11826/tele/delock/LWT = Online (beibehalten)
delock-1828 ESP-MQT: lamp/11826/cmnd/delock/POWER =
delock-1828 ESP-MQT: abonniere lamp/11826/cmnd/delock/#
delock-1828 ESP-MQT: abonniere lamp/11826/cmnd/donoffs/#
delock-1828 ESP-MQT: abonniere cmnd/DVES_D8E724_fb/#
delock-1828 ESP-MQT: lamp/11826/tele/delock/INFO1 = {"Module":"Delock 11826v1","Version":"6.7.1(basic)","FallbackTopic":"cmnd/DVES_D8E724_fb/","GroupTopic":"donoffs"}
delock-1828 ESP-MQT: lamp/11826/tele/delock/INFO2 = {"WebServerMode":"Admin","Hostname":"delock-1828","IPAddress":""}
delock-1828 ESP-MQT: lamp/11826/tele/delock/INFO3 = {"RestartReason":"Fatal exception:9 flag:2 (EXCEPTION) epc1:0x40105590 epc2:0x00000000 epc3:0x00000000 excvaddr:0x000

See that crash report in the last line? The device keep crashing and rebooting until I reconfigured its syslog report to “info” down from “debug”.

So, with the topics available, I can now switch the lamps on, off, or toggle their status. I’m indicating this here with pub for publish and sub for what I get when subscribed to the specific topic:

pub lamp/11826/cmnd/donoffs/POWER -m TOGGLE

sub lamp/11826/stat/delock/RESULT {"POWER":"ON"}
sub lamp/11826/stat/delock/POWER ON

Every five minutes, the device reports state at lamp/11826/tele/delock/STATE:

  "Time": "2020-11-22T14:21:45",
  "Uptime": "0T00:10:16",
  "UptimeSec": 616,
  "Vcc": 3.466,
  "Heap": 26,
  "SleepMode": "Dynamic",
  "Sleep": 50,
  "LoadAvg": 19,
  "MqttCount": 1,
  "POWER": "ON",
  "Wifi": {
    "AP": 1,
    "SSId": "my-wifi",
    "BSSId": "01:01:01:01:AB:CD",
    "Channel": 6,
    "RSSI": 88,
    "LinkCount": 1,
    "Downtime": "0T00:00:05"

Let’s plug in the 11827 with energy monitor and do the same setup. Immediately on its Web-based home page I see the energy consumption of the small test lamp I’ve plugged into it:

11827 with energy monitor

After a short time the device publishes to lamp/11827/tele/delock/SENSOR:

  "Time": "2020-11-22T15:20:42",
  "ENERGY": {
    "TotalStartTime": "2020-11-22T15:12:39",
    "Total": 0.003,
    "Yesterday": 0,
    "Today": 0.003,
    "Power": 28,
    "ApparentPower": 28,
    "ReactivePower": 0,
    "Factor": 1,
    "Voltage": 233,
    "Current": 0.12

Last, but possibly not least, is a scheduler (think cron(8)) with up to sixteen timers. Each timer can switch the load on or off, or toggle it’s state and blink it (we might not want to do that with most loads). I can specify days of the week at which this should occur, a particular time or let it do its thing at sunrise or sundown, and even set a random fudge factor of up to +- 15 minutes to pretend the schedule is not a schedule.


When the schedule fires, I get a POWER ON/OFF published to MQTT, but I’m not told it’s due to the schedule, which could be a nice thing to have.

I can type commands into the Web UI to set, e.g. dark mode, and there’s a list of valid MQTT commands on the Tasmota pages.

All in all, apart from the documentation which is very minimal, these little devices are quite nice and do what they’re supposed to do. Once again, I don’t believe WiFi is the correct carrier for doing this kind of thing (what do I do if I have to or want to change the WiFi password, eh?), but at this price, no complaints.

Oh, the price? I paid EUR 17.14 for the 11826 and EUR 19.90 for the 11827 with energy monitoring, about a third of what the nice Fibaro switches cost me back then.

Further reading


  • Thanks to @J3Gr_ who teaches me how to configure the device’s template after reflashing it OTA with the stock (9.1.0) tasmota.bin:

    Did the following: took the bin from official tasmota OTA, flashed, after flashing the template seemed resetted, go to and took the template string, inserted into UI, save and done.

    For posterity, the template string is {"NAME":"Delock 11827","GPIO":[0,0,0,17,133,132,0,0,131,158,21,0,0],"FLAG":0,"BASE":53}

  • I discover I can publish a command Webserver with parameter 1 (user mode) or 2 (admin mode) which hides/shows the full UI. In user mode I see the Toggle button on the UI only. The Webserver mode is persistent. (Warning: 0 disables the Web server entirely.)

MQTT, HTTP, DIY, and home automation :: 22 Nov 2020 :: e-mail

I began using Joplin for keeping notes a few months ago; it may have been Mischa of OpenBSD Amsterdam who recommended it, but in any case he raves about it. I’ll admit it’s the note-taking solution I like best so far, and I’ve been through a number of “solutions”.

screenshot of Joplin on macOS

Joplin has three features which convinced me to adopt it:

  • WebDAV as one of the supported cloud services
  • Clients for macOS, iOS, and Android
  • Markdown syntax for the notes

In addition to different methods for importing and exporting data (I wouldn’t want to lock myself in, would I now), Joplin has an API. It is enabled by launching Joplin’s Web Clipper service via its preferences and listens for HTTP requests on port 41184 by default. The preference page also lists the token required for accessing the API.

Tweets in Joplin

For a long time I’ve wanted to be able to collect specific tweets in my notes, and I thought I’d use the API to do so. The Python program can surely be improved upon, but it works well for what I want it to do.

I give it a URL to an individual tweet (it could also go through my favorites, but I don’t necessarily want only favorites), and the utility uses Tweepy to grab the status’ text, downloads its images and uploads these as so-called resources to Joplin, and then creates a new note. The Markdown of the note has image links to Joplin’s resources.

The screenshots show the result: above on macOS, below on iOS.

a Joplin note on an iPhone

Joplin’s been very reliable so far, and I hope that doesn’t change with me battering it this way. Updating Joplin is a bit cumbersome on macOS because I have to download and install a .dmg which I then allow macOS to launch, but I can live with that.

notes and api :: 09 Oct 2020 :: e-mail

I miss travel, and I miss flying. At the end of March Lufthansa canceled two transatlantic flights I had booked, and that was that; no more flying for me (or for almost anybody else). Is that a reason I’m currently interested in IATA airport codes? Maybe it’s the desire to fly to far-away places.

I began using a list of six thousand airport codes when I developed the first Ansible training, using them as fodder in an application students have to set up, and I decided to use the same data set in an exercise for an advanced Ansible training we’re developing.

The problem with the data I had is that I was about 97.2% sure that I was permitted to use it, but there wasn’t a really clear license associated with it. I’ve meanwhile found OurAirports; they provide a public domain data set, and on the weekend I had a bit of fun with it.

First I wrote an HTTP airport data server which will be running on lab machines. Students will be tasked with the assembly of an Ansible lookup plugin to obtain data from that server for use in templates. (I should probably clarify that my trainings are designed to run without a connection to the Internet – there have been a number of occasions when I’ve had to give trainings in environments which just do not permit their users to use such resources, hence the local service.)

      fra: "{{ lookup('airport', 'fra') }}"
      - debug: var=fra
TASK [debug] ************************************************************************
ok: [localhost] => {
    "fra": {
        "cc": "DE",
        "city": "Frankfurt am Main",
        "iata": "FRA",
        "id": "2212",
        "lat": "50.033333",
        "lon": "8.570556",
        "name": "Frankfurt am Main Airport",
        "osm": "",
        "type": "large_airport"

Just put it in the DNS!

I thought it’d be amusing to provide the public domain data via the DNS, so I did just that. Each airport has a couple of TXT and a LOC record associated with it. The domain name is the 3-letter IATA code:

$ dig +short TXT
"cc:ES; m:Barcelona; t:large, n:Barcelona International Airport"
$ dig +short LOC
41 17 49.560 N 2 4 42.456 E 4.00m 1m 10000m 10m

we also provide a URI which loads an map to the correct location, and if it’s in the source data, a URI to the Wikipedia page:

$ dig +short URI
10 1 ""
10 2 ""

You can obtain the IATA codes indexed by city, providing you spell it as OurAirports has, and in addition to IDNA names I’ve un-unicoded the names, so Münster becomes “munster”, and Tromsø becomes “tromso”. Query the domain for TXT records for the city:

$ dig +short TXT
"PHT," "PRX," "LBG," "CDG," "ORY"

I generate a file with resource records I then $INCLUDE into a zone master file, and I’m making this zone data file available in this repository for self-hosting. Do tell me if you use it, and I’ll add a pointer to your DNS server if you like.

I assume the data is relatively static (who is building airports nowadays – even BER has been completed but marked “closed”), but if we should notice it’s very volatile we can easily add dynamic DNS updates to update the DNS on the fly.

Whether this is useful is in the eye of the beholder. I can say, that I quite frequently use my country-code lookup service in the DNS.

$ dig +short TXT

So, have I now finally earned myself this mug?

put it in the DNS mug

As usual, do talk to me if you have ideas for improvement.


  • Standing of the shoulders of a giant, I’ve added support for IDNA in the city to IATA mappings; use a client which has IDNA support to query for “düsseldorf”
  • Mentioned on Stéphane Bortzmeyer’s on peut tout mettre dans le DNS, même les codes postaux.
  • Oli Schacher whipped up a service which provides the locations of postal codes in Switzerland via the DNS in the zone, e.g. via the looking glass: zipdns via the looking glass
  • Inspired by Stéphane’s and Oli’s work on zip codes, I’ve set up an additional zone with forward/reverse lookups of zip codes in Germany (Postleitzahlen), based on publicly available data.
  $ dig TXT
  ;; ANSWER SECTION: 604800	IN	TXT	"94036" 604800	IN	TXT	"94034" 604800	IN	TXT	"94032"

  $ dig TXT
  ;; ANSWER SECTION:	604800	IN	TXT	"Passau"
  • Florian blames me became inspired by what we all did here, and he built a zone which currently holds 200,000 entries in PowerDNS with UN/LOCODE. Florian and his colleagues use these codes to name anycast instances of k-root as IATA codes are too coarse for their purposes.
  $ dig +short LOC
  52 24 0.000 N 4 49 0.000 E 0.00m 1m 10000m 10m

  $ dig +short TXT
  "Amsterdam, NL"

All these zones we speak of here are DNSSEC signed.

DNS and ansible :: 04 Oct 2020 :: e-mail

Other recent entries