NatLabRockies / NatLabRockies/openstudio-server-helm
Remediate cluster-admin ClusterRoleBinding on default/default ServiceAccount (fabric8-rbac) — GuardDuty finding
Nobody has claimed this yet.
- Dominant language
- Go Template
- Stars
- 12
- Forks
- 24
- PR merge metrics
- No merged PRs in 30d
Description
Summary
openstudio-server/templates/service-account/nfs-disconnect-rbac.yaml unconditionally creates a ClusterRoleBinding named fabric8-rbac that grants cluster-admin to the default ServiceAccount in the default namespace:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: fabric8-rbac
subjects:
- kind: ServiceAccount
name: default
namespace: default
roleRef:
kind: ClusterRole
name: cluster-admin
apiGroup: rbac.authorization.k8s.io
This is legacy boilerplate dating back to 07a7afd ("update nfs client disconnect with job hook", 2020) and was almost certainly never meant to grant blanket cluster-wide admin — it's named after the pre-delete NFS-disconnect hook but binds an unrelated, unscoped subject (default/default) rather than a dedicated service account. Any pod in the default namespace running as the default ServiceAccount inherits full cluster-admin.
Why this matters now
This binding is already live on at least one cluster (openstudio-server-03), predating any Helm-ownership tracking (its labels/annotations had been stripped, likely by an external kubectl apply outside Helm's tracking, causing helm upgrade to fail with "cannot be imported into the current release" until manually re-labeled/annotated to unblock a routine upgrade). AWS GuardDuty flags exactly this class of finding (admin access via the default ServiceAccount).
A fix already exists on another branch
Commit 3dcb1e2 ("feat(chart): add per-workload service accounts and least-privilege RBAC") on branch feat/chart-security-hardening (common ancestor a136501 with 179d_spot_instance) already implements the correct remediation, but was never merged into openstack_stable:
- Adds a dedicated
ServiceAccountper workload (web, web-background, worker, rserve, db, redis, pre-delete-hook) via a newworkload-serviceaccounts.yaml. - Replaces the legacy binding with a
security.legacyDefaultServiceAccountClusterAdmin.enabledtoggle innfs-disconnect-rbac.yaml:true(legacy): keeps the old cluster-admin-on-default-SA binding, for phased migration.false(target state): instead creates a least-privilege, namespace-scopedRole/RoleBinding(nfs-client-cleanup) that only allowsdeleteon the specific namedweb/web-background/rserveDeployments, bound to the dedicated pre-delete-hook ServiceAccount — notdefault/default.
- Assigns
serviceAccountNameon every deployment spec. - Documents a phased rollout in
aws/cluster_setup.md(§10.1): enable legacy flag temporarily during migration → verify no workload actually depends on thedefaultSA → set the flag tofalse→ confirmkubectl get clusterrolebinding fabric8-rbacno longer exists (or is no longer cluster-admin-bound).
A straight git cherry-pick 3dcb1e2 onto openstack_stable currently conflicts (touches db/redis/rserve/web/web-background/worker-deploy.yaml and values.yaml, which have diverged significantly on this branch) — it needs a manual, targeted port rather than an automatic cherry-pick.
Proposed work
- Manually port from
feat/chart-security-hardening(commit3dcb1e2) ontoopenstack_stable:templates/service-account/workload-serviceaccounts.yaml(new)- Toggle-gated
templates/service-account/nfs-disconnect-rbac.yaml serviceAccountNamewiring ondb,redis,rserve,web,web-background,worker, and the pre-delete-hook Jobvalues.yamladditions:security.legacyDefaultServiceAccountClusterAdmin.enabled(defaultfalse) and per-workload SA name config- Relevant
aws/cluster_setup.mddocumentation
- Validate with
helm lint/helm templatein both toggle states. - Roll out per the documented phased plan: set the flag
truetemporarily if needed for a safe migration window, verify viakubectl get pods -n <ns> -o custom-columns=NAME:.metadata.name,SA:.spec.serviceAccountNamethat no workload actually relies on thedefaultSA, then set the flag tofalse. - On each live cluster, after the least-privilege Role/RoleBinding is confirmed working, explicitly
kubectl delete clusterrolebinding fabric8-rbacto fully remove the cluster-admin grant (do not just leave it orphaned/Helm-adopted).
Interim state (as of this issue)
On openstudio-server-03, the existing fabric8-rbac ClusterRoleBinding was re-labeled/annotated with Helm ownership metadata (app.kubernetes.io/managed-by=Helm, meta.helm.sh/release-name=openstudio-server, meta.helm.sh/release-namespace=openstudio-server) purely to unblock a routine helm upgrade that was failing on unrelated image-architecture and HPA issues. No permissions were changed — the binding's subjects/roleRef are untouched and it is still a live cluster-admin grant. This issue tracks the actual remediation.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Compare commit 3dcb1e2 with openstack_stable, focusing on templates/service-account/nfs-disconnect-rbac.yaml, the new workload-serviceaccounts.yaml, values.yaml, workload deployment files, and aws/cluster_setup.md. Start with helm lint and helm template in both toggle states, then verify the rendered ServiceAccounts, Role/RoleBinding, and absence of the legacy binding before following the documented cluster rollout checks.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- helm, kubernetes
- Domain
- devops, infrastructure, security
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 35/100