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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ and the project uses SemVer.

## [Unreleased]

### Security
- **Scoped Secret access**: the manager role no longer grants cluster-wide
`get/list/watch` on every Secret. The credential reconciler's Secret access is
now a namespaced `Role`/`RoleBinding` in the operator's own namespace (kustomize
+ Helm), driven by a namespaced `+kubebuilder:rbac` marker.

## [2026.06.1] - 2026-06-20

### Changed
Expand Down
29 changes: 26 additions & 3 deletions config/rbac/rbac.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,6 @@ rules:
- apiGroups: ["forail.forail-platform.io"]
resources: ["jobtemplates/finalizers", "inventories/finalizers", "credentials/finalizers", "schedules/finalizers", "projects/finalizers", "organizations/finalizers", "teams/finalizers", "workflows/finalizers"]
verbs: ["update"]
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["events"]
verbs: ["create", "patch"]
Expand All @@ -46,3 +43,29 @@ roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: forail-operator
---
# needtofix M12: Secret access is namespaced, not cluster-wide. The credential
# reconciler reads referenced Secrets only in the operator's namespace.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: forail-operator-secrets
namespace: forail-operator
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: forail-operator-secrets
namespace: forail-operator
subjects:
- kind: ServiceAccount
name: forail-operator
namespace: forail-operator
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: forail-operator-secrets
25 changes: 17 additions & 8 deletions config/rbac/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,6 @@ kind: ClusterRole
metadata:
name: manager-role
rules:
- apiGroups:
- ""
resources:
- secrets
verbs:
- get
- list
- watch
- apiGroups:
- forail.forail-platform.io
resources:
Expand Down Expand Up @@ -61,3 +53,20 @@ rules:
- get
- patch
- update
---
# needtofix M12: namespaced Secret access (generated from the namespace= rbac
# marker on CredentialReconciler). Regenerate with `make manifests`.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: manager-role
namespace: forail-operator
rules:
- apiGroups:
- ""
resources:
- secrets
verbs:
- get
- list
- watch
38 changes: 33 additions & 5 deletions helm/templates/rbac.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,6 @@ rules:
- apiGroups: ["forail.forail-platform.io"]
resources: ["jobtemplates/finalizers", "inventories/finalizers", "credentials/finalizers", "schedules/finalizers", "projects/finalizers", "organizations/finalizers", "teams/finalizers", "workflows/finalizers"]
verbs: ["update"]
# Credential reconciler reads referenced k8s Secrets for sensitive
# input fields (passwords, ssh keys, vault tokens).
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["events"]
verbs: ["create", "patch"]
Expand All @@ -43,4 +38,37 @@ roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: {{ .Release.Name }}-forail-operator
---
# needtofix M12: the credential reconciler reads referenced k8s Secrets for
# sensitive input fields (passwords, ssh keys, vault tokens). That access is
# a namespaced Role in the operator's own namespace — NOT a cluster-wide
# secrets grant. A multi-namespace setup adds one RoleBinding per namespace
# that holds Credential Secrets.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: {{ .Release.Name }}-forail-operator-secrets
namespace: {{ .Release.Namespace }}
labels:
{{- include "forail-operator.labels" . | nindent 4 }}
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: {{ .Release.Name }}-forail-operator-secrets
namespace: {{ .Release.Namespace }}
labels:
{{- include "forail-operator.labels" . | nindent 4 }}
subjects:
- kind: ServiceAccount
name: {{ include "forail-operator.serviceAccountName" . }}
namespace: {{ .Release.Namespace }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: {{ .Release.Name }}-forail-operator-secrets
{{- end }}
5 changes: 4 additions & 1 deletion internal/controller/credential_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,10 @@ type CredentialReconciler struct {
// +kubebuilder:rbac:groups=forail.forail-platform.io,resources=credentials,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=forail.forail-platform.io,resources=credentials/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=forail.forail-platform.io,resources=credentials/finalizers,verbs=update
// +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch
// needtofix M12: namespaced secret access only — the reconciler must not be
// able to read every Secret in the cluster. The namespace marker makes
// controller-gen emit a namespaced Role instead of a ClusterRole rule.
// +kubebuilder:rbac:groups="",namespace=forail-operator,resources=secrets,verbs=get;list;watch

func (r *CredentialReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
logger := log.FromContext(ctx)
Expand Down
Loading