feat(api): 内部面 token 按调用方拆分为 velocity/limbo/build/ops 并按路由限定调用方,审计来源区分调用方,安装器生成并下发各自 Secret,新增 felis rotate-token 轮换命令

This commit is contained in:
Lemon-miaow committed 2026-09-25 15:21:41 +08:00
1 parent d3769b5c31
commit a883c1fe07
38 files changed
+1359 -209

No files matched your search

+38
View File
@@ -0,0 +1,38 @@
package main
import (
"net/http/httptest"
"strings"
"testing"
"felis.lolicon.best/internal/api"
)
// felis-api maps each env var onto the caller the Deployment feeds it for, so the
// build Job's token (FELIS_BUILD_TOKEN) authenticates as build and only as build.
func TestInternalCallerTokensReadEachCallersEnv(t *testing.T) {
env := map[string]string{
"FELIS_SERVICE_TOKEN": "v-tok",
"FELIS_LIMBO_TOKEN": "l-tok",
"FELIS_BUILD_TOKEN": " b-tok\n",
"FELIS_OPS_TOKEN": "o-tok",
}
auth, err := internalCallerTokens(func(k string) string { return env[k] })
if err != nil {
t.Fatal(err)
}
for tok, want := range map[string]api.Caller{"v-tok": api.CallerVelocity, "l-tok": api.CallerLimbo, "b-tok": api.CallerBuild, "o-tok": api.CallerOps} {
r := httptest.NewRequest("GET", "/", nil)
r.Header.Set("Authorization", "Bearer "+tok)
if got, err := auth.Authenticate(r); err != nil || got != want {
t.Errorf("%s: got (%q, %v), want %q", tok, got, err, want)
}
}
// An install where the build namespace still holds a copy of the proxy's token
// would let that copy act as the proxy; the api refuses to start on it.
env["FELIS_BUILD_TOKEN"] = "v-tok"
if _, err := internalCallerTokens(func(k string) string { return env[k] }); err == nil || !strings.Contains(err.Error(), "same value") {
t.Fatalf("shared token: err = %v, want a same-value refusal", err)
}
}