feat(ai): mcp-k8s через ToolHive (read-only RBAC)
Flate / Flate - Filter (pull_request) Successful in 5s
Flate / Flate (pull_request) Successful in 18s
Flate / Flate - Success (pull_request) Canceled after 0s

MCP-сервер k8s из реестра ToolHive: ghcr.io/stackloklabs/mkp/server:0.4.3, transport streamable-http.
Отдельный SA mcp-k8s и ClusterRole mcp-k8s-readonly (только get/list/watch).
Core-группа перечислена поимённо, чтобы не открывать secrets; остальные группы явным списком,
потому что apiGroups: [*] включает и core-группу. Наружу не выводим: без oidcConfigRef прокси
ToolHive неаутентифицирован.
This commit is contained in:
2026-09-17 23:10:17 +00:00
parent 0243dbfb9c
commit c4cf3ba798
6 changed files with 147 additions and 0 deletions
@@ -0,0 +1,7 @@
---
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ./serviceaccount.yaml
- ./rbac.yaml
- ./mcpserver.yaml
@@ -0,0 +1,28 @@
---
# Kubernetes MCP из реестра ToolHive (сервер k8s, проект StacklokLabs/mkp).
# Транспорт streamable-http — mkp отдаёт MCP по HTTP сам, прокси-обёртка над
# stdio не нужна. Порты оставлены дефолтными: proxyPort и mcpPort оба 8080.
#
# ВАЖНО: без oidcConfigRef прокси ToolHive работает без аутентификации — любой,
# кто достучится до его порта, получит доступ к API под правами SA ниже.
# Поэтому наружу сервер не выводим: пока агент вне кластера, адрес только
# внутрикластерный, а внешний доступ появится вместе с OIDC после Authelia.
apiVersion: toolhive.stacklok.dev/v1beta1
kind: MCPServer
metadata:
name: mcp-k8s
spec:
image: ghcr.io/stackloklabs/mkp/server:0.4.3
transport: streamable-http
# SA существует заранее (см. serviceaccount.yaml) — вместе с ним приезжает
# ClusterRoleBinding mcp-k8s-readonly.
serviceAccount: mcp-k8s
permissionProfile:
type: builtin
name: network
resources:
requests:
cpu: 50m
memory: 128Mi
limits:
memory: 256Mi
+84
View File
@@ -0,0 +1,84 @@
---
# Права MCP-сервера на API. Только чтение: mkp умеет и apply_resource, и
# delete_resource, и exec в поды — ограничение живёт исключительно здесь,
# поэтому список verbs закрыт чтением.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: mcp-k8s-readonly
rules:
# Core-группа перечислена поимённо: resources: ["*"] открыл бы и secrets,
# а cluster-secrets, токен Proxmox CSI и прочие креды агенту читать незачем.
- apiGroups: [""]
resources:
- configmaps
- endpoints
- events
- limitranges
- namespaces
- nodes
- persistentvolumeclaims
- persistentvolumes
- pods
- pods/log
- pods/status
- replicationcontrollers
- resourcequotas
- serviceaccounts
- services
verbs: ["get", "list", "watch"]
# Остальные группы перечислены явно, потому что apiGroups: ["*"] включает и
# core-группу — то есть снова открыл бы secrets. Список закрытый: не хватает
# группы — сервер просто не увидит её ресурсы, ничего не сломается.
- apiGroups:
- admissionregistration.k8s.io
- apiextensions.k8s.io
- apiregistration.k8s.io
- apps
- authentication.k8s.io
- authorization.k8s.io
- autoscaling
- batch
- certificates.k8s.io
- coordination.k8s.io
- discovery.k8s.io
- events.k8s.io
- flowcontrol.apiserver.k8s.io
- metrics.k8s.io
- networking.k8s.io
- node.k8s.io
- policy
- rbac.authorization.k8s.io
- scheduling.k8s.io
- storage.k8s.io
- acme.cert-manager.io
- cert-manager.io
- cilium.io
- dragonflydb.io
- external-secrets.io
- gateway.envoyproxy.io
- gateway.networking.k8s.io
- generators.external-secrets.io
- helm.toolkit.fluxcd.io
- image.toolkit.fluxcd.io
- kustomize.toolkit.fluxcd.io
- monitoring.coreos.com
- notification.toolkit.fluxcd.io
- snapshot.storage.k8s.io
- source.toolkit.fluxcd.io
- toolhive.stacklok.dev
resources: ["*"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: mcp-k8s-readonly
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: mcp-k8s-readonly
subjects:
- kind: ServiceAccount
name: mcp-k8s
namespace: ai
@@ -0,0 +1,8 @@
---
# SA создаётся здесь намеренно: spec.serviceAccount в MCPServer означает «уже
# существующий» — если его не создать, оператор сделает свой и права выдавать
# будет некуда.
apiVersion: v1
kind: ServiceAccount
metadata:
name: mcp-k8s
+19
View File
@@ -0,0 +1,19 @@
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: mcp-k8s
spec:
interval: 1h
path: ./kubernetes/apps/ai/mcp-k8s/app
prune: true
sourceRef:
kind: GitRepository
name: flux-system
namespace: flux-system
targetNamespace: ai
wait: true
# Зависимость внутри своего ns, поэтому поле namespace не нужно (по CRD оно
# по умолчанию равно ns самого объекта-зависимости).
dependsOn:
- name: toolhive-operator