Files
homelab/kubernetes/apps/monitoring/ci-diagnostics-rbac.yaml
T
claude-botandClaude Fable 5 3e9e414018 fix(ci-diagnostics): grant read on velero.io and postgresql.cnpg.io CRDs
Found on the first real use of the restricted identity: assessing the
VeleroBackupFailed issue (#251) needs backups.velero.io (latest
apps-daily run is PartiallyFailed — data-mover pod unschedulable on an
unbound PVC), and the ClusterRole had neither velero.io nor CNPG.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NV7bZzZLeoFUEdAeCSLEei
2026-07-16 10:35:53 +02:00

136 lines
4.6 KiB
YAML

# Least-privilege identity for the CI diagnostic agents (alert-monitor and
# issue-fix pipelines, see .woodpecker/). The pipelines mint a short-lived
# (1 h) token for this ServiceAccount at run time via `kubectl create token`
# — no long-lived credential exists anywhere.
#
# Scope: read-only on workload/config/CRD state cluster-wide, plus the
# Prometheus and Alertmanager HTTP APIs via the apiserver service proxy.
# Secrets are deliberately NOT readable: the agents write their findings into
# public Gitea issues/PRs, so nothing secret may be readable in the first
# place. pods/log IS granted — it is what turns guessed diagnoses into
# verified ones (e.g. the pve-exporter 401) — accepting the residual risk
# that a workload logs something sensitive; the agent prompts forbid pasting
# bulk output.
apiVersion: v1
kind: ServiceAccount
metadata:
name: ci-diagnostics
namespace: monitoring
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: ci-diagnostics-readonly
rules:
- apiGroups: [""]
resources:
- pods
- pods/log
- events
- nodes
- namespaces
- services
- endpoints
- persistentvolumeclaims
- persistentvolumes
- configmaps
- resourcequotas
- limitranges
verbs: ["get", "list"]
- apiGroups: ["apps"]
resources: ["deployments", "statefulsets", "daemonsets", "replicasets", "controllerrevisions"]
verbs: ["get", "list"]
- apiGroups: ["batch"]
resources: ["jobs", "cronjobs"]
verbs: ["get", "list"]
- apiGroups: ["events.k8s.io"]
resources: ["events"]
verbs: ["get", "list"]
- apiGroups: ["networking.k8s.io"]
resources: ["ingresses", "networkpolicies"]
verbs: ["get", "list"]
- apiGroups: ["cilium.io"]
resources: ["ciliumnetworkpolicies", "ciliumclusterwidenetworkpolicies"]
verbs: ["get", "list"]
- apiGroups:
- kustomize.toolkit.fluxcd.io
- helm.toolkit.fluxcd.io
- source.toolkit.fluxcd.io
- image.toolkit.fluxcd.io
- notification.toolkit.fluxcd.io
resources: ["*"]
verbs: ["get", "list"]
- apiGroups: ["external-secrets.io"]
resources: ["externalsecrets", "secretstores", "clustersecretstores"]
verbs: ["get", "list"]
- apiGroups: ["monitoring.coreos.com"]
resources: ["prometheusrules", "servicemonitors", "podmonitors", "probes", "prometheuses", "alertmanagers"]
verbs: ["get", "list"]
- apiGroups: ["cert-manager.io"]
resources: ["certificates", "certificaterequests", "issuers", "clusterissuers"]
verbs: ["get", "list"]
- apiGroups: ["ceph.rook.io"]
resources: ["*"]
verbs: ["get", "list"]
# Velero backup/restore state — needed to assess VeleroBackupFailed-class
# alerts (found missing on the first real triage of issue #251). NOTE for
# agents: `get backup -n velero` resolves to CNPG's backups.postgresql
# first — always use the full form `backups.velero.io`.
- apiGroups: ["velero.io"]
resources: ["*"]
verbs: ["get", "list"]
# CNPG cluster/backup state — same reasoning for database alerts.
- apiGroups: ["postgresql.cnpg.io"]
resources: ["*"]
verbs: ["get", "list"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses", "volumeattachments"]
verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: ci-diagnostics-readonly
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: ci-diagnostics-readonly
subjects:
- kind: ServiceAccount
name: ci-diagnostics
namespace: monitoring
---
# Prometheus + Alertmanager HTTP APIs through the apiserver service proxy —
# the same path the alert-monitor fetch step uses (see the netpol note in
# .woodpecker/alert-monitor.yml). resourceNames list both the bare and the
# :port-qualified form because the proxy URL carries the port in the name
# segment and RBAC matches it verbatim.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: ci-diagnostics-service-proxy
namespace: monitoring
rules:
- apiGroups: [""]
resources: ["services/proxy"]
verbs: ["get"]
resourceNames:
- kube-prometheus-stack-prometheus
- "kube-prometheus-stack-prometheus:9090"
- kube-prometheus-stack-alertmanager
- "kube-prometheus-stack-alertmanager:9093"
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: ci-diagnostics-service-proxy
namespace: monitoring
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: ci-diagnostics-service-proxy
subjects:
- kind: ServiceAccount
name: ci-diagnostics
namespace: monitoring