The Birthday Paradox

November 11, 2021

How many people have to be in a room before the probability that there is a shared birthday is greater than 50%?

This is called the Birthday Problem, and the solution is known as the birthday paradox. It is interesting because the answer is counterintuitive and the ramifications affect the security of cryptographic hash algorithms.

The explanation is a bit long for a blog post, so I wrote a full article:

The Birthday Paradox

Wireguard is Awesome

November 6, 2021

I’ve been using WireGuard since late 2019. Several months ago I installed the Android client on my phone and tablet, and the Windows client in a Windows 10 VM.

A few months ago I was able to disable external SSH access to my home network and public servers, and a few weeks ago disabled external IMAPS access too.

What’s so great about WireGuard?

Here’s a complete WireGuard client configuration file from my laptop with the keys, hosts, and subnets changed:

PrivateKey = sEJqK6KqBVkYdMi/66ORZXyD5NFzVcPcq/m0/Sd29m0=
Address =

PublicKey = WMoOWb0FMF516mGgKMyQefjMvD7xTO8NNCrQJJQnpUE=
PresharedKey = jhhJ1oFjHKEZ8pMK+hmar9SaQEQtJrd2lW6710kQ/d8=
EndPoint =
AllowedIPs =


That’s it.

If you’ve ever struggled with the mountain of configuration needed for IPsec or a TLS VPN like OpenVPN, then the example above should be a breathe of fresh air.

By the way, if you’re trying to route traffic from a client on a common reserved subnet (ex: to network behind a VPN with the same subnet, take a look at the DNATs and Maps section of my NFtables Examples article.

Feed Bloater

November 5, 2021

In addition to fixing the RSS feed for this site, I also created a simple command-line tool named Feed Bloater which expands truncated RSS 2.0 feeds by doing the following:

  1. Fetch the contents of the feed.
  2. Fetch the HTML from the <link> for each feed item.
  3. Filter the HTML from the previous step based on a CSS selector.
  4. Replace the truncated item descriptions with the HTML from the previous step.
  5. Write a new RSS feed to the given output path.

Feed Bloater maintains an internal cache and respects the ETag and Last-Modified headers, and by default it won’t update the output file if the source feed has not changed.

Here is an example that uses Feed Bloater to expand the truncated LLVM Weekly RSS feed:

feedbloater path/to/llvmweekly.xml


Here’s what the original LLVM Weekly RSS feed looks like in The Old Reader:

Truncated LLVM Weekly RSS feed, viewed in The Old Reader.

Truncated LLVM Weekly RSS feed, viewed in The Old Reader.

Here’s what the expanded RSS feed generated by the example above looks like:

LLVM Weekly RSS feed, expanded by Feed Bloater, and viewed in The Old Reader.

LLVM Weekly RSS feed, expanded by Feed Bloater, and viewed in The Old Reader.

Much better! I’ve been happily using Feed Bloater to expand several truncated feeds for about a week.

If you’re interested in trying Feed Bloater, you can find installation, usage, and configuration instructions in the documentation on the Feed Bloater GitHub Repository.

RSS Feed No Longer Annoyingly Trunca...

October 26, 2021

Hugo’s default RSS feed template truncates RSS feeds by default. Long time readers probably know how I feel about that.

I copied the default Hugo RSS feed template and made the following changes:

  • Limited the feed to pages in the “Posts” section (Note: this may not work for your site).
  • Set the default post limit to 15 posts, because Hugo was trying to include all posts in the RSS feed.
  • Changed feed items to include complete post contents as HTML.

The updated template is available here. You can use it on your site by copying it to layouts/_default/index.xml.

The Nuclear Option (No More unsafe-inline)

October 25, 2021

As you can see from the last post, I went with the nuclear option and created a Hugo table shortcode, then did the following:

  1. Updated all the tables on the site to use the new table shortcode.
  2. Removed style-src 'self' 'unsafe-inline' from the Content-Security-Policy header.
  3. Re-ran the Security Headers scan.

Here is the updated Content-Security-Policy from the Apache config:

# look ma, no unsafe-inline!
Header append "Content-Security-Policy" "default-src 'self'; img-src 'self'"


And here is the updated Security Headers scan result:

Updated Security Headers scan result.

Updated Security Headers scan result.

A couple of recommendations for folks getting started with Hugo:

  1. Do not use <img>; use the figure shortcode instead. The latter is far more flexible and also works well with a responsive design.
  2. If you are embedding complex tables or you are generating tables with alignment and want to avoid inline style attributes, do not use the Markdown table syntax. Use hugo-shortcode-table instead.

Table Shortcode for Hugo

October 25, 2021

I just posted hugo-shortcode-table, a shortcode for Hugo which allows you to generate CSS-only (no inline style attributes) tables.


  • Much more powerful than built-in Markdown table syntax.
  • Emits CSS classes for alignment rather than inline style attributes (no more style-src unsafe-inline).
  • Table can be defined as front matter or site data.
  • Column tooltips.
  • Cell-specific overrides (alignment, tooltip, etc).
  • HTML and Markdown markup support.
  • Easy to configure emitted CSS classes for a variety of frameworks, including Bulma and Bootstrap.


TLS and Header Fixes

October 21, 2021

Yesterday I scanned this site using the following tools:

I made a several Apache configuration changes based on the initial scan results:

  1. Disabled ancient versions of TLS.
  2. Set an explicit cipher suite list using the Mozilla SSL Configuration Generator.
  3. Refined the value of the Access-Control-Allow-Origin header.
  4. Added two new headers: Referrer-Policy and Permissions-Policy.

After a couple of iterations of changes and testing, I:

  • Updated my recent post to reflect the response header changes in the Apache configuration.
  • Applied the same changes (with minor tweaks) to several other sites.

Tip: Use the Mozilla SSL Configuration Generator to generate your TLS configuration. It has three client profiles (“modern”, “intermediate”, and “old”) and supports a variety of servers (web, email, database, application, etc).

Random thoughts:

Screenshots of the improved scan results are available below. Click the SSL Labs and Security Headers pictures to see the scan details.

SSL Labs scan results.

SSL Labs scan results.

Security Headers scan results.

Security Headers scan results.

Lighthouse desktop scan results.

Lighthouse desktop scan results.

Nftables Sitrep

October 20, 2021

In April I decided to switch the firewalls for my laptop and a couple of servers from iptables to nft and nftables.

After several months of use I can report that the experience has been positive.


  • Simple declarative configuration file. No more hacky shell scripts.
  • Atomic (all or nothing) ruleset changes.
  • Faster ruleset changes.
  • Built-in JSON support.


  • Occasionally finicky parser.
  • Remapping IP ranges can be more verbose than iptables.

The detauls are a bit long for a blog post (even for me!), so they are available as a separate “Nftables Examples” article instead.

Hugo/Content-Security-Policy Impedance Mismatch

October 19, 2021

As part of the site upgrades, I’ve also been clamping down on the response headers. The full list is below, but I ran into a couple of problems trying to remove 'unsafe-inline' from the style-src section of the the Content-Security-Policy header.

The two issues I encountered were:

  1. Chroma renders syntax highlighting fragments with inline style attributes, rather than CSS classes.
  2. Goldmark renders table cell alignment with inline style attributes.

Neither of these defaults is ideal if you don’t want to allow inline styles.

After reading this documentation, I was able to configure Hugo and Chroma to use CSS classes instead of inline style attributes by doing the following:

# switch to theme assets directory
cd themes/hugo-pt2021/assets

# generate static chroma stylesheet
hugo gen chromaclasses --style=monokai > chroma.css

# append chroma to style.sass
echo '@import "chroma"' >> style.sass

Finally, I added the following to config.toml:

  # syntax highlighting
    # use classes instead of inline style attributes
    noClasses = false

That leaves me with table cell alignment issue, which does not appear to be as easy to fix. According to the documentation, the Goldmark Table extension does support modifying the table cell alignment rendering behavior, but the options are limited:

  1. align attribute: deprecated in HTML5
  2. style='text-align: ...': what I am trying to avoid
  3. none: requires AST post-processing

Options #1 and #2 are out, and I don’t see an obvious way to configure Hugo to do something useful with #3.

One nuclear option would be to:

  1. define a custom table shortcode,
  2. move table data to the data directory, then
  3. bypass the table renderer entirely

Frankly the table markup syntax is so atrocious that I’m not sure the nuclear option would be all that bad.

In any case, here are my response headers so far:

# set security headers (2021-10-17)
Header append "Strict-Transport-Security" "max-age=31536000"
Header append "X-Frame-Options" "SAMEORIGIN"
Header append "X-Content-Type-Options" "nosniff"
Header append "Cross-Origin-Opener-Policy" "same-origin"
Header append "Cross-Origin-Resource-Policy" "same-origin"
Header append "Access-Control-Allow-Origin" ""
Header append "Access-Control-Allow-Methods" "POST, GET, HEAD, OPTIONS"

# updates (2021-10-20)
Header append "Referrer-Policy" "strict-origin-when-cross-origin"
Header append "Permissions-Policy" "camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), midi=(), payment=(), usb=()"

# 'unsafe-inline' needed for hugo table cell alignment :/
Header append "Content-Security-Policy" "default-src 'self'; img-src 'self'; style-src 'self' 'unsafe-inline'"


Update (2021-10-20): Based on the the scan results from, I constrained Access-Control-Allow-Origin, added Referrer-Policy, and added Permissions-Policy.

Update 2 (2021-10-25): I went with the nuclear option.

Imported Old Repos

October 17, 2021

Several months ago I used fast-export to convert most of my old repositories from Mercurial to Git. This morning I finally imported the converted repositories to GitHub and archived them.

The old repositories are as follows:

Here is the repo import script:


set -eu 

# space-delimited list of repos
repos='... omitted for brevity ...'

# get base repo dir

# loop through repos
for repo in $repos; do
  # print repo name, switch to repo
  echo repo: $repo
  cd "$base/$repo"

  # create repo
  gh repo create $repo

  # fix origin
  # (gh sets the origin to "
  git remote rm origin
  git remote add origin$repo.git

  # push upstream
  git push -u origin master

  # archive repo
  gh repo archive $repo
Archived Posts...
© 1998-2021 Paul Duncan