kubernetes에서는 두 가지 계정 개념을 통해 '인증'을 처리한다. 참고로 이전에 배운 Role
, RoleBinding
의 경우는 '권한'에 관한 문제로, '인증'조차 되지 않으면 kubernetes API에 접근 조차 불가능하므로 '인증'이 먼저되어야 한다.
user account는 이전에 kubeconfig를 통해 user를 설정할 때 보았다. service account는 kubernetes상에서의 resource로 다음 명령어를 통해서 만들 수 있다.
kubectl create serviceaccount dashboard-sa
각 serviceaccount는 API token을 가지는데, 이를 통해서 service account를 가진 app, service, bot들이 인증되어 kubernetes API에 접근할 수 있는 것이다.
그런데, 해당 token은 사실 secret object로 저장된다. 즉, service account를 만들 때 맨 처음에는 service account resource를 만들고, token값을 secret에 넣어서 secret을 service account token field에 넣어준다.
kubectl get secrets
dashboard-sa-6bpf
kubectl describe secret dashboard-sa-6bpf
Name: dashboard-sa-6bpf
Namespace: default
Labels: <none>
Annotations: kubernetes.io/service-account.name: my-service-account
kubernetes.io/service-account.uid: xxxx-xxxx-xxxx-xxxx
Type: kubernetes.io/service-account-token
Data
====
ca.crt: 1025 bytes
namespace: 7 bytes
token: eyJhbGciOiJSUzI1NiIsImtpZCI6...
이 token만 제공해주면 curl
로도 kubernetes apiserver에 접근할 수 있다.
namespace가 만들어지면 자동으로 해당 namespace에 service account가 하나 만들어지는데, 이 namespace안에 배포되는 pod들은 모두 해당 service account의 token을 갖게된다.
가령 default
namespace역시도 default
라는 serviceaccount를 가지는데, default
namespace에 pod를 배포하면 해당 serviceaccount의 token이 pod volume에 적재된다.
사실 token을 가진 secret이 volume에 적재되는 것이다. 해당 token이 적재되는 위치는 default로 /var/run/secrets/kubernetes.io/serviceaccount
이다. 여기서 token
이라는 파일로 token 내용이 저장되어 있다.
단, 이렇게 부여받은 serviceaccount의 token은 매우 제한적이기 때문에 kubernetes API에 접근하는데 있어서 한계가 존재한다.
만약, 우리가 직접 만든 dashboard-sa
serviceaccount를 pod에 넣고싶다면 다음과 같이 할 수 있다.
apiVersion: v1
kind: Pod
metadata:
name: my-kubernetes-dashboard
spec:
containers:
- name: my-kubernetes-dashboard
image: my-kubernetes-dashboard
serviceAccountName: dashboard-sa
serviceAccountName
에 넣어주기만 하면 된다.
만약 자동으로 적재되는 serviceaccount token을 받고 싶지 않다면 다음과 같이 쓸 수 있다.
apiVersion: v1
kind: Pod
metadata:
name: my-kubernetes-dashboard
spec:
containers:
- name: my-kubernetes-dashboard
image: my-kubernetes-dashboard
automountServiceAccountToken: false
v1.22가 되기전까지는 위의 방식대로 각 namespace마다 serviceaccount가 생기고, 해당 serviceaccount의 token이 각 pod의 volume에 들어갔다면, v1.22부터는 token request API가 도입되었다. 이는 보안과 scaleable을 위한 것으로 token을 담은 secret없이, 그때그때 token request API에 요청을 보내어 token을 갱신하는 방식이다.
apiVersion: v1
kind: Pod
metadata:
name: nginx
namespace: default
spec:
containers:
- image: nginx
name: nginx
volumeMounts:
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: kube-api-access-6mtg8
readOnly: true
volumes:
- name: kube-api-access-6mtg8
sources:
- serviceAccountToken:
expirationSeconds: 3607
path: token
...
v1.22부터는 새로운 pod가 만들어지면 default service account의 token을 주입하는 것이 아니라, admission controller를 통해서 token을 요청해 받도록 한다. 즉, 이제 더이상 해당 namespace의 service account token을 pod에 자동으로 주입해주지 않는 것이다. 단, 그렇다하더라도 해당 service account에 연결된 token이 존재하므로 secret이 존재한다.
v1.24부터는 serviceaccount가 만들질 때 token을 만들 지 않는다. 그러나 serviceaccount를 pod에 직접 연결하면 유효기간이 계속 자동 갱신되는 token을 pod에 적재하여 kubernetes api에 계속 인증할 수 있도록 해준다.
가령 my-service-account
를 my-pod
에 연결하면 my-pod
는 kubernetes api에 인증이 계속해서 영원히 성공하게 된다.
apiVersion: v1
kind: Pod
metadata:
name: my-pod
namespace: default
spec:
serviceAccountName: my-service-account
containers:
- name: my-container
image: my-image
my-service-account
가 pod의 /var/run/secrets/kubernetes.io/serviceaccount/token
경로에 token을 마운트하고, 갱신 기간이 지나도 자동 갱신하는 것이다.
그런데, 만약 자동 갱신되는 token이 아니라 수동 갱신되는 token을 만들고 싶다면 어떻게해야할까?? 아래와 같이 직접 만들고 맵핑할 수 있다.
kubectl create serviceaccount dashboard-sa
serviceaccount인 dashboard-sa
을 만들었지만, token에 대한 secret은 만들어지지 않는다.
kubectl create token dashboard-sa
위 명령어로 token을 만들면 token만을 만들어주게 된다. 해당 token을 serviceaccount을 따로 pod에 전달해주면 된다.
위의 방법은 수동으로 token을 만들기 때문에, 유효 기간이 지난다고 해서 자동으로 token을 갱신주지 않는다. 따라서, 계속해서 수동으로 token 갱신해주어야 하기 때문에 특별한 경우가 아니면 잘 안쓰인다.
만약 이전처럼 v1.24에서도 serviceaccount와 secret 조합을 사용하고 싶다면 아래와 같이 secret을 만들고 pod의 volume에 전달해주면 된다.
apiVersion: v1
kind: Secret
type: kubernetes.io/service-account-token
metadata:
name: mysecretname
annotations:
kubernetes.io/service-account.name: dashboard-sa
다음과 같이 annotation을 통해 secret
에 serviceaccount 이름을 제공해주면 된다. 단, serviceaccount를 먼저 만들고 secret을 만들어야 한다는 순서를 잊지말도록 하자.
serviceaccount 역시도 계정이기 때문에 Role
과 바인딩될 수 있다. 참고로 token은 '인증'에 관한 문제이고, Role
, RoleBinding
은 '권한'에 관한 문제를 해결하므로 헷갈리지 말도록 하자.
가령, dashboard-sa
라는 serviceaccount가 있다고 하자.
apiVersion: v1
kind: ServiceAccount
metadata:
creationTimestamp: null
name: dashboard-sa
해당 dashboard-sa
serviceaccount에 다음의 Role
을 바인딩하도록 하자.
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: default
name: pod-reader
rules:
- apiGroups:
- ''
resources:
- pods
verbs:
- get
- watch
- list
pod-reader-role
을 dashboard-sa
에 바인딩해주면 된다.
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: read-pods
namespace: default
subjects:
- kind: ServiceAccount
name: dashboard-sa # Name is case sensitive
namespace: default
roleRef:
kind: Role #this must be Role or ClusterRole
name: pod-reader # this must match the name of the Role or ClusterRole you wish to bind to
apiGroup: rbac.authorization.k8s.io
subjects
을 보면 kind
가 ServiceAccount
로 되어있고, dashboard-sa
가 적혀있다. 해당 serviceaccount를 roleRef
의 Role
인 pod-reader
에 연결해준 것이다.