v0.345.1: SECURITY — cloudflared 2026.10.3 (Cloudflare's eMudhra notice, 2026-10-20)
Built from v0.345.0 (6bfb784) + this commit only. infra.CloudflaredImage 2026.9.3 -> 2026.10.3;
TestEnsureCloudflared_ANewImageRecreatesTheTunnel pins that an image-only change recreates the
running tunnel (red-proved). MinAgent 0.158.1 unchanged.
This commit is contained in:
@@ -1,3 +1,22 @@
|
|||||||
|
## v0.345.1 (2026-10-26) — SECURITY: cloudflared 2026.10.3 (Cloudflare's eMudhra notice) — built from v0.345.0 + this one commit (R-1300)
|
||||||
|
|
||||||
|
**MinAgent: 0.158.1** (unchanged). Nothing else from `main` is in this build: it is the v0.345.0 commit (`6bfb784`) plus
|
||||||
|
this commit only, tagged `v0.345.1`; the same change is on `main`.
|
||||||
|
|
||||||
|
- **Security:** `infra.CloudflaredImage` `cloudflare/cloudflared:2026.9.3` → `2026.10.3` (Docker Hub index digest
|
||||||
|
`sha256:faa8e0a1475f137c910be6025f43c8e4092fdd35461b31e19fd1390657a1ff39`). Cloudflare's notice of 2026-10-20:
|
||||||
|
cloudflared before 2026.10.2 trusts eight eMudhra (emSign) intermediate CAs whose private keys were weak and are
|
||||||
|
recoverable — anyone holding them could pose as any website to cloudflared. The fix is the image's trust store
|
||||||
|
(2026.10.2 rebuilt on a new distroless base; 2026.10.3 a newer base and Go).
|
||||||
|
- How a box takes it: after the controller update, `EnsureBaseStack` → `ensureCloudflared` sees the rendered compose
|
||||||
|
differ (the `image:` line) and runs `compose up -d`, which recreates cloudflared on the new image; the tunnel
|
||||||
|
reconnects in seconds. New test `TestEnsureCloudflared_ANewImageRecreatesTheTunnel` (an image-only change recreates;
|
||||||
|
the same render again does not), red-proved with the change check removed.
|
||||||
|
- Origin check (read only, before the update): every tunnel's remote route is `*.<domain>` → `https://traefik` with
|
||||||
|
`noTLSVerify: true` (the day-0 recipe, R-510); traefik answers with its self-signed `TRAEFIK DEFAULT CERT` — no
|
||||||
|
eMudhra chain, so the new trust store refuses nothing on the origin leg. felhom.eu
|
||||||
|
`documentation/audits/cloudflared-2026.10.3-2026-10-26/`.
|
||||||
|
|
||||||
## v0.345.0 (2026-10-26) — the 50 % rule owns qBittorrent's NORMAL limit only (R-1292) — attended release run (afternoon)
|
## v0.345.0 (2026-10-26) — the 50 % rule owns qBittorrent's NORMAL limit only (R-1292) — attended release run (afternoon)
|
||||||
|
|
||||||
Live before the release: Tester 1 on `0.345.0-rc.1` with a throwaway qBittorrent, alternative speed ON (the household's
|
Live before the release: Tester 1 on `0.345.0-rc.1` with a throwaway qBittorrent, alternative speed ON (the household's
|
||||||
|
|||||||
@@ -20,10 +20,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Pinned image tags — NEVER ":latest" (a floating tag breaks reproducible golden bakes and lets the
|
// 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.
|
// 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.
|
||||||
const (
|
const (
|
||||||
TraefikImage = "traefik:v3.7.13"
|
TraefikImage = "traefik:v3.7.13"
|
||||||
CloudflaredImage = "cloudflare/cloudflared:2026.9.3"
|
CloudflaredImage = "cloudflare/cloudflared:2026.10.3"
|
||||||
FileBrowserImage = "gtstef/filebrowser:1.5.6-stable"
|
FileBrowserImage = "gtstef/filebrowser:1.5.6-stable"
|
||||||
// FileBrowserImportMount is the in-container mount point NAME for the canonical drop-zone
|
// FileBrowserImportMount is the in-container mount point NAME for the canonical drop-zone
|
||||||
// (R-75): the bind lands at /srv/<this>. ASCII and space-free on purpose — it appears in a
|
// (R-75): the bind lands at /srv/<this>. ASCII and space-free on purpose — it appears in a
|
||||||
|
|||||||
@@ -326,3 +326,33 @@ func TestEnsureForwardedHeaders_R1033_RewritesAnOldFile(t *testing.T) {
|
|||||||
t.Fatal("the rewritten file must carry the https-only redirect")
|
t.Fatal("the rewritten file must carry the https-only redirect")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A controller update that changes only cloudflared's IMAGE recreates the running container on the new image (the
|
||||||
|
// 2026.10.3 security bump rides this: no other step moves a household's tunnel). The control: the same render again
|
||||||
|
// is a no-op.
|
||||||
|
func TestEnsureCloudflared_ANewImageRecreatesTheTunnel(t *testing.T) {
|
||||||
|
state := stubDocker(t)
|
||||||
|
touch(t, filepath.Join(state, "running-cloudflared"), "")
|
||||||
|
m, calls := tunnelTestManager(t, "")
|
||||||
|
dir := t.TempDir()
|
||||||
|
cur, _ := infra.RenderCloudflared(infra.CloudflaredData{CFTunnelToken: "tok-test", Tunnel: true})
|
||||||
|
old := strings.Replace(cur["docker-compose.yml"].Content, "image: "+infra.CloudflaredImage, "image: cloudflare/cloudflared:2026.9.3", 1)
|
||||||
|
if old == cur["docker-compose.yml"].Content {
|
||||||
|
t.Fatal("the render has no `image:` line to age — the test would prove nothing")
|
||||||
|
}
|
||||||
|
touch(t, filepath.Join(dir, "docker-compose.yml"), old)
|
||||||
|
|
||||||
|
if err := m.ensureCloudflared(dir, true); err != nil {
|
||||||
|
t.Fatalf("ensureCloudflared: %v", err)
|
||||||
|
}
|
||||||
|
cmp, _ := os.ReadFile(filepath.Join(dir, "docker-compose.yml"))
|
||||||
|
if !strings.Contains(string(cmp), "image: "+infra.CloudflaredImage) {
|
||||||
|
t.Fatalf("the compose must carry %s:\n%s", infra.CloudflaredImage, cmp)
|
||||||
|
}
|
||||||
|
if len(*calls) != 1 || (*calls)[0].args != "up -d" {
|
||||||
|
t.Fatalf("want one `up -d` (compose recreates on the new image), got %+v", *calls)
|
||||||
|
}
|
||||||
|
if err := m.ensureCloudflared(dir, true); err != nil || len(*calls) != 1 {
|
||||||
|
t.Fatalf("an unchanged image must not recreate the tunnel again; err %v calls %+v", err, *calls)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user