Let’s Encrypt and Zabbix Agents

A week or two ago, some of a number of servers became unreachable via Zabbix. Both pairs of servers (as we run everything in pairs) showed the same problem, and no other host did.

Looking in the Zabbix log showed this rather cryptic error:

SSL_shutdown() with set result code to 6

That’s not very helpful, and several hours of head-scratching went by before we finally stumbled across what was happening.

The X.509 (or SSL) certificate on the target machine is issued by Let’s Encrypt, who are in the process of signing new certificates with a new key. Within Zabbix, we check that the certificate presented by the client when we connect is issued by a specific issuer and since this had changed, the server was refusing to connect.

How did we fix it? Really easily – by going in to the host configuration in Zabbix, and setting the issuer to:

CN=R3,O=Let's Encrypt,C=US

Ridiculously straightforward, and if you hover over the red ‘ZBX’ status box, you’ll see an error saying that the wrong issuer was found on the certificate.

That’s a few hours we’ll never get back, but it’s great to have solved the problem. And as it happens, there was a correlation between the servers affected – two pairs were built at the same time, and the remaining pair was built just about 90 days before the others. Let’s Encrypt certificates have a lifetime of 90 days.

We’re expecting further servers to drop off Zabbix, but at least we know why and we can fix it.

Reading a VAXstation 3100 ROM

I’ve had a VAXstation 3100 M38 sitting in my flat for years, but it doesn’t quite work. I’ll write up the repair process elsewhere, but as part of troubleshooting I decided to see what’s on some of the ROMS.

There are two M27C1024 chips installed on the main board. These are STMicroelectronics chips, both 1Mbit EPROMs. Reading them on Minipro produced an interesting error:

pwh@angel:~/src/minipro$ ./minipro -p "M27C1024@DIP40" -r rom2.bin -y
WARNING: Chip ID mismatch: expected 0x20008C00, got 0x20FF8CFF (unknown)

Maybe this is a DEC variant. Throwing caution to the wind, I re-ran Minipro with the -y switch to ignore the error. This produced a 131,072 byte file for each chip, which is correct for a 1Mbit IC because 131072 bytes is 1 megabit.

The contents of the files were all over the place – nothing stood out as text except for this:

) ns Fn
s uie mae) 1Desc 9)taan
Dts (hwz) 10Nerlds 3Enis 1)or
) glh rishri) 1 Ptu
s 5Es
o 3)uo
) anis 1 Sns
) anisCadi) 1 Vam

If you’ve used a VAX before, you might recognise that this looks a little like the language selection menu:

0) Dansk                      8) Français (Suisse Romande)
1) Deutsch                    9) Italiano
2) Deutsch (Schweiz)         10) Nederlands
3) English                   11) Norsk
4) English (British/Irish)   12) Português
5) Español                   13) Suomi
6) Français                  14) Svenska
7) Français (Canadien)       15) Vlaams

Since this is the second ROM of a pair, let’s look at what the first contains:

0Dak 8)raai(SssRond
) uth Ilio
2)euchScei ) dean
) glh 1 Nsk 4Enis(Bti/Ish 2)orgu
) pal 1 Smi 6Fr
a 4)veka 7Fr a
(naen 5)las

If we take two bytes from one ROM, and two bytes from the second ROM, we can take a guess as to how the image is laid out. The characters “En” are from the the second ROM, “gl” from the first ROM, “is” from the second ROM, “h ” from the first ROM, and so on.

If it sounds like madness, but the datasheet makes it clear why:

It is ideally suited for microprocessor systems requiring large data or program storage and is organized as 65,536 words of 16 bits.

This handy bit of Python code will reassemble the data by reading two bytes from one file, two bytes from the second and so on:

#!/usr/bin/env python
#  Read the content of two files and output an interleaved file

from pathlib import Path

FILE_A = './rom1.bin'
FILE_B = './rom2.bin'

if Path(FILE_A).stat().st_size != Path(FILE_B).stat().st_size:
    print('Files differ')

fileA = open(FILE_A, 'rb')
fileB = open(FILE_B, 'rb')

data = b""

for n in range(Path(FILE_A).stat().st_size):
    data = data + fileA.read(2)
    data = data + fileB.read(2)

f = open('./out.bin', 'w+b')

How do we know it’s worked? Simple – the content of the binary file is exactly the same as the original KA42B firmware!

OpenVMS on a Raspberry Pi

Having recently changed jobs and become self-employed, I have a lot more spare time. What better way to spend it than getting an OpenVMS machines running on a Raspberry Pi.

VAX 8550 and console, courtesy of Michael L. Umbricht (CC BY-SA 4.0)

These instructions are necessarily quick and make assumptions that you know what you’re doing. They’re also heavily influenced by this YouTube video, and I’m very grateful for the simple explanation of Linux bridge and tap interfaces.

The basics

I started by copying the 32-bit version of Ubuntu 18.04LTS for the Raspberry Pi 3 on to an SD card. Pop that in your Pi and boot up with an Ethernet cable connected and a DHCP server available. There’s no need to attach a keyboard or monitor – just ssh in to the server with username and password, and it’ll prompt you to change your password on first login.

Cloning the simh repository and installing dependencies is straightforward, and takes about four minutes in my Pi 3B+:

apt install make libsdl2-dev libpng-dev libpcap-dev libvdeplug-dev bridge-utils
git clone https://github.com/simh/simh.git
cd simh
make -j4 vax8600

There are other targets you can build for, but we’re going to use the VAX 8600.

microvax1MicroVAX I (KA610)
microvax2MicroVAX II (KA630)
microvax3900MicroVAX 3900
rtvax1000rtVAX100 (KA620)
vaxMicroVAX 3900
vax730VAX 11/730
vax750VAX 11/750
vax780VAX 11/780
vax8200VAX 8200 (KA820)
VAX architecture build targets for SimH

Whilst it’s compiling, create a directory to hold your SimH installation, and copy your OpenVMS 7.3 (VAX) ISO image over to it. I’ve used ~/simh, but you can use whatever you like as long as you’re consistent.

Create the file vax8600.ini with the following content. I’ve annotated each line so you can see what it does:

; Set the memory size to 512 megabytes
set cpu 512M

; Use a TCP socket for the console
set console telnet=12344

; Set the CPU idle detection method to VMS to improve performance when OpenVMS isn't doing anything
set cpu idle=vms

; Set the CPU to a model 8650 (https://en.wikipedia.org/wiki/VAX_8000)
set cpu model=8650

; Configure an 1.5 gigabyte RA92 disk (http://bitsavers.informatik.uni-stuttgart.de/pdf/dec/disc/ra90/EK-ORA90-SV-003_RA90_RA92_Service_Jun90.pdf) on disk interface RQ0
set rq0 ra92

; Attach a disk image to interface RQ0 - SimH will create this on boot
attach RQ0 rq0-ra92.dsk

; Configure a CD-ROM drive (RRD40) on disk interface RQ3
set rq3 cdrom

; Attach the installation disk ISO image to interface RQ3
attach RQ3 -r openvms73.iso

; Disable  the RP Massbus controller
set rp disable

; Disable the RL11 cartridge disk controller
set rl disable

; Disable the RK611 cartridge disk controller
set hk disable

; Disable the RX211 floppy disk controller
set ry disable

; Disable the ??
set ru disable

; Disable the TS11 magnetic tape controller
set ts disable

; Disable the TUK50 magnetic tape controller
set tq disable

; Disable the DZ11 8-line terminal multiplexer
set dz disable

; Disable the LP11 line printer
set lpt disable

; Enable the Ethernet controller
set xu enable

; Set the MAC address to use for the Ethernet controller
set xu mac=08-00-2b-00-00-0a

; Attach the Ethernet controller to a TAP interface 'vaxa'
attach xu tap:tapvax

When your VAX binary has compiled, copy it from simh/BIN/vax in to your VAX directory.

Setting up networking

We’re ready to boot our emulator, but we need to set up networking first. It’s not much fun to have a standalone VAX, so we’re going to create a TAP interface and a bridge to allow our VAX to access our network.

A TAP interface virtual interface which runs at the Data Link layer (MAC) and allows us to connect the VAX’s Ethernet interface to an interface on our host operating system.

Setting the TAP interface is as easy as running this command:

ip tuntap add mode tap user ubuntu tapvax
ip link set dev tapvax up

If you’re not running under user ‘ubuntu’, change it to your user name.

To access the network, we need to set up a bridge – which is like a switch (or hub, if you’ve been working with Ethernet for as long as I have) and connects many interfaces together. We’re going to create a bridge called ‘br0’, and add the ‘tapvax’ interface to it, and add the Pi’s ‘eth0’ interface.

My preferred way of doing this is to configure Netplan to do it for us. It’s quite straightforward – create /etc/netplan/01-network.yaml with the following:

  version: 2
  renderer: networkd
      dhcp4: no
      dhcp4: yes
        - eth0

After rebooting your Pi, you’ll have an interface named ‘br0’ with a DHCP-assigned IP address, and another interface named ‘eth0’ with no IP address. Here’s the first issue – I haven’t yet found a way to configure the TAP interface with NetPlan, so each time you reboot, you’ll have to run the TAP interface setup above.

Booting the emulator

Now comes the exciting part – run ./vax which will start and take a moment or two to create your RQ0 disk. At the simh> prompt, enter ‘boot dua3’. This will boot a minimal OpenVMS image to install the base system. It will ask for the date and time, so enter it – then it’ll search for and configure devices on your system.

Three devices should appear:

Available device DUA0: device type RA92
Available device DUA1: device type RD54
Available device DUA2: device type RD54
Available device DUA3: device type RRD40

DUA0 is your system disk. DUA1 and DUA2 aren’t connected but are available, and DUA3 is your CD-ROM image. Enter ‘YES’ when prompted, and you’ll be dropped at a ‘$’ prompt. Enter the following command:


This will copy over a minimal install of OpenVMS in about 3-4 minutes. To actually boot from this image, press CTRL+E, type ‘boot rq0’ and the emulator will reboot.

Installing OpenVMS

After entering the current date and time, you will be prompted to enter a volume label for the system disk. I use the nodename followed by SYS, but it can be anything from 1-12 characters.

Next, you’ll be asked which components you want to install. A reasonable set is:

  • OpenVMS library
  • OpenVMS optional
  • OpenVMS Help Message
  • DECnet Phase IV networking

Before installing DECnet Phase IV, you’ll be asked if you want to continue – just select yes.

A word on DECnet

Setting the SCSSYSTEMID parameter needs a bit of background knowledge about DECnet addressing. In short, DECnet addresses comprise an area (1 to 63) and a node (1 to 1023). There is a relationship between a DECnet address and a MAC address, and all DECnet hosts have a MAC address starting AA-00-04-00.

The SCSSYSTEMID is calculated by multiplying the DECnet area by 1024, and then adding the node number. For simplicity, I’ve used addresses 1.1 and 1.2 for my two hosts, which means the SCSSYSTEMIDs will be 1025 and 1026.

After DECnet Phase IV has been configured, you’ll be asked if you want to register any Product Authorization Keys. Select no – we’ll do that later.

Select your timezone and daylight savings settings, and the installation process will finish up by running AUTOGEN and rebooting.

Configuring your system

The first task to do is decompress the OpenVMS libraries. Disk space isn’t an issue, so we might as well run @SYS$UPDATE:LIBDECOMP.COM and decompress all libraries. This will take a while!

At this point, we should install the OpenVMS Hobbyist licences we’ve got. These can either by typed in at the console very slowly, or the script run from a CD-ROM image. We’re going to go with the latter.

To do this, copy the licence script in to a file called vms_paks.com and put this in a directory with no other files. The ‘mkisofs’ command will create an ISO image, and we run it as follows:

mkisofs . > /tmp/licence.iso

To make this image accessible to our OpenVMS machine, we need to quickly break out of the running operating system and type this at the console:

attach rq3 -r /tmp/licence.iso

When you’re back in OpenVMS, mount the image using the following command:


You can shorten OVERRIDE=IDENTIFICATION down to OVER=ID. This parameter tells OpenVMS to mount the image as-is, without trying to mount it according to the volume name.

We could run the script straight from our CD-ROM image by running @DUA3:[000000]VMS_PAKS.COM, but this won’t work – the line endings are wrong. To work around this, copy the file to your home directory and set some attributes on it:


When you run @VMS_PAKS.COM, it’ll happily install all the licences. You can see what’s installed by running SHOW LICENSE:

Installing TCP/IP

Almost all conventional operating systems have an IP stack available by default. OpenVMS doesn’t, so it needs to be installed.

Before we install TCP/IP, we’ll need to increase the global pages value – by default, it’s 15000 and we need it to be higher.


Add the following lines:


Next, run AUTOGEN:


The installation is in the director TCPIP_VAX051 on the OpenVMS CD-ROM. We can mount it and install with the following commands:


There isn’t much to configure with the initial installation, so we can start to configure:


Select option 1 – Core Environment, and option 2 – Interfaces. Select option 2 to use DHCP, and option 1 to enable DHCP_CLIENT.

That’s all folks

Wow, what a lot of work. Operating systems from decades past were installed and configured, then often never shut down for months or years. How times have changed.

Installing TLS certificates on HP printers automatically

Installing a TLS (SSL) certificate on an HP LaserJet printer automatically isn’t as difficult as you might think

I wrote an article about installing a Let’s Encrypt TLS certificate on an HP LaserJet printer a while ago.  Since then, I’ve been annoyed by having to install updated certificates manually, so I decided to look at how I could automate it.

HP LaserJet printer control panel
Photo by Alex Furr from FreeImages

TechRadar has a great article on securing printers, but how do you automate it? Well, with a certificate authority like Let’s Encrypt for starters, but there’s no mechanism for the printer to automatically update its certificate after it expires.

I’ve set my desktop machine to certbot and renew the certificate automatically. An evening’s hacking around the web interface showed it’s really easy to install a certificate automatically.

This is the magic command to install the certificate:

curl -v --insecure https://HOSTNAME/hp/device/Certificate.pfx --form upload=@/tmp/cert.pfx --form Password=password

Replace HOSTNAME with the hostname of your printer and change /tmp/cert.pfx as required. If you want to know how to create the PFX file, see my original post.

Has anyone else found out how to do this? If they have, they’ve not posted about it!

Running Postgres as a normal user

Whilst testing out some PostgreSQL replication scenarios, I needed to have multiple instances of Postgres running under my user. I could have used multiple Docker machines with ports exposed, but I opted for another solution. Here’s how I did it.

First, I created /home/user/pgdata to hold the data directories, and /tmp/postgres to hold the UNIX socket files.

Then, it’s just a case of running /usr/lib/postgresql/12/bin/initdb /home/user/pgdata/1 -E UTF-8 to create a data directory, then editing postgresql.conf and changing the TCP port from 5432 to, say, 50432, and unix_socket_directories to /tmp/postgresql.

To start the server, run /usr/lib/postgresql/12/bin/pg_ctl -D /home/user/pgdata/1 -l logfile start, and to stop it, replace "start" with "stop". The final step is to create a postgres user with /usr/lib/postgresql/12/bin/createuser -drs postgres -h localhost -p 50432 and you’re away.

Some use cases I can think of for this are:

  • An isolated Postgres instance for integration testing
  • Trying out a new version of Postgres in parallel with your existing version
  • Verifying your disaster recovery procedures

There are probably more.

Hue-ge pain in the butt

I have a Philips Hue bridge which lets me control the lights in my flat in a variety of useful ways. It’s a good bit of kit, but with one major problem – it assumes you’re running PAT (Port Address Translation), and that your Hue bridge and the device you access https://account.meethue.com/bridge both access the Internet from the same source IP address. If not, even though the devices may be in the same broadcast domain and the same IPv4 subnet, you won’t be able to link your Hue to your account.

Despite tweeting for assistance, I ended up crying shibboleet and reverse-engineering the method of linking they’re using. Here’s what I found out, in the hope it’ll save somebody else a lot of time.

My Internet services are through the excellent A&A, and I can’t recommend them highly enough. I have a public IPv4 subnet, and each of my devices accesses the Internet without any address translation. Inbound connectivity is restricted – there are only a few things I need accessible from the Internet. (As an aside, I have two DSL lines, with my IPv4 subnet routed down each – load balancing and resilience)

My Hue bridge connects to https://discovery.meethue.com/, and that service makes a note of an inventory that the bridge sends to it. Here’s where the problem is – visiting discovery.meethue.com only returns the devices that registered from the IP address you’re connecting from. That’s fine if all your devices go through address translation and appear to come from a single external IP address, but useless for me – my mobile device uses an entirely different IPv4 address, as does my desktop and laptop. The Hue app reports that no devices were found.

After some frustrating interactions on Twitter, I solved the problem myself. I set up IP Masquerade – essentially port address translation behind the router’s external IPv4 address – for my Hue bridge and my mobile device, so they’d appear to be coming from the router’s external IP address. Rebooting the Hue, disabling one of the PPP connections on my router (necessary since they both have an IP address assigned, and my outbound traffic is load-balanced per TCP connection) and linking the device from my mobile phone then worked. Rolling it all back and rebooting the Hue again leaves the device linked to my account.

What a mess. Adding a “Enter the IP address of your Hue then when prompted, press the button” on the device linking page would have been a whole load easier. Not everyone’s Internet connection is the same, nor is everyone as experienced in network engineering as I am… yet still it took me three days to work out a fix.

In summary: buy Hue devices – they’re good, but beware if you’re doing anything that possibly deviates from the common case.

Integration and Unit Tests with IntelliJ IDEA and JUnit 5

When working on a project in Java, I like to name my integration and unit tests separately. Integration tests end ‘-IT’, and unit tests don’t.

This makes it really easy to run just unit or integration tests in IntelliJ by using one of these two patterns:

  • Integration tests – ^(.*IT.*).*$
  • Unit tests – ^(?!.*IT.*).*$

Make sure the Run configuration has the Test Kind set to Pattern, and searches for tests in the whole project.

OpenLDAP with TLS and LetsEncrypt on Ubuntu 16.04

Using TLS (SSL) certificiates with OpenLDAP

Every time we added this, an error cropped up:

A project I’m working on requires a Kerberos and LDAP infrastructure. Most technology projects are easy to do badly, and more difficult to do well – and documented.

We use LetsEncrypt to issue a certificate to each server, and OpenLDAP can take the certificate and use it to encrypt and authenticate connections from other LDAP servers.

One of the problems I encountered was when setting up replication between servers. We use SaltStack to build and maintain our server estate, so deployment and configuration needs to be automated. This normally means spending a week automating a process which you’ll only do twice – once when you install it, and once again when you’re rebuilding the server and have forgotten everything you’ve done.

To secure OpenSSL, add this LDIF file to your directory:

dn: cn=config
add: olcTLSCACertificateFile
olcTLSCACertificateFile: /etc/letsencrypt/live/ldap1.example.com/fullchain.pem
add: olcTLSCertificateFile
olcTLSCertificateFile: /etc/letsencrypt/live/ldap1.example.com/cert.pem
add: olcTLSCertificateKeyFile
olcTLSCertificateKeyFile: /etc/letsencrypt/live/ldap1.example.com/privkey.pem

Every time we did this, a vague error popped up:

$ sudo ldapmodify -Y EXTERNAL -H ldapi:/// -f ./ssl.ldif
SASL/EXTERNAL authentication started
SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
SASL SSF: 0 modifying entry "cn=config"
ldap_modify: Other (e.g., implementation specific) error (80)

Restarting slapd, the LDAP daemon, with full debugging showed nothing, neither did running the daemon through strace.

Several days of painful troubleshooting followed. We eventually found the issue – the LDAP daemon wasn’t able to access the TLS certificates! AppArmor was blocking access to the files under /etc/letsencrypt, and so we did two simple things.

First, we used setfacl to give the openldap user ‘rx’ permissions on /etc/letsencrypt/live and /etc/letsencrypt/archive:

$ sudo setfacl -m user:openldap:r-x /etc/letsencrypt/live
$ sudo setfacl -m user:openldap:r-x /etc/letsencrypt/archive

This lets the LDAP daemon read and following the symbolic links in the live directory to the actual files.

Next, we needed to tell AppArmor to let the LDAP daemon, slapd, access the files themselves. It’s as easy as creating a file called /etc/apparmor.d/local/usr.sbin.slapd with the following:

/etc/letsencrypt/live/{{ grains.id }} r,
/etc/letsencrypt/archive/{{ grains.id }} r,
/etc/letsencrypt/archive/{{ grains.id }}/** r,

The trailing comma on the last line isn’t a mistake – if you don’t include it, AppArmor won’t install the rule. I won’t admit how long it took us to realise this!

Turning off SSID broadcast for HP LaserJet printers

How do you turn off your HP LaserJet printer’s built-in WiFi Direct SSID?

A new HP LaserJet printer arrived in the office recently. After plugging it on to the office network, the setup was easy, but I couldn’t find a way to stop the printer broadcasting a wireless network name.

Photo by Rybson from FreeImages

Several hours of testing, failing, resetting and re-testing led me to HP’s Support Forum.  I found some instructions from a user who worked out how to disable the network.  It’s not logical or obvious.

First, make sure you don’t have a USB cable attached to the printer. Go to the web configuration page and select Networking > Wi-Fi Direct Setup, and turn WiFi Direct on.

Then, set the connection method to Advanced, and check “Do not broadcast the Wi-Fi Direct Name”. Click Apply, restart the printer, then go back in to the configuration page and turn Wi-Fi Direct off.

I don’t know why this isn’t an obvious option, but HP might fix it in a later release of the printer’s firmware.

Converting from assert() to assertEquals() in Java

When I was inexperienced in Java, I wrote a lot of tests using assert(), rather than using assertEquals().
Revisiting code today, I wanted to update many test suites to use assertEquals(), which requires I flip the expected and actual values around. Too difficult to do quickly by hand, so I used the following regular expression:
Find: assert \((.+)\)\.equals\((.+)\)\);
Replace: assertEquals($2, $1));
It worked like a treat.