Belajar Kubernetes - Episode 32 - Pengenalan dan Penjelasan ServiceAccount

Belajar Kubernetes - Episode 32 - Pengenalan dan Penjelasan ServiceAccount

Di episode ini kita akan coba bahas Kubernetes ServiceAccount untuk Pod identity dan authentication. Kita akan mempelajari bagaimana ServiceAccount work, cara create dan use, token management, dan best practice untuk secure Pod authentication.

Arman Dwi Pangestu
Arman Dwi PangestuApril 7, 2026
0 views
9 min read

Pendahuluan

Catatan

Untuk kalian yang ingin membaca episode sebelumnya, bisa click thumbnail episode 31 di bawah ini

Episode 31Episode 31

Di episode sebelumnya kita sudah belajar tentang Vertical Pod Autoscaler (VPA) untuk automatic resource sizing. Selanjutnya di episode 32 kali ini, kita akan coba bahas ServiceAccount, yang provide identity untuk Pod dan enable mereka untuk authenticate dengan Kubernetes API server.

Catatan: Disini saya akan menggunakan Kubernetes Cluster yang di install melalui K3s.

Seperti user need account untuk access system, Pod need ServiceAccount untuk interact dengan Kubernetes API. ServiceAccount enable secure, controlled access ke cluster resource, allowing application untuk query cluster state, create resource, atau perform operation based on assigned permission.

Apa Itu ServiceAccount?

ServiceAccount adalah Kubernetes resource yang provide identity untuk process running di Pod. Dia enable Pod untuk authenticate dengan Kubernetes API server dan perform authorized operation.

Bayangkan ServiceAccount seperti employee badge - dia identify siapa kalian (authentication) dan determine pintu apa yang bisa kalian buka (authorization via RBAC). Setiap Pod dapat badge (ServiceAccount) yang grant specific access level.

Karakteristik kunci ServiceAccount:

  • Pod identity - Provide identity untuk application di Pod
  • API authentication - Enable Pod authenticate dengan API server
  • Token-based - Gunakan JWT token untuk authentication
  • Namespace-scoped - Belong ke specific namespace
  • RBAC integration - Work dengan Role-Based Access Control
  • Automatic mounting - Token automatically mounted di Pod
  • Default ServiceAccount - Setiap namespace punya default ServiceAccount
  • Custom ServiceAccount - Create specific account untuk different need

ServiceAccount vs User Account

Memahami key difference:

AspekServiceAccountUser Account
PurposeUntuk Pod/applicationUntuk human
ScopeNamespace-scopedCluster-wide
ManagementManaged oleh KubernetesExternal (LDAP, OIDC, etc.)
TokenStored di SecretExternal auth system
Creationkubectl createExternal identity provider
Use CaseApplication accessHuman access
LifecycleTied ke namespaceIndependent

Kenapa Gunakan ServiceAccount?

ServiceAccount solve critical authentication dan authorization challenge:

  • Secure API access - Controlled access ke Kubernetes API
  • Principle of least privilege - Grant hanya necessary permission
  • Application identity - Setiap app bisa punya unique identity
  • Audit trail - Track ServiceAccount mana yang perform action
  • Token rotation - Refresh credential tanpa redeployment
  • Namespace isolation - Limit access ke specific namespace
  • RBAC enforcement - Fine-grained permission control
  • Automation - Enable CI/CD dan automation tool

Tanpa ServiceAccount, semua Pod akan gunakan same identity, making it impossible untuk implement proper access control atau audit siapa yang did what.

Bagaimana ServiceAccount Bekerja

Default ServiceAccount

Setiap namespace automatically dapat default ServiceAccount:

Kubernetesbash
kubectl get serviceaccount

Output:

Kubernetesbash
NAME      SECRETS   AGE
default   0         10d

Setiap Pod automatically gunakan default ServiceAccount kecuali specified otherwise.

ServiceAccount Token

ServiceAccount token adalah JWT (JSON Web Token) yang authenticate Pod:

Kubernetes 1.24+:

  • Token time-bound (default 1 jam)
  • Automatically rotated
  • Projected ke Pod via TokenRequest API

Sebelum Kubernetes 1.24:

  • Token stored di Secret
  • Never expire
  • Manually managed

Token Mounting

Token automatically mounted di Pod di:

plaintext
/var/run/secrets/kubernetes.io/serviceaccount/

Contain:

  • token - JWT authentication token
  • ca.crt - CA certificate untuk API server
  • namespace - Current namespace

Membuat ServiceAccount

Basic ServiceAccount

Kubernetesmy-serviceaccount.yml
apiVersion: v1
kind: ServiceAccount
metadata:
    name: my-app-sa
    namespace: default

Create:

Kubernetesbash
kubectl apply -f my-serviceaccount.yml

Verify:

Kubernetesbash
kubectl get serviceaccount my-app-sa

ServiceAccount dengan Annotation

Kubernetesannotated-sa.yml
apiVersion: v1
kind: ServiceAccount
metadata:
    name: my-app-sa
    namespace: default
    annotations:
        description: "ServiceAccount untuk my application"
        owner: "platform-team"

ServiceAccount dengan Image Pull Secret

Untuk pulling image dari private registry:

Kubernetessa-with-imagepull.yml
apiVersion: v1
kind: ServiceAccount
metadata:
    name: my-app-sa
imagePullSecrets:
    - name: docker-registry-secret

Menggunakan ServiceAccount di Pod

Specify ServiceAccount di Pod

Kubernetespod-with-sa.yml
apiVersion: v1
kind: Pod
metadata:
    name: my-app
spec:
    serviceAccountName: my-app-sa
    containers:
        - name: app
          image: nginx:1.25

Specify ServiceAccount di Deployment

Kubernetesdeployment-with-sa.yml
apiVersion: apps/v1
kind: Deployment
metadata:
    name: my-app
spec:
    replicas: 3
    selector:
        matchLabels:
            app: my-app
    template:
        metadata:
            labels:
                app: my-app
        spec:
            serviceAccountName: my-app-sa
            containers:
                - name: app
                  image: nginx:1.25

Disable Automatic Token Mounting

Prevent token dari being mounted:

Kubernetespod-no-token.yml
apiVersion: v1
kind: Pod
metadata:
    name: my-app
spec:
    serviceAccountName: my-app-sa
    automountServiceAccountToken: false
    containers:
        - name: app
          image: nginx:1.25

ServiceAccount dengan RBAC

ServiceAccount work dengan RBAC untuk control permission.

Create ServiceAccount

Kubernetesapp-serviceaccount.yml
apiVersion: v1
kind: ServiceAccount
metadata:
    name: app-reader
    namespace: default

Create Role

Kubernetespod-reader-role.yml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
    name: pod-reader
    namespace: default
rules:
    - apiGroups: [""]
      resources: ["pods"]
      verbs: ["get", "list", "watch"]

Create RoleBinding

Kubernetespod-reader-binding.yml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
    name: read-pods
    namespace: default
subjects:
    - kind: ServiceAccount
      name: app-reader
      namespace: default
roleRef:
    kind: Role
    name: pod-reader
    apiGroup: rbac.authorization.k8s.io

Apply semua:

Kubernetesbash
kubectl apply -f app-serviceaccount.yml
kubectl apply -f pod-reader-role.yml
kubectl apply -f pod-reader-binding.yml

Complete Example

Kubernetescomplete-rbac-example.yml
# ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
    name: app-reader
    namespace: default
---
# Role
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
    name: pod-reader
    namespace: default
rules:
    - apiGroups: [""]
      resources: ["pods"]
      verbs: ["get", "list", "watch"]
---
# RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
    name: read-pods
    namespace: default
subjects:
    - kind: ServiceAccount
      name: app-reader
      namespace: default
roleRef:
    kind: Role
    name: pod-reader
    apiGroup: rbac.authorization.k8s.io
---
# Pod menggunakan ServiceAccount
apiVersion: v1
kind: Pod
metadata:
    name: app-pod
spec:
    serviceAccountName: app-reader
    containers:
        - name: app
          image: nginx:1.25

Accessing Kubernetes API dari Pod

Menggunakan ServiceAccount Token

Inside Pod, access API menggunakan mounted token:

Kubernetesbash
# Get token
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
 
# Get namespace
NAMESPACE=$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace)
 
# Get CA certificate
CACERT=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
 
# Call API
curl --cacert $CACERT \
     -H "Authorization: Bearer $TOKEN" \
     https://kubernetes.default.svc/api/v1/namespaces/$NAMESPACE/pods

Menggunakan kubectl di Pod

Kuberneteskubectl-pod.yml
apiVersion: v1
kind: Pod
metadata:
    name: kubectl-pod
spec:
    serviceAccountName: app-reader
    containers:
        - name: kubectl
          image: bitnami/kubectl:latest
          command: ["sleep", "3600"]

Inside Pod:

Kubernetesbash
kubectl exec -it kubectl-pod -- /bin/bash
 
# Inside Pod
kubectl get pods
kubectl get services

Menggunakan Client Library

Python example:

python
from kubernetes import client, config
 
# Load in-cluster config (gunakan ServiceAccount)
config.load_incluster_config()
 
# Create API client
v1 = client.CoreV1Api()
 
# List pod
pods = v1.list_namespaced_pod(namespace="default")
for pod in pods.items:
    print(f"Pod: {pod.metadata.name}")

Token Management

Membuat Long-Lived Token (Pre-1.24)

Kubernetessa-token-secret.yml
apiVersion: v1
kind: Secret
metadata:
    name: my-app-sa-token
    annotations:
        kubernetes.io/service-account.name: my-app-sa
type: kubernetes.io/service-account-token

Get token:

Kubernetesbash
kubectl get secret my-app-sa-token -o jsonpath="{.data.token}" | base64 --decode

Membuat Token (1.24+)

Create short-lived token:

Kubernetesbash
kubectl create token my-app-sa

Create token dengan custom duration:

Kubernetesbash
kubectl create token my-app-sa --duration=24h

Token Expiration

Check token expiration:

Kubernetesbash
kubectl create token my-app-sa | jwt decode -

Contoh Praktis

Contoh 1: Read-Only Application

Kubernetesreadonly-app.yml
# ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
    name: readonly-app
    namespace: production
---
# Role - Read pod dan service
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
    name: readonly-role
    namespace: production
rules:
    - apiGroups: [""]
      resources: ["pods", "services"]
      verbs: ["get", "list", "watch"]
---
# RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
    name: readonly-binding
    namespace: production
subjects:
    - kind: ServiceAccount
      name: readonly-app
      namespace: production
roleRef:
    kind: Role
    name: readonly-role
    apiGroup: rbac.authorization.k8s.io
---
# Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
    name: readonly-app
    namespace: production
spec:
    replicas: 2
    selector:
        matchLabels:
            app: readonly-app
    template:
        metadata:
            labels:
                app: readonly-app
        spec:
            serviceAccountName: readonly-app
            containers:
                - name: app
                  image: myapp:latest

Contoh 2: CI/CD ServiceAccount

Kubernetescicd-serviceaccount.yml
# ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
    name: cicd-deployer
    namespace: default
---
# ClusterRole - Deploy application
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
    name: deployer-role
rules:
    - apiGroups: ["apps"]
      resources: ["deployments", "replicasets"]
      verbs: ["get", "list", "create", "update", "patch", "delete"]
    - apiGroups: [""]
      resources: ["services", "configmaps", "secrets"]
      verbs: ["get", "list", "create", "update", "patch", "delete"]
---
# ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
    name: cicd-deployer-binding
subjects:
    - kind: ServiceAccount
      name: cicd-deployer
      namespace: default
roleRef:
    kind: ClusterRole
    name: deployer-role
    apiGroup: rbac.authorization.k8s.io

Contoh 3: Monitoring Application

Kubernetesmonitoring-app.yml
# ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
    name: monitoring-app
    namespace: monitoring
---
# ClusterRole - Read metric
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
    name: metrics-reader
rules:
    - apiGroups: [""]
      resources: ["nodes", "pods", "services"]
      verbs: ["get", "list", "watch"]
    - apiGroups: ["metrics.k8s.io"]
      resources: ["nodes", "pods"]
      verbs: ["get", "list"]
---
# ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
    name: monitoring-binding
subjects:
    - kind: ServiceAccount
      name: monitoring-app
      namespace: monitoring
roleRef:
    kind: ClusterRole
    name: metrics-reader
    apiGroup: rbac.authorization.k8s.io
---
# Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
    name: monitoring-app
    namespace: monitoring
spec:
    replicas: 1
    selector:
        matchLabels:
            app: monitoring
    template:
        metadata:
            labels:
                app: monitoring
        spec:
            serviceAccountName: monitoring-app
            containers:
                - name: prometheus
                  image: prom/prometheus:latest

Contoh 4: Job dengan ServiceAccount

Kubernetesjob-with-sa.yml
# ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
    name: backup-job-sa
    namespace: default
---
# Role - Access ke backup resource
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
    name: backup-role
    namespace: default
rules:
    - apiGroups: [""]
      resources: ["persistentvolumeclaims"]
      verbs: ["get", "list"]
    - apiGroups: [""]
      resources: ["pods"]
      verbs: ["get", "list", "create"]
---
# RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
    name: backup-binding
    namespace: default
subjects:
    - kind: ServiceAccount
      name: backup-job-sa
      namespace: default
roleRef:
    kind: Role
    name: backup-role
    apiGroup: rbac.authorization.k8s.io
---
# Job
apiVersion: batch/v1
kind: Job
metadata:
    name: backup-job
spec:
    template:
        spec:
            serviceAccountName: backup-job-sa
            containers:
                - name: backup
                  image: backup-tool:latest
                  command: ["./backup.sh"]
            restartPolicy: OnFailure

Kesalahan Umum dan Pitfall

Kesalahan 1: Menggunakan Default ServiceAccount

Problem: Default ServiceAccount tidak punya specific permission.

Kubernetesyml
# Bad: Menggunakan default ServiceAccount
spec:
    # No serviceAccountName specified
    containers:
        - name: app
          image: myapp:latest

Solusi: Create dedicated ServiceAccount:

Kubernetesyml
# Good: Dedicated ServiceAccount
spec:
    serviceAccountName: my-app-sa
    containers:
        - name: app
          image: myapp:latest

Kesalahan 2: Overly Permissive Role

Problem: Granting cluster-admin ke ServiceAccount.

Kubernetesyml
# Bad: Terlalu banyak permission
roleRef:
    kind: ClusterRole
    name: cluster-admin

Solusi: Grant minimum necessary permission:

Kubernetesyml
# Good: Specific permission
rules:
    - apiGroups: [""]
      resources: ["pods"]
      verbs: ["get", "list"]

Kesalahan 3: Sharing ServiceAccount

Problem: Multiple application menggunakan same ServiceAccount.

Solusi: Create separate ServiceAccount per application:

Kubernetesyml
# App 1
serviceAccountName: app1-sa
 
# App 2
serviceAccountName: app2-sa

Kesalahan 4: Tidak Disable Token Mounting

Problem: Mounting token di Pod yang tidak need API access.

Solusi: Disable ketika not needed:

Kubernetesyml
spec:
    automountServiceAccountToken: false

Kesalahan 5: Menggunakan Long-Lived Token

Problem: Token yang never expire adalah security risk.

Solusi: Gunakan short-lived token (1.24+):

Kubernetesbash
kubectl create token my-app-sa --duration=1h

Best Practice

Principle of Least Privilege

Grant hanya necessary permission:

Kubernetesyml
rules:
    - apiGroups: [""]
      resources: ["pods"]
      verbs: ["get", "list"]  # Hanya yang needed

One ServiceAccount Per Application

Separate identity untuk setiap app:

Kubernetesyml
# Frontend
serviceAccountName: frontend-sa
 
# Backend
serviceAccountName: backend-sa
 
# Database
serviceAccountName: database-sa

Gunakan Namespace-Scoped Role

Prefer Role over ClusterRole ketika possible:

Kubernetesyml
# Good: Namespace-scoped
kind: Role
metadata:
    namespace: production
 
# Avoid kecuali necessary
kind: ClusterRole

Disable Token Mounting Ketika Not Needed

Kubernetesyml
spec:
    automountServiceAccountToken: false
    containers:
        - name: app
          image: nginx:1.25  # Tidak need API access

Document ServiceAccount Purpose

Kubernetesyml
metadata:
    name: my-app-sa
    annotations:
        description: "ServiceAccount untuk my-app dengan read-only access ke pod"
        owner: "platform-team"
        permissions: "pods:get,list,watch"

Regular Audit

Review ServiceAccount permission:

Kubernetesbash
# List ServiceAccount
kubectl get serviceaccounts --all-namespaces
 
# Check permission
kubectl auth can-i --list --as=system:serviceaccount:default:my-app-sa

Gunakan Token Expiration

Untuk external access, gunakan time-bound token:

Kubernetesbash
kubectl create token my-app-sa --duration=8h

Troubleshooting ServiceAccount

ServiceAccount Not Found

Kubernetesbash
kubectl get pod my-pod
# Error: serviceaccount "my-app-sa" not found

Solusi: Create ServiceAccount dulu:

Kubernetesbash
kubectl create serviceaccount my-app-sa

Permission Denied

Kubernetesbash
# Inside Pod
kubectl get pods
# Error: pods is forbidden

Solusi: Check dan fix RBAC:

Kubernetesbash
# Check permission
kubectl auth can-i get pods --as=system:serviceaccount:default:my-app-sa
 
# Create Role dan RoleBinding
kubectl create role pod-reader --verb=get,list --resource=pods
kubectl create rolebinding read-pods --role=pod-reader --serviceaccount=default:my-app-sa

Token Not Mounted

Kubernetesbash
# Inside Pod
ls /var/run/secrets/kubernetes.io/serviceaccount/
# Directory not found

Solusi: Enable token mounting:

Kubernetesyml
spec:
    automountServiceAccountToken: true  # Ensure ini true

Wrong Namespace

Kubernetesbash
# ServiceAccount di namespace A, Pod di namespace B
# Error: serviceaccount not found

Solusi: Ensure same namespace:

Kubernetesyml
# ServiceAccount
metadata:
    namespace: production
 
# Pod
metadata:
    namespace: production
spec:
    serviceAccountName: my-app-sa

Melihat Detail ServiceAccount

Get ServiceAccount

Kubernetesbash
kubectl get serviceaccounts
kubectl get sa  # Short form
kubectl get sa --all-namespaces

Describe ServiceAccount

Kubernetesbash
kubectl describe serviceaccount my-app-sa

View ServiceAccount YAML

Kubernetesbash
kubectl get serviceaccount my-app-sa -o yaml

Check ServiceAccount Permission

Kubernetesbash
kubectl auth can-i --list --as=system:serviceaccount:default:my-app-sa

Get Token

Kubernetesbash
# Kubernetes 1.24+
kubectl create token my-app-sa
 
# Pre-1.24
kubectl get secret <sa-token-secret> -o jsonpath="{.data.token}" | base64 --decode

Menghapus ServiceAccount

Kubernetesbash
kubectl delete serviceaccount my-app-sa

Warning

Peringatan: Menghapus ServiceAccount akan cause Pod yang menggunakannya lose API access. Ensure no Pod menggunakannya sebelum deletion.

Penutup

Pada episode 32 ini, kita telah membahas ServiceAccount di Kubernetes secara mendalam. Kita sudah belajar bagaimana ServiceAccount provide identity untuk Pod, enable API authentication, dan work dengan RBAC untuk fine-grained access control.

Key takeaway:

  • ServiceAccount provide identity untuk Pod
  • Enable API authentication dengan JWT token
  • Namespace-scoped - belong ke specific namespace
  • Default ServiceAccount exist di setiap namespace
  • Token automatically mounted di /var/run/secrets/kubernetes.io/serviceaccount/
  • Work dengan RBAC untuk authorization
  • Kubernetes 1.24+ gunakan time-bound token
  • Create dedicated ServiceAccount per application
  • Follow principle of least privilege
  • Gunakan Role over ClusterRole ketika possible
  • Disable token mounting ketika not needed
  • Document ServiceAccount purpose dan permission
  • Regular audit ServiceAccount permission
  • Token expiration improve security
  • Berbeda dari User Account (untuk human)

ServiceAccount fundamental untuk Kubernetes security dan access control. Dengan memahami ServiceAccount dan RBAC, kalian bisa implement proper authentication dan authorization untuk application, ensuring secure, controlled access ke cluster resource.

Bagaimana, makin jelas kan tentang ServiceAccount di Kubernetes? Jadi, pastikan tetap semangat belajar dan nantikan episode selanjutnya!

Catatan

Untuk kalian yang ingin melanjutkan ke episode selanjutnya, bisa click thumbnail episode 33 di bawah ini

Episode 33Episode 33

Related Posts