Kubernetes Service Account Nedir?
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.
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.
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.