Kubernetes Service Account Nedir?
7 min read

Kubernetes Service Account Nedir?

Service Account'lar, pod'ların Kubernetes API'sine erişmesine ve diğer pod'larla iletişim kurmasına izin vermek için kullanılır.
Kubernetes Service Account Nedir?
Photo by Kenny Eliason / Unsplash

Kubernetes Service Account, bir Kubernetes pod'unun Kubernetes API Server'a erişmesine ve Kubernetes kaynaklarını kullanmasına izin veren bir kimliktir.

Bir Service Account, bir pod'un Kubernetes API'sine erişmesine izin vermek için RBAC izinlerini ve bir JWT token'ı kullanır. Bu token, pod'un Kubernetes API'sine istekte bulunduğunda pod'un kimliğini doğrulamak için kullanılır.

Service Account'lar, Kubernetes resource'larını kullanmak için de kullanılabilir. Örneğin, bir Service Account, bir pod'un Kubernetes Secret'larına erişmesine izin verebilir.

1. Neden Service Account Kullanmalıyız?

Bir cluster'ı hem insan kullanıcılar hem bot kullanıcılar kullanıyor olabilir. CI/CD araçlarında eğer insan kullanıcıların authentication bilgilerini kullanırsanız herhangi bir anda revoke etmeniz gerektiğinde kullanıcının RoleBinding veya ClusterRoleBinding'lerini kaldırmanız veya clusterın CA sertifikasını yenilemeniz gerekir. Bunları yapmak ise pratikte uygun değildir.

💡
Kubernetes User nesnesi sağlamaz. Sadece API Server ile iletişime geçilen sertifika bilgilerindeki CN ve O değerleri ile kullanıcı ve grup bilgilerini anlayabilir ve RBAC ile yetki kontrolünü yapar. Bu nedenle kubectl get user gibi komutlar mevcut değildir. Haliyle insan kullanıcıları da delete gibi bir komutla silemezsiniz.

Bir Service Account kullandığınızda ise, ilgili accountu revoke etmek için service account'u kubectl komutuyla direkt silebilirsiniz. Böylece bot kullanıcıya atanan access token da silinmiş olur.

Peki her namespace de bir default service account varsa neden yenisi oluşturmalıyız?

En büyük sebep güvenliktir. Aynı service account'u tekrar tekrar kullanıyorsanız ve bu service account'un güvenliği ihlal edilirse, tüm uygulamalarınız başı büyük bir belaya girer. Kötü niyetli bir uygulama, her yerde kullandığınız ve yetkilendirdiğiniz bu default service account ile cluster genelinde her yere erişebilir hale gelir ve zararlı komutlar çalıştırabilir.

Diğer bir sebep ise güvenlik unsuru kadar önemli olmasa da ortamın anlaşılırlığıdır. Bir admin email hesabını kullanıcılara kullanmaları için vermek ne kadar mantıksızsa, her işlem için aynı service account'u kullanmakta o kadar mantıksızdır.

Bu sebeplerden dolayı, service account'ları uygun şekilde oluşturmalı ve role tanımlarını yapmalıyız.

2. Service Account Oluşturmak

Her namespace'de başlangıçta hiçbir yetkiye sahip olmayan "default" isimli bir default Service Account vardır. Bu default Service Account, pod'lar için varsayılan kimlik olarak kullanılır.

# kubectl get sa
NAME      SECRETS   AGE
default   0         152d

Pod'lar için farklı bir Service Account tanımlamak isterseniz, bunu kubectl komutunu kullanarak yapabilirsiniz.

kubectl create serviceaccount my-service-account

Oluşturduğumuz service account'u describe edelim.

# kubectl describe sa my-service-account
Name:                my-service-account
Namespace:           default
Labels:              <none>
Annotations:         <none>
Image pull secrets:  <none>
Mountable secrets:   <none>
Tokens:              <none>
Events:              <none>

1.24'ten önceki K8s sürümünde, her service account oluşturduğumuzda, varsayılan olarak süresi dolmayan bir secret token yaratılıyordu. Ancak, 1.24 sürümünden itibaren bir service account oluşturduğumuzda varsayılan olarak hiçbir secret token oluşturulmuyor. Ancak, gerektiğinde kendimiz oluşturabiliriz. Şimdi service account secret token'a biraz daha derinlemesine bakalım.

2.1. Service Account Token Tipleri

Kubernetes, 1.22 sürümünden itibaren iki tür token'ı destekler.

  • Long-Lived Token
  • Time Bound Token

2.1.1. Long-Lived Token

Adından da anlaşılacağı gibi, bir long-lived token asla sona ermeyen bir token'dır. Bu nedenle, daha az güvenlidir ve kullanılması önerilmez.

K8S'in 1.24 sürümünden önce, bir service account oluşturulduğunda, secret token içeren bir secret nesnesi de yaratılıyordu. Bu tokenların son kullanma tarihi olmadığı için Kubernetes sürüm 1.24'te güvenlik ve ölçeklenebilirlik endişeleri nedeniyle artık kullanımdan kaldırılmıştır.

Tavsiye edilmese de K8A, long-lived token oluşturmamızı sağlar. Bunun için önce bir service account oluşturmalıyız.

kubect create serviceaccount my-service-account

Ardından secret oluşturun ve service account adını metadata bölümünde belirtin.

kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
  name: my-long-lived-secret
  annotations:
    kubernetes.io/service-account.name: my-service-account
type: kubernetes.io/service-account-token
EOF

Artık service accountu describe ettiğimizde atanan long-lived token'a sahip secret adını görebiliriz.

# kubectl describe sa my-service-account
Name:                my-service-account
Namespace:           default
Labels:              <none>
Annotations:         <none>
Image pull secrets:  <none>
Mountable secrets:   <none>
Tokens:              my-long-lived-secret
Events:              <none>

Oluşturduğunuz secret datasındaki JWT token'ı base64 ile decode ettikten sonra API Server iletişimde kullanmaya başlayabilirsiniz.

2.1.2. Time Bound Token

Kubernetes 1.22 sürümüyle birlikte, artık TokenRequest API geldi. Bu API aracılığıyla oluşturulan bir token, belirli bir süre sonra sona eren zamana bağlı bir token'dır. Hem default service account hem de özel tanımlı service accountlar için geçerlidir.

time-bound bir token oluşturabilmek için aşağıdaki komutu o namespace içinde bulunan service account adıyla çalıştırabiliriz.

kubectl create token my-service-account

Ancak, manuel olarak bir token oluşturmak gerekli değildir. default service account örneğini ele alırsak, automountServiceAccountToken değeri True olarak ayarlanmış bir pod başlatıldığında (default değeri zaten True'dur), K8s control plane, pod'a bir project volume bağlar. Ardından o node'da çalışan kubelet, bu volume'daki token'ı otomatik oluşturur.

# kubectl get pod nginx -o yaml
***
spec:
  serviceAccountName: default
  containers:
  - image: nginx
    volumeMounts:
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: kube-api-access-j79nd
      readOnly: true  
***

Kubernetes, token'ı bir saat içinde sona erdirmek üzere tasarlanmıştır, ancak non-expiring token'la çalışan birçok eski uygulama vardır. Kubernetes, time-bound token'ların kademeli olarak benimsenmesine izin vermek için, cluster yöneticilerinin Kube API Server'a --service-account-extend-token-expiration=true parametresi belirtmesine izin verir. Bu parametre eklendiğinde, tokenların geçici olarak daha uzun süreli (365 gün) olmasına izin verir.

2.2. Service Account'a Yetki Vermek

Bir service account oluşturduğunuzda, bu service account başlangıçta hiçbir yetkiye sahip değildir. Kullanacağınız amaçlara uygun yetkileri (podları listeleme, servis oluşturma vs) tanımlayabilmek için öncelikle bu service account'u uygun bir Role veya ClusterRole atamanız gerekir. Bunun için şu yazımızı okuyabilirsiniz.

Kubernetes RBAC ve Role, ClusterRole, RoleBinding ve ClusterRoleBinding Nedir?
Kubernetes RBAC, Kubernetes nesneleri üzerinde izinleri yönetmek için kullanılan bir izin sistemidir. Role, ClusterRole, RoleBinding ve ClusterRoleBinding, Kubernetes RBAC’de kullanılan dört temel kavramdır

Demo amacıyla, service account'umuza tüm kaynakların görüntülenmesini sağlayan "view" adlı otomatik gelen bir Kubernetes ClusterRole atayalım. Ardından, RoleBinding oluşturmanız gerekir.

kubectl create rolebinding web-sa-readonly \
  --clusterrole=view \
  --serviceaccount=default:my-service-account \
  --namespace=default

2.3. Pod'a Service Account Eklemek

default service account'ların aksine, özel oluşturduğunuz service account'lar bir pod için otomatik olarak yapılandırılmaz. Bir pod için service account'u yapılandırmak istiyorsak, koşullar şunlardır:

  • Bir service account çalışan bir pod'a mount eklenemez

Bir pod'a service account eklemek için serviceAccountName parametresini özelleştirebilirsiniz.

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  serviceAccountName: my-service-account
  automountServiceAccountToken: true

automountServiceAccountToken parametresi zaten default olarak True ayarlıdır. Eğer bir pod'a default veya başka bir service account'un mount edilmesini istemiyorsanız False ayarlayabilirsiniz.

2.4. Pod'a İçinde Service Account'u Kullanmak

Bir service account mount ettiğimiz pod'u incelerseniz; time-bound token'ın, cluster ca sertifikasının ve namespace adının project volume olarak otomatik pod'a mount edildiğini görürsünüz.

# kubectl get pod nginx -o yaml
apiVersion: v1
kind: Pod
***
  volumes:
  - name: kube-api-access-j79nd
    projected:
      defaultMode: 420
      sources:
      - serviceAccountToken:
          expirationSeconds: 3607
          path: token
      - configMap:
          items:
          - key: ca.crt
            path: ca.crt
          name: kube-root-ca.crt
      - downwardAPI:
          items:
          - fieldRef:
              apiVersion: v1
              fieldPath: metadata.namespace
            path: namespace

Peki bu ne demek?

Pod içinden, bu token, namespace ve sertifika bilgilerini kullanarak API Server ile izinleriniz doğrultusunda iletişime geçebilirsiniz. Örneğin aşağıdaki komutları inceleyebilirsiniz:

kubectl exec -it my-pod-with-service-account

# Kubernetes API Server adresini tanımlayalım
$ APISERVER=https://kubernetes.default.svc

# ServiceAccount'un mount edildiği dizini tanımlayalım
$ SERVICEACCOUNT=/var/run/secrets/kubernetes.io/serviceaccount

# ServiceAccount'un token'ını tanımlayalım
$ TOKEN=$(cat ${SERVICEACCOUNT}/token)

# internal Kubernetes certificate authority (CA)
$ CACERT=${SERVICEACCOUNT}/ca.crt

# hangi namespace'in kullanılacağını tanımlayalım
$ NAMESPACE=$(cat ${SERVICEACCOUNT}/namespace)

# API Server'a bu bilgilerle bir istek atalım
$ curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -X GET ${APISERVER}/api/v1/namespaces/${NAMESPACE}/pods
{
  "kind": "PodList",
  "apiVersion": "v1",
  "metadata": {
    "resourceVersion": "52233"
  },
  "items": [
    {
      "metadata": {
        "name": "nginx1-65448895f9-5j6b6",
        "generateName": "nginx1-65448895f9-",
        "namespace": "default",
        "uid": "b09bfa93-a388-4cd9-9495-131f620613d0",
        "resourceVersion": "49536",
(...)

Ve beklendiği gibi, pod'umuz service account üzerinden API Server ile haberleşebiliyor. Herhangi bir nedenle pod'larınızın bir Kubernetes API'sine erişmesi gerekiyorsa bunun için yeni bir ServiceAccount oluşturun, ardından ona RoleBinding aracılığıyla bir Role veya ClusterRole atayın, ardından pod manifestinde o ServiceAccount adını belirtin. İşte bu kadar!


Sıradaki yazı ile eğitim serisine devam edebilirsiniz.

Helm Nedir? Nasıl Kurulur ve Kullanılır?
Helm, Kubernetes için bir paket yöneticisidir ve dağıtım, güncelleme ve yönetim işlerinizi kolaylaştırır.