R-1281: FileBrowser is served on its own felhom-files network (traefik, and OnlyOffice while linked) and leaves traefik-public, so no app container reaches it. R-1126's check-then-publish gate unchanged. Red-proved. Commit only.
gates / gates (push) Successful in 1m29s

This commit is contained in:
2026-10-25 22:26:45 +01:00
parent 838c834169
commit 7ea942a118
6 changed files with 223 additions and 23 deletions
@@ -279,20 +279,21 @@ func TestFileBrowserGate_PublishedOnlyAfterTheCheck(t *testing.T) {
// A new run whose FileBrowser does not answer: nothing is published.
down = true
m.fileBrowserGateLocked()
if nets["filebrowser@traefik-public"] {
if nets["filebrowser@traefik-public"] || nets["filebrowser@felhom-files"] {
t.Fatal("published while this run's default login was never checked")
}
// It answers, on its default: replaced first, published after.
down = false
m.fileBrowserGateLocked()
want := []string{"connect felhom-controller felhom-filebrowser", "password replaced", "connect filebrowser traefik-public"}
// R-1281: published on the files network (traefik joins it first), never on traefik-public.
want := []string{"connect felhom-controller felhom-filebrowser", "password replaced", "connect traefik felhom-files", "connect filebrowser felhom-files"}
if strings.Join(events, " | ") != strings.Join(want, " | ") {
t.Fatalf("order:\n got %v\n want %v", events, want)
}
// A new run (a restart) on the box's own password: checked, nothing changes, nothing connected twice.
run = "run-2"
m.fileBrowserGateLocked()
if len(events) != 3 {
if len(events) != 4 {
t.Fatalf("a checked restart did more: %v", events)
}
}
@@ -313,7 +314,82 @@ func TestFileBrowserGate_ConnectsTheCheckedContainerById(t *testing.T) {
return nil
}
m.fileBrowserGateLocked()
if len(connected) != 2 || connected[1] != "3f2a9c@traefik-public" {
if len(connected) != 3 || connected[2] != "3f2a9c@felhom-files" {
t.Fatalf("the checked container must be published by its id, got %v", connected)
}
}
// R-1281 (D44 #23): every app container is on traefik-public, so FileBrowser on it could be reached directly — measured
// 2026-10-18: from an app container, a forged sign-in header got the drive's listing. The checked FileBrowser is served
// on the files network only, and one an earlier controller put on traefik-public leaves it.
// RED-PROOF: skip the traefik-public disconnect → it stays on the app network; connect to traefik-public as before →
// it is on it.
func TestR1281_FileBrowserLeavesTheAppNetworkAndIsServedOnTheFilesNetwork(t *testing.T) {
fb := &fakeFB{password: "admin"}
srv := httptest.NewServer(fb.handler())
defer srv.Close()
m, _, _ := fbManager(t, srv.URL)
m.fbStarted = func() string { return "3f2a9c 2026-10-25T20:00:00Z" }
nets := map[string]bool{"3f2a9c@traefik-public": true} // as a 0.343.0 controller left it
m.fbNetOnFn = func(c, n string) bool { return nets[c+"@"+n] }
m.fbNetConnectFn = func(c, n string) error { nets[c+"@"+n] = true; return nil }
m.fbNetDisconnectFn = func(c, n string) error { delete(nets, c+"@"+n); return nil }
m.fbNetEnsureFn = func(string) error { return nil }
m.fbOfficeLinkedFn = func() (bool, bool) { return false, false }
m.fileBrowserGateLocked()
if nets["3f2a9c@traefik-public"] {
t.Fatal("FileBrowser is still on traefik-public — every app container reaches it")
}
if !nets["3f2a9c@felhom-files"] || !nets["traefik@felhom-files"] {
t.Fatalf("FileBrowser and traefik must share the files network: %v", nets)
}
}
// The files network keeps R-1126's guarantee: an unchecked run joins nothing, so a re-created FileBrowser (default
// password) is not reachable from outside.
func TestR1281_AnUncheckedRunJoinsNothing(t *testing.T) {
fb := &fakeFB{password: "admin"}
srv := httptest.NewServer(fb.handler())
defer srv.Close()
m, _, _ := fbManager(t, srv.URL)
m.fbStarted = func() string { return "" } // the run cannot be read
var joined []string
m.fbNetOnFn = func(string, string) bool { return false }
m.fbNetConnectFn = func(c, n string) error { joined = append(joined, c+"@"+n); return nil }
m.fbNetEnsureFn = func(string) error { return nil }
m.fileBrowserGateLocked()
for _, j := range joined {
if strings.HasSuffix(j, "@felhom-files") || strings.HasSuffix(j, "@traefik-public") {
t.Fatalf("an unchecked run joined %s", j)
}
}
}
// OnlyOffice shares the files network while — and only while — its FileBrowser link is on.
// RED-PROOF: drop ensureOfficeOnFilesNetwork → the linked case never joins.
func TestR1281_OnlyOfficeJoinsTheFilesNetworkOnlyWhileLinked(t *testing.T) {
fb := &fakeFB{password: "admin"}
srv := httptest.NewServer(fb.handler())
defer srv.Close()
m, _, _ := fbManager(t, srv.URL)
m.fbStarted = func() string { return "3f2a9c 2026-10-25T20:00:00Z" }
nets := map[string]bool{}
m.fbNetOnFn = func(c, n string) bool { return nets[c+"@"+n] }
m.fbNetConnectFn = func(c, n string) error { nets[c+"@"+n] = true; return nil }
m.fbNetDisconnectFn = func(c, n string) error { delete(nets, c+"@"+n); return nil }
m.fbNetEnsureFn = func(string) error { return nil }
linked := true
m.fbOfficeLinkedFn = func() (bool, bool) { return linked, true }
m.fileBrowserGateLocked()
if !nets["onlyoffice@felhom-files"] {
t.Fatalf("a linked OnlyOffice must reach FileBrowser: %v", nets)
}
linked = false
m.fileBrowserGateLocked()
if nets["onlyoffice@felhom-files"] {
t.Fatal("an unlinked OnlyOffice must leave the files network")
}
if nets["onlyoffice@traefik-public"] || nets["3f2a9c@traefik-public"] {
t.Fatalf("nothing joins traefik-public here: %v", nets)
}
}