NatLabRockies / NatLabRockies/openstudio-server-helm

Remediate cluster-admin ClusterRoleBinding on default/default ServiceAccount (fabric8-rbac) — GuardDuty finding

Open
#98 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
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 ServiceAccount per workload (web, web-background, worker, rserve, db, redis, pre-delete-hook) via a new workload-serviceaccounts.yaml.
  • Replaces the legacy binding with a security.legacyDefaultServiceAccountClusterAdmin.enabled toggle in nfs-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-scoped Role/RoleBinding (nfs-client-cleanup) that only allows delete on the specific named web/web-background/rserve Deployments, bound to the dedicated pre-delete-hook ServiceAccount — not default/default.
  • Assigns serviceAccountName on 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 the default SA → set the flag to false → confirm kubectl get clusterrolebinding fabric8-rbac no 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

  1. Manually port from feat/chart-security-hardening (commit 3dcb1e2) onto openstack_stable:
    • templates/service-account/workload-serviceaccounts.yaml (new)
    • Toggle-gated templates/service-account/nfs-disconnect-rbac.yaml
    • serviceAccountName wiring on db, redis, rserve, web, web-background, worker, and the pre-delete-hook Job
    • values.yaml additions: security.legacyDefaultServiceAccountClusterAdmin.enabled (default false) and per-workload SA name config
    • Relevant aws/cluster_setup.md documentation
  2. Validate with helm lint/helm template in both toggle states.
  3. Roll out per the documented phased plan: set the flag true temporarily if needed for a safe migration window, verify via kubectl get pods -n <ns> -o custom-columns=NAME:.metadata.name,SA:.spec.serviceAccountName that no workload actually relies on the default SA, then set the flag to false.
  4. On each live cluster, after the least-privilege Role/RoleBinding is confirmed working, explicitly kubectl delete clusterrolebinding fabric8-rbac to 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

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.