fix(apiserver): never sign or accept JWTs with the shipped default secret (bug-1945)

Cause: config/app.yaml shipped secretKey "xce-api-key" and
resolveJWTSecret used it whenever XCE_JWT_SECRET was unset, so anyone
could forge tokens for any user on such a deployment.

Fix: resolveJWTSecret uses XCE_JWT_SECRET, else the config value, but
treats an empty value or the old public default as unset; then it
generates a random 256-bit per-process secret and InitConf logs a warning
(secret never logged): tokens do not survive a restart and apiservers
sharing one endpoint must set the same XCE_JWT_SECRET. The shipped
app.yaml no longer carries a secret. The controllers test secret is no
longer the old default.

Tests: TestResolveJWTSecret_NeverUsesKnownDefaultOrEmpty,
TestShippedAppYAML_NoJWTSecret, TestInitConf_NoJWTSecretGeneratesAndWarns,
TestInitConf_JWTSecretFromEnvNoWarning.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Rongsong Shen 2026-10-10 08:37:01 +00:00
parent c4c63f11e7
commit 19a1a6f023
Signed by untrusted user: rshen
GPG key ID: 80533536B0EB651C
4 changed files with 131 additions and 8 deletions

View file

@ -2,7 +2,7 @@
// Integration tests for XCE API server controllers (§2, §6, §8).
//
// Test signing secret: "xce-api-key" (test-only, never use in production).
// Test signing secret: testJWTSecret (test-only, never use in production).
// JWT tokens must embed "ip": "127.0.0.1" to match req.RemoteAddr set by httptest.
package controllers
@ -26,7 +26,7 @@ import (
"go.uber.org/zap"
)
const testJWTSecret = "xce-api-key"
const testJWTSecret = "controllers-test-only-secret"
// ========== Test Helpers ==========