fix: RBAC bind cluster-admin + list namespaces + TEST_MODE multi-tenant test

This commit is contained in:
Naeel
2026-04-19 15:41:11 +03:00
parent 9ebd36eed3
commit 551a5b2f8e
2 changed files with 110 additions and 76 deletions
+29 -1
View File
@@ -59,6 +59,7 @@ type server struct {
http *http.Client http *http.Client
saTokenPath string saTokenPath string
invokeTimeout time.Duration invokeTimeout time.Duration
testMode bool // FISSION_TEST_MODE=true — пропускает deck auth, X-Test-Sub задаёт user
authUser string authUser string
authPass string authPass string
@@ -129,6 +130,7 @@ func main() {
invokeTimeout: invokeTimeout, invokeTimeout: invokeTimeout,
authUser: authUser, authUser: authUser,
authPass: authPass, authPass: authPass,
testMode: os.Getenv("FISSION_TEST_MODE") == "true",
} }
mux := http.NewServeMux() mux := http.NewServeMux()
@@ -154,6 +156,19 @@ func main() {
auth := func(h http.HandlerFunc) http.HandlerFunc { auth := func(h http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
var ns string
if s.testMode {
// TEST_MODE: deck API не вызывается.
// X-Test-Sub задаёт произвольный sub → разные namespace-ы для тестирования.
sub := strings.TrimSpace(r.Header.Get("X-Test-Sub"))
if sub == "" {
writeJSONError(w, http.StatusUnauthorized, "test mode: X-Test-Sub required")
return
}
h32 := sha256.Sum256([]byte(sub))
ns = "fission-" + hex.EncodeToString(h32[:8])
} else {
token := strings.TrimSpace(r.Header.Get("X-Auth-Token")) token := strings.TrimSpace(r.Header.Get("X-Auth-Token"))
env := strings.TrimSpace(strings.ToLower(r.Header.Get("X-Auth-Env"))) env := strings.TrimSpace(strings.ToLower(r.Header.Get("X-Auth-Env")))
if _, ok := deckAPIs[env]; !ok { if _, ok := deckAPIs[env]; !ok {
@@ -167,11 +182,14 @@ func main() {
writeJSONError(w, http.StatusUnauthorized, "unauthorized") writeJSONError(w, http.StatusUnauthorized, "unauthorized")
return return
} }
ns, err := namespaceFromJWT(token) var err error
ns, err = namespaceFromJWT(token)
if err != nil { if err != nil {
log.Printf("namespaceFromJWT: %v", err) log.Printf("namespaceFromJWT: %v", err)
ns = s.ns ns = s.ns
} }
}
ctx := context.WithValue(r.Context(), ctxKeyNS{}, ns) ctx := context.WithValue(r.Context(), ctxKeyNS{}, ns)
h(w, r.WithContext(ctx)) h(w, r.WithContext(ctx))
} }
@@ -258,6 +276,16 @@ func (s *server) handleCreateFunction(w http.ResponseWriter, r *http.Request) {
return return
} }
// Гарантируем что namespace + RBAC созданы до любых операций с ресурсами.
// handleAuth делает это при логине, но в test mode или при прямом вызове API
// namespace может отсутствовать — создаём idempotent.
nsCtx, nsCancel := context.WithTimeout(r.Context(), 30*time.Second)
defer nsCancel()
if err := s.ensureUserNamespace(nsCtx, ns); err != nil {
writeJSONError(w, http.StatusBadGateway, fmt.Sprintf("ensure namespace: %v", err))
return
}
req.Name = strings.TrimSpace(req.Name) req.Name = strings.TrimSpace(req.Name)
req.Language = strings.TrimSpace(req.Language) req.Language = strings.TrimSpace(req.Language)
req.Environment = strings.TrimSpace(req.Environment) req.Environment = strings.TrimSpace(req.Environment)
+7 -1
View File
@@ -5,13 +5,19 @@ metadata:
rules: rules:
- apiGroups: [""] - apiGroups: [""]
resources: ["namespaces"] resources: ["namespaces"]
verbs: ["get", "create"] verbs: ["get", "create", "list"]
- apiGroups: ["fission.io"] - apiGroups: ["fission.io"]
resources: ["*"] resources: ["*"]
verbs: ["*"] verbs: ["*"]
- apiGroups: ["rbac.authorization.k8s.io"] - apiGroups: ["rbac.authorization.k8s.io"]
resources: ["rolebindings"] resources: ["rolebindings"]
verbs: ["get", "create"] verbs: ["get", "create"]
# bind позволяет создавать RoleBindings ссылающиеся на cluster-admin
# без необходимости самому иметь все его права (RBAC escalation prevention)
- apiGroups: ["rbac.authorization.k8s.io"]
resources: ["clusterroles"]
resourceNames: ["cluster-admin"]
verbs: ["bind"]
- apiGroups: ["apps"] - apiGroups: ["apps"]
resources: ["deployments"] resources: ["deployments"]
verbs: ["get", "patch"] verbs: ["get", "patch"]