Comments that pointed at other YAML as `file.yaml:N` went stale on any edit above the cited line; three drift fixes had been merged by hand (REN-181). The cited block now carries `# anchor: <name>` and the comment cites `path.yaml#<name>`. - Sweep: all 32 line-number citations in YAML comments converted. Several were already stale at head and now point at the right block (see PR). Vendored files (Flux gotk-components, IMC CRD) get no anchor; those two citations name the object/schema path instead. - Guard: scripts/ci/yaml-citations.sh in ci-manifests-test.yml; fails on a line-number citation or a path#name whose file/anchor is gone. Self-test in ci-scripts-test.yml. - CONTRIBUTING.md: the convention in three lines. Comment-only under kubernetes/: every file parses to identical objects before and after, so Flux applies nothing and no ConfigMap data changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
99 lines
4.8 KiB
YAML
99 lines
4.8 KiB
YAML
---
|
|
# Cluster-wide READ for the agents in the paperclip pod (REN-111, decided on
|
|
# REN-52 2026-09-25 by the owner, option B).
|
|
#
|
|
# WHAT THIS IS
|
|
#
|
|
# One ClusterRoleBinding. It binds the Kubernetes built-in `view` ClusterRole to
|
|
# the ServiceAccount that REN-36 created in `../paperclip/prometheus-read-rbac.yaml`.
|
|
# No new ServiceAccount, no new ClusterRole, no write verb anywhere. `view` is
|
|
# maintained upstream and holds get/list/watch only; revocation is deleting this
|
|
# one object.
|
|
#
|
|
# WHY IT IS A SEPARATE FILE
|
|
#
|
|
# `prometheus-read-rbac.yaml` is deliberately narrow and its header argues for
|
|
# staying that way. This grant is the opposite in character — cluster-wide,
|
|
# every namespace — so it is kept in its own file and its own kustomization
|
|
# entry. Reverting it is deleting this file and one line from
|
|
# `kustomization.yaml`; nothing in the REN-36 path is touched, and the token
|
|
# falls back to `get services/proxy` on two named Services.
|
|
#
|
|
# WHY THE OWNER WANTED IT
|
|
#
|
|
# REN-108 part B had to validate a CiliumNetworkPolicy allowlist whose selector
|
|
# was `app.kubernetes.io/name: prometheus-blackbox-exporter`, and could not:
|
|
# kube-state-metrics here does not export `kube_pod_labels`, and
|
|
# blackbox-exporter is not a scrape target so Prometheus has no
|
|
# `__meta_kubernetes_pod_label_*` for it either. A CNP selector that matches no
|
|
# pod allows nothing and reports no error, so the proposal shipped unverified.
|
|
# `list pods` closes that, and the same gap recurs on every netpol or
|
|
# scrape-selector change.
|
|
#
|
|
# WHAT IT COSTS — ACCEPTED ON REN-52, NOT DISCOVERED HERE
|
|
#
|
|
# * EVERY process in the paperclip pod can read the mounted token, so this is
|
|
# a grant to every agent, not to one. REN-36 was approved on the premise
|
|
# that "the token is only worth what this Role grants"; this file changes
|
|
# that arithmetic, and the owner priced it knowingly.
|
|
# * `view` reads ConfigMaps in EVERY namespace. This repo keeps application
|
|
# configuration in ConfigMaps. Chosen over Hermes' narrower alternative.
|
|
# * `view` grants `pods/log`, which surfaces anything an application prints,
|
|
# including credentials it prints by mistake.
|
|
#
|
|
# WHAT IT DOES NOT GRANT
|
|
#
|
|
# * Secrets. `view` excludes them. "Secrets, metadata only" was the original
|
|
# ask and is NOT expressible in Kubernetes RBAC — the authorizer has no
|
|
# field-level verb — so that part of the ask is dead. Do not re-add Secrets
|
|
# here to approximate it.
|
|
# * Any write verb, on anything.
|
|
# * ClusterRoles and RoleBindings: `view` excludes the whole
|
|
# `rbac.authorization.k8s.io` group, on purpose, so a holder cannot read the
|
|
# shape of its own cage or find a Role to escalate through.
|
|
#
|
|
# FLUX CRDs — COVERED BY AGGREGATION, NO EXTRA ClusterRole NEEDED
|
|
#
|
|
# REN-52 pre-approved adding a read-only ClusterRole for the Flux CRDs *if*
|
|
# aggregation did not already cover them. It does, so none is added:
|
|
# `flux-view-flux-system` carries `rbac.authorization.k8s.io/aggregate-to-view:
|
|
# "true"` and lists get/list/watch on `*` across all six API groups —
|
|
# the `flux-view-flux-system` ClusterRole in
|
|
# `../../clusters/homelab/flux-system/gotk-components.yaml`. The
|
|
# aggregation controller merges those rules into `view` itself, so this one
|
|
# binding picks up kustomizations, helmreleases, gitrepositories, imagepolicies,
|
|
# imagerepositories and imageupdateautomations. Read from git, not from a live
|
|
# ClusterRole — `view` cannot read ClusterRoles, so it is unverifiable from
|
|
# inside the pod. The post-merge SelfSubjectAccessReview on REN-111 proves
|
|
# whether aggregation actually fired in the cluster; if `list kustomizations`
|
|
# comes back `allowed: false` there, the pre-approved ClusterRole is the fix.
|
|
#
|
|
# PRODUCTION
|
|
#
|
|
# Adding a ClusterRoleBinding mutates no workload, restarts no Pod and moves no
|
|
# traffic. RBAC is evaluated per request, so the grant takes effect on the
|
|
# already-mounted token with no remount and no rollout. That sequencing was the
|
|
# point of REN-52 putting this after REN-36: the paperclip Deployment restart
|
|
# was paid once, by REN-36, and is already paid.
|
|
apiVersion: rbac.authorization.k8s.io/v1
|
|
kind: ClusterRoleBinding
|
|
metadata:
|
|
name: paperclip-cluster-view
|
|
labels:
|
|
app.kubernetes.io/name: paperclip
|
|
roleRef:
|
|
apiGroup: rbac.authorization.k8s.io
|
|
kind: ClusterRole
|
|
# Built-in, shipped by kube-controller-manager's default RBAC bootstrap. Not
|
|
# defined in this repo and must not be: it is auto-reconciled, so a local copy
|
|
# would be overwritten on every apiserver start.
|
|
name: view
|
|
subjects:
|
|
- kind: ServiceAccount
|
|
name: paperclip-prometheus-read
|
|
# A ClusterRoleBinding has no namespace of its own, so this is the only
|
|
# place the subject's namespace can be stated. Omit it and the binding
|
|
# resolves to no subject and fails closed, looking identical to a missing
|
|
# grant.
|
|
namespace: paperclip
|