Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 90 additions & 2 deletions SETUP.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ section below says which.
| Pi LAN IP | `10.0.1.19` |
| slartibartfast LAN IP | `10.0.1.11` (tailnet `100.68.10.65`, SSH user `mathewcsims`) |
| Public/WAN IP | `curl -4 ifconfig.me` (static) |
| NAS ("Eddie") | `eddie.nas` / `10.0.1.12`, SMB — see the Kopia section for the `AppleBackups` share |
| NAS ("Eddie") | `eddie.nas` / `10.0.1.12`, SMB — see the Kopia section for the `AppleBackups` share. Powers off nightly 04:00–06:00 (dashboard → Settings → Energy Saver → Power Schedule, set 2026-08-09 after an overnight NIC lockup only a power-cycle would clear — the EX2 Ultra's known drop-off-the-network failure mode). Pinged by Uptime Kuma monitor `Eddie (host)`, so the window shows as a nightly DOWN/UP pair |

Give **the Pi, the Mac, and slartibartfast each a fixed LAN IP** via a DrayTek
DHCP reservation (Part 4) so the forward and proxy config never break.
Expand Down Expand Up @@ -3431,6 +3431,21 @@ pointing the Landing Page monitor at a nonexistent path, confirmed both a
correctly red-colored DOWN embed and a green UP embed (with downtime
duration) arrived in Discord.

**A second notification, "ntfy" (id 2, native ntfy provider), was added
2026-08-09** alongside Discord — not default, not applied to existing
monitors; attach it per-monitor where a phone push is wanted (first user:
`Eddie (host)`, which carries both Discord and ntfy). Config: server
`https://ntfy.mathewcsims.uk`, topic `alerts`, auth method **access token**
using the same write-only `PUBLISHER_TOKEN` from the "Ntfy" Pass item that
Apprise embeds. Priorities are deliberate: **3 (default) for UP, 4 (high)
for DOWN** — never 5/max, because max bypasses phone Do-Not-Disturb and the
Eddie monitor fires a DOWN/UP pair *every night* during the NAS's scheduled
04:00–06:00 power-off window (see Known values). Kuma's provider defaults
would have been 4/5 (`ntfyPriorityDown` falls back to `priority + 1`,
capped at 5 — `server/notification-providers/ntfy.js` at this tag).
Verified live via the `testNotification` Socket.IO event: test push arrived
on the `alerts` topic.

### Adding/managing monitors programmatically (Socket.IO)

Kuma **has no REST API for monitor CRUD** — `/api/monitors` just returns the
Expand Down Expand Up @@ -3514,7 +3529,9 @@ credentials problem and isn't. Wait for the next window before retrying.
**Tag conventions.** Every monitor carries `Homelab` plus **one tag naming
the machine it runs on** — `Heart of Gold` (Mac), `Babel` (Pi),
`Slartibartfast` (the third host, added 2026-07-30 with Immich; emerald
`#059669`). Adding a fourth host means adding a fourth tag. Tag events are
`#059669`), `Eddie` (the NAS, added 2026-08-09 with the `Eddie (host)`
ping monitor; sky `#0284C7`). Adding a new host means adding a tag for it.
Tag events are
`getTags`, `addTag` (payload `{name, color, new: True}`) and
`addMonitorTag` (positional tuple: `(tagId, monitorId, value)`). Note the
tag goes with the machine the *monitored process* runs on — e.g.
Expand Down Expand Up @@ -7046,6 +7063,16 @@ cut Paperless comes up on its own once the NAS is back. But the ordering is
real — if Paperless is down and nothing obvious is wrong with it, check
whether Eddie is up before debugging the container.

Two Eddie-downtime facts to hold in mind here (both from 2026-08-09, after
the NAS's NIC locked up overnight and needed a physical power-cycle):
**Eddie now powers off nightly 04:00–06:00** by its own Power Schedule (see
Known values), so the CIFS mount goes stale for that window every night —
a running Paperless tolerates this (`soft` mount, below), it only matters
if the container tries to *start* or consume documents inside the window.
And **Eddie is now pinged by Uptime Kuma** (`Eddie (host)`, Discord + ntfy
alerts on both down and up), so an unplanned outage announces itself
rather than surfacing as Paperless mysteriously refusing to start.

The mount is `soft` (visible in `mount | grep cifs` inside the container),
which is the right choice here rather than the default reflex of `hard`: a
NAS that goes away mid-operation returns I/O errors instead of blocking
Expand Down Expand Up @@ -8553,6 +8580,67 @@ NET_BIND_SERVICE. The failure mode is nasty: **Next.js keeps serving on
502s.** Same entrypoint pattern that bit Donetick and Tududi. Add the caps
from the start on any image using supervisord + nginx.

### Weekly CVE pass, 2026-08-09 (334 new findings) — one bump survived

Trivy flagged 334 findings across 16 images (159 "fixable"). Applying last
week's verify-before-bumping method — scan the old AND new artifact, keep
only what reduces findings — **exactly one bump survived out of nine
candidates with newer artifacts**:

| Candidate | Verdict |
|---|---|
| **uptime-kuma 2.4.0 → 2.5.0** | **KEPT** — fixes CVE-2026-12143/-12151/-67214, introduces nothing. Note: last week's 2.5.0 build was CVE-identical and rejected; this week's *rebuild* of 2.5.0 carries the fixes. Version and digest move independently — check both |
| fizzy (rebuild) | identical: 76/13/76 both sides |
| hedgedoc 1.11.1 (rebuild) | identical: 28/7/25 |
| karakeep (rebuild) | identical (+1 uHIGH) |
| litellm v1.94.0 → v1.95.0 | identical: 5 fixable both — version bump, zero CVE value |
| tailscale v1.98.10 → v1.102.2 | identical: 2 both |
| ghost, traffic-analytics 1.0.329 | identical |
| mysql 8.4.10 → 8.4.11 | identical: 21 both. **`mysql:latest` scanned 18 but is MySQL 26.7.0** — an innovation-release major jump for a −3 count; not a serious option for Ghost's LTS database |

No newer artifact at all (their "fixable" waits on upstream rebuilds):
super-productivity, paperless, postgres:17-alpine, bookstack, wanderer-web,
immich-server, apprise.

**Mitigation half.** The 38 unfixed CRITICALs sit in the four new apps, all
already LAN/tailnet-gated at Caddy. Reachability checked per app:

- **fizzy — genuinely reachable, now hardened.** Its criticals include
libraw, OpenEXR and ImageMagick, which decode **attacker-supplied images**
— uploading images to cards is what Fizzy is for. It had **zero**
hardening. Now `no-new-privileges` + bare `cap_drop: ALL` (verified safe:
the image runs as `rails` uid 1000 from PID 1, no root-then-drop, no
privileged port — checked with `podman top`, not assumed) + limits sized
from measured steady state (3.75 GB/151 PIDs → 6g/512).
- **super-productivity — unreachable furniture, hardened anyway.** Its
criticals are the perl-base cluster + libxml2 in a container that serves a
static SPA. nginx binds :80 as root then drops, so it gets the
root-then-drop cap set + NET_BIND_SERVICE (the nimbus lesson), 256m/128.
- **paperless, docs (HedgeDoc)** — already hardened at deployment
(cap_drop/no-new-privileges/limits present), already gated. Their perl/
image-lib criticals are the same base-furniture cluster; paperless *does*
decode documents, but its consumer already runs under the existing caps.
- **postgres:17-alpine** — same story as 16.14-alpine last week: Go stdlib
CVEs in a helper binary, nothing actionable here.

Deploys verified: uptime-kuma healthy with 62 heartbeats in 2 minutes;
fizzy healthy with secrets confirmed non-blank after redeploy
(`SECRET_KEY_BASE` length checked inside the container — the blanked-secrets
failure mode is real, see the pass-deploy notes); sp healthy and serving.

**Operational note:** the repo+tag scan keying means the uptime-kuma bump
creates a new state key (`:2.5.0-slim-rootless`), so the next scan re-reports
that image's full finding set once and prunes the 2.4.0 key. Expected, per
the keying design — not a regression.

**pass-cli session wedge seen during deploy:** `pass-cli info` failing with
"Error getting personal access token name" while `pass-cli login` says
"Already authenticated" means the scratch session dir is corrupt. Fix:
`rm -rf /tmp/pass-agent-selfhosted/.session` and rerun — the durable PAT
re-authenticates. A deploy in this state emits "variable is not set"
warnings for EVERY Pass field; a single missing-field warning (e.g.
`SMTP_USERNAME`, genuinely absent from the item) is normal.

### Branch protection + required CI (GitHub)

Nothing lands on `main` — not even from the repo owner, not even from an
Expand Down
20 changes: 20 additions & 0 deletions fizzy/compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,26 @@ services:
image: ghcr.io/basecamp/fizzy@sha256:c0aadd02a8249752998d3fe930ce99e24de6997522511ee052d1bb44616ac521
container_name: fizzy
restart: unless-stopped
# Hardening added 2026-08-09 (weekly CVE pass). Fizzy carries 13 unfixed
# CRITICAL findings, and unlike most of the estate's perl-base noise,
# several are genuinely reachable here: libraw, OpenEXR and ImageMagick
# decode ATTACKER-SUPPLIED images — uploading images to cards is exactly
# what Fizzy is for. No patched build exists (the 2026-08-09 rebuild was
# scanned and is CVE-identical), so this caps the blast radius instead:
# a crash or exploit attempt stays a contained restart.
#
# A bare `cap_drop: ALL` with NO cap_add is deliberate and verified: the
# image runs as `rails` (uid 1000) from PID 1 — there is no root-then-
# drop entrypoint (checked with podman top, unlike nimbus/donetick), and
# thruster binds 8080, puma 3000, both unprivileged.
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
# Steady state measured at 3.75 GB / 151 PIDs (Rails + SolidQueue
# workers) — limits sized ~1.6x so normal operation never trips them.
mem_limit: 6g
pids_limit: 512
# Do NOT set `user:` — on podman-machine (macOS) the container's root maps
# to the host user, giving sane ownership on the bind mount below. Same
# reasoning as copyparty's and Ghost's compose.yaml.
Expand Down
25 changes: 25 additions & 0 deletions super-productivity/compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,31 @@ services:
image: docker.io/johannesjo/super-productivity@sha256:ae91fe9ac19561e0f3669d15a2c4c71d7a75c43a29eb44ddc010ae50d1f63c82
container_name: super-productivity
restart: unless-stopped
# Hardening added 2026-08-09 (weekly CVE pass). This image's 5 unfixed
# CRITICALs are the estate-wide perl-base cluster plus libxml2 — base
# furniture in a container that only serves a static SPA via nginx, so
# none of it is in the request path. Hardening is defence in depth, not
# a response to reachable risk.
#
# Capability set verified against the running container (podman top):
# nginx master runs as root and drops to the `nginx` user, and binds :80
# INSIDE the container (3601 is the host side) — so this needs the
# root-then-drop set plus NET_BIND_SERVICE. A bare `cap_drop: ALL`
# breaks this shape of image — see the nimbus note in SETUP.md.
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- CHOWN
- DAC_OVERRIDE
- FOWNER
- SETGID
- SETUID
- NET_BIND_SERVICE
# Steady state measured at 12 MB / 6 PIDs — a static file server.
mem_limit: 256m
pids_limit: 128
environment:
# The image's entrypoint templates nginx's config from this; 80 is its
# own default, stated explicitly so the port mapping below is readable
Expand Down
8 changes: 7 additions & 1 deletion uptime-kuma/compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,13 @@
# wants to fire a notification without embedding a webhook URL of its own.
services:
uptime-kuma:
image: louislam/uptime-kuma:2.4.0-slim-rootless@sha256:8f5c20a6002de53f8ed8a9b744ea08f566cfb690e5bbbfc1c278483aaf374992
# 2026-08-09 CVE pass: 2.4.0 -> 2.5.0. Verified by scanning both digests
# before bumping: fixes CVE-2026-12143, CVE-2026-12151 and CVE-2026-67214,
# introduces nothing (86 -> 83 fixable, unfixed unchanged). Note last
# week's 2.5.0 build was CVE-identical to 2.4.0 and was rejected; this
# week's REBUILD of 2.5.0 is what carries the fixes — version and digest
# move independently, so both were checked.
image: louislam/uptime-kuma:2.5.0-slim-rootless@sha256:37894805e7df2211dc2c69374034849c9a73efd8dd55469953261cafda091bc4
container_name: uptime-kuma
restart: unless-stopped
volumes:
Expand Down