Compare commits
6 Commits
codex/whc4
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
30e04a10c6 | ||
|
|
fc24102fb9 | ||
|
|
a77fbd0381 | ||
|
|
191eb91642 | ||
| e4e03643a4 | |||
|
|
14195e5da7 |
@@ -75,19 +75,12 @@ metadata:
|
||||
name: agent-zero
|
||||
namespace: agent-zero
|
||||
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: agent-zero-cluster-admin
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: cluster-admin
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: agent-zero
|
||||
namespace: agent-zero
|
||||
# SEC-6 / audit RBAC-001 (operator directive 2026-06-17): agent-zero is an LLM
|
||||
# agent and must reach the cluster ONLY through gated MCP tools — NOT raw kubectl.
|
||||
# It therefore has NO ClusterRole/ClusterRoleBinding at all, and the pod sets
|
||||
# automountServiceAccountToken: false (below) so no Kubernetes API token is even
|
||||
# mounted. History: cluster-admin -> read-only -> (now) no cluster RBAC / no token.
|
||||
# Detail: FlowerCore.Notes docs/security/sec-6-agent-zero-rbac-remediation.md.
|
||||
|
||||
# =============================================================================
|
||||
# Agent Zero — AI Agent Web UI (NUC Edition, Blue Jay Profile)
|
||||
@@ -189,6 +182,9 @@ spec:
|
||||
app: agent-zero
|
||||
spec:
|
||||
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:
|
||||
# Wait for fc-llm-bridge to be reachable before starting Agent Zero.
|
||||
- name: wait-for-llm-bridge
|
||||
|
||||
@@ -3616,7 +3616,8 @@ data:
|
||||
kubectl_manager.py: |
|
||||
# Kubernetes Cluster Management Tool
|
||||
# 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.
|
||||
|
||||
import subprocess
|
||||
@@ -4442,7 +4443,8 @@ data:
|
||||
|
||||
### 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`.
|
||||
- Long outputs are truncated to 4000 characters to avoid flooding.
|
||||
- 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
|
||||
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
|
||||
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:
|
||||
- websecure
|
||||
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
|
||||
services:
|
||||
- name: updatecenter-web
|
||||
|
||||
@@ -225,8 +225,7 @@ spec:
|
||||
- "--port=8001"
|
||||
- "--address=127.0.0.1"
|
||||
- "--accept-hosts=.*"
|
||||
- "--accept-paths=.*"
|
||||
- "--disable-filter=true"
|
||||
- "--accept-paths=^/api/v1/namespaces/(argocd|gitea|telephony|traefik-system|zabbix|matrix|irc|mail|selenium)/pods(/[^/]+(/(exec|attach))?)?$"
|
||||
- "--v=2"
|
||||
resources:
|
||||
requests:
|
||||
@@ -526,10 +525,13 @@ metadata:
|
||||
name: guacd-exec
|
||||
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
|
||||
kind: ClusterRole
|
||||
kind: Role
|
||||
metadata:
|
||||
name: guacd-pod-exec
|
||||
namespace: argocd
|
||||
labels:
|
||||
app.kubernetes.io/component: proxy
|
||||
app.kubernetes.io/name: guacd
|
||||
@@ -540,20 +542,282 @@ rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["pods/exec", "pods/attach"]
|
||||
verbs: ["create", "get"]
|
||||
- apiGroups: [""]
|
||||
resources: ["namespaces"]
|
||||
verbs: ["list", "get"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: guacd-pod-exec
|
||||
namespace: argocd
|
||||
labels:
|
||||
app.kubernetes.io/component: proxy
|
||||
app.kubernetes.io/name: guacd
|
||||
roleRef:
|
||||
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
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
|
||||
@@ -1013,6 +1013,22 @@ public sealed class FleetManifestLintTests
|
||||
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]
|
||||
public void DnsAndMediaIngressRoutes_MatchLiveInternalHosts()
|
||||
{
|
||||
|
||||
@@ -9,8 +9,6 @@ package bluejayinfra.public_readwrite_allowlist
|
||||
public_readwrite_hosts := {
|
||||
"updatecenter.iamworkin.lan",
|
||||
"updates.iamworkin.lan",
|
||||
"update.flowercore.io",
|
||||
"updates.flowercore.io",
|
||||
}
|
||||
|
||||
required_methods := {"GET", "HEAD", "POST", "OPTIONS"}
|
||||
|
||||
Reference in New Issue
Block a user