diff options
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 Binary files differnew file mode 100644 index 0000000..5bb018c --- /dev/null +++ b/static/files/articles/site-backend/0-editing-1024.png diff --git a/static/files/articles/site-backend/0-editing-1024.webp b/static/files/articles/site-backend/0-editing-1024.webp Binary files differnew file mode 100644 index 0000000..3fcc332 --- /dev/null +++ b/static/files/articles/site-backend/0-editing-1024.webp diff --git a/static/files/articles/site-backend/0-editing-raw.png b/static/files/articles/site-backend/0-editing-raw.png Binary files differnew file mode 100644 index 0000000..e1ca53a --- /dev/null +++ b/static/files/articles/site-backend/0-editing-raw.png diff --git a/static/files/articles/site-backend/0-editing.png b/static/files/articles/site-backend/0-editing.png Binary files differnew file mode 100644 index 0000000..1df3181 --- /dev/null +++ b/static/files/articles/site-backend/0-editing.png 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 Binary files differnew file mode 100644 index 0000000..6e2c95f --- /dev/null +++ b/static/files/articles/site-backend/1-ssl-labs-20240530-1024.png 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 Binary files differnew file mode 100644 index 0000000..5664f4f --- /dev/null +++ b/static/files/articles/site-backend/1-ssl-labs-20240530-1024.webp diff --git a/static/files/articles/site-backend/1-ssl-labs-20240530.png b/static/files/articles/site-backend/1-ssl-labs-20240530.png Binary files differnew file mode 100644 index 0000000..da74161 --- /dev/null +++ b/static/files/articles/site-backend/1-ssl-labs-20240530.png diff --git a/static/files/articles/site-backend/2-securityheaders-1024.png b/static/files/articles/site-backend/2-securityheaders-1024.png Binary files differnew file mode 100644 index 0000000..b709598 --- /dev/null +++ b/static/files/articles/site-backend/2-securityheaders-1024.png diff --git a/static/files/articles/site-backend/2-securityheaders-1024.webp b/static/files/articles/site-backend/2-securityheaders-1024.webp Binary files differnew file mode 100644 index 0000000..747507c --- /dev/null +++ b/static/files/articles/site-backend/2-securityheaders-1024.webp diff --git a/static/files/articles/site-backend/2-securityheaders.png b/static/files/articles/site-backend/2-securityheaders.png Binary files differnew file mode 100644 index 0000000..8a92c38 --- /dev/null +++ b/static/files/articles/site-backend/2-securityheaders.png 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" + } + } + } +}] |