From 35e4764d354a827f518f6316954e85162b2a7519 Mon Sep 17 00:00:00 2001 From: jochen Date: Thu, 22 Oct 2026 13:40:42 +0200 Subject: [PATCH] Pin only-office's secure-link secret, so its document service and nginx sign and check Editor.bin links with one value across restarts (issue 1837) --- modules/only-office/files/server.env | 1 + modules/only-office/manifest_test.go | 32 ++++++++++++++++++++++++++++ modules/only-office/module.json | 6 +++++- 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/modules/only-office/files/server.env b/modules/only-office/files/server.env index 19fab119..a92e1c8f 100644 --- a/modules/only-office/files/server.env +++ b/modules/only-office/files/server.env @@ -2,4 +2,5 @@ JWT_ENABLED=true JWT_SECRET=${secret:jwt} JWT_HEADER=Authorization JWT_IN_BODY=true +SECURE_LINK_SECRET=${secret:secure-link} ALLOW_PRIVATE_IP_ADDRESS=true diff --git a/modules/only-office/manifest_test.go b/modules/only-office/manifest_test.go index cbaec2b4..dbc98cfa 100644 --- a/modules/only-office/manifest_test.go +++ b/modules/only-office/manifest_test.go @@ -2,6 +2,7 @@ package office import ( "os" + "strings" "testing" "git.novox.be/novox/mesh-sdk/go/catalogue" @@ -61,3 +62,34 @@ func TestManifestOffersDocumentServer(t *testing.T) { t.Errorf("serves.document-server.public-root = %q, want https://${bound:route:name}/", root) } } + +// 1837/AC1 (unit: manifest check) +// Given the catalogue, when only-office's manifest and its server.env template are read, then the +// document server's secure-link secret is an own secret taken at start, and server.env hands it to +// the container as SECURE_LINK_SECRET, so its document service and its nginx sign and check +// Editor.bin links with one value across every restart. +func TestSecureLinkSecretIsPinned(t *testing.T) { + raw, err := os.ReadFile("module.json") + if err != nil { + t.Fatalf("reading module.json: %v", err) + } + m, err := catalogue.ParseManifest(raw) + if err != nil { + t.Fatalf("parsing module.json: %v", err) + } + own, ok := m.OwnSecrets["secure-link"] + if !ok { + t.Fatal("manifest declares no own-secret secure-link") + } + if own.Taken != catalogue.TakenAtStart { + t.Errorf("own-secrets.secure-link.taken = %q, want %q", own.Taken, catalogue.TakenAtStart) + } + + env, err := os.ReadFile("files/server.env") + if err != nil { + t.Fatalf("reading files/server.env: %v", err) + } + if !strings.Contains(string(env), "\nSECURE_LINK_SECRET=${secret:secure-link}\n") { + t.Errorf("files/server.env does not set SECURE_LINK_SECRET=${secret:secure-link}:\n%s", env) + } +} diff --git a/modules/only-office/module.json b/modules/only-office/module.json index 157ca954..ba86cc59 100644 --- a/modules/only-office/module.json +++ b/modules/only-office/module.json @@ -76,6 +76,10 @@ "owner": "only-office-converter", "taken": "at-start" }, + "secure-link": { + "path": "${dir:state}/secure-link.secret", + "taken": "at-start" + }, "broker-converter": { "path": "${dir:state}/broker-converter.secret", "owner": "only-office-converter" @@ -225,7 +229,7 @@ "${dir:redis}:/var/lib/redis", "${dir:fonts}:/usr/share/fonts/truetype/custom" ], - "secrets-in-environment": "run-document-server.sh regenerates JWT_SECRET from the environment on every start and overwrites local.json; not convertible" + "secrets-in-environment": "run-document-server.sh regenerates JWT_SECRET and SECURE_LINK_SECRET from the environment on every start and overwrites local.json and nginx's ds.conf; not convertible" }, { "id": "converter",