💸Save up to $132K/month in CI costs!👉 Try Free
Skip to main content
Kubernetes Service Accounts: Guide
12 min read

Kubernetes Service Accounts: Guide

Optimize Your CI/CD Pipeline

Get instant insights into your CI/CD performance and costs. Reduce build times by up to 45% and save on infrastructure costs.

45% Faster Builds
60% Cost Reduction

Introduction

Kubernetes has evolved to be the cornerstone of modern cloud-native infrastructure. Amongst the plethora of different components, ServiceAccounts turn out to be one of the most critical objects in providing non-human identities for seamless interaction between workloads and the Kubernetes API.

This article goes in deep to explain what ServiceAccounts are, how they are created, the main use cases, and how they guarantee security within a cluster environment. It is very important for me to show how such objects are created and used, and optimized in normal day-to-day Kubernetes operations for driving robust identity management and secure communications.

Steps we will cover in this article:

Understanding Service Accounts

ServiceAccounts in a Kubernetes system are credentials or identifiers of an application running inside the cluster. ServiceAccounts enable Pods and system components to authenticate securely. Each ServiceAccount exists in a namespace, which provides a layer of organization. Such namespacing ensures that configurations can be light and portable across different environments, assuring smooth application deployments.

Unlike the user accounts, which deal with human users, ServiceAccounts are purely Kubernetes API objects. They are a means of authenticating Pods to the API server without revealing user-level credentials, hence more secure. For example, a Pod might need to access secrets. In this case, it would use an appropriate ServiceAccount so that the permissions are maintained through RBAC. This again shows the flexibility imposed by ServiceAccounts in how operations are carried out in Kubernetes-imposing permissions and identities within the cluster effectively.

Understanding Default Service Accounts

In Kubernetes, when creating a cluster, every namespace by default gets its own default ServiceAccount. That default ServiceAccount usually has very limited permissions-just enough for basic things like API discovery. If you don't specify a ServiceAccount on a Pod, that default one gets assigned, and therefore your Pod usually only gets permission to speak to other resources for minimal operations. Although the default ServiceAccount is a good starting point, relying on that introduces security risks.

For instance, if the Pod needs to access sensitive Secrets or perform certain privileged actions like listing resources spanning multiple namespaces, then the default set permissions will not be sufficient. Consequently, most of the time, this enforces the creation and use of custom-made ServiceAccounts with detailed permissions using RBAC. In this way, you keep the principle of least privilege; the Pods will only have the access they really need to operate. It also improves general security: should a workload become critical and block any potential over-permissions from the default account.

Key Use Cases of ServiceAccounts

ServiceAccounts in Kubernetes apply to many essential cases involving workloads and interacting with cluster resources. Key use cases include the following:

  1. API Communications: ServiceAccounts enable Pods to authenticate with the Kubernetes API. For instance, based on the initialization of a certain Pod, it may be necessary for it to pull some information on configuration from the API server. In that respect, a ServiceAccount provides such a Pod with the required credentials that ensure secure authentication without the exposure of human credentials.

  2. Cross-Namespace Access: In more advanced deployments, there might be a need for Pods in one namespace to access resources in another. This could be a case where, for example, a Pod in the namespace development has been granted permissions to view Jobs in the maintenance namespace. This is achieved by assigning a custom ServiceAccount that has those permissions defined through RBAC.

  3. External Service Authentication: ServiceAccounts can be utilized by Pods when the requirement to communicate with external services arises, such as cloud APIs. This allows them to establish trust relationships in a secure way, enabling the required API calls without compromising security.

  4. Private Image Registries: ServiceAccounts ease the process of authentication to private container registries. They can be configured with an imagePullSecret for securely pulling images. Thus, they support easy deployments and maintain access control.

  5. Integration with CI/CD Pipelines: Interaction with most external services, like CI/CD, depends on validation for triggering a deployment or running tests. ServiceAccounts can help manage this authentication towards the Kubernetes API safely and effectively. In general, ServiceAccounts are relied on for permission management, enhancing security, and facilitating interaction by Pods with various APIs or external services.

Granting Permissions to ServiceAccounts using RBAC

Managing permissions becomes crucial in any Kubernetes environment, where the designed ServiceAccounts work on the principle of least privilege. Kubernetes offers an effective way to manage permissions through the powerful Role-Based Access Control mechanism.

To grant permissions to a ServiceAccount, create a Role. A Role describes a set of permissions, such as get, list, or create, for certain resources within a namespace. Thus, for example, if you have a Pod which must read Secrets, you will create the Role with permission to get the secrets.

Once the Role has been defined, permissions are then enforced through RoleBinding. A RoleBinding associates a Role with the specific ServiceAccount in which this Role should grant permissions. For example, if your Pod is using a ServiceAccount named my-serviceaccount, and you want it to have access to Secrets, you'll create a RoleBinding in the same namespace. This binding gives the my-serviceaccount the permissions outlined in the previously created Role.

In summary, the flow goes as follows:

  1. Create a Role: Grant permissions for carrying out specific actions against resources in the namespace.
  2. Create a RoleBinding: Bind the Role to the desired ServiceAccount.
  3. Create the Pod: The Pod should, with this ServiceAccount, inherit these permissions.

This organized method lets you manage permissions easily and secure your Kubernetes cluster.

Ensuring Secure Cross-Namespace Access

Kubernetes uses RBAC to enable security for ServiceAccounts to perform on resources in another namespace securely. This is important in complex environments where workloads running in different namespaces need to collaborate without compromising security.

We'll use a simple example of having two namespaces here: dev and maintenance. In maintenance, there are some Jobs that may need to be monitored or managed by Pods running in the dev namespace. By default, this is not allowed to happen; the ServiceAccounts residing in the dev namespace have built-in limitations when trying to access resources of maintenance. To allow this you will create a Role in the maintenance namespace. This Role can be given permission to get and list Job resources. For example:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: maintenance
name: job-reader
rules:
- apiGroups: ["batch"] # API group
resources: ["jobs"] # Resources this Role manages
verbs: ["get", "list"] # Define allowed actions

Having the Role assigned, you will now create a RoleBinding that will link it to the ServiceAccount in the dev namespace. The RoleBinding effectively grants the permissions of the Role to the targeted ServiceAccount. Here's how that should look:

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: dev-job-reader
namespace: maintenance
subjects:
- kind: ServiceAccount # Type of subject
name: my-serviceaccount # Name of the ServiceAccount in the namespace dev
namespace: dev # The namespace of the ServiceAccount
roleRef:
kind: Role
name: job-reader # Name of the role to be bound to
apiGroup: rbac.authorization.k8s.io

With this setup, ServiceAccount my-serviceaccount in the namespace dev would have permission to get and list Jobs in the namespace maintenance. This setting is one good example of how RBAC can handle cross-namespace access securely. Permission is granted only to those that are required to be able to perform integrations among different app sides.

Customizing ServiceAccounts in Pod Specifications

In general, one way to further lock down the workloads of an application within Kubernetes is by specifying ServiceAccounts at the time a Pod is specified. Probably, the most important field hereby involved is spec.serviceAccountName; this is a reference to the ServiceAccount that should be used by the Pod. Such customization gives full control over what the Pod can and cannot access, ensuring only necessary permissions are accorded.

If the spec.serviceAccountName is specified in the specification of a Pod, Kubernetes will automatically attach the corresponding credentials of the ServiceAccount to the Pod. With this, for example, a Pod with the setting spec.serviceAccountName: my-custom-sa will have permissions specified under the my-custom-sa ServiceAccount.

This means a few important things by not setting this field. Namely: if spec.serviceAccountName is omitted, Kubernetes will use that namespace's default ServiceAccount. While this makes many things easier, this often gives the Pod way more permissions than are desired, particularly if those permissions are not properly constrained with RBAC. This can be a security risk by leaking access to sensitive data and resources.

Kubernetes will handle the generation of a token based on whether or not a ServiceAccount is specified. Thus, if one is specified, it would be of a short-lived, auto-rotating token via the TokenRequest API mounted into the Pod with such a specification.

This, compared to when the default ServiceAccount is utilized-and no specific account is set-a long-lived, static token provided by the system does not automatically rotate and presents certain security risks. Besides the functionality aspect, therefore, customization of ServiceAccounts in Pod specifications is actually a significant security concern. Correctly defined ServiceAccounts support Pods adhering to the principle of minimum privilege, hence minding potential attack vectors and keeping the environment secure.

Advanced Authentication with ServiceAccount Tokens

ServiceAccounts in Kubernetes have been using JSON Web Tokens-also known as JWTs-for authentication. This is a very strong and secure mechanism that allows Pods to communicate with the Kubernetes API. There are two kinds of tokens providing different functions, which introduce different security concerns: short-lived tokens and legacy tokens.

Short-Lived Tokens

Since Kubernetes 1.22, the tokens for Pods are created as short-lived through the TokenRequest API. The short-lived token rotates and expires after some time, reducing the window where the token could be compromised. They have the advantage of being much more secure since they essentially become ephemeral due to the automatic rotation and reduce the risk of token compromise.

Because a ServiceAccount is assigned to a Pod, Kubernetes maintains an implicit lifetime in regard to such tokens. It mounts the ServiceAccount credentials as projected volume within the Pod and hence accessible by the application running inside the Pod at its discretion.

Legacy Tokens

In contrast to that, the legacy tokens were generated before the TokenRequest API was available. These tokens are long-lived and provide continuous access without automatic expiration or rotation thus leaving them vulnerable if exposed either inadvertently or otherwise. Prior to v1.24, legacy tokens were generated by default as Secrets, which made them accessible to all processes running within a Pod; however this has been deprecated to a large extent due to the security risks involved.

Using Tokens Externally

In general, long-lived bearer tokens should be avoided when integrating into external systems. When possible, the Kubernetes project recommends getting short-lived tokens with the TokenRequest API offered. This further reduces security vulnerabilities in that the tokens become time-bound and rotated, limiting exposure to a breach even when a token is compromised.

Guidance on Token Usage

Given the differences of token types:

  1. Use short-lived tokens to get inside access to the Kubernetes API for internal and external use, while benefiting from additional security.

  2. Do not rely on legacy tokens; if they are in use, instead migrate to the short-lived method via TokenRequest API.

  3. Validate tokens against expected audiences to make tokens secure and for specific uses only. In other words, good housekeeping of ServiceAccount tokens—favoring short-lived tokens over their legacy variants—keeps your Kubernetes applications secure while still allowing them to operate good authentication mechanisms.

Securing ServiceAccount Credentials

ServiceAccount credentials are actually very important for maintaining security within a Kubernetes cluster. Poor handling could result in significant vulnerabilities. Authentication is based on JSON Web Tokens, so careful handling is highly required to minimize risks.

The Risks of Static Tokens

Static tokens, if issued as Secrets in earlier Kubernetes versions, can pose a security risk of the highest order. These tokens never expire and are therefore vulnerable to unauthorized use if ever exposed. Any malicious actor who gains access to the static tokens could hijack the ServiceAccount and thus have inappropriate access to sensitive cluster resources.

Best Practices with TokenRequest API

To properly lock down the ServiceAccount credentials, it is highly recommended to utilize the TokenRequest API in order to generate short-lived tokens. These tokens would be dynamically generated and automatically rotated, hence narrowing the validity window for any one token. As a matter of fact, for a Pod that has been assigned a ServiceAccount via the TokenRequest API, its token will only last for some hours, while transparently refreshing, hence requiring no manual intervention.

Security Annotations

Also, Kubernetes provides a set of annotations like kubernetes.io/enforce-mountable-secrets, which can be attached to the ServiceAccounts as an added security enhancement. Having this annotation in place would suggest that the secret of the respective ServiceAccount could get mounted only on types of resources specified, therefore increasing the tightness of control over sensitive pieces of information.

Example Manifest

Here is a sample manifest which highlights how it can allocate security annotation to the ServiceAccount:

apiVersion: v1
kind: ServiceAccount
metadata:
name: my-secure-sa
namespace: my-namespace

annotations:
kubernetes.io/enforce-mountable-secrets: "true"

This would ensure that any secret mounted by this ServiceAccount is bound by certain restrictions to really nail down the security posture of this Kubernetes cluster. These best practices will literally minimize the risk associated with credentials of ServiceAccount and give one a more security-focused Kubernetes world.

Conclusion

Service accounts are one of the most important tools for securely handling identities and permissions within Kubernetes. Mastery of configuration and use cases alone grants access to all the potential of Kubernetes while still strong in security. Follow best practices for token management and use RBAC to provide granular access control, and DevOps teams can rest assured that their Kubernetes deployments will be both efficient and secure. Thoughtful implementation of ServiceAccounts is not merely a technical requirement but rather a strategic advantage when it comes to cloud-native infrastructure.