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.

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

Jekyll to Hugo

October 16, 2021

I took some time this weekend to do the following:

  1. Migrate this site from Jekyll to Hugo.
  2. Create a new site theme.
  3. Update the About page.
  4. Add a new Articles section.

The Articles section will allow me to write longer articles without polluting the main blog.

Hugo Migration


  • Significantly faster. Jekyll sometimes tool 30 seconds to regenerate this site, and Hugo does it in less than a second.
  • Single static binary. Much easier to install.
  • Built-in SASS/SCSS support.
  • HTML, CSS, and JavaScript minification. In particular, HTML minification is content-aware so it doesn’t strip significant whitespace in <pre> or <code> blocks. View the source of this page to see what I mean).
  • Go template support.
  • Jekyll importer that works reasonably well (but see below).


  • The default Markdown parser is limited and not based on GitHub-flavored Markdown.
  • Because of the previous item, many of the literal <img> elements in my imported Jekyll content had to be fixed by hand.
  • Occasionally baffling template search order.
  • Default RSS feed entry contents are truncated and stripped of HTML.

Theme Rewrite

Changes in the new theme:

  • Clean rewrite based on Bulma v0.9.3.
  • Icons from Feather Icons.
  • Consistent font sizes.
  • Improved layout. In particular, I removed the sidebar and improved the page header.

COVID-19 in the United States

March 28, 2020

I put together a tool to track COVID-19 in the US by state.

It is available here: COVID-19 US.

Weather Sage 0.1.2 Released

August 25, 2019

A couple days ago I released Weather Sage 0.1.2.

Weather Sage is a command-line tool and Ruby library to get the weather forecast and current weather observations for a domestic United States street address.

It uses the Census Bureau Geocoding API to geocode street addresses, and the National Weather Service Weather API to get weather forecasts and current weather observations.

The command-line tool queries the APIs above to obtain the requested information for the given street address, and then prints the results to standard output in CSV format.

Here’s an example of using the weather-sage command-line tool to get the weather forecast for the address 1600 pennsylvania ave nw, washington dc:

> weather-sage forecast '1600 pennsylvania ave nw washington dc'
1600 pennsylvania ave washington dc,Today,97,F,7 mph,SW,Scattered Showers And Thunderstorms
1600 pennsylvania ave washington dc,Tonight,76,F,2 to 7 mph,SW,Scattered Showers And Thunderstorms then Mostly Cloudy
1600 pennsylvania ave washington dc,Tuesday,94,F,6 mph,E,Slight Chance Rain Showers then Chance Showers And Thunderstorms
1600 pennsylvania ave washington dc,Tuesday Night,75,F,2 to 6 mph,S,Chance Showers And Thunderstorms
1600 pennsylvania ave washington dc,Wednesday,94,F,3 to 9 mph,SW,Chance Showers And Thunderstorms
1600 pennsylvania ave washington dc,Wednesday Night,76,F,5 to 8 mph,SW,Chance Showers And Thunderstorms

And here’s an example of using the weather-sage command-line tool to get the current weather observations from the weather station closest to the address 1600 pennsylvania ave nw, washington dc:

> weather-sage now '1600 pennsylvania ave washington dc'
1600 pennsylvania ave washington dc,timestamp,time,2019-08-19T06:52:00+00:00
1600 pennsylvania ave washington dc,textDescription,text,Mostly Cloudy
1600 pennsylvania ave washington dc,temperature,value,26.700000000000045,unit:degC,qc:V
1600 pennsylvania ave washington dc,relativeHumidity,value,81.65039907186703,unit:percent,qc:C

You can install Weather Sage via RubyGems like this:

# install weather-sage with rubygems
> gem install weather-sage

Note: This is the initial release, so the API documentation, test suite, and command-line interface are not yet complete.

What to do with an STM32?

August 25, 2019

STM32F103C8T6 (Blue Pill).

STM32F103C8T6 (Blue Pill).

Last week my bag of generic STM32F103C8T6 “Blue Pill” boards arrived.

So far I’ve flashed Pill Duck and a couple of examples from Beginning STM32.

Now all I need are some project ideas.

All I’ve come up with so far are another Pocket Jim or another BME280-based temperature monitor.

Releases: Luigi Template 0.5.0, ZipStream-PHP 0.3.0, and Mathy

August 25, 2019

Some stuff I released months ago but never posted:

Pi 4 Benchmarks

August 25, 2019

world-renowned pablotron test facility

world-renowned pablotron test facility

A few weeks ago I picked up a 1GB Raspberry Pi 4 Model B and decided to run some openssl speed benchmarks.

Test systems:

  • Raspberry Pi Zero W
  • Raspberry Pi 3 Model B
  • Raspberry Pi 3 Model B+
  • Raspberry Pi 4 Model B
  • Linode VM (1 Core)
  • Intel i7-8650U (Lenovo X1 Carbon, 6th Gen)
  • AMD Ryzen Threadripper 1950X

Test algorithms:

  • blake2b512
  • blake2s256
  • sha256
  • sha512
  • aes-128-cbc
  • aes-192-cbc
  • aes-256-cbc

Results are available here. The code used to run the tests and generate the charts is here.

Archived Posts...
© 1998-2021 Paul Duncan