Compare commits
6 Commits
codex/mdm-
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
30e04a10c6 | ||
|
|
fc24102fb9 | ||
|
|
a77fbd0381 | ||
|
|
191eb91642 | ||
| e4e03643a4 | |||
|
|
14195e5da7 |
@@ -75,19 +75,12 @@ metadata:
|
|||||||
name: agent-zero
|
name: agent-zero
|
||||||
namespace: agent-zero
|
namespace: agent-zero
|
||||||
|
|
||||||
---
|
# SEC-6 / audit RBAC-001 (operator directive 2026-06-17): agent-zero is an LLM
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
# agent and must reach the cluster ONLY through gated MCP tools — NOT raw kubectl.
|
||||||
kind: ClusterRoleBinding
|
# It therefore has NO ClusterRole/ClusterRoleBinding at all, and the pod sets
|
||||||
metadata:
|
# automountServiceAccountToken: false (below) so no Kubernetes API token is even
|
||||||
name: agent-zero-cluster-admin
|
# mounted. History: cluster-admin -> read-only -> (now) no cluster RBAC / no token.
|
||||||
roleRef:
|
# Detail: FlowerCore.Notes docs/security/sec-6-agent-zero-rbac-remediation.md.
|
||||||
apiGroup: rbac.authorization.k8s.io
|
|
||||||
kind: ClusterRole
|
|
||||||
name: cluster-admin
|
|
||||||
subjects:
|
|
||||||
- kind: ServiceAccount
|
|
||||||
name: agent-zero
|
|
||||||
namespace: agent-zero
|
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Agent Zero — AI Agent Web UI (NUC Edition, Blue Jay Profile)
|
# Agent Zero — AI Agent Web UI (NUC Edition, Blue Jay Profile)
|
||||||
@@ -189,6 +182,9 @@ spec:
|
|||||||
app: agent-zero
|
app: agent-zero
|
||||||
spec:
|
spec:
|
||||||
serviceAccountName: agent-zero
|
serviceAccountName: agent-zero
|
||||||
|
# SEC-6: no Kubernetes API token mounted — agent-zero reaches the cluster
|
||||||
|
# only via gated MCP tools, never raw kubectl. (RBAC is also removed above.)
|
||||||
|
automountServiceAccountToken: false
|
||||||
initContainers:
|
initContainers:
|
||||||
# Wait for fc-llm-bridge to be reachable before starting Agent Zero.
|
# Wait for fc-llm-bridge to be reachable before starting Agent Zero.
|
||||||
- name: wait-for-llm-bridge
|
- name: wait-for-llm-bridge
|
||||||
|
|||||||
@@ -3616,7 +3616,8 @@ data:
|
|||||||
kubectl_manager.py: |
|
kubectl_manager.py: |
|
||||||
# Kubernetes Cluster Management Tool
|
# Kubernetes Cluster Management Tool
|
||||||
# Manages Kubernetes resources via kubectl on a Rancher Desktop (k3s) cluster.
|
# Manages Kubernetes resources via kubectl on a Rancher Desktop (k3s) cluster.
|
||||||
# The pod runs with a cluster-admin ServiceAccount so all operations are permitted.
|
# SEC-6: the pod has no mounted Kubernetes API token and no cluster RBAC.
|
||||||
|
# Cluster operations must go through gated FlowerCore MCP tools instead.
|
||||||
# kubectl is located at /usr/local/bin/kubectl.
|
# kubectl is located at /usr/local/bin/kubectl.
|
||||||
|
|
||||||
import subprocess
|
import subprocess
|
||||||
@@ -4442,7 +4443,8 @@ data:
|
|||||||
|
|
||||||
### Notes
|
### Notes
|
||||||
|
|
||||||
- The Agent Zero pod has `cluster-admin` privileges; all kubectl operations are permitted.
|
- The Agent Zero pod has no mounted Kubernetes API token and no cluster RBAC.
|
||||||
|
- Cluster operations must go through gated FlowerCore MCP tools instead of raw kubectl.
|
||||||
- kubectl is located at `/usr/local/bin/kubectl`.
|
- kubectl is located at `/usr/local/bin/kubectl`.
|
||||||
- Long outputs are truncated to 4000 characters to avoid flooding.
|
- Long outputs are truncated to 4000 characters to avoid flooding.
|
||||||
- The `exec_command` action has a 30-second timeout.
|
- The `exec_command` action has a 30-second timeout.
|
||||||
|
|||||||
@@ -43,5 +43,6 @@ shared origin cert must exist in every namespace that serves a
|
|||||||
```powershell
|
```powershell
|
||||||
kubectl.exe --kubeconfig C:\Users\AndrewStoltz\.kube\rke2.yaml -n argocd get application infra-fc-updater
|
kubectl.exe --kubeconfig C:\Users\AndrewStoltz\.kube\rke2.yaml -n argocd get application infra-fc-updater
|
||||||
kubectl.exe --kubeconfig C:\Users\AndrewStoltz\.kube\rke2.yaml -n fc-updater get deploy,svc,ingressroute,certificate,pvc
|
kubectl.exe --kubeconfig C:\Users\AndrewStoltz\.kube\rke2.yaml -n fc-updater get deploy,svc,ingressroute,certificate,pvc
|
||||||
curl.exe -sk https://update.flowercore.io/api/v1/manifests/_schema
|
curl.exe -sk https://update.flowercore.io/
|
||||||
|
curl.exe -sk -o NUL -w "%{http_code}`n" https://update.flowercore.io/login
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -266,7 +266,7 @@ spec:
|
|||||||
entryPoints:
|
entryPoints:
|
||||||
- websecure
|
- websecure
|
||||||
routes:
|
routes:
|
||||||
- match: (Host(`update.flowercore.io`) || Host(`updates.flowercore.io`)) && (Method(`GET`) || Method(`HEAD`) || Method(`POST`) || Method(`OPTIONS`))
|
- match: (Host(`update.flowercore.io`) || Host(`updates.flowercore.io`)) && (Method(`GET`) || Method(`HEAD`))
|
||||||
kind: Rule
|
kind: Rule
|
||||||
services:
|
services:
|
||||||
- name: updatecenter-web
|
- name: updatecenter-web
|
||||||
|
|||||||
@@ -225,8 +225,7 @@ spec:
|
|||||||
- "--port=8001"
|
- "--port=8001"
|
||||||
- "--address=127.0.0.1"
|
- "--address=127.0.0.1"
|
||||||
- "--accept-hosts=.*"
|
- "--accept-hosts=.*"
|
||||||
- "--accept-paths=.*"
|
- "--accept-paths=^/api/v1/namespaces/(argocd|gitea|telephony|traefik-system|zabbix|matrix|irc|mail|selenium)/pods(/[^/]+(/(exec|attach))?)?$"
|
||||||
- "--disable-filter=true"
|
|
||||||
- "--v=2"
|
- "--v=2"
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
@@ -526,10 +525,13 @@ metadata:
|
|||||||
name: guacd-exec
|
name: guacd-exec
|
||||||
namespace: guacamole
|
namespace: guacamole
|
||||||
---
|
---
|
||||||
|
# Namespace-scoped exec/list rights for the Kubernetes protocol and sync job.
|
||||||
|
# Keep this allowlist in lockstep with TARGET_NAMESPACES below.
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
kind: ClusterRole
|
kind: Role
|
||||||
metadata:
|
metadata:
|
||||||
name: guacd-pod-exec
|
name: guacd-pod-exec
|
||||||
|
namespace: argocd
|
||||||
labels:
|
labels:
|
||||||
app.kubernetes.io/component: proxy
|
app.kubernetes.io/component: proxy
|
||||||
app.kubernetes.io/name: guacd
|
app.kubernetes.io/name: guacd
|
||||||
@@ -540,20 +542,282 @@ rules:
|
|||||||
- apiGroups: [""]
|
- apiGroups: [""]
|
||||||
resources: ["pods/exec", "pods/attach"]
|
resources: ["pods/exec", "pods/attach"]
|
||||||
verbs: ["create", "get"]
|
verbs: ["create", "get"]
|
||||||
- apiGroups: [""]
|
|
||||||
resources: ["namespaces"]
|
|
||||||
verbs: ["list", "get"]
|
|
||||||
---
|
---
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
kind: ClusterRoleBinding
|
kind: RoleBinding
|
||||||
metadata:
|
metadata:
|
||||||
name: guacd-pod-exec
|
name: guacd-pod-exec
|
||||||
|
namespace: argocd
|
||||||
labels:
|
labels:
|
||||||
app.kubernetes.io/component: proxy
|
app.kubernetes.io/component: proxy
|
||||||
app.kubernetes.io/name: guacd
|
app.kubernetes.io/name: guacd
|
||||||
roleRef:
|
roleRef:
|
||||||
apiGroup: rbac.authorization.k8s.io
|
apiGroup: rbac.authorization.k8s.io
|
||||||
kind: ClusterRole
|
kind: Role
|
||||||
|
name: guacd-pod-exec
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: guacd-exec
|
||||||
|
namespace: guacamole
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: Role
|
||||||
|
metadata:
|
||||||
|
name: guacd-pod-exec
|
||||||
|
namespace: gitea
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: proxy
|
||||||
|
app.kubernetes.io/name: guacd
|
||||||
|
rules:
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["pods"]
|
||||||
|
verbs: ["get", "list"]
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["pods/exec", "pods/attach"]
|
||||||
|
verbs: ["create", "get"]
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: RoleBinding
|
||||||
|
metadata:
|
||||||
|
name: guacd-pod-exec
|
||||||
|
namespace: gitea
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: proxy
|
||||||
|
app.kubernetes.io/name: guacd
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: Role
|
||||||
|
name: guacd-pod-exec
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: guacd-exec
|
||||||
|
namespace: guacamole
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: Role
|
||||||
|
metadata:
|
||||||
|
name: guacd-pod-exec
|
||||||
|
namespace: telephony
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: proxy
|
||||||
|
app.kubernetes.io/name: guacd
|
||||||
|
rules:
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["pods"]
|
||||||
|
verbs: ["get", "list"]
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["pods/exec", "pods/attach"]
|
||||||
|
verbs: ["create", "get"]
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: RoleBinding
|
||||||
|
metadata:
|
||||||
|
name: guacd-pod-exec
|
||||||
|
namespace: telephony
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: proxy
|
||||||
|
app.kubernetes.io/name: guacd
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: Role
|
||||||
|
name: guacd-pod-exec
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: guacd-exec
|
||||||
|
namespace: guacamole
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: Role
|
||||||
|
metadata:
|
||||||
|
name: guacd-pod-exec
|
||||||
|
namespace: traefik-system
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: proxy
|
||||||
|
app.kubernetes.io/name: guacd
|
||||||
|
rules:
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["pods"]
|
||||||
|
verbs: ["get", "list"]
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["pods/exec", "pods/attach"]
|
||||||
|
verbs: ["create", "get"]
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: RoleBinding
|
||||||
|
metadata:
|
||||||
|
name: guacd-pod-exec
|
||||||
|
namespace: traefik-system
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: proxy
|
||||||
|
app.kubernetes.io/name: guacd
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: Role
|
||||||
|
name: guacd-pod-exec
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: guacd-exec
|
||||||
|
namespace: guacamole
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: Role
|
||||||
|
metadata:
|
||||||
|
name: guacd-pod-exec
|
||||||
|
namespace: zabbix
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: proxy
|
||||||
|
app.kubernetes.io/name: guacd
|
||||||
|
rules:
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["pods"]
|
||||||
|
verbs: ["get", "list"]
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["pods/exec", "pods/attach"]
|
||||||
|
verbs: ["create", "get"]
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: RoleBinding
|
||||||
|
metadata:
|
||||||
|
name: guacd-pod-exec
|
||||||
|
namespace: zabbix
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: proxy
|
||||||
|
app.kubernetes.io/name: guacd
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: Role
|
||||||
|
name: guacd-pod-exec
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: guacd-exec
|
||||||
|
namespace: guacamole
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: Role
|
||||||
|
metadata:
|
||||||
|
name: guacd-pod-exec
|
||||||
|
namespace: matrix
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: proxy
|
||||||
|
app.kubernetes.io/name: guacd
|
||||||
|
rules:
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["pods"]
|
||||||
|
verbs: ["get", "list"]
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["pods/exec", "pods/attach"]
|
||||||
|
verbs: ["create", "get"]
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: RoleBinding
|
||||||
|
metadata:
|
||||||
|
name: guacd-pod-exec
|
||||||
|
namespace: matrix
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: proxy
|
||||||
|
app.kubernetes.io/name: guacd
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: Role
|
||||||
|
name: guacd-pod-exec
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: guacd-exec
|
||||||
|
namespace: guacamole
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: Role
|
||||||
|
metadata:
|
||||||
|
name: guacd-pod-exec
|
||||||
|
namespace: irc
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: proxy
|
||||||
|
app.kubernetes.io/name: guacd
|
||||||
|
rules:
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["pods"]
|
||||||
|
verbs: ["get", "list"]
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["pods/exec", "pods/attach"]
|
||||||
|
verbs: ["create", "get"]
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: RoleBinding
|
||||||
|
metadata:
|
||||||
|
name: guacd-pod-exec
|
||||||
|
namespace: irc
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: proxy
|
||||||
|
app.kubernetes.io/name: guacd
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: Role
|
||||||
|
name: guacd-pod-exec
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: guacd-exec
|
||||||
|
namespace: guacamole
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: Role
|
||||||
|
metadata:
|
||||||
|
name: guacd-pod-exec
|
||||||
|
namespace: mail
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: proxy
|
||||||
|
app.kubernetes.io/name: guacd
|
||||||
|
rules:
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["pods"]
|
||||||
|
verbs: ["get", "list"]
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["pods/exec", "pods/attach"]
|
||||||
|
verbs: ["create", "get"]
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: RoleBinding
|
||||||
|
metadata:
|
||||||
|
name: guacd-pod-exec
|
||||||
|
namespace: mail
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: proxy
|
||||||
|
app.kubernetes.io/name: guacd
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: Role
|
||||||
|
name: guacd-pod-exec
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: guacd-exec
|
||||||
|
namespace: guacamole
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: Role
|
||||||
|
metadata:
|
||||||
|
name: guacd-pod-exec
|
||||||
|
namespace: selenium
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: proxy
|
||||||
|
app.kubernetes.io/name: guacd
|
||||||
|
rules:
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["pods"]
|
||||||
|
verbs: ["get", "list"]
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["pods/exec", "pods/attach"]
|
||||||
|
verbs: ["create", "get"]
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: RoleBinding
|
||||||
|
metadata:
|
||||||
|
name: guacd-pod-exec
|
||||||
|
namespace: selenium
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: proxy
|
||||||
|
app.kubernetes.io/name: guacd
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: Role
|
||||||
name: guacd-pod-exec
|
name: guacd-pod-exec
|
||||||
subjects:
|
subjects:
|
||||||
- kind: ServiceAccount
|
- kind: ServiceAccount
|
||||||
|
|||||||
@@ -1013,6 +1013,22 @@ public sealed class FleetManifestLintTests
|
|||||||
match.Should().NotContain("Method(`POST`)");
|
match.Should().NotContain("Method(`POST`)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void UpdateCenterPublicIngress_KeepsDeliveryOnlyGetHeadMethodAllowlist()
|
||||||
|
{
|
||||||
|
var publicIngress = AppDocuments("fc-updater")
|
||||||
|
.Single(document => document.Kind == "IngressRoute" && document.Name == "updatecenter-web-public");
|
||||||
|
var route = publicIngress.MappingSequence("spec", "routes").Should().ContainSingle().Subject;
|
||||||
|
var match = ManifestNodeExtensions.Scalar(route, "match");
|
||||||
|
|
||||||
|
match.Should().Contain("Host(`update.flowercore.io`)");
|
||||||
|
match.Should().Contain("Host(`updates.flowercore.io`)");
|
||||||
|
match.Should().Contain("Method(`GET`)");
|
||||||
|
match.Should().Contain("Method(`HEAD`)");
|
||||||
|
match.Should().NotContain("Method(`POST`)");
|
||||||
|
match.Should().NotContain("Method(`OPTIONS`)");
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void DnsAndMediaIngressRoutes_MatchLiveInternalHosts()
|
public void DnsAndMediaIngressRoutes_MatchLiveInternalHosts()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,8 +9,6 @@ package bluejayinfra.public_readwrite_allowlist
|
|||||||
public_readwrite_hosts := {
|
public_readwrite_hosts := {
|
||||||
"updatecenter.iamworkin.lan",
|
"updatecenter.iamworkin.lan",
|
||||||
"updates.iamworkin.lan",
|
"updates.iamworkin.lan",
|
||||||
"update.flowercore.io",
|
|
||||||
"updates.flowercore.io",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
required_methods := {"GET", "HEAD", "POST", "OPTIONS"}
|
required_methods := {"GET", "HEAD", "POST", "OPTIONS"}
|
||||||
|
|||||||
Reference in New Issue
Block a user