Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .coverage-baseline
Original file line number Diff line number Diff line change
@@ -1 +1 @@
75.4
75.5
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ Start with the stable docs surface:
- [`docs/configuration.md`](docs/configuration.md)
- [`docs/attribution-setup-guide.md`](docs/attribution-setup-guide.md)
- [`docs/security-model.md`](docs/security-model.md)
- [`docs/rbac.md`](docs/rbac.md): the two ClusterRoles, and how to stop the reverser enumerating Secrets
- [`docs/commit-signing.md`](docs/commit-signing.md)
- [`docs/github-setup-guide.md`](docs/github-setup-guide.md)
- [`docs/sops-age-guide.md`](docs/sops-age-guide.md)
Expand Down
8 changes: 8 additions & 0 deletions charts/gitops-reverser/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,9 @@ nodeSelector:
|-----------|-------------|---------|
| `replicaCount` | Number of controller replicas (can't be higher than 1 for now, sorry) | `1` |
| `image.repository` | Container image repository | `ghcr.io/configbutler/gitops-reverser` |
| `env` | Extra container env vars, as Kubernetes `EnvVar` entries | `[]` |
| `volumes` / `volumeMounts` | Extra pod volumes and their mounts, appended as-is | `[]` |
| `servers.enableHTTP2` | Serve HTTP/2 on the TLS servers. Off by default: disabling it mitigates the HTTP/2 Rapid-Reset CVE class | `false` |
| `servers.audit.bindAddress` | host:port the audit ingress server binds to (`--audit-bind-address`) | `0.0.0.0:9444` |
| `servers.audit.port` | Audit container/Service port; must match the port in `bindAddress` | `9444` |
| `servers.audit.tls.enabled` | Serve audit ingress with TLS | `true` |
Expand All @@ -185,11 +188,16 @@ nodeSelector:
| `queue.redis.auth.existingSecret` | Name of a pre-created Secret holding the Redis password (only used when `queue.redis.addr` is set) | `""` |
| `queue.redis.auth.existingSecretKey` | Key within the Secret that holds the password | `password` |
| `queue.redis.auth.username` | Optional Redis ACL username | `""` |
| `queue.redis.db` | Redis logical database index. Redis offers 16; use `queue.redis.keyPrefix` to go past that | `0` |
| `queue.redis.keyPrefix` | Root of every key this release writes (watch cursors, attribution facts, command author records). Give each reverser its own prefix to share one Redis/Valkey between more reversers than `db` can separate. Changing it orphans the previous prefix's keys: cursors cold-replay once, which is safe. Allowed: `[A-Za-z0-9]`, `-`, `_`, `.`, `:` | `gitops-reverser` |
| `queue.redis.tls.enabled` | Enable TLS for Redis connection | `false` |
| `attribution.enabled` | Run audit ingress and name mirrored-resource commit authors from matching kube-apiserver audit facts | `false` |
| `attribution.ttl` | How long an attribution fact is retained waiting for the matching watch event to join it | `10m` |
| `attribution.grace` | Bounded per-event wait for a matching audit fact before a watch event ships as the committer | `3s` |
| `servers.admission.enabled` | Install the validate-operator-types admission webhook that captures CommitRequest authors (a form of author attribution). Enabled by default; a no-op until `queue.redis.addr` is set | `true` |
| `rbac.create` | Create the manager ClusterRole and its binding | `true` |
| `rbac.watchTypes.mode` | Which types a `WatchRule` may read. `any` grants cluster-wide read on everything — convenient, but the reverser can then read every Secret in the cluster. `selected` grants read on `rbac.watchTypes.selected` only, so the reverser cannot list or watch Secrets (it keeps `get` on named Secrets it is pointed at). See [`docs/rbac.md`](../../docs/rbac.md) | `any` |
| `rbac.watchTypes.selected` | Types to grant when `mode: selected`, as `{apiGroups, resources}` entries (verbs are always `get,list,watch`). Required and non-empty in that mode; `namespaces`, `customresourcedefinitions` and `apiservices` come from the manager role and must not be restated | `[]` |
| `servers.metrics.bindAddress` | Metrics listener bind address | `:8080` |
| `servers.metrics.tls.enabled` | Serve metrics with TLS | `false` |
| `servers.metrics.tls.certPath` | Metrics TLS certificate mount path | `/tmp/k8s-metrics-server/metrics-server-certs` |
Expand Down
3 changes: 3 additions & 0 deletions charts/gitops-reverser/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ spec:
imagePullPolicy: {{ .Values.image.pullPolicy }}
args:
- --health-probe-bind-address={{ .Values.servers.healthProbe.bindAddress }}
{{- if .Values.servers.enableHTTP2 }}
- --enable-http2
{{- end }}
- --metrics-bind-address={{ .Values.servers.metrics.bindAddress }}
{{- if not .Values.servers.metrics.tls.enabled }}
- --metrics-insecure
Expand Down
57 changes: 57 additions & 0 deletions charts/gitops-reverser/templates/rbac.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,63 @@ roleRef:
kind: ClusterRole
name: {{ include "gitops-reverser.fullname" . }}-manager-role
subjects:
- kind: ServiceAccount
name: {{ include "gitops-reverser.serviceAccountName" . }}
namespace: {{ .Release.Namespace }}
{{/*
mode, the shape of each `selected` entry, and "selected must not be empty" are all enforced
by values.schema.json, which helm checks on template, lint, install and upgrade. Keeping the
rules there rather than as `fail` calls means one statement of the contract, and errors that
name the offending path.
*/}}
{{- $watchTypes := .Values.rbac.watchTypes | default dict }}
{{- $mode := $watchTypes.mode | default "any" }}
{{- $selected := $watchTypes.selected | default list }}
---
# The read access a WatchRule needs. Separate from the manager role because RBAC is additive:
# folded in, a wildcard here would grant cluster-wide Secret list/watch however narrow the
# Secret rule beside it is. Verbs are always get/list/watch — the reverser never writes to a
# watched type. See docs/rbac.md.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: {{ include "gitops-reverser.fullname" . }}-watch-{{ $mode }}
labels:
{{- include "gitops-reverser.labels" . | nindent 4 }}
rules:
{{- if eq $mode "any" }}
- apiGroups:
- '*'
resources:
- '*'
verbs:
- get
- list
- watch
{{- else }}
{{- range $rule := $selected }}
- apiGroups:
{{- toYaml $rule.apiGroups | nindent 4 }}
resources:
{{- toYaml $rule.resources | nindent 4 }}
verbs:
- get
- list
- watch
{{- end }}
{{- end }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: {{ include "gitops-reverser.fullname" . }}-watch-{{ $mode }}
labels:
{{- include "gitops-reverser.labels" . | nindent 4 }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: {{ include "gitops-reverser.fullname" . }}-watch-{{ $mode }}
subjects:
- kind: ServiceAccount
name: {{ include "gitops-reverser.serviceAccountName" . }}
namespace: {{ .Release.Namespace }}
Expand Down
Loading