The HTTP/3 Book

Today I published my first book, “The HTTP/3 Book”!

This book is the latest incarnation of things I’ve produced relating to HTTP/3. The first was a conference talk I first gave at ConFoo 2024 in Montreal. That went down pretty well, and I was asked to do the same talk at the International PHP Conference 2024 in Berlin. The room was so packed for that talk that they have asked me back to do the same task again at IPC Munich 2024 later this year!

S&S Media, the German company behind IPC, publishes lots of magazines for developers, and I was approached by their representative in Berlin about writing an article on this subject. So after a little negotiation (particularly about rights!), I sat down to turn my talk into an article. This was made quite easy given the framework I had from my talk, but I was pretty pleased and surprised to be able to crank out a decent 4,000 word article in a bit over 2 hours. It’s not been published yet, but I’ll amend this post when it has.

I’d enjoyed writing this article so I wondered about expanding it into a short book. Asking around on Mastodon suggested that the easiest way to go was self-publishing on Leanpub; I didn’t want to get pulled into writing some monster tome for a publisher.

It took about 2 days of writing, plus another day or so learning the ins and outs of the Markua markup format, tweaking, proofreading (thanks to my mum!), drawing diagrams, and building a site to demonstrate the difference in network activity across different HTTP versions.

A week on from my first thoughts of doing this, I’m very happy to find myself with a freshly published 54-page, 12,000-word book! I’ve of course publicised it across all my social media, but need to put a bit more effort into marketing, perhaps look into Leanpub’s translation services. Anyway, we will see how it goes, but it’s been a good experience so far.

Blue Team – The Song

In the world of penetration testing, as I’m often involved in with ROS, those taking on the role of attackers are referred to as the “red team”, and those defending as the “blue team”. Red team people are often regarded as the rock stars of INFOSEC, but one key difference is that red-teamers only have to succeed in their efforts once, whereas blue-teamers have to succeed every time. Unfortunately, when the blue team succeeds, nothing notable happens, so they don’t get much of the glory.

This song is a tribute to the unsung heroes of the blue team; Gotta keep out the bad guys, baby!

Unsurprisingly, the “gotta keep out the bad guys” line was about the first thing I thought of, and everything stemmed from that. There were lots of blue-teamy things I could have written about, but I preferred to keep it short. The string-bend bass riff was the first bit of music, then the funky guitar parts, though I ended up dialling them back a bit in favour of some chuggy rhythm guitar. As in my other tracks, I was keen to use Synthesizer V for vocals, and the backing vocals came out really well. I wrote the lead guitar solo, then thought a higher vocal part alongside it might work, and it was also a chance to have a dig at the red team; it’s my favourite bit of the song.

There are a lot of guitar parts overall (all played by me), and only some small pad and organ keyboards for backing. I was especially happy that I managed to pull off the more aggressive bass parts and the lead solo. As usual, the drums were all done with Logic’s Drummer instrument, which does a great job without getting drunk and falling asleep during rehearsals, and its excellent “follow” mode meant that the drums could match what I’d played on the bass, rather than being some disconnected pattern.

As usual, I’m not too happy with my vocals (PRs welcome!), but Logic’s Flex Pitch editor works enough magic to get the job done. This track could really do with someone that can get a bit more grungy in the verses, with a hint of Elvis for the chorus.

[Intro]
Don’t break a sweat
from a constant threat.
We’ve got the tools to meet them
and firewall rules to defeat them.

[Verse]
We’ll take our time
to build our defences.
No need to be concerned,
we know the consequences.

They’re going to attack
our networking stack,
but we can keep them guessing as
their port scans come to nothing.

[Chorus]
Because, I’m on the blue team, baby,
we’ve got to always win.
Gotta keep out the bad guys,
can’t ever let them in.

Come join the blue team, baby,
we need your awesome skills.
Come watch that bad actor
try to guess my second factor.

[Solo]
Oooh red team stays outside,
don’t want you here.
Just go away
and don’t come back.
You’ve gotta find another way.

[Verse]
Alarm bells ring
from a tripwire’s string.
Logs tell a sad, sad story
of a search for a way in.

SOC screens flash
for a matching hash
We’ve seen this one before
and there’ll be many more

[Chorus]
That’s why I’m on the blue team, baby, (ooh yeah)
we’ve got to always win. (blue team, blue team)
Gotta keep out the bad guys, (ooh yeah, gotta keep out the bad guys baby)
can’t ever let them in.

Come join the blue team, baby,
we need your awesome skills.
Show your strength, let it shine,
help take those APTs offline

We’re on the blue team, (ooh yeah)
got to always win. (blue team, blue team)
Gotta keep out the bad guys, (ooh yeah, gotta keep out the bad guys baby)
can’t ever let them in.

We’re on the blue team, baby,
we’ve got to always win.
We’re the unsung heroes.
(gotta keep out the bad guys, baby)

If you like this song, please consider supporting me by buying my album, “Developer Music” on Bandcamp, and sharing links to my song posts on here. If you can sing (or play something) and would like to be involved in musical projects like this, please get in touch, as I could really use your help!

Git & SSH sitting in a tree…

I do work for a bunch of different clients, who variously use GitLab and GitHub. For many years I put up with the incessant problem of accidentally signing my commits as the wrong user. It’s just so easy to forget to set the right GPG key and email address when you just want to get on with a project. It’s not the end of the world, but it’s annoying.

Often the same thing goes for the SSH keys you use to push and pull from your git repo; it’s a bit too easy to be lazy and use the same SSH key across multiple clients, when a little isolation would be a good idea from a security perspective.

What if you could work some magic so that identities and GPG and SSH keys are set to the right values right from the start, for every project for each of your clients? Read on…

This whole setup reminds me very much of a post I wrote in 2009 (13 years ago!) on the “holy trinity” of DNS, TLS, and virtual host wildcards that allow you to dynamically host vast numbers of previously undefined sites without having to touch your web server config at all, a classic example of convention over configuration.

First of all let me introduce you to .gitconfig. This file usually sits in your home directory, so for me on macOS that’s /Users/marcus/.gitconfig. This file contains your global git defaults, and is an easy-to-read config file in an “ini” style (and no, those are not real values!):

[user]
    name = Marcus Bointon
    email = marcus@example.com
    signingkey = AC34DF5B434BB76
[github]
    user = Synchro
    token = f693251e52043a23fe5fbd955cff56ff
...

You’ll find lots of other sections in here, which you can read about in the git config docs. But we are only really interested in one option: includeIf. This directive conditionally includes another git config file into your settings, and one of the things you can make it conditional upon is the path to your project. This is useful. I typically set up my client’s projects in the macOS default Sites folder within my home directory. Each client gets a folder, and each of their projects lives within that. This provides a tidy location to put a separate .gitconfig file that can be applied to all of their projects. It ends up like this:

~/.gitconfig
~/Sites/
    client1/
        .gitconfig
        project1/
        project2/
    client2/
        .gitconfig
        project1/
        project2/

Each .gitconfig file only needs to include the differences from the defaults that are set in the primary config file that lives in your home dir. To set up the GPG signing key and email for all of their projects, the file would contain this:

[user]
    email = remotedev1@client1.example.net
    signingkey = 434BB76AC34DF5B

Back in our primary file, we would add this conditional statement to automatically pull in this extra config whenever git is operating in this folder:

[includeIf "gitdir:~/Sites/client1/"]
    path = ~/Sites/client1/.gitconfig

And that’s it as far as GPG goes – commits will now be signed with the key and email address that are specific to this client, so when you set up your next project for them, you won’t have to do anything to set it up; it’ll Just Work™.

But what about SSH? The chances are that your client will have asked you for an SSH public key to add to their repo to provide you with sufficient access, but setting the GPG key doesn’t do anything towards selecting an SSH key for that purpose. You could do that using environment variables (which can be quite annoying) before, but fortunately, git 2.10.0 added the core.sshCommand config option that allows us to specify the SSH command that git uses for file transfer operations, and that can include a -i parameter to select an SSH identity (and -C to use compression for a possible speed boost). Add this to your client’s .gitconfig file, using the path to your client-specific identity file (not the public key which has a .pub suffix) like this:

[core]
    sshCommand = "ssh -i ~/.ssh/id_ed25519_client1 -F /dev/null"

Side note: I do hope you’re using Ed25519 keys for SSH; they’re newer, smaller, stronger, and faster than RSA keys, and they’ve been supported in OpenSSH since version 6.5 in 2014, so if your server doesn’t support them, you probably have bigger problems, or maybe you’re just running RHEL… I hope you’ve seen the post-quantum features of OpenSSH 9.0 too. The SSH client config file (usually found in ~/.ssh/config) is also really useful for twiddling per-directory or per-server configs that you can just set and forget.

Once you’ve done that, your commits will now be signed using your clients’ GPG key, and pushed to their repo using their specific ssh key, and you won’t have to change anything when you start new projects for them, so long as you put them in the same folder.

“What about my IDE?”, I hear you ask. Not to worry, most IDEs use your system’s git and ssh configs, so all this should work just fine with PHPStorm, VSCode, etc.

While I’m sure some bright spark can make this even more dynamic to automate this across clients, I find new clients are rare, but projects turn over fast enough for this to be a real win for getting that first commit signed and pushed correctly, first time.

Postman pre-request script for Laravel registration

A common way to register in a Laravel API is to send a POST request to /users containing a username, password, any other info, and also an HMAC signature using a server-side secret. In this example, it’s validated on the server by this class:

    public const HASH_ALGORITHM = 'sha256';

    protected const REQUEST_KEYS = [
        'email',
        'name',
    ];

    private $secret;

    public function __construct(string $secret = null)
    {
        if (! $secret) {
            throw new \InvalidArgumentException('The registration secret must be provided');
        }

        $this->secret = $secret;
    }

    public function verify(Request $request): bool
    {
        return rescue(
            function () use ($request) {
                $hash = hash_hmac(
                    self::HASH_ALGORITHM,
                    json_encode($request->only(self::REQUEST_KEYS)),
                    $this->secret
                );

                return hash_equals(
                    base64_encode($hash),
                    $request->get('signature', '')
                );
            },
            false
        );
Code language: PHP (php)

So we can see that it’s expecting a Base64-encoded HMAC-SHA256 signature of a JSON array containing the email and name properties.

If you’re trying to make this request in Postman, you obviously need to calculate this same signature or it won’t work. Fortunately Postman has pre-request scripts that can inspect bits of your request and environment and generate new elements before your request is sent, and that’s what we need to use.

We don’t want the secret to be saved in our request collection, so we keep it in an environment, and pull it out dynamically when the request is made. Postman includes the Crypto-js package, which includes the necessary signature and encoding functions we need. Coming from PHP, the syntax for these operations feels very convoluted, but it goes like this:

const signature_string = '{"email":"' + request.data.email + '","name":"' + request.data.name + '"}';
const hmac = CryptoJS.HmacSHA256(signature_string, pm.environment.get('REGISTRATION_SECRET'));
const b64 = CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(hmac));
pm.environment.set("REGISTRATION_SIGNATURE", b64);Code language: JavaScript (javascript)

This builds the string to sign from the request elements we need, calculates the HMAC of it using our secret, and then base64-encodes it before saving it in the Postman environment.

You can then add the signature into your request by adding the REGISTRATION_SIGNATURE variable to the body:

Hope that helps someone!