docs: add detailed comments to addNSToFission and ensureUserNamespace RBAC block
This commit is contained in:
+77
-43
@@ -800,7 +800,18 @@ func namespaceFromJWT(token string) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ensureUserNamespace создаёт K8s namespace и shared environments если не существуют.
|
// ensureUserNamespace создаёт K8s namespace и shared environments если не существуют.
|
||||||
// addNSToFission dynamically adds ns to FISSION_RESOURCE_NAMESPACES on all Fission deployments.
|
// addNSToFission динамически добавляет namespace в FISSION_RESOURCE_NAMESPACES у всех Fission deployments.
|
||||||
|
//
|
||||||
|
// Зачем это нужно: Fission компоненты смотрят только те namespace-ы, что указаны в
|
||||||
|
// FISSION_RESOURCE_NAMESPACES. Если не добавить новый namespace — executor/router не будут
|
||||||
|
// создавать пулы и обрабатывать триггеры, invoke вернёт 404.
|
||||||
|
//
|
||||||
|
// Алгоритм:
|
||||||
|
// 1. Читаем текущее значение FISSION_RESOURCE_NAMESPACES из router deployment.
|
||||||
|
// 2. Если namespace уже в списке — выходим (idempotent).
|
||||||
|
// 3. Иначе добавляем namespace к списку и патчим все Fission deployments через StrategicMergePatch.
|
||||||
|
//
|
||||||
|
// StrategicMergePatch позволяет обновить только одну env переменную не трогая остальные.
|
||||||
func (s *server) addNSToFission(ctx context.Context, ns string) error {
|
func (s *server) addNSToFission(ctx context.Context, ns string) error {
|
||||||
fissionNS := os.Getenv("FISSION_SYSTEM_NAMESPACE")
|
fissionNS := os.Getenv("FISSION_SYSTEM_NAMESPACE")
|
||||||
if fissionNS == "" {
|
if fissionNS == "" {
|
||||||
@@ -808,20 +819,23 @@ func (s *server) addNSToFission(ctx context.Context, ns string) error {
|
|||||||
}
|
}
|
||||||
fissionDeployments := []string{"router", "executor", "buildermgr", "kubewatcher", "timer"}
|
fissionDeployments := []string{"router", "executor", "buildermgr", "kubewatcher", "timer"}
|
||||||
|
|
||||||
// Читаем текущее значение из router
|
// Читаем текущее значение FISSION_RESOURCE_NAMESPACES из router deployment.
|
||||||
|
// Используем router как источник истины — он первым получает изменения.
|
||||||
routerDep, err := s.dyn.Resource(deploymentGVR).Namespace(fissionNS).Get(ctx, "router", metav1.GetOptions{})
|
routerDep, err := s.dyn.Resource(deploymentGVR).Namespace(fissionNS).Get(ctx, "router", metav1.GetOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("get router deployment: %w", err)
|
return fmt.Errorf("get router deployment: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
currentVal := "default"
|
currentVal := "default" // fallback если переменная не найдена
|
||||||
containerName := "router"
|
containerName := "router" // имя контейнера нужно для StrategicMergePatch
|
||||||
containers, _, _ := unstructured.NestedSlice(routerDep.Object, "spec", "template", "spec", "containers")
|
containers, _, _ := unstructured.NestedSlice(routerDep.Object, "spec", "template", "spec", "containers")
|
||||||
for _, c := range containers {
|
for _, c := range containers {
|
||||||
cont, ok := c.(map[string]any)
|
cont, ok := c.(map[string]any)
|
||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
// Запоминаем реальное имя контейнера — оно используется как merge key в StrategicMergePatch.
|
||||||
|
// Без точного имени патч создаст дублирующий контейнер вместо обновления существующего.
|
||||||
if n, ok := cont["name"].(string); ok {
|
if n, ok := cont["name"].(string); ok {
|
||||||
containerName = n
|
containerName = n
|
||||||
}
|
}
|
||||||
@@ -837,7 +851,7 @@ func (s *server) addNSToFission(ctx context.Context, ns string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break // берём только первый контейнер
|
||||||
}
|
}
|
||||||
|
|
||||||
// Проверяем что namespace ещё не в списке
|
// Проверяем что namespace ещё не в списке
|
||||||
@@ -848,7 +862,9 @@ func (s *server) addNSToFission(ctx context.Context, ns string) error {
|
|||||||
}
|
}
|
||||||
newVal := currentVal + "," + ns
|
newVal := currentVal + "," + ns
|
||||||
|
|
||||||
// Патчим все Fission deployments
|
// Патчим все Fission deployments одним и тем же значением.
|
||||||
|
// StrategicMergePatch обновляет только указанные поля (env var), не затрагивая остальные.
|
||||||
|
// Обычный MergePatch заменил бы весь массив containers — нельзя использовать.
|
||||||
patch := map[string]any{
|
patch := map[string]any{
|
||||||
"spec": map[string]any{
|
"spec": map[string]any{
|
||||||
"template": map[string]any{
|
"template": map[string]any{
|
||||||
@@ -873,7 +889,8 @@ func (s *server) addNSToFission(ctx context.Context, ns string) error {
|
|||||||
return fmt.Errorf("marshal patch: %w", err)
|
return fmt.Errorf("marshal patch: %w", err)
|
||||||
}
|
}
|
||||||
for _, dep := range fissionDeployments {
|
for _, dep := range fissionDeployments {
|
||||||
// container name совпадает с deployment name в fission
|
// В Fission каждый deployment имеет один контейнер с тем же именем что и deployment.
|
||||||
|
// Подставляем имя контейнера под конкретный deployment для корректного merge key.
|
||||||
patch["spec"].(map[string]any)["template"].(map[string]any)["spec"].(map[string]any)["containers"].([]any)[0].(map[string]any)["name"] = dep
|
patch["spec"].(map[string]any)["template"].(map[string]any)["spec"].(map[string]any)["containers"].([]any)[0].(map[string]any)["name"] = dep
|
||||||
patchBytes, _ = json.Marshal(patch)
|
patchBytes, _ = json.Marshal(patch)
|
||||||
_, patchErr := s.dyn.Resource(deploymentGVR).Namespace(fissionNS).Patch(
|
_, patchErr := s.dyn.Resource(deploymentGVR).Namespace(fissionNS).Patch(
|
||||||
@@ -906,43 +923,60 @@ func (s *server) ensureUserNamespace(ctx context.Context, ns string) error {
|
|||||||
return fmt.Errorf("create namespace %s: %w", ns, err)
|
return fmt.Errorf("create namespace %s: %w", ns, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1b. Создаём RoleBindings для Fission SA (всегда — idempotent через IsAlreadyExists)
|
// 1b. RoleBindings для Fission SA в user namespace.
|
||||||
fissionSAs := []string{"fission-executor", "fission-router", "fission-buildermgr", "fission-kubewatcher", "fission-timer", "fission-fetcher", "fission-builder"}
|
//
|
||||||
fissionSysNS := os.Getenv("FISSION_SYSTEM_NAMESPACE")
|
// Проблема: Fission компоненты (executor, router, buildermgr и др.) работают в namespace
|
||||||
if fissionSysNS == "" {
|
// "fission", но при добавлении нового namespace в FISSION_RESOURCE_NAMESPACES они начинают
|
||||||
fissionSysNS = "fission"
|
// туда смотреть (list/watch). По умолчанию у их SA нет прав в чужих namespace-ах →
|
||||||
}
|
// "forbidden: cannot list environments.fission.io in namespace X".
|
||||||
for _, sa := range fissionSAs {
|
//
|
||||||
rbObj := &unstructured.Unstructured{
|
// Почему cluster-admin, а не admin:
|
||||||
Object: map[string]any{
|
// ClusterRole "admin" не включает custom resource группы (fission.io/*).
|
||||||
"apiVersion": "rbac.authorization.k8s.io/v1",
|
// Fission executor при старте пытается создать Role с правами на fission.io/packages,
|
||||||
"kind": "RoleBinding",
|
// и получает "attempting to grant RBAC permissions not currently held" — RBAC escalation
|
||||||
"metadata": map[string]any{
|
// prevention. ClusterRole "cluster-admin" в контексте RoleBinding (не ClusterRoleBinding)
|
||||||
"name": "fission-" + sa + "-user-ns",
|
// даёт полный доступ ТОЛЬКО внутри конкретного namespace — это безопасно.
|
||||||
"namespace": ns,
|
//
|
||||||
},
|
// Операция idempotent: если RoleBinding уже существует — IsAlreadyExists игнорируется.
|
||||||
"roleRef": map[string]any{
|
fissionSAs := []string{"fission-executor", "fission-router", "fission-buildermgr", "fission-kubewatcher", "fission-timer", "fission-fetcher", "fission-builder"}
|
||||||
"apiGroup": "rbac.authorization.k8s.io",
|
fissionSysNS := os.Getenv("FISSION_SYSTEM_NAMESPACE")
|
||||||
"kind": "ClusterRole",
|
if fissionSysNS == "" {
|
||||||
"name": "cluster-admin",
|
fissionSysNS = "fission"
|
||||||
},
|
}
|
||||||
"subjects": []any{
|
rbGVR := schema.GroupVersionResource{Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "rolebindings"}
|
||||||
map[string]any{
|
for _, sa := range fissionSAs {
|
||||||
"kind": "ServiceAccount",
|
rbObj := &unstructured.Unstructured{
|
||||||
"name": sa,
|
Object: map[string]any{
|
||||||
"namespace": fissionSysNS,
|
"apiVersion": "rbac.authorization.k8s.io/v1",
|
||||||
},
|
"kind": "RoleBinding",
|
||||||
},
|
"metadata": map[string]any{
|
||||||
},
|
"name": "fission-" + sa + "-user-ns",
|
||||||
}
|
"namespace": ns,
|
||||||
rbGVR := schema.GroupVersionResource{Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "rolebindings"}
|
},
|
||||||
_, rbErr := s.dyn.Resource(rbGVR).Namespace(ns).Create(ctx, rbObj, metav1.CreateOptions{})
|
"roleRef": map[string]any{
|
||||||
if rbErr != nil && !apierrors.IsAlreadyExists(rbErr) {
|
"apiGroup": "rbac.authorization.k8s.io",
|
||||||
log.Printf("ensureUserNamespace: create rolebinding %s/%s: %v", ns, sa, rbErr)
|
"kind": "ClusterRole",
|
||||||
}
|
"name": "cluster-admin", // namespace-scoped через RoleBinding, не ClusterRoleBinding
|
||||||
}
|
},
|
||||||
|
"subjects": []any{
|
||||||
|
map[string]any{
|
||||||
|
"kind": "ServiceAccount",
|
||||||
|
"name": sa,
|
||||||
|
"namespace": fissionSysNS,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
_, rbErr := s.dyn.Resource(rbGVR).Namespace(ns).Create(ctx, rbObj, metav1.CreateOptions{})
|
||||||
|
if rbErr != nil && !apierrors.IsAlreadyExists(rbErr) {
|
||||||
|
log.Printf("ensureUserNamespace: create rolebinding %s/%s: %v", ns, sa, rbErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 1c. Патчим Fission если namespace новый
|
// 1c. Регистрируем новый namespace в Fission (FISSION_RESOURCE_NAMESPACES).
|
||||||
|
// Только при первом создании — повторный патч не нужен, Fission уже знает о namespace.
|
||||||
|
// addNSToFission читает текущее значение переменной у router-а, добавляет ns и патчит
|
||||||
|
// все Fission deployments (router, executor, buildermgr, kubewatcher, timer).
|
||||||
if newlyCreated {
|
if newlyCreated {
|
||||||
if patchErr := s.addNSToFission(ctx, ns); patchErr != nil {
|
if patchErr := s.addNSToFission(ctx, ns); patchErr != nil {
|
||||||
log.Printf("ensureUserNamespace: addNSToFission: %v", patchErr)
|
log.Printf("ensureUserNamespace: addNSToFission: %v", patchErr)
|
||||||
|
|||||||
Reference in New Issue
Block a user