aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--content/articles/site-backend.md638
-rw-r--r--static/files/articles/site-backend/0-editing-1024.pngbin0 -> 79220 bytes
-rw-r--r--static/files/articles/site-backend/0-editing-1024.webpbin0 -> 62528 bytes
-rw-r--r--static/files/articles/site-backend/0-editing-raw.pngbin0 -> 346242 bytes
-rw-r--r--static/files/articles/site-backend/0-editing.pngbin0 -> 123954 bytes
-rw-r--r--static/files/articles/site-backend/1-ssl-labs-20240530-1024.pngbin0 -> 22450 bytes
-rw-r--r--static/files/articles/site-backend/1-ssl-labs-20240530-1024.webpbin0 -> 17940 bytes
-rw-r--r--static/files/articles/site-backend/1-ssl-labs-20240530.pngbin0 -> 44361 bytes
-rw-r--r--static/files/articles/site-backend/2-securityheaders-1024.pngbin0 -> 21164 bytes
-rw-r--r--static/files/articles/site-backend/2-securityheaders-1024.webpbin0 -> 14280 bytes
-rw-r--r--static/files/articles/site-backend/2-securityheaders.pngbin0 -> 15081 bytes
-rw-r--r--static/files/articles/site-backend/pablotron.org.conf.txt51
-rw-r--r--static/files/articles/site-backend/script.js.txt37
-rw-r--r--static/files/articles/site-backend/style.sass.txt58
-rw-r--r--static/files/articles/site-backend/tls.conf.txt9
-rw-r--r--static/files/articles/site-backend/webhook.conf.txt34
16 files changed, 708 insertions, 119 deletions
diff --git a/content/articles/site-backend.md b/content/articles/site-backend.md
index 6bfa592..5fb750b 100644
--- a/content/articles/site-backend.md
+++ b/content/articles/site-backend.md
@@ -3,7 +3,7 @@ slug: "site-backend"
title: "Site Backend"
# date (optional for articles)
-date: "2023-09-08T05:07:47-04:00"
+date: "2024-05-31T03:57:18-04:00"
# draft articles are not visible on live site
draft: true
@@ -13,21 +13,40 @@ show: true
# uncomment to show table of contents
toc: true
+
+pics:
+ editing:
+ css: "image"
+ tip: "Editing this article with Vim and Firefox."
+ sources:
+ - "/files/articles/site-backend/0-editing-1024.webp"
+ - src: "/files/articles/site-backend/0-editing-1024.png"
+ width: 1024
+ height: 640
+ ssl-labs-report:
+ css: "image"
+ tip: "SSL Labs SSL Test results for this site."
+ sources:
+ - "/files/articles/site-backend/1-ssl-labs-20240530-1024.webp"
+ - src: "/files/articles/site-backend/1-ssl-labs-20240530-1024.png"
+ width: 1024
+ height: 525
+ securityheaders-report:
+ css: "image"
+ tip: "Security headers report for this site from securityheaders.com"
+ sources:
+ - "/files/articles/site-backend/2-securityheaders-1024.webp"
+ - src: "/files/articles/site-backend/2-securityheaders-1024.png"
+ width: 1024
+ height: 320
---
## Overview
-This site is statically generated by [Hugo][] and themed with a
-stripped-down version of [Bulma][]. Both have been heavily customized;
-see the [Configuration](#configuration) section for details.
-
-The goals of this setup are:
+Site content is managed as [Markdown][] files stored in a [Git][]
+repository. The theme is a modified version of [Bulma][]. The site is
+statically generated by [Hugo][].
-- Size
-- Speed
-- Security
-- Mobile Support
-- Accessibility
-- Simplicity (for me)
+[{{< pe-figure "editing" >}}][editing-this-article]
## History
@@ -36,72 +55,375 @@ written by me. In 2019 I resurrected the site after a several year
hiatus and migrated the site from the custom [PHP][] backend to
[Jekyll][]. In 2021 I switched from [Jekyll][] to [Hugo][].
+## Goals
+
+The goals of this site backend are:
+
+- Small
+- Fast
+- Secure
+- Mobile-friendly
+- Accessible
+- Simple (for me)
+
+Static generation neatly addresses the first three goals:
+
+- The site is small because the [Hugo][] can be configured to
+ aggressively pack and minify content and assets. Images are
+ also compressed and served in multiple formats (see [Hugo
+ Configuration](#hugo-configurationi "Hugo Configuration") and [Images](#images
+ "Images")).
+- The site is fast because web servers can serve static content
+ extremely quickly, and because static content can be cached. Caching
+ and compression are enabled in [Apache][] (see [Apache
+ Configuration](#apache-configuration "Apache Configuration")).
+- The site is secure because the content is static; there is no
+ web-accessible endpoint which can upload files or modify content.
+ Additional security measures are discussed in several of the following
+ sections.
+
+Explanations of how I keep the site mobile-friendly and accessible are
+available in the [HTML](#html "HTML") and [Hugo
+Configuration](#hugo-configuration "Hugo Configuration") sections below.
+
+**TODO:** discuss simplicity
+
## Content
### HTML
-I create and edit site content with [vim][] in [Markdown][] format and
+I create and edit site content with [Vim][] in [Markdown][] format and
preview the results locally with `hugo serve -D ...`. Articles and blog
posts are saved as drafts and committed to the [Git][] repository so I
can work on them as time permits.
-The content and site layout is designed with accessibility and mobile in
-mind. This means:
+The content and layout is designed to be accessible and mobile-friendly:
-- links, and table components have `title`, `aria-label` attributes
-- images have captions, fallback formats, `title` and `alt` attributes.
+- links and table components have `title` and `aria-label` attributes
+- images have captions, fallback formats, and `title` and `alt` attributes
- images scale gracefully to thumbnails on mobile
-- the site honors users' [color scheme preference][] and has a manual
- theme override
-- The page menu bar collapes to a hamburger menu on mobile.
+- the site supports [dark mode][], checks the [color scheme
+ preference][prefers-color-scheme] to determine the default theme,
+ and has a [manual theme switcher][post-theme-switcher]
+- The menu bar collapes to a hamburger menu on mobile.
### Images
Images are created as follows:
- Mathematical content is created using [Mathy][], saved as [SVG][],
- and minified using [minify][].
-- Charts are created with [Matplotlib][], saved as [SVG][], and minified
- using [minify][].
-- Lossless images (e.g. screenshots) are scaled and cropped with
- [GraphicsMagick][] and served as a lossless [WebP][]
- with a [PNG][] fallback. The fallback is compressed with
- [pngquant][].
-- Lossy images are [GraphicsMagick][] and served as a lossy [WebP][]
- with a [JPEG][] fallback.
+ minified using [minify][], and served cached and compressed by [Apache][].
+- Charts are created with [Matplotlib][], saved as [SVG][], minified
+ with [minify][], and served cached and compressed by [Apache][]
+ Example: [This Python script][bench-chart] was used to generate the
+ bar charts in [this blog post][post-fips203ipd].
+- Lossless raster images (e.g. screenshots) are scaled and cropped with
+ [GraphicsMagick][] and served as a lossless [WebP][] with a [PNG][]
+ fallback compressed by [pngquant][].
+- Lossy raster images (e.g. pictures) are scaled and cropped with
+ [GraphicsMagick][] and served as a lossy [WebP][] with a [JPEG][]
+ fallback.
+
+Other notes:
+
+- The animated site logo is an [SVG][] generated by [this Ruby script][gen-logo.rb].
- Menubar icons are borrowed from [Bootstrap Icons][].
-- The animated site logo is generated by [this Ruby script][logo-script].
+- I reviewed several [PNG][] compressors in [this post][post-png-compressors].
### JavaScript
-There is almost no [JavaScript][], by design. The one script that is
-used is minimal, [deferred][], used for two things:
-
-1. to handle the [theme toggle][].
-2. to toggle the mobile site menu.
+This site has almost no [JavaScript][], by design. The [JavaScript][]
+that is used is minimal, [modern][es2015], [deferred][], and only used
+for the [theme switcher][post-theme-switcher] and burger menu.
+
+Below is the unminified `script.js` for this site with some notes
+removed. It is 777 bytes minified and 586 bytes minified and
+compressed:
+
+```js
+'use strict';
+
+//
+// script.js - script which handles:
+//
+// - set theme
+// - theme switcher and burger menu event handlers
+//
+
+const D = document,
+ C = D.body.parentElement.classList,
+ L = localStorage,
+ M = window.matchMedia,
+ on = (el, id, fn) => el.addEventListener(id, fn);
+
+// use theme if set, otherwise fall back to browser preference
+if (L && L.theme && L.theme === 'dark') {
+ C.add('dark'); // theme set to "dark"
+} else if ((!L || !L.theme) && M && M('(prefers-color-scheme: dark)').matches) {
+ C.add('dark'); // prefers dark color scheme
+}
+
+document.addEventListener('DOMContentLoaded', () => {
+ // theme toggle event handler
+ on(D.querySelector('.navbar-item[data-id="theme"]'), 'click', (e) => {
+ e.preventDefault(); // stop event
+ L.theme = C.toggle('dark') ? 'dark' : 'light'; // toggle
+ });
+
+ // iterate through burgers, bind to click events
+ D.querySelectorAll('.navbar-burger').forEach(e => on(e, 'click', () => {
+ // then toggle is-active on burger and menu
+ [e, D.getElementById(e.dataset.target)].forEach(
+ e => e.classList.toggle('is-active')
+ )
+ }));
+});
+```
+[Download][script.js]
## Deployment
Once I am ready to apply any outstanding changes to the public web site,
I push the outstanding commits to a remote [Git][] repository. This
-triggers a [`post-receive` hook][], which sends an authenticated `POST`
-request to a [web hook][] exposed on a TLS endpoint on the web server.
+triggers a [`post-receive` Git hook][post-receive], which sends an
+[HMAC][]-authenticated `POST` request to a [web hook][] endpoint on the
+web server.
-The [web hook][] runs a [deployment script (`deploy.rb`)][] which does
-the following:
+The [web hook][] verifies the [HMAC][] and then runs [a deployment
+script][deploy.rb] which does the following:
-1. Verifies the authenticated timestamp (to prevent replay attacks).
+1. Verifies the authenticated timestamp (to prevent [replay attacks][]).
2. Clones the upstream repository.
-3. Executes [Hugo][] (`hugo --minify -d ...`) to build the site minified
- in a unique output directory.
-4. Updates the `htdocs` symlink for the public web site to point at
- the output directory from the previous step.
+3. Executes [Hugo][] (`hugo --minify -d ...`) to build the site in an
+ isolated output directory.
+4. Updates the `htdocs` symlink for the public-facing web site to point
+ at the output directory from the previous step.
5. Removes any stale builds.
## Configuration
-This section discusses the configuration for [Hugo][], [Bulma][] and
-[Apache][].
+This section discusses the configuration for [Apache][], [Bulma][],
+[Hugo][], and [Webhook][].
+
+### Apache Configuration
+
+This section disusses the [Apache][] configuration for this site. I
+have broken this section into three sub-sections to make them easier to
+digest.
+
+The [Apache][] configuration relies on the following modules:
+
+- [mod\_deflate][mod-deflate] (see note below)
+- [mod\_http2][mod-http2]
+- [mod\_macro][mod-macro]
+- [mod\_proxy][mod-proxy]
+- [mod\_rewrite][mod-rewrite]
+
+Note: It is safe for this site to enable [mod\_deflate][mod-deflate]
+because it does not use [cookies][] and is not vulnerable to [BREACH][].
+and does not use [cookies][].
+
+#### Virtual Host Configuration
+
+The [Apache][] virtual host configuration is modified as follows:
+
+- Unconditionally redirect from [HTTP][] to [HTTPS][]
+- Unconditionally redirect to strip the `www.` hostname prefix
+- Enable [HTTP/2][]
+- Set security headers (discussed in [Security Headers](#security-headers))
+- Enable aggressive caching of image, script, and style assets
+
+Below is the [Apache][] virtual host configuration for this site with
+extraneous comments and configuration for logging, [TLS][], and legacy
+redirects removed:
+
+```apache
+# unconditionally redirect to https://pablotron.org
+<VirtualHost *:80>
+ RewriteEngine On
+ RewriteRule ^/(.*)$ https://pablotron.org/$1 [R,L]
+</VirtualHost>
+
+<VirtualHost *:443>
+ # strip "www." prefix and enable mod_deflate
+ Use STRIP_WWW https://pablotron.org
+ Use MOD_DEFLATE
+
+ # enable http2
+ Protocols h2 http/1.1
+
+ # set restrictive content security policy
+ Header append "Content-Security-Policy" "default-src 'self'; img-src 'self' https://pmdn.org"
+
+ # set remaining security headers
+ 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" "https://pablotron.org"
+ Header append "Referrer-Policy" "strict-origin-when-cross-origin"
+
+ # set permissions policy
+ Header append "Permissions-Policy" "camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), midi=(), payment=(), usb=()"
+
+ # POST needed for /hooks
+ Header append "Access-Control-Allow-Methods" "POST, GET, HEAD, OPTIONS"
+
+ # cache images, stylesheets, and javascript for 1 year
+ <FilesMatch "\.(ico|jpg|jpeg|png|gif|webp|svg|js|json|css)$">
+ Header set Cache-Control "max-age=31536000, public"
+ </FilesMatch>
+
+ # expose webhook
+ <Location /hooks/>
+ ProxyPass "http://localhost:9000/"
+ ProxyPassReverse "http://localhost:9000/"
+ </Location>
+</VirtualHost>
+```
+
+[Download][apache-vhost.conf]
+
+#### Security Headers
+
+This site uses a strict [Content-Security-Policy][] header; it rejects
+links to all external assets with one exception: references to images
+hosted on <https://pmdn.org/> are allowed.
+
+The trickiest part of restricting [Content-Security-Policy][] was
+`style-src`; many content generation tools (including the [Markdown][]
+table generator in [Hugo][]) break without `style-src 'unsafe-inline'`
+(e.g., the ability to emit arbitrary `style` attributes).
+
+In order to work around the `style-src` issues I ended up [reconfiguring
+the Hugo syntax highlighter](#hugo-configuration) and writing [a custom
+Hugo table shortcode][hugo-shortcode-table] which only emits `class`
+attributes for inline styling.
+
+The journey to a strict [Content-Security-Policy][] is documented in
+this series of blog posts from October 2021:
+
+- [Hugo/Content-Security-Policy Impedance Mismatch (October 19, 2021)][post-headers-1]
+- [TLS and Header Fixes (October 21, 2021)][post-headers-2]
+- [The Nuclear Option (No More unsafe-inline) (October 25, 2021)][post-headers-3]
+
+The remaining security headers are explained in the following articles:
+
+- [Security headers quick reference][security-headers-quick-ref]
+- [Goodby Feature Policy and hello Permissions Policy!][permissions-policy]
+- [Permissions Policy Explainer][]
+- [Referrer-Policy][]
+
+The security headers on this site earn an A+ rating from
+[securityheaders.com][]:
+
+[{{< pe-figure "securityheaders-report" >}}][securityheaders-report]
+
+#### TLS Configuration
+
+The certificate for this site is issued by [Let's Encrypt][] and managed
+automatically by [certbot][] with the [certbot-dns-linode][] plugin.
+
+The [Apache][] [TLS][] configuration is based on the intermediate
+settings from the [Mozilla SSL Configuration Generator][ssl-config]. In
+particular, only [TLS 1.2][] and [TLS 1.3][] are enabled, and the
+insecure ciphers from [TLS 1.2][] have been disabled.
+
+```apache
+# explicit list of cipher suites (from ssl-config.mozilla.org)
+SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
+
+# use server priorities for cipher algorithm choice
+SSLHonorCipherOrder on
+
+# protocols to enable (only TLS 1.2 and TLS 1.3)
+SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
+```
+
+[Download][apache-tls.conf]
+
+This [TLS][] configuration earns an A+ result from the [SSL Labs SSL
+Test][ssl-labs-ssl-test]:
+
+[{{< pe-figure "ssl-labs-report" >}}][ssl-labs-report]
+
+### Bulma Configuration
+
+The site [CSS][] based on the [Bulma][] with the following
+modifications:
+
+1. All unused components removed
+2. monokai style for [Chroma][] added
+3. Styles for navbar icon highlighting and table captions added
+4. dark mode styles added
+
+Here is the [SASS][]:
+
+```sass
+// style.sass: based on bulma-0.9.3/sass/bulma.sass with the following
+// changes:
+//
+// 1. all unused components removed
+// 2. monokai style for chroma added
+// 3. styles for navbar icon highlighting and table captions added
+// 4. dark mode styles added
+@charset "utf-8"
+
+// import chroma style
+//
+// generated with the following command:
+//
+// cd themes/hugo-pt2021/assets
+// hugo gen chromaclasses --style=monokai > chroma.css
+//
+@import "chroma"
+
+@import "bulma-0.9.3/sass/utilities/_all"
+@import "bulma-0.9.3/sass/base/_all"
+
+// elements
+@import "bulma-0.9.3/sass/elements/button"
+@import "bulma-0.9.3/sass/elements/container"
+@import "bulma-0.9.3/sass/elements/content"
+@import "bulma-0.9.3/sass/elements/image"
+@import "bulma-0.9.3/sass/elements/table"
+@import "bulma-0.9.3/sass/elements/title"
+@import "bulma-0.9.3/sass/elements/other"
+
+// components
+@import "bulma-0.9.3/sass/components/media"
+@import "bulma-0.9.3/sass/components/navbar"
+
+// grid (reenabled, used for images)
+@import "bulma-0.9.3/sass/grid/_all"
+
+// helpers
+@import "bulma-0.9.3/sass/helpers/_all"
+
+// layout
+@import "bulma-0.9.3/sass/layout/section"
+@import "bulma-0.9.3/sass/layout/footer"
+
+// dim navbar icons by default
+.navbar-item .menu-icon
+ opacity: 60%
+
+// highlight icons on hover
+.navbar-item:hover .menu-icon
+ opacity: 100%
+
+// table captions below tables
+table.table
+ caption-side: bottom
+
+// dark mode (2024-05-27)
+@import "dark"
+```
+[Download][style.sass.txt]
+
+With these changes the generated [CSS][] is 137 kB minified and 16 kB
+minified and compressed.
### Hugo Configuration
@@ -120,37 +442,56 @@ enhancement][].
The generated [HTML][] has been modified to:
-- add support `go-import`
+**TODO:**
+
+- add support `go-import`
- add a [Mastodon][] `<link rel='me' ...>` tag.
-- remove all unnecessary tags
+- remove all unnecessary tags
- to combine and [minify][] and [JavaScript][] and [CSS][] assets.
- to add `integrity` attributes to `<link>` and `<script>` tags.
-### Bulma Configuration
-
-**TODO:**
-
-- bulma modified to remove all unnecessary components
- - show `style.css`
-- added chroma styles
-- added dark mode
-
-### Apache Configuration
-
-**TODO:**
-
-- security headers (see next section)
-- `static/files/articles/site-backend/pablotron.org.conf.txt` (config w/ garbage removed)
-- mod deflate
-- mod rewrite for old links
-- aggressive caching
-- http2
-- letsencrypt
-- tls config (1.2 or newer, remove garbage algos)
-
-## HTTP Security Headers
+### Webhook Configuration
+
+[webhook][] configuration with [HMAC][] key removed:
+
+```json
+[{
+ "id": "deploy-pablotron-org",
+ "execute-command": "/data/www/pablotron.org/git/bin/hook/deploy.rb",
+
+ "pass-arguments-to-command": [{
+ "source": "payload",
+ "name": "time"
+ }],
+
+ "pass-environment-to-command": [{
+ "source": "string",
+ "envname": "DEPLOY_HTDOCS_PATH",
+ "name": "/data/www/pablotron.org/builds/current"
+ }, {
+ "source": "string",
+ "envname": "DEPLOY_REPO_DIR",
+ "name": "/data/www/pablotron.org/git"
+ }, {
+ "source": "string",
+ "envname": "DEPLOY_BUILDS_DIR",
+ "name": "/data/www/pablotron.org/builds"
+ }],
+
+ "trigger-rule": {
+ "match": {
+ "type": "payload-hmac-sha256",
+ "secret": "(omitted)",
+ "parameter": {
+ "source": "header",
+ "name": "X-Hub-Signature"
+ }
+ }
+ }
+}]
+```
-TODO
+[Download][webhook.conf]
## Validation
@@ -165,14 +506,157 @@ I periodically verify the following manually:
## Other
-- content served minified, cached, and compressed.
-(more stuff from `TODO.md`)
+**TODO:**
-```apache
-# TODO: include annotated files/articles/site-backend/pablotron.org.conf.txt
-```
+- wireguard
+- private ssh, private git
+- (more stuff from `TODO.md`)
[hugo]: https://gohugo.io/
"Hugo static site generator."
[bulma]: https://bulma.io/
"Bulma minimal CSS framework."
+[mathy]: https://mathy.pmdn.org/
+ "Online LaTeX editor."
+[svg]: https://en.wikipedia.org/wiki/SVG
+ "Scalable Vector Graphics vector image format (SVG)"
+[png]: https://en.wikipedia.org/wiki/PNG
+ "Portable Network Graphics lossless raster image format (PNG)"
+[webp]: https://en.wikipedia.org/wiki/WebP
+ "WebP raster image format"
+[vim]: https://www.vim.org/
+ "vim text editor"
+[markdown]: https://en.wikipedia.org/wiki/Markdown
+ "Markdown lightweight markup language"
+[matplotlib]: https://matplotlib.org/
+ "Python charting library"
+[pngquant]: https://pngquant.org/
+ "Command-line tool for \"lossy\" compression of PNG images"
+[graphicsmagick]: http://www.graphicsmagick.org/
+ "Command-line swiss army knife tool for image processing"
+[php]: https://www.php.net/
+ "PHP programming language"
+[bootstrap icons]: https://icons.getbootstrap.com/
+ "Free high quality open source icon library"
+[jekyll]: https://jekyllrb.com/
+ "Jekyll static site generator"
+[git]: https://git-scm.com/
+ "Free and open source distributed version control"
+[javascript]: https://en.wikipedia.org/wiki/JavaScript
+ "JavaScript scripting language"
+[minify]: https://github.com/tdewolff/minify
+ "Minifier Go library and command-line tool"
+[mastodon]: https://en.wikipedia.org/wiki/Mastodon_(social_network)
+ "Mastodon distributed social network"
+[css]: https://en.wikipedia.org/wiki/CSS
+ "Cascading Style Sheets"
+[editing-this-article]: /files/articles/site-backend/0-editing.png
+ "Editing this article with Vim and Firefox"
+[jpeg]: https://en.wikipedia.org/wiki/JPEG
+ "Joint Photographic Experts Group (JPEG) lossy raster image format"
+[es2015]: https://en.wikipedia.org/wiki/ECMAScript_version_history#6th_Edition_%E2%80%93_ECMAScript_2015
+ "Modern JavaScript"
+[post-theme-switcher]: {{< relref "posts/2024-05-27-site-updates-dark-mode-and-about-pics" >}}
+ "Blog post about dark mode and theme switcher"
+[prefers-color-scheme]: https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme
+ "prefers-color-scheme media query"
+[dark mode]: https://en.wikipedia.org/wiki/Light-on-dark_color_scheme
+ "Dark mode"
+[webhook]: https://github.com/adnanh/webhook
+ "Lightweight webhook server to run shell commands"
+[webhook.conf]: /files/articles/site-backend/webhook.conf.txt
+ "Download webhook.conf"
+[hmac]: https://en.wikipedia.org/wiki/HMAC
+ "Hash-based message authentication code (HMAC)"
+[apache]: https://apache.org/
+ "Apache web server"
+[apache-vhost.conf]: /files/articles/site-backend/pablotron.org.conf.txt
+ "Download Apache virtual host configuration"
+[apache-tls.conf]: /files/articles/site-backend/tls.conf.txt
+ "Download Apache TLS configuration"
+[web hook]: https://en.wikipedia.org/wiki/Webhook
+ "HTTP-accessible callback"
+[deferred]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#defer
+ "defer attribute of script tag"
+[replay attacks]: https://en.wikipedia.org/wiki/Replay_attack
+ "Network attack where a valid data transmission is repeated or delayed."
+[tls]: https://en.wikipedia.org/wiki/Transport_Layer_Security
+ "Transport Layer Security"
+[post-receive]: https://git-scm.com/docs/githooks#post-receive
+ "Git hook triggered on a remote repository after a push operation"
+[deploy.rb]: https://git.pablotron.org/sites/pablotron.org/plain/bin/hook/deploy.rb
+ "Deployment script invoked as a web hook."
+[gen-logo.rb]: https://git.pablotron.org/sites/pablotron.org/plain/bin/logo-0/gen-logo.rb
+ "Ruby script to generate animated SVG logo."
+[post-png-compressors]: {{< relref "posts/2023-06-02-end-of-may-miscellany" >}}
+ "Blog post with a review of several PNG compressors"
+[bench-chart]: https://github.com/pablotron/fips203ipd/tree/main/scripts/bench-chart
+ "Scripts to build and run fips203ipd bench utility with several combinations of backend and compiler, generate a summary CSV, and render a bar chart as an SVG."
+[post-fips203ipd]: {{< relref "posts/2024-05-15-c11-fips203ipd-v0.6.md" >}}
+ "Blog post about release of fips203ipd v0.6."
+[script.js]: /files/articles/site-backend/script.js.txt
+ "Unminified JavaScript for this site with some comments removed."
+[http]: https://en.wikipedia.org/wiki/HTTP
+ "Hypertext Transfer Protocol (HTTP)"
+[https]: https://en.wikipedia.org/wiki/HTTPS
+ "Hypertext Transfer Protocol Secure (HTTPS)"
+[http/2]: https://en.wikipedia.org/wiki/HTTP/2
+ "Hypertext Transfer Protocol, version 2 (HTTP/2)"
+[post-headers-1]: {{< relref "posts/2021-10-19-hugo-csp-impedance-mismatch.md" >}}
+ "Hugo/Content-Security-Policy Impedance Mismatch - October 19, 2021"
+[post-headers-2]: {{< relref "posts/2021-10-21-tls-and-header-fixes.md" >}}
+ "TLS and Header Fixes - October 21, 2021"
+[post-headers-3]: {{< relref "posts/2021-10-25-the-nuclear-option-no-more-unsafe-inline.md" >}}
+ "The Nuclear Option (No More unsafe-inline) - October 25, 2021"
+[mod-http2]: https://httpd.apache.org/docs/current/mod/mod_http2.html
+ "Apache module which provides HTTP/2 support."
+[mod-proxy]: https://httpd.apache.org/docs/current/mod/mod_proxy.html
+ "Apache module which provides proxy and reverse proxy support."
+[mod-rewrite]: https://httpd.apache.org/docs/current/mod/mod_rewrite.html
+ "Apache module which provides rules-based rewriting and redirecting engine."
+[mod-macro]: https://httpd.apache.org/docs/current/mod/mod_macro.html
+ "Apache module which provides macros within configuration files."
+[mod-deflate]: https://httpd.apache.org/docs/current/mod/mod_deflate.html
+ "Apache module which provides DEFLATE output compression."
+[ssl-config]: https://ssl-config.mozilla.org/
+ "Mozilla SSL Configuration Generator"
+[tls 1.2]: https://en.wikipedia.org/wiki/Transport_Layer_Security#TLS_1.2
+ "TLS version 1.2"
+[tls 1.3]: https://en.wikipedia.org/wiki/Transport_Layer_Security#TLS_1.3
+ "TLS version 1.3"
+[ssl-labs-report]: https://www.ssllabs.com/ssltest/analyze.html?d=pablotron.org&hideResults=on
+ "SSL Labs SSL Test results for this site."
+[ssl-labs-ssl-test]: https://www.ssllabs.com/ssltest/
+ "SSL Labs SSL Test"
+[security-headers-quick-ref]: https://web.dev/articles/security-headers
+ "Security headers quick ref"
+[permissions-policy]: https://scotthelme.co.uk/goodbye-feature-policy-and-hello-permissions-policy/
+ "Goodbye Feature Policy and hello Permissions Policy!"
+[permissions policy explainer]: https://github.com/w3c/webappsec-permissions-policy/blob/main/permissions-policy-explainer.md
+ "Permissions Policy Explainer"
+[referrer-policy]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy#strict-origin-when-cross-origin
+ "Documentation for \"Referrer-Policy: strict-origin-when-cross-origin\""
+[content-security-policy]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
+ "Content-Security-Policy header"
+[hugo-shortcode-table]: https://github.com/pablotron/hugo-shortcode-table
+ "Powerful Hugo table shortcode which emits HTML using CSS classes rather than inline style attributes."
+[breach]: https://en.wikipedia.org/wiki/BREACH
+ "Browser Reconnaissance and Exfiltration via Adaptive Compression of Hypertext (BREACH)"
+[cookies]: https://en.wikipedia.org/wiki/HTTP_cookie
+ "Small blocks of data created by a web server when a user is browsing a web site."
+[let's encrypt]: https://letsencrypt.org/
+ "Free, automated, and open certificate authority (CA)"
+[certbot]: https://certbot.eff.org/
+ "Free, open source software tool for automatically using let's Encrypt certificates."
+[certbot-dns-linode]: https://certbot-dns-linode.readthedocs.io/en/stable/
+ "Certbot plugin which automates the process of completing an ACME dns-01 challenge by creating TXT records using the Linode API."
+[securityheaders-report]: https://securityheaders.com/?q=pablotron.org&hide=on&followRedirects=on
+ "Security headers report for this site from securityheaders.com"
+[securityheaders.com]: https://securityheaders.com/
+ "Free and easy to use tool to check the web site security headers."
+[sass]: https://en.wikipedia.org/wiki/Sass_(style_sheet_language)
+ "Syntactically Awesome Style Sheet (SASS)"
+[style.sass.txt]: /files/articles/site-backend/style.sass.txt
+ "Download SASS configuration"
+[html]: https://en.wikipedia.org/wiki/HTML
+ "HyperText Markup Language (HTML)"
diff --git a/static/files/articles/site-backend/0-editing-1024.png b/static/files/articles/site-backend/0-editing-1024.png
new file mode 100644
index 0000000..5bb018c
--- /dev/null
+++ b/static/files/articles/site-backend/0-editing-1024.png
Binary files differ
diff --git a/static/files/articles/site-backend/0-editing-1024.webp b/static/files/articles/site-backend/0-editing-1024.webp
new file mode 100644
index 0000000..3fcc332
--- /dev/null
+++ b/static/files/articles/site-backend/0-editing-1024.webp
Binary files differ
diff --git a/static/files/articles/site-backend/0-editing-raw.png b/static/files/articles/site-backend/0-editing-raw.png
new file mode 100644
index 0000000..e1ca53a
--- /dev/null
+++ b/static/files/articles/site-backend/0-editing-raw.png
Binary files differ
diff --git a/static/files/articles/site-backend/0-editing.png b/static/files/articles/site-backend/0-editing.png
new file mode 100644
index 0000000..1df3181
--- /dev/null
+++ b/static/files/articles/site-backend/0-editing.png
Binary files differ
diff --git a/static/files/articles/site-backend/1-ssl-labs-20240530-1024.png b/static/files/articles/site-backend/1-ssl-labs-20240530-1024.png
new file mode 100644
index 0000000..6e2c95f
--- /dev/null
+++ b/static/files/articles/site-backend/1-ssl-labs-20240530-1024.png
Binary files differ
diff --git a/static/files/articles/site-backend/1-ssl-labs-20240530-1024.webp b/static/files/articles/site-backend/1-ssl-labs-20240530-1024.webp
new file mode 100644
index 0000000..5664f4f
--- /dev/null
+++ b/static/files/articles/site-backend/1-ssl-labs-20240530-1024.webp
Binary files differ
diff --git a/static/files/articles/site-backend/1-ssl-labs-20240530.png b/static/files/articles/site-backend/1-ssl-labs-20240530.png
new file mode 100644
index 0000000..da74161
--- /dev/null
+++ b/static/files/articles/site-backend/1-ssl-labs-20240530.png
Binary files differ
diff --git a/static/files/articles/site-backend/2-securityheaders-1024.png b/static/files/articles/site-backend/2-securityheaders-1024.png
new file mode 100644
index 0000000..b709598
--- /dev/null
+++ b/static/files/articles/site-backend/2-securityheaders-1024.png
Binary files differ
diff --git a/static/files/articles/site-backend/2-securityheaders-1024.webp b/static/files/articles/site-backend/2-securityheaders-1024.webp
new file mode 100644
index 0000000..747507c
--- /dev/null
+++ b/static/files/articles/site-backend/2-securityheaders-1024.webp
Binary files differ
diff --git a/static/files/articles/site-backend/2-securityheaders.png b/static/files/articles/site-backend/2-securityheaders.png
new file mode 100644
index 0000000..8a92c38
--- /dev/null
+++ b/static/files/articles/site-backend/2-securityheaders.png
Binary files differ
diff --git a/static/files/articles/site-backend/pablotron.org.conf.txt b/static/files/articles/site-backend/pablotron.org.conf.txt
index 8934bad..b2c498b 100644
--- a/static/files/articles/site-backend/pablotron.org.conf.txt
+++ b/static/files/articles/site-backend/pablotron.org.conf.txt
@@ -1,49 +1,21 @@
+# unconditionally redirect to https://pablotron.org
<VirtualHost *:80>
- Use BASIC_SITE pablotron.org www-admin@pablotron.org
- Use BASIC_LOGS pablotron.org
- Use STRIP_WWW https://pablotron.org
- Use MOD_DEFLATE
-
- # unconditionally rewrite to https://pablotron.org
RewriteEngine On
RewriteRule ^/(.*)$ https://pablotron.org/$1 [R,L]
</VirtualHost>
<VirtualHost *:443>
- Use BASIC_SITE pablotron.org www-admin@pablotron.org
- Use BASIC_LOGS pablotron.org
+ # strip "www." prefix and enable mod_deflate
Use STRIP_WWW https://pablotron.org
Use MOD_DEFLATE
- SSLEngine on
- SSLCertificateFile /etc/letsencrypt/live/pablotron.org/cert.pem
- SSLCertificateKeyFile /etc/letsencrypt/live/pablotron.org/privkey.pem
- SSLCertificateChainFile /etc/letsencrypt/live/pablotron.org/fullchain.pem
-
- # redirect old rss feed to new one
- RewriteCond %{QUERY_STRING} theme=rss
- RewriteCond %{REQUEST_URI} ^/$
- RewriteRule (.*) /index.xml [R=301,L]
-
- # enable http2 (added 2022-01-29)
+ # enable http2
Protocols h2 http/1.1
- # set security headers
- # (added on 2021-10-17)
- #
- # refs:
- # - https://web.dev/security-headers/#xfo
- # - https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
- # - https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
- # - https://scotthelme.co.uk/a-new-security-header-referrer-policy/
- # - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy
- # - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
- #
- # permissions-policy docs (seems poorly thought out):
- # * https://scotthelme.co.uk/goodbye-feature-policy-and-hello-permissions-policy/
- # * feature list (for old feature-policy header, but a good reference): https://source.chromium.org/chromium/chromium/src/+/master:third_party/blink/renderer/platform/feature_policy/feature_policy.cc;drc=ab90b51c5b60de15054a32b0bd18e4839536a1c9;l=138
- # https://github.com/w3c/webappsec-permissions-policy/blob/main/permissions-policy-explainer.md
- #
+ # set restrictive content security policy
+ Header append "Content-Security-Policy" "default-src 'self'; img-src 'self' https://pmdn.org"
+
+ # set remaining security headers
Header append "Strict-Transport-Security" "max-age=31536000"
Header append "X-Frame-Options" "SAMEORIGIN"
Header append "X-Content-Type-Options" "nosniff"
@@ -52,23 +24,18 @@
Header append "Access-Control-Allow-Origin" "https://pablotron.org"
Header append "Referrer-Policy" "strict-origin-when-cross-origin"
- # not sure about these yet
+ # set permissions policy
Header append "Permissions-Policy" "camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), midi=(), payment=(), usb=()"
# POST needed for /hooks
Header append "Access-Control-Allow-Methods" "POST, GET, HEAD, OPTIONS"
- # 'unsafe-inline' is needed for goldmark table cell alignment
- # Header append "Content-Security-Policy" "default-src 'self'; img-src 'self' https://pmdn.org; style-src 'self' 'unsafe-inline'"
- # removed all tables w/ alignment, so i nuked unsafe-inline (2021-10-21)
- Header append "Content-Security-Policy" "default-src 'self'; img-src 'self' https://pmdn.org"
-
# cache images, stylesheets, and javascript for 1 year
- # (added 2022-01-29, i may regret this...)
<FilesMatch "\.(ico|jpg|jpeg|png|gif|webp|svg|js|json|css)$">
Header set Cache-Control "max-age=31536000, public"
</FilesMatch>
+ # expose webhook
<Location /hooks/>
ProxyPass "http://localhost:9000/"
ProxyPassReverse "http://localhost:9000/"
diff --git a/static/files/articles/site-backend/script.js.txt b/static/files/articles/site-backend/script.js.txt
new file mode 100644
index 0000000..ecf41b8
--- /dev/null
+++ b/static/files/articles/site-backend/script.js.txt
@@ -0,0 +1,37 @@
+'use strict';
+
+//
+// script.js - script which handles:
+//
+// - set theme
+// - theme switcher and burger menu event handlers
+//
+
+const D = document,
+ C = D.body.parentElement.classList,
+ L = localStorage,
+ M = window.matchMedia,
+ on = (el, id, fn) => el.addEventListener(id, fn);
+
+// use theme if set, otherwise fall back to browser preference
+if (L && L.theme && L.theme === 'dark') {
+ C.add('dark'); // theme set to "dark"
+} else if ((!L || !L.theme) && M && M('(prefers-color-scheme: dark)').matches) {
+ C.add('dark'); // prefers dark color scheme
+}
+
+document.addEventListener('DOMContentLoaded', () => {
+ // theme toggle event handler
+ on(D.querySelector('.navbar-item[data-id="theme"]'), 'click', (e) => {
+ e.preventDefault(); // stop event
+ L.theme = C.toggle('dark') ? 'dark' : 'light'; // toggle
+ });
+
+ // iterate through burgers, bind to click events
+ D.querySelectorAll('.navbar-burger').forEach(e => on(e, 'click', () => {
+ // then toggle is-active on burger and menu
+ [e, D.getElementById(e.dataset.target)].forEach(
+ e => e.classList.toggle('is-active')
+ )
+ }));
+});
diff --git a/static/files/articles/site-backend/style.sass.txt b/static/files/articles/site-backend/style.sass.txt
new file mode 100644
index 0000000..febddd5
--- /dev/null
+++ b/static/files/articles/site-backend/style.sass.txt
@@ -0,0 +1,58 @@
+// style.sass: based on bulma-0.9.3/sass/bulma.sass with the following
+// changes:
+//
+// 1. all unused components removed
+// 2. monokai style for chroma added
+// 3. styles for navbar icon highlighting and table captions added
+// 4. dark mode styles added
+@charset "utf-8"
+
+// import chroma style
+//
+// generated with the following command:
+//
+// cd themes/hugo-pt2021/assets
+// hugo gen chromaclasses --style=monokai > chroma.css
+//
+@import "chroma"
+
+@import "bulma-0.9.3/sass/utilities/_all"
+@import "bulma-0.9.3/sass/base/_all"
+
+// elements
+@import "bulma-0.9.3/sass/elements/button"
+@import "bulma-0.9.3/sass/elements/container"
+@import "bulma-0.9.3/sass/elements/content"
+@import "bulma-0.9.3/sass/elements/image"
+@import "bulma-0.9.3/sass/elements/table"
+@import "bulma-0.9.3/sass/elements/title"
+@import "bulma-0.9.3/sass/elements/other"
+
+// components
+@import "bulma-0.9.3/sass/components/media"
+@import "bulma-0.9.3/sass/components/navbar"
+
+// grid (reenabled, used for images)
+@import "bulma-0.9.3/sass/grid/_all"
+
+// helpers
+@import "bulma-0.9.3/sass/helpers/_all"
+
+// layout
+@import "bulma-0.9.3/sass/layout/section"
+@import "bulma-0.9.3/sass/layout/footer"
+
+// dim navbar icons by default
+.navbar-item .menu-icon
+ opacity: 60%
+
+// highlight icons on hover
+.navbar-item:hover .menu-icon
+ opacity: 100%
+
+// table captions below tables
+table.table
+ caption-side: bottom
+
+// dark mode (2024-05-27)
+@import "dark"
diff --git a/static/files/articles/site-backend/tls.conf.txt b/static/files/articles/site-backend/tls.conf.txt
new file mode 100644
index 0000000..011930d
--- /dev/null
+++ b/static/files/articles/site-backend/tls.conf.txt
@@ -0,0 +1,9 @@
+# explicit list of cipher suites
+# (from ssl-config.mozilla.org)
+SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
+
+# use server priorities for cipher algorithm choice
+SSLHonorCipherOrder on
+
+# protocols to enable (TLS 1.2 and 1.3 only)
+SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
diff --git a/static/files/articles/site-backend/webhook.conf.txt b/static/files/articles/site-backend/webhook.conf.txt
new file mode 100644
index 0000000..254155d
--- /dev/null
+++ b/static/files/articles/site-backend/webhook.conf.txt
@@ -0,0 +1,34 @@
+[{
+ "id": "deploy-pablotron-org",
+ "execute-command": "/data/www/pablotron.org/git/bin/hook/deploy.rb",
+
+ "pass-arguments-to-command": [{
+ "source": "payload",
+ "name": "time"
+ }],
+
+ "pass-environment-to-command": [{
+ "source": "string",
+ "envname": "DEPLOY_HTDOCS_PATH",
+ "name": "/data/www/pablotron.org/builds/current"
+ }, {
+ "source": "string",
+ "envname": "DEPLOY_REPO_DIR",
+ "name": "/data/www/pablotron.org/git"
+ }, {
+ "source": "string",
+ "envname": "DEPLOY_BUILDS_DIR",
+ "name": "/data/www/pablotron.org/builds"
+ }],
+
+ "trigger-rule": {
+ "match": {
+ "type": "payload-hmac-sha256",
+ "secret": "(omitted)",
+ "parameter": {
+ "source": "header",
+ "name": "X-Hub-Signature"
+ }
+ }
+ }
+}]