apiVersion: v1
kind: ServiceAccount
metadata:
name: deployer
namespace: ci
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: deployer
namespace: apps
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
- apiGroups: [""]
resources: ["services", "configmaps"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
- apiGroups: ["networking.k8s.io"]
resources: ["ingresses"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: deployer
namespace: apps
subjects:
- kind: ServiceAccount
name: deployer
namespace: ci
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: deployer
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: deployer-view
namespace: monitoring
subjects:
- kind: ServiceAccount
name: deployer
namespace: ci
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: view
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: node-reader
rules:
- apiGroups: [""]
resources: ["nodes"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: deployer-node-reader
subjects:
- kind: ServiceAccount
name: deployer
namespace: ci
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: node-readerKubernetes graph
RBAC: how a ServiceAccount gets its permissions
A CI deployer's permissions traced through RBAC: ServiceAccount, projected token, Role and RoleBinding, a ClusterRole reused in one namespace, and a ClusterRoleBinding for cluster-wide reads.
RBAC permissions are always additive. A subject starts with nothing, and each binding adds the rules of one role. Nothing can deny. To answer "can this ServiceAccount do X?", you find every binding that names it and check their roles.
Where the binding lives decides the scope, not the role. A RoleBinding grants its role's rules in its own namespace only, even when the role is a ClusterRole. Only a ClusterRoleBinding grants across the whole cluster.
Interactive graph
Click a resource to see what it does, or follow the setup step by step.
How it works
All 10 resources and what they do
- Pod ci-runner-8x2kqv1
- A CI job Pod that runs
kubectl applyagainst theappsnamespace.serviceAccountName: deployerdecides which identity it gets. - ServiceAccount deployerv1
- The identity. Its full username is
system:serviceaccount:ci:deployer, and bindings in any namespace can name it. On its own it can do almost nothing. - Projected token kube-api-access-…
- The kubelet asks the API server for a short-lived token for
deployerand mounts it at/var/run/secrets/kubernetes.io/serviceaccount/token. It is bound to this Pod, expires after an hour by default and is refreshed before that. No Secret is involved. - API server kube-apiserver
- Authenticates the token, then asks the RBAC authorizer whether
system:serviceaccount:ci:deployermay do this verb on this resource in this namespace. If no binding allows it, the answer is 403 Forbidden. - Role deployerrbac.authorization.k8s.io/v1
- Rules for deploying in
apps: manage Deployments, Services, ConfigMaps and Ingresses. It leaves out Secrets on purpose, becausegeton Secrets would expose every credential in the namespace. - RoleBinding deployerrbac.authorization.k8s.io/v1
- Gives the
deployerRole to the ServiceAccount from thecinamespace. The binding lives inapps, so that's where the rules apply. - ClusterRole viewrbac.authorization.k8s.io/v1
- Built into every cluster. Read access to most namespaced objects, but not Secrets. It's an aggregated role, so CRDs can add their own rules to it with the label
rbac.authorization.k8s.io/aggregate-to-view: "true". - RoleBinding deployer-viewrbac.authorization.k8s.io/v1
- Reuses the
viewClusterRole, but as a RoleBinding inmonitoring, so the deployer can read there and nowhere else. This is how you avoid writing the same Role in every namespace. - ClusterRole node-readerrbac.authorization.k8s.io/v1
- Read access to Nodes. Nodes are cluster-scoped, so only a ClusterRole can grant it, and only a ClusterRoleBinding makes it take effect.
- ClusterRoleBinding deployer-node-readerrbac.authorization.k8s.io/v1
- Grants
node-readeracross the whole cluster. The CI job uses it to wait for capacity before a rollout.
Common mistakes
kubectl auth can-i create deployments -n apps --as=system:serviceaccount:ci:deployertells you whether a request would pass, andkubectl auth can-i --listwith the same flags shows everything the account can do.roleRefcan't be changed after a binding is created. To point a binding at another role, delete it and create it again.listandwatchon Secrets return their contents, not just their names, so they give the same access asget.- Being allowed to create Pods in a namespace means being able to run as any ServiceAccount there, and so to use its permissions. Treat
create podsas access to every ServiceAccount in the namespace. - Set
automountServiceAccountToken: falseon Pods that never call the API. A token that isn't mounted can't be stolen.
The manifests you write
Everything above that is marked "You write it", in one file. The rest comes from add-ons and controllers.
Keep going
More graphs
- Deployment, Service and Ingress: how a request reaches your Pods
- Gateway API: GatewayClass, Gateway and HTTPRoute with a canary split
- PVC, StorageClass and CSI: how a cloud disk ends up in your Pod
- cert-manager with Let's Encrypt: Certificate, CertificateRequest, Order and Challenge
- A private CA with cert-manager and trust-manager
- Istio traffic management: Gateway, VirtualService, DestinationRule and policies
- Flux dependencies: Git and OCI sources, Kustomizations and HelmReleases
- Argo CD: ApplicationSet, AppProject and the Applications it generates
- Inside an OCI image: tag, index, manifests, config and layers
- OCI artifacts: Helm charts, Flux artifacts and image volumes