Service Accounts
What is a Service Account?
- In Kubernetes, there are two main types of users: Human Users and Service Accounts.1
- Human Users are typically managed externally (e.g., via certificates, LDAP, or integration with identity providers like Google Accounts or AWS IAM).
- Service Accounts are designed for processes running in pods that need to access the Kubernetes API, not for human administrators. They provide an identity for workloads.
- Service Accounts are namespaced resources within Kubernetes. A default Service Account is automatically created in every namespace.
What is a Service Account Token?
- A Service Account Token is a credential (specifically, a type of JSON Web Token - JWT) that a process inside a pod uses to authenticate its identity to the Kubernetes API server.
- When a pod runs, it is associated with a Service Account.2 By default, this is the
defaultService Account in its namespace, but you can specify a different one in the pod’s specification (serviceAccountName). - If a pod is configured to automount its Service Account token (which is the default behavior unless disabled), Kubernetes makes the Service Account’s token available to the processes inside the pod, typically mounted as a file in the pod’s filesystem (by default, at
/var/run/secrets/kubernetes.io/serviceaccount/token).
How do they work?
- A pod is created and associated with a Service Account.
- Kubernetes ensures the Service Account’s token is available inside the pod’s filesystem (either automatically mounted or via a projected volume).3
- The application running inside the pod reads the token from the designated file path.4
- When the application makes a request to the Kubernetes API server (e.g., to list pods, create a deployment, etc.), it includes this token in the
Authorizationheader, typically as a Bearer Token (Authorization: Bearer <token>).5 - The Kubernetes API server receives the request and the token. It verifies the token’s signature (using a signing key configured on the API server) and validates its contents (claims) to identify the Service Account and potentially check its audience or expiry.
- Based on the authenticated Service Account identity, Kubernetes’s Authorization layer (commonly RBAC - Role-Based Access Control) determines if the Service Account has the necessary permissions to perform the requested action.
Types of Service Account Tokens (Evolution):
Kubernetes has evolved how Service Account tokens are managed to improve security:
- Legacy/Secret-Based Tokens (Deprecated for new clusters):
- Historically, a default, non-expiring token was automatically created as a
Secretobject for every Service Account. - These tokens were essentially static secrets that didn’t expire unless the associated Secret was manually deleted.
- This approach had security drawbacks because a compromised token could be used indefinitely.6
- While still present for backward compatibility in many clusters, creating these automatically for new Service Accounts is being phased out.
- Historically, a default, non-expiring token was automatically created as a
- Bound Service Account Tokens (Recommended):
- This is the modern, more secure approach.
- These tokens have a configurable time-bound lifespan (they expire).7
- They can be audience-bound, meaning they are intended for a specific recipient or service (e.g., the Kubernetes API server, or a specific external service configured to accept them).8 This limits their usability if leaked.
- They are typically delivered to pods using a projected volume (
projectedtype in the Pod spec), rather than being stored as static Secrets.9 This allows the kubelet to fetch and rotate the token periodically within the pod’s volume. - Clusters are moving towards defaulting to bound tokens and discouraging the use of the legacy Secret-based tokens.
Key Considerations and Best Practices:
- RBAC is Essential: Service Accounts themselves have no permissions by default. You must grant permissions to a Service Account using Kubernetes RBAC (Roles and RoleBindings or ClusterRoles and ClusterRoleBindings) for it to be able to perform any actions on the API. Grant only the minimum permissions required (Principle of Least Privilege).
- Disable Automounting When Not Needed: For pods that do not need to communicate with the Kubernetes API, set
automountServiceAccountToken: falsein the pod spec or the Service Account itself.10 This reduces the attack surface. - Use Bound Tokens and Projected Volumes: Prefer the modern bound tokens delivered via projected volumes for better security due to their limited lifespan and audience binding.
- Token Signing Key Rotation: The private key used by the API server to sign Service Account tokens is a critical credential. Kubernetes supports rotating this key without invalidating existing tokens immediately, which is a good security practice.
- Creating a Service Account
-
Service Accounts can be created using
kubectlor directly through a YAML manifest. -
Example using
kubectl:kubectl create serviceaccount my-service-account -
Example using YAML:
apiVersion: v1 kind: ServiceAccount metadata: name: my-service-account
-
- Associating a Service Account with a Pod
-
Specify the Service Account in the Pod specification.
-
Example:
apiVersion: v1 kind: Pod metadata: name: my-pod spec: serviceAccountName: my-service-account containers: - name: my-container image: my-image
-
- Service Account Tokens
- When a Pod is created, Kubernetes automatically mounts a Service Account token into the Pod at
/var/run/secrets/kubernetes.io/serviceaccount/token. - This token can be used to authenticate with the Kubernetes API.
- When a Pod is created, Kubernetes automatically mounts a Service Account token into the Pod at
Advanced Topics
- Role-Based Access Control (RBAC)
-
RBAC is used to manage permissions for Service Accounts.
-
Example of creating a Role and RoleBinding:
apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: namespace: default name: pod-reader rules: - apiGroups: [""] resources: ["pods"] verbs: ["get", "watch", "list"]apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: read-pods namespace: default subjects: - kind: ServiceAccount name: my-service-account namespace: default roleRef: kind: Role name: pod-reader apiGroup: rbac.authorization.k8s.io
-
- ClusterRoles and ClusterRoleBindings
-
For permissions that span across the entire cluster, use ClusterRoles and ClusterRoleBindings.
-
Example:
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: cluster-admin-role rules: - apiGroups: [""] resources: ["pods"] verbs: ["get", "list", "watch", "create", "delete"]apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: cluster-admin-binding subjects: - kind: ServiceAccount name: my-service-account namespace: default roleRef: kind: ClusterRole name: cluster-admin-role apiGroup: rbac.authorization.k8s.io
-
- Service Account Automation
- Tools like Helm and Kustomize can be used to manage Service Accounts and their bindings as part of your deployment configurations.
- Service Account Security
- Limit the permissions of Service Accounts to the minimum required.
- Rotate Service Account tokens periodically.
- Monitor and audit the usage of Service Accounts.
Example: Secure Application with Service Account
-
Create a Service Account
apiVersion: v1 kind: ServiceAccount metadata: name: secure-app-sa -
Create a Role with limited permissions
apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: secure-app-role namespace: default rules: - apiGroups: [""] resources: ["pods"] verbs: ["get", "list"] -
Bind the Role to the Service Account
apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: secure-app-binding namespace: default subjects: - kind: ServiceAccount name: secure-app-sa namespace: default roleRef: kind: Role name: secure-app-role apiGroup: rbac.authorization.k8s.io -
Deploy a Pod with the Service Account
apiVersion: v1 kind: Pod metadata: name: secure-app-pod spec: serviceAccountName: secure-app-sa containers: - name: secure-app-container image: my-secure-app-image
To access custom resources
To allow a Service Account to list custom resources, such as Crossplane resources, you need to create a Role or ClusterRole with the appropriate permissions and bind it to the Service Account using a RoleBinding or ClusterRoleBinding.
Here’s a step-by-step guide to achieve this:
Step 1: Identify the Custom Resource Definitions (CRDs)
First, identify the CRDs you want the Service Account to list. For Crossplane, you might have CRDs like compositions, compositeresourcedefinitions, etc.
You can list the available CRDs in your cluster with:
kubectl get crdsStep 2: Create a Role or ClusterRole
Create a Role or ClusterRole that grants the Service Account permission to list the desired custom resources.
Role (Namespace-Scoped)
If the custom resources are namespace-scoped, create a Role:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default # Change to the appropriate namespace
name: crossplane-resource-reader
rules:
- apiGroups: ["apiextensions.crossplane.io"] # Change to the appropriate API group
resources: ["compositions", "compositeresourcedefinitions"] # List the specific custom resources
verbs: ["get", "list", "watch"]ClusterRole (Cluster-Scoped)
If the custom resources are cluster-scoped or if you want to grant permissions across all namespaces, create a ClusterRole:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: crossplane-resource-reader
rules:
- apiGroups: ["apiextensions.crossplane.io"] # Change to the appropriate API group
resources: ["compositions", "compositeresourcedefinitions"] # List the specific custom resources
verbs: ["get", "list", "watch"]Step 3: Create a RoleBinding or ClusterRoleBinding
Bind the Role or ClusterRole to the Service Account.
RoleBinding (for Role)
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: crossplane-resource-binding
namespace: default # Change to the appropriate namespace
subjects:
- kind: ServiceAccount
name: my-service-account # Name of your Service Account
namespace: default # Namespace of your Service Account
roleRef:
kind: Role
name: crossplane-resource-reader
apiGroup: rbac.authorization.k8s.ioClusterRoleBinding (for ClusterRole)
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: crossplane-resource-binding
subjects:
- kind: ServiceAccount
name: my-service-account # Name of your Service Account
namespace: default # Namespace of your Service Account
roleRef:
kind: ClusterRole
name: crossplane-resource-reader
apiGroup: rbac.authorization.k8s.ioStep 4: Apply the YAML Manifests
Save the above YAML configurations to files (e.g., role.yaml, rolebinding.yaml) and apply them using kubectl:
kubectl apply -f role.yaml
kubectl apply -f rolebinding.yamlExample
Here is a complete example:
Service Account
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-service-account
namespace: defaultClusterRole
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: crossplane-resource-reader
rules:
- apiGroups: ["apiextensions.crossplane.io"]
resources: ["compositions", "compositeresourcedefinitions"]
verbs: ["get", "list", "watch"]ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: crossplane-resource-binding
subjects:
- kind: ServiceAccount
name: my-service-account
namespace: default
roleRef:
kind: ClusterRole
name: crossplane-resource-reader
apiGroup: rbac.authorization.k8s.ioApply Manifests
kubectl apply -f serviceaccount.yaml
kubectl apply -f clusterrole.yaml
kubectl apply -f clusterrolebinding.yaml