From 5ba1b86293d0a3f701ecdb83948e9fd573508e25 Mon Sep 17 00:00:00 2001 From: kisfenyo Date: Tue, 27 Oct 2026 15:48:38 +0100 Subject: [PATCH] =?UTF-8?q?v0.346.1:=20SECURITY=20=E2=80=94=20traefik=20v3?= =?UTF-8?q?.7.16,=20the=20file=20manager=201.5.8-stable,=20cloudflared=202?= =?UTF-8?q?026.10.4=20(felhom.eu=20R-1304,=20R-1305);=20test=20TestEnsureT?= =?UTF-8?q?raefik=5FANewImageRecreatesTraefik=20(red=20first)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Built from the v0.346.0 release commit b1b9be3 + this one commit. MinAgent 0.158.1 (unchanged). --- CHANGELOG.md | 16 +++++++++ REPORT-box-programs-0.346.1.md | 19 ++++++++++ controller/internal/infra/infra.go | 12 ++++--- .../internal/stacks/infra_tunnel_test.go | 36 +++++++++++++++++++ 4 files changed, 79 insertions(+), 4 deletions(-) create mode 100644 REPORT-box-programs-0.346.1.md diff --git a/CHANGELOG.md b/CHANGELOG.md index d28a4205..58406c4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,19 @@ +## v0.346.1 (2026-10-27) — SECURITY: traefik v3.7.16, the file manager 1.5.8-stable, cloudflared 2026.10.4 — built from v0.346.0 (`b1b9be3`) + one commit (felhom.eu R-1304, R-1305) + +**MinAgent: 0.158.1** (unchanged). Box programs only; no other change from v0.346.0. + +- `internal/infra`: `TraefikImage` `traefik:v3.7.13` → `traefik:v3.7.16` (12 notices between; none reaches our config — + docker + file providers, no FastProxy, no NTLM backend, no Kubernetes; the v3.7.14–16 migration notes touch only + Kubernetes provider names, OTel histograms and a CONNECT request now answered 501). `FileBrowserImage` + `gtstef/filebrowser:1.5.6-stable` → `1.5.8-stable` (GHSA-qx86-4v5r-26g5 TOTP re-enrolment, GHSA-3846-gh75-gp3m upload + share, and the 1.5.8 fixes; 1.5.7-stable is skipped — it broke existing TOTP sign-ins). `CloudflaredImage` + `2026.10.3` → `2026.10.4` (VULN-176088: the metrics server's debug endpoints; ours is not reachable from outside — low). +- How a box takes them: the reconcile at start, as for 0.345.1 — traefik's compose changes → `up -d --force-recreate` + (new test `TestEnsureTraefik_ANewImageRecreatesTraefik`, red-proved: it fails when a compose-only change is skipped); + cloudflared `TestEnsureCloudflared_ANewImageRecreatesTheTunnel`; the file manager + `TestReconcileFileBrowserImage_MovesOnlyTheImage` (mounts unchanged; it rejoins the app network only after the + password check, R-1281). + ## v0.346.0 (2026-10-27) — D53: the hub learns which apps are held; an unreadable app.yaml keeps its folder (R-1220); the ceremony keeps the off-site key (R-1218, R-1219); safe .fab export/import (R-1187); holds of uninstalled apps are not reported (R-1298); Go 1.27.3 (R-972); the line-test hold waits for the seeder to slow down (R-1250) — RELEASED on the operator's yes (attended release block) **MinAgent: 0.158.1** (unchanged). Operator ruling D53 (2026-10-26, felhom.eu `09` §3 decision 265): operator-only diff --git a/REPORT-box-programs-0.346.1.md b/REPORT-box-programs-0.346.1.md new file mode 100644 index 00000000..f561d34c --- /dev/null +++ b/REPORT-box-programs-0.346.1.md @@ -0,0 +1,19 @@ +# REPORT — controller v0.346.1: traefik v3.7.16, the file manager 1.5.8-stable, cloudflared 2026.10.4 (security), 2026-10-27 (attended) + +> A sibling of `REPORT.md`, not an overwrite: the release session of the same day (v0.346.0) owns `REPORT.md`. + +## In plain words + +- **What:** three box programs had public security notices: traefik (12 notices, none reaching our config), the file + manager (a TOTP re-enrolment and an upload-share flaw; 1.5.7 is skipped, it broke TOTP sign-ins) and cloudflared + (debug endpoints on its metrics server, not reachable from outside). Controller **0.346.1** = the released 0.346.0 + (`b1b9be3`) + only the three new pins. +- **How a box takes them:** the reconcile at start recreates each container on its new image (traefik pinned by the new + test `TestEnsureTraefik_ANewImageRecreatesTraefik`, red-proved). + +## Gates + +`controller_gates.py` (full) green; `go test ./...` green (41 packages); `go build`, `go vet` green. + +Evidence and the box proofs: felhom.eu `documentation/audits/security-app-updates-2026-10-27/part5/`. Register: +felhom.eu R-1304 (traefik), R-1305 (file manager). diff --git a/controller/internal/infra/infra.go b/controller/internal/infra/infra.go index 45ebfc42..b65e9a78 100644 --- a/controller/internal/infra/infra.go +++ b/controller/internal/infra/infra.go @@ -22,11 +22,15 @@ import ( // Pinned image tags — NEVER ":latest" (a floating tag breaks reproducible golden bakes and lets the // deployed version drift). Verified to resolve on Docker Hub before baking. cloudflared follows Cloudflare's security // notices (felhom.eu `01` „Tunnel placement"): 2026.10.3 since the notice of 2026-10-20 — before 2026.10.2 the image -// trusts eight eMudhra (emSign) intermediates with weak keys. +// trusts eight eMudhra (emSign) intermediates with weak keys; 2026.10.4 since 2026-10-27 (VULN-176088, the metrics +// server's debug endpoints). traefik v3.7.16 and the file manager 1.5.8-stable since 2026-10-27 for their security +// notices (felhom.eu R-1304, R-1305; 1.5.7-stable is skipped — it broke existing TOTP sign-ins). Each new image reaches +// a running box by the reconcile at start: TestEnsureTraefik_ANewImageRecreatesTraefik, +// TestEnsureCloudflared_ANewImageRecreatesTheTunnel, TestReconcileFileBrowserImage_MovesOnlyTheImage. const ( - TraefikImage = "traefik:v3.7.13" - CloudflaredImage = "cloudflare/cloudflared:2026.10.3" - FileBrowserImage = "gtstef/filebrowser:1.5.6-stable" + TraefikImage = "traefik:v3.7.16" + CloudflaredImage = "cloudflare/cloudflared:2026.10.4" + FileBrowserImage = "gtstef/filebrowser:1.5.8-stable" // FileBrowserImportMount is the in-container mount point NAME for the canonical drop-zone // (R-75): the bind lands at /srv/. ASCII and space-free on purpose — it appears in a // container path, in the generated compose, and (percent-encoded) in the deep-link URL. diff --git a/controller/internal/stacks/infra_tunnel_test.go b/controller/internal/stacks/infra_tunnel_test.go index 7ce4d603..07f40818 100644 --- a/controller/internal/stacks/infra_tunnel_test.go +++ b/controller/internal/stacks/infra_tunnel_test.go @@ -162,6 +162,42 @@ func TestEnsureTraefik_ReconcilesARunningTraefik(t *testing.T) { } } +// A controller update that changes only traefik's IMAGE recreates the running traefik on the new image (the v3.7.16 +// security bump of 2026-10-27 rides this, felhom.eu R-1304: traefik.yml is unchanged, only the compose's `image:` line +// moves). The control: the same render again is a no-op. +func TestEnsureTraefik_ANewImageRecreatesTraefik(t *testing.T) { + state := stubDocker(t) + touch(t, filepath.Join(state, "running-traefik"), "") + m, calls := tunnelTestManager(t, "owner@example.com") + dir := t.TempDir() + cur, err := infra.RenderTraefik(infra.TraefikData{ACMEEmail: "owner@example.com", Tunnel: true}) + if err != nil { + t.Fatal(err) + } + old := strings.Replace(cur["docker-compose.yml"].Content, "image: "+infra.TraefikImage, "image: traefik:v3.7.13", 1) + if old == cur["docker-compose.yml"].Content { + t.Fatal("the render has no `image:` line to age — the test would prove nothing") + } + for name, f := range cur { + touch(t, filepath.Join(dir, name), f.Content) + } + touch(t, filepath.Join(dir, "docker-compose.yml"), old) + + if err := m.ensureTraefik(dir, true); err != nil { + t.Fatalf("ensureTraefik: %v", err) + } + cmp, _ := os.ReadFile(filepath.Join(dir, "docker-compose.yml")) + if !strings.Contains(string(cmp), "image: "+infra.TraefikImage) { + t.Fatalf("the compose must carry %s:\n%s", infra.TraefikImage, cmp) + } + if len(*calls) != 1 || (*calls)[0].args != "up -d --force-recreate" { + t.Fatalf("want ONE `up -d --force-recreate` (traefik on the new image), got %+v", *calls) + } + if err := m.ensureTraefik(dir, true); err != nil || len(*calls) != 1 { + t.Fatalf("an unchanged image must not recreate traefik again; err %v calls %+v", err, *calls) + } +} + // A rewrite that would DROP the certificate resolver the running file has is refused (no e-mail in the config). func TestEnsureTraefik_RefusesToDropTheCertResolver(t *testing.T) { state := stubDocker(t)