NIS2 Technical Readiness Checklist

Scope A technical self-assessment for entities in scope of the NIS2 Directive (Directive (EU) 2022/2555). Each item is a thing a sysadmin can verify on a host now — a command output, a config flag, a file’s contents, an external scan result — not a policy approval or a named role. This is the box-level companion to nis2-infrastructure. That guide explains why each Article 21(2) measure matters and how to implement it; this checklist is the “is it actually configured?” layer. ...

Last updated:  · 8 min · Paul Masterson

Deploying a Flask Tools Site on a Hardened VPS Behind Caddy

Tested on: Ubuntu 24.04 LTS, Python 3.12.3, Caddy 2.x, gunicorn 23.0, Flask 3.x, SQLite as the data store. The pattern is portable to any small Python web app — drop in whichever Flask / Starlette / FastAPI codebase you have. Why this matters Most Flask deployment tutorials end at flask run --host 0.0.0.0 and a hand-wave about Nginx. That gap — between “the app starts” and “the app is a service my colleagues would trust at 2am” — is where the operationally interesting decisions live: which user runs it, how it talks to the reverse proxy, where its secrets live, how it survives a reboot, how its certificate renews itself. ...

Last updated:  · 11 min · Paul Masterson

tls-audit

What this script does tls-audit runs a read-only audit against a deployed HTTPS endpoint and reports on each item from the Nginx TLS 2026 guide and the nginx-tls config generator: Protocol support — TLS 1.0 and 1.1 should be rejected; TLS 1.2 and 1.3 should be accepted. Certificate — covers the hostname, not expired (and not expiring in the next 21 days), modern key algorithm, complete chain with at least one intermediate, not self-signed. OCSP stapling — the server should be stapling an OCSP response. HSTS — Strict-Transport-Security header present, max-age of at least one year, includeSubDomains directive set, preload flagged if present (informational, not a recommendation). Security headers — X-Content-Type-Options: nosniff and Referrer-Policy present; a Server header that leaks a version number is flagged. It uses only openssl s_client, curl, and standard text tools — no external scanner like testssl.sh required. Designed to run from a machine other than the host being audited, with no privilege. ...

Last updated:  · 9 min · Paul Masterson

Redis Hardening

Tested on: Ubuntu 24.04 LTS, Redis 7.2.x (from the official redis apt repository at packages.redis.io). Configuration paths use /etc/redis/redis.conf — RHEL-family installs use /etc/redis/redis.conf as well, so the file paths below are portable. Why this matters Almost every newsworthy Redis incident reduces to one of three failures: The server was bound to 0.0.0.0 “for testing” and reachable from the internet. There was no password (or the password was the example one from a tutorial). FLUSHALL, CONFIG, or DEBUG were left available, so a connected client could wipe data, exfiltrate keys, or write arbitrary files through CONFIG SET dir / BGSAVE. Redis is fast, in-memory, and trusts its clients by default. That trust is appropriate inside a private subnet with disciplined access controls. It is catastrophic everywhere else. This guide closes those three holes and adds the obvious next layer (TLS, resource limits, persistence safety). ...

Last updated:  · 6 min · Paul Masterson

Nginx TLS Configuration — 2026 Baseline

Tested on: Ubuntu 24.04 LTS, Nginx 1.26.x (from nginx.org stable repository), Let’s Encrypt via certbot 2.x. The same config works on RHEL 9 / AlmaLinux 9 with path adjustments noted inline. Why this matters Most Nginx TLS configurations on the internet were copy-pasted from a tutorial written three to seven years ago. That means they typically have one or more of the following problems: TLS 1.0 and 1.1 still enabled “for compatibility” with browsers that no longer exist. A hand-rolled cipher list that excludes modern AEAD suites or includes long-deprecated ones (3DES, RC4, CBC-mode without AEAD). ssl_prefer_server_ciphers on — which was correct advice once but is now the wrong default for TLS 1.3. No OCSP stapling, so every visitor’s browser does a side-channel OCSP lookup to the CA on first connection. HSTS missing, or HSTS set without includeSubDomains on a domain that has subdomains people forgot about. HTTP-only Strict-Transport-Security header (it must be served over HTTPS to be honoured at all). This guide gives you a single, opinionated baseline that addresses all of the above. It targets the Mozilla “Intermediate” profile — broad client compatibility without enabling anything embarrassing. ...

Last updated:  · 5 min · Paul Masterson

PostgreSQL Hardening

Tested on: Ubuntu 24.04 LTS, PostgreSQL 16.x (apt package from pgdg repository). Commands assume the default data directory at /etc/postgresql/16/main/. Why this matters PostgreSQL ships with defaults that are reasonable for a development laptop and wrong for an internet-exposed server. Three settings in particular cause most real-world incidents: listen_addresses = 'localhost' is fine — until someone changes it to '*' for “convenience” and the database is suddenly reachable from anywhere the firewall lets through. pg_hba.conf defaults still allow trust and md5 in some packaging. trust is no authentication. md5 is no longer considered safe and should be replaced with scram-sha-256. TLS is off by default. Application traffic to the database — including passwords and query results — travels in cleartext on the loopback or on the LAN. This guide fixes those three problems and tightens a handful of related items. It does not cover row-level security, backup hardening, or replication — those get their own guides. ...

Last updated:  · 4 min · Paul Masterson