Collected scribblings

Things written in times past

I used to write things for a previous employer's tech blog, but the old URLs may succumb to bitrot. These should work as long as Medium works:

The challenges of running a betting exchange (2016)

Notes on interviewing engineers (2016) -- This one was also picked up by a recruiter's blog.

DevOps is culture, not a title prefix (2017)

Security and Devops - a natural fit (2017)

Wait, what is my fleet doing (2018)

Hey, guess what? Your passwords have been compromised

Shields up on user information (2019)

Coronation Time

Dedicated to Cause

UK government's refusal to negotiate with the NHS staff can only be taken as dedication to monarchy. They want to make sure that King Charles's coronation will be a once-in-a-lifetime experience for as many people as possible.

Audits explained

Audits explained

A pentest is like going to the GP for a check-up. An audit is like having a month-long colonoscopy.

from copilot import vulnerabilities

Not your grandfather's MVC

This was perfectly predictable. CoPilot generates insecure code, as expected.

Machine Learning, the magic pixie dust of the past decade, is all about volume. And writing secure code is harder than writing insecure code. So by sheer volume there will be a lot more insecure code around.

Given that a lot of code in the wild is essentially a minimum viable copypaste from the highest scoring answer on StackOverflow, teaching the code generator model has obviously consumed a lot of insecure code. Since SO rewards speed, the answers that take the least time to write will receive most points.

Writing secure code takes more time and more space - so by the time someone submits an answer that considers security aspects, the person asking the question has already accepted (and ran with) the first and shortest working answer instead.

StackOverflow has redefined the MVC programming model. It now stands for Minimum Viable Copypaste.


Lusikoitavaa chiliä

Arvioitu valmistusaika: 1,5h


Muokattu Guardianin reseptistä


  • Reilu 300g tuoreita chilejä
  • 1kg hienosokeria
  • Pektiiniä
  • 400ml siideriviinietikkaa

Muut tarvikkeet

  • Hillopurkkeja
  • 5l kattila
  • Soseutin


Mittaa reilu 5g pektiiniä ja sekoita hienosokeriin.

Keitä purkit ja kannet erillisessä kattilassa. Ota syrjään ja pidä veden alla kunnes hillo on valmista.



Leikkaa chilien tyvet pois, halkaise ja poista siemenet sekä suurin osa valkoisesta lihasta.

ilman siemeniä


Perattuna chilejä pitäisi olla noin 250g. Ei ole kovin tarkkaa. (Alkuperäisen reseptin mukaan 200g riittää mutta nyt lisätään vähän potkua ja makua.)

noin 250g

Pilko chilit sopivan pieniksi ja soseuta.



Laita aineet kattilaan:

  • sokeri ja pektiini
  • soseutettu chili
  • etikka

kaikki sekaisin

Keitä ja sekoita noin 15m verran. Keitos saa kuohua ihan kunnolla.


Hillo on valmista kun se jähmettyy kylmälle lautaselle. Jos keittämistä jatkaa muutaman minuutin pitempään, tulos on gelatiinimaisempi.

Kaada hillo purkkeihin. Vie ensin joksikin aikaa huoneenlämpöön tai ulos jäähtymään, ja kun purkit eivät enää ole kuumia, siirrä jääkaappiin.




Pysyy hyvänä jääkaapissa jopa puolikin vuotta. Ellei lopu ensin.

Swipe to SSH

SSH (Yubi)Key Authentication

SSH with private keys coming from secure hardware. What's not to like?

You've read the How-To. You've changed the pin with:

yubico-piv-tool -a change-pin

You're generating a resident key with:

ssh-keygen -t ed25519-sk -O resident -f ~/.ssh/yubi_ed255_key

After entering your pin, the above command breaks with "enrollment failed: invalid format". Surely you're doing something wrong? After a minute of head scratching, you try again, this time with:

ssh-keygen -vv -t ed25519-sk -O resident -f ~/.ssh/yubi_ed255_key

And are greeted with a confusing error. According to the error code (FIDO_ERR_PIN_NOT_SET) the resident key can not be generated because your YubiKey is not protected with a pin. But you've changed it already - what gives?

You've changed the PIN for the PIV application... which is different from the FIDO2 application.

Right idea, right PIN, wrong application?

Turns out you're missing the right tool. Get the correct one with:

${SUDO} apt install yubikey-manager

And then configure the FIDO2 application code with:

ykman fido set-pin

Now you can rerun the command from above and generate a private key directly with the YubiKey.

Things that work

The private key file, generated by the ssh-keygen command, can be nuked. It is after all a resident key, accessible directly from the YubiKey device. And you probably didn't add a keyphrase for it either.

So you can now load the private key into SSH agent, with:

ssh-add -K

You'll need to type in the PIN you set earlier.

... and a few that don't

The main problem with the above setup is that every use of the private key, even when loaded to the agent, requires to touch the magic button. To make things worse, the client doesn't show any hint what is needed, from a casual observer's point of view establishing the connection seems to hang.

This is okay for random logins, but breaks non-interactive workflows, and utterly messes up remote autocomplete. Touch the key one time too few, and the autocomplete never finishes. Touch it one time too many, and you've just vomited an OTP string to your terminal.

An ideal setup would allow the agent to authenticate without interaction for a configurable time, but so far this seems not to be supported.

Near-future experimentation

Documentation for ssh-keygen states that the resident key may be generated with the additional option -O no-touch-required to allow fully non-interactive use. At least at the time of writing, portable OpenSSH v8.4 does not appear to support the option, which may be for the best. Additionally, the public key requires special annotation for its entry in authorised_keys but even then it's not a good idea.

Because this option essentially would turn the YubiKey into a USB-attached SSH trust/identity dongle, it's far too dangerous to be used without other mitigations.

The missing hint

The bit about FIDO2 application for SSH client and the necessary command was found here.

Helpful two-liners

When changing the PIN/PUK codes, of course you want the new codes to be random. A really easy way to generate them is with python. Like this:

% python3

import secrets

secrets.randbelow(10**6) # for PIN

secrets.randbelow(10**8) # for PUK

Getting home for plague'mas

Be honest

Planning to travel to visit your family in these plague-ridden times, you're really saying:

"I miss my family so much they will see me if it's the last thing they do."

It's that time of the year again

Page from an undated journal

Daddy's crying. Mommy has a black eye. Little sister's hiding under her bed.

Yep, it's christmas.

Perception is almost everything

Misunderstood Indexing

Holding the number-one spot on Corruption Perceptions Index tells nothing about how well a country is doing.

It merely highlights how bad the situation is even for the runner-up.

Dear online surveillance addicts

Ground rules for acceptable ads online

The following is an edit of a piece originally written in November 2015.

The pinnacle of non-intrusive online ads were the original Google search ads. They were out of the way, clearly marked as ads - and hence could be visually filtered out. They were pure text, so could be neatly included as elements on the rendered page. And they were always targeting an INTEREST. Not an individual.

I will take that as the minimum acceptable advertising behaviour. I'm not implying it's perfect, but at least we set a clear set of ground rules. With that in mind, my ideal, non-intrusive ads mechanism builds on the following rules:

  • Ads must never be inline to page content.
  • Even when clearly out of the way, ads must not be allowed to mimic page content; they must be clearly marked as ads.
  • Text only.
  • I might accept an image within the ad, provided it was always served from the content provider's system.
  • As an extension to previous point: if the served image size would exceed a notable fraction of the page size, it must not be included in the output.
  • No user tracking of any kind.
  • No third-party javascript. Ever.
  • At most 15% of display real estate allowed to be used by ads. Including the padding in the UI. (It all counts as space denied from content.)
  • Not allowed to affect page content load times. Ad material must be included at the end of the page code. If your service pushes ads from internal and separate system, hard timeouts must be imposed: if the internal system cannot serve an ad within an allotted time, the frontend must never be forced to wait. You just missed an ad impression. Tough.
  • If clicking an ad takes a user through a bounce page, all identifiable information from the user must be stripped. Bounce page or redirect must not impose any further page loading delay.
  • No beacons.

Breaking even one of the rules automatically disqualifies you.

If you, as an advertiser, find these rules unacceptable - well, then we are in mutual disagreement. I find your ads equally unacceptable and will treat them as a form of cancer.

However, as a genuine service to the user... please allow the users to search for ads that have been displayed to them. Preferably by display context. I would be glad to return to a subject at a later date and search for something I remember seeing earlier.

The above set of rules is still not ideal, but everything that behaved according to them would at least be palatable.