Harthelm Docs

Permissions

What each Harthelm component may do in the cluster.

Last updated 2026-10-19

To install

You create everything in the release namespace, and cluster-wide a ClusterRole, ClusterRoleBinding, ValidatingAdmissionPolicy and ValidatingAdmissionPolicyBinding (plus whatever extraObjects holds). Granting a ClusterRole requires holding its permissions, so in practice this is cluster admin.

With Argo CD, the AppProject must allow these cluster-scoped kinds and the release namespace as a destination.

What Harthelm gets

With rbac.create on (the default):

ServiceAccountPermissions
core (ClusterRole)Namespaces: create and manage (the admission guard limits this to rg-*). ConfigMaps, Secrets, Services, PVCs, ResourceQuotas, LimitRanges, Deployments and their scale, Jobs, NetworkPolicies, and with rbac.cnpg CloudNativePG Clusters: full. Pods, pod logs, nodes, PersistentVolumes, EndpointSlices, StorageClasses: read. tokenreviews: create. Ingresses, HTTPRoutes and Certificates: read. Extra kinds from rbac.extraClusterRules.
core (Role in the release namespace)Ingresses or HTTPRoutes and Certificates (custom domains), the backup CronJob (read, for dumps on demand), ReplicaSets (read).
routerEndpointSlices: read. Nothing else, no writes.
preflightget of exactly the objects the values name, list of StorageClasses.
workspace podsNo Kubernetes API token at all.

RBAC cannot narrow a ClusterRole to a name prefix or a label, so core can read Secrets and ConfigMaps in every namespace. Its caches only watch objects it labelled, and only core's pod holds its token.

Since 1.4.0 core also runs the doctor's write probe as short Jobs harthelm-write-probe-… in the release namespace, with the rights above (The write probe).

Cilium

On Cilium, core also renders a CiliumClusterwideNetworkPolicy per team namespace as a backstop. Give core the kind:

values.yaml
rbac:
  extraClusterRules:
    - apiGroups: [cilium.io]
      resources: [ciliumclusterwidenetworkpolicies]
      verbs: [get, list, watch, create, update, patch, delete]

Without the rule core leaves the backstop out and relies on the NetworkPolicies alone.

The admission guard

A ValidatingAdmissionPolicy bound to core's ServiceAccount (security.admissionGuard.enabled, on by default) limits what core may write:

  • namespaces only when they are named rg-* and carry harthelm.dev/managed=true. An update needs the label to be there already, so core cannot adopt a namespace someone else created;
  • namespaced objects only in such namespaces and in the release namespace;
  • other cluster-scoped objects only with harthelm.dev/managed=true, before and after the change.

The policy fails closed and applies to nobody but core.

Pod security

The platform pods run as any non-root UID, drop all capabilities, use a read-only root filesystem and set no runAsUser, so the release namespace can enforce Pod Security restricted:

Terminal
kubectl create namespace harthelm-system
kubectl label namespace harthelm-system pod-security.kubernetes.io/enforce=restricted

Core labels the team namespaces itself: restricted, or baseline for a team that a cluster admin allows to set runAsUser for container services. Workspace pods run as security.defaultUid (default 1001) with group 0; that UID cannot change while workspace volumes exist, see Storage.

On this page