Container Orchestration/Kubernetes

Kuberntes Service Account란?

Somaz 2023. 5. 10. 20:34
728x90
반응형

Overview

Kubernetes 클러스터를 운영하다 보면 사람(사용자)뿐만 아니라 클러스터 내부에서 실행되는 애플리케이션(Pod)도 API 서버와 안전하게 통신할 수 있도록 인증 메커니즘이 필요하다. 오늘은 바로 이러한 역할을 담당하는 Kubernetes Service Account에 대해 알아보고, 그 동작 원리와 실제 실습 예제를 통해 내부 동작을 살펴보려고 한다.

 

이 글에서는 User Account와 Service Account의 차이부터, 쿠버네티스 버전별 토큰 생성 방식, 자동 마운트 설정, 토큰 회전과 RBAC 연동까지 실무에서 꼭 알아야 할 내용을 정리해보았다.

 

서비스 어카운트를 안전하고 효율적으로 관리하기 위한 보안 설정 및 실습 예제까지 포함되어 있으니, 서비스 계정 기반 인증을 제대로 이해하고 싶은 분들에게 많은 도움이 될 것이다.

 

 

출처 : https://www.bogotobogo.com/DevOps/Docker/Docker-Kubernetes-Service-Account.php

 

 

 

 

먼저 간단하게 User Account와 Service Account에 대해 설명해보자면,

 

사용자 어카운트는 사람을 위한 것이다. 서비스 어카운트는 파드에서 실행되는 프로세스를 위한 것이다.

  

구분 User Account Service Account
사용 대상 실제 사람 쿠버네티스 내부 프로세스 (Pod)
인증 방법 인증서 기반 (kubectl config use-context) 토큰 기반 (/var/run/secrets/kubernetes.io/serviceaccount/token)
권한 관리 RBAC(Role-Based Access Control) 사용 RBAC(Role, ClusterRole, Binding) 사용
라이프사이클 클러스터 외부에서 관리 클러스터 내부에서 자동 관리
예제 사용 kubectl apply -f kubeconfig.yaml kubectl apply -f serviceaccount.yaml

 

 

요점

  • User Account는 kubectl을 사용하는 사람(관리자) 을 위한 것
  • Service Account는 Kubernetes 내부에서 실행되는 애플리케이션(Pod) 을 위한 것

 

 

 

 


 

 

Service Account란?

Kubernetes Service Account는 쿠버네티스 클러스터 내에서 실행되는 팟(Pod)이 API 서버와 상호 작용할 수 있도록 권한을 부여하는 데 사용되는 자격증명이다. 서비스 어카운트는 특정 네임스페이스(namespace)에 속하며, 자동으로 생성되거나 사용자가 직접 생성할 수 있다.  그리고 네임스페이스 생성시 디폴트 서비스 어카운트가 생성된다.

 

 

쿠버네티스 1.24 이전까지는 서비스 어카운트를 처음 생성하면 자동으로 서비스 어카운트 토큰이 생성된다.

$ kubectl get serviceaccount -n somaz
NAME                                   SECRETS   AGE
default                                1         27d

$ kubectl get secret -n somaz
NAME                                               TYPE                                  DATA   AGE
default-token-7c6dm                                kubernetes.io/service-account-token   3      27d

 

 

쿠버네티스 1.24 이후에는 보안 강화로 인해 서비스 어카운트를 생성하여도 서비스 어카운트 토큰이 생성되지 않는다.

따라서 아래의 방법으로 서비스 어카운트 토큰까지 생성해야 한다.

apiVersion: v1
kind: ServiceAccount
metadata:
  name: servicenow-discovery
  namespace: default
---
apiVersion: v1
kind: Secret
type: kubernetes.io/service-account-token
metadata:
  name: servicenow-discovery-token
  namespace: default
  annotations:
    kubernetes.io/service-account.name: "servicenow-discovery"
---
apiVersion: v1
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  # "namespace" omitted since ClusterRoles are not namespaced
  name: read-only
rules:
- apiGroups:
    - apps
    - extensions
    - "*"
    - ""
  resources: ["*"]
  verbs: ["get", "watch", "list"]
---
apiVersion: v1
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: discovery-global
subjects:
- kind: ServiceAccount
  name: servicenow-discovery
  namespace: default
- kind: User
  name: servicenow-discovery
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: read-only
  apiGroup: rbac.authorization.k8s.io

 

 

 

 

 

 


 

 

 

 

 

 

Kubernetes 서비스 어카운트 자동 마운트 방지(보안강화)

 

✅ 자동 마운트 방지 옵션 추가 예제

apiVersion: v1
kind: ServiceAccount
metadata:
  name: my-service-account
  namespace: somaz
automountServiceAccountToken: false  # ✅ 자동 마운트 방지

 

 

설명

  • automountServiceAccountToken: false 를 설정하면 해당 ServiceAccount를 사용하는 모든 Pod에서 자동으로 API 토큰이 마운트되지 않음.
  • 보안 강화에 유용 (API 서버와 불필요한 통신을 방지).

 

 

 

 


 

 

 

 

 

 

서비스 어카운트 토큰을 Pod에 수동으로 마운트하는 방법

자동 마운트를 비활성화한 경우, 서비스 어카운트 토큰을 수동으로 마운트할 수도 있음.

 

 

✅ Pod에 서비스 어카운트 토큰을 수동으로 마운트하는 예제

yaml
복사편집
apiVersion: v1
kind: Pod
metadata:
  name: my-pod
  namespace: somaz
spec:
  serviceAccountName: my-service-account
  containers:
    - name: my-container
      image: busybox
      volumeMounts:
        - name: sa-token
          mountPath: "/var/run/secrets/kubernetes.io/serviceaccount"
          readOnly: true
  volumes:
    - name: sa-token
      projected:
        sources:
          - serviceAccountToken:
              path: token
              expirationSeconds: 3600
              audience: "<https://kubernetes.default.svc>"

 

 

설명

  • `serviceAccountToken.expirationSeconds: 3600` → 토큰 만료 시간 1시간으로 설정
  • `audience: "<https://kubernetes.default.svc>"` → API 서버와 안전한 인증

 

이렇게 하면 Pod가 필요할 때만 토큰을 가져와서 사용할 수 있음.

 

 

 

 

 


 

 

 

 

 

 

 

RBAC에 대한 내용은 아래의 게시물을 보면된다.

2023.04.19 - [Container Orchestration/Kubernetes] - Kubernetes API Server, Group / RBAC란?

 

Kubernetes API Server, Group / RBAC란?

Overview 오늘은 Kubernets API Server, Group 그리고 RBAC 에 대해서 공부해보려고 한다. Kubernetes에 개념과 간단한 실습을 하고싶다면 아래의 링크를 참고하길 바란다. 2022.03.22 - [Container Orchestration/Kubernetes]

somaz.tistory.com

 

 

 

 

Cluster Role Binding

출처 : https://www.cncf.io/blog/2020/08/28/kubernetes-rbac-101-authorization/

 

 

 

 

Role Binding

출처 : https://www.cncf.io/blog/2020/08/28/kubernetes-rbac-101-authorization/

 

 

 

 

 

 


 

 

 

 

 

Service Account 주요 요소

출처 : https://ssup2.github.io/theory_analysis/Kubernetes_Authentication_Service_Account/

  • ServiceAccount admission 컨트롤러
  • ServiceAccount Token 컨트롤러
  • ServiceAccount 컨트롤러

 

서비스 어카운트 주요 요소는 클러스터 내의 인증 및 권한 부여 프로세스에서 필수적인 역할을 한다.

 

 

 

 

 

 


 

 

 

 

 

 

ServiceAccount admission 컨트롤러

 

ServiceAccount admission 컨트롤러는 default Service account를 지정하지 않은 Pod에 할당하는 역할을 한다.

그리고 K8S-api-server에 포함된다.

 

새로운 파드가 생성되면 포드 사양에 ServiceAccount가 지정되어 있는지 확인한다. 그렇지 않은 경우 Pod가 생성되는 네임스페이스에 대한 기본 ServiceAccount를 자동으로 할당한다. 이렇게 하면 클러스터의 모든 Pod에 Kubernetes API 서버에 액세스할 때 인증에 사용할 수 있는 연결된 ServiceAccount가 존재한다.

 

 

 

 

 

ServiceAccount Token 컨트롤러

Token 컨트롤러는 클러스터의 각 ServiceAccount에 대한 토큰 생성 및 관리를 담당한다.

그리고 kube-controller-manager에 포함된다.

 

새로운 ServiceAccount가 생성되면 토큰 컨트롤러는 JWT 토큰, 네임스페이스 및 인증서를 포함하는 해당 암호를 자동으로 생성한다 ca.crt 토큰은 ServiceAccount를 대신하여 Kubernetes API 서버에 대한 요청을 인증하는 데 사용할 수 있다. 토큰 컨트롤러는 또한 회전 및 삭제를 포함하여 토큰의 수명 주기를 관리한다.

 

 

 

 

 

 

ServiceAccount 컨트롤러

ServiceAccount 컨트롤러는 ServiceAccount 및 관련 Secret 생성 및 삭제를 관리한다.

그리고 kube-controller-manager에 포함된다.

 

새로운 네임스페이스가 생성되면 ServiceAccount 컨트롤러는 토큰 컨트롤러에서 생성된 해당 암호와 함께 해당 네임스페이스에 대한 기본 ServiceAccount를 자동으로 생성한다. ServiceAccount가 삭제되면 ServiceAccount 컨트롤러는 연결된 암호도 클러스터에서 제거한다.

 

 

 

 

 


 

 

 

 

Service Account Secret 주요 요소

  • Token
  • ca.crt
  • Namespace

 

서비스 어카운트가 생성되면 Kubernetes는 다음 세 가지 구성요소를 포함하는 보안 Secret을 자동으로 생성한다.

 

 

 

Token

토큰은 서비스 계정을 대신하여 Kubernetes API 서버에 대한 요청을 인증하는 데 사용할 수 있는 JWT(JSON 웹 토큰)이다. 이 토큰은 Kubernetes API 서버의 개인 키로 서명되며

 

해당 공개 키(ca.crt에 있는)를 사용하여 확인할 수 있다. 토큰에는 해당 이름 및 속한 네임스페이스와 같은 서비스 계정에 대한 정보가 포함된다.

 

 

 

 

ca.crt

ca.crt 파일에는 Kubernetes 클러스터에 대한 인증 기관(CA) 인증서가 포함되어 있다. 요청을 할 때 클라이언트와 API 서버 간의 신뢰를 설정하는 데 사용된다.

 

클라이언트는 이 CA 인증서를 사용하여 API 서버의 인증서가 유효하고 동일한 CA에서 서명했는지 확인할 수 있다. 이렇게 하면 클라이언트가 악의적인 행위자가 아닌 인증된 API 서버와 통신하고 있는지 확인할 수 있다.

 

 

 

 

Namespace

네임스페이스는 Kubernetes의 다중 테넌트 아키텍처의 핵심 구성 요소이다. 네임스페이스는 클러스터 내의 리소스를 논리적으로 분리하는 데 사용되므로 여러 팀이나 프로젝트가 서로 간섭하지 않고 동일한 클러스터를 공유할 수 있다.

 

서비스 계정이 생성되면 특정 네임스페이스와 연결된다. 서비스 계정에 ClusterRoleBindings을 통해 클러스터 전체 권한이 부여되지 않은 경우 RoleBindings를 통해 해당 네임스페이스의 권한을 부여 받은 서비스 계정의 토큰은 해당 네임스페이스 내의 리소스에 액세스 하는 용도로만 사용할 수 있다.

 

 

 

ServiceAccount 실사용 사례

  • CI/CD 파이프라인 (예: ArgoCD, GitLab Runner): 클러스터에 배포 권한이 있는 서비스 어카운트를 통해 자동 배포 수
  • Monitoring & Logging (예: Prometheus, Fluentd): 클러스터 정보를 수집하기 위해 `get`/`list` 권한 부여
  • Job 또는 CronJob: API 서버에 접근하거나 특정 namespace 리소스를 조회할 때 사용

 

 

 

 

 

 

 


 

 

 

 

Service Account 실습

 

기존에 생성되어 있는 서비스 어카운트와 서비스 어카운트 토큰으로 진행해 보려고 한다.

$ k get serviceaccounts -n somaz
NAME                                   SECRETS   AGE
default                                1         27d

$ k get secret -n somaz
NAME                                               TYPE                                  DATA   AGE
default-token-7c6dm                                kubernetes.io/service-account-token   3      27d

 

 

아래와 같이 파드에 서비스 어카운트가 마운트 되어 있다.

k describe po -n somaz somaz-db-d7d785ff4-mwgfm
    Mounts:
      /etc/mysql/conf.d from config-volume (rw)
      /var/lib/mysql from mysql-persistent-storage-dev2 (rw)
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-9dnzq (ro)

 

 

파드를 확인해보면 마운트 되어있는 장소에 token이 있다.

 

따라서 해당 token을 가지고 쿠버네티스 API 서버와 통신할 수 있다.

$ k exec -ti -n somaz somaz-db-d7d785ff4-mwgfm -- ls -l /var/run/secrets/kubernetes.io/serviceaccount/
total 0
lrwxrwxrwx 1 root root 13 Apr 12 10:00 ca.crt -> ..data/ca.crt
lrwxrwxrwx 1 root root 16 Apr 12 10:00 namespace -> ..data/namespace
lrwxrwxrwx 1 root root 12 Apr 12 10:00 token -> ..data/token

 

 

token 파일을 확인해보면 값이 들어가 있다.

k exec -ti -n somaz somaz-db-d7d785ff4-mwgfm -- cat /var/run/secrets/kubernetes.io/serviceaccount/token
eyJhbGciOi...

 

 

 

그리고 Role을 생성 후 Rolebinding을 해준다.

 

간단하게 설명하자면, Role은 특정 namespace에서만 동작하는 역할이고

ClusterRole은 클러스터 전체에서 동작하는 역할이다.

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: service-account-somaz-role
  namespace: somaz
rules:
  - apiGroups: [""] 
    resources: ["pods"]
    verbs: ["get", "watch", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: service-account-somaz-role-binding
  namespace: somaz 
subjects:
  - kind: ServiceAccount
    name: default
    namespace: somaz
roleRef:
  kind: Role 
  name: service-account-somaz-role 
  apiGroup: rbac.authorization.k8s.io

 

 

 

아래와 같이 통신을 확인해본다.

k exec -ti -n somaz somaz-db-d7d785ff4-mwgfm -- bash
bash-4.4# cd /var/run/secrets/kubernetes.io/serviceaccount/
bash-4.4# TOKEN=$(cat token)
bash-4.4# curl -X GET https://$KUBERNETES_SERVICE_HOST/api/v1/namespaces/somaz/pods --header "Authorization: Bearer $TOKEN" --insecure
{
  "kind": "PodList",
  "apiVersion": "v1",
  "metadata": {
    "resourceVersion": "79523984"
  },
  "items": [
    {
      "metadata": {
        "name": "somaz-db-d7d785ff4-mwgfm",
        "generateName": "somaz-db-d7d785ff4-",
        "namespace": "somaz",
        "uid": "2f44c054-937a-478c-b719-20298846f236",
...

 

 

 

ServiceAccount와 RBAC 최소 권한 원칙

 

서비스 어카운트를 사용할 때 가장 중요한 보안 원칙은 최소 권한 원칙(Least Privilege Principle)이다. 즉, 해당 Pod가 실제로 접근이 필요한 리소스에 대해서만 `get`, `list`, `watch` 같은 권한을 주고 그 외는 차단하는 것이 이상적이다.

예를 들어 로그 수집 에이전트라면 `pods`, `nodes`에 대한 `list` 및 `watch`만 있으면 충분할 수 있으며, 전체 클러스터 조회 권한은 불필요하다.

실무에서는 `Role`/`ClusterRole`을 재사용하고, 여러 서비스 어카운트에 대해 `RoleBinding`을 적절히 조합하는 방식으로 관리한다.

 

 

 

 

 

 


 

 

 

 

 

해당 실습을 하면서 두가지 궁금증이 생겼다.

 

  1. `$KUBERNETES_SERVICE_HOST` 변수의 IP는 어떤의미일까?
  2. 파드의 토큰 값과 클러스터의 서비스 어카운트 토큰 값이 다른데 어떻게 통신이 되는걸까?

 

 

 

 

 

$KUBERNETES_SERVICE_HOST 변수의 IP는 어떤의미일까?

 

바로 쿠버네티스 API 서버의 IP 주소를 나타낸다.

 

Pod 환경에 자동으로 주입되며 실행중인 Pod가 API 서버와 통신할 수 있게 해준다.

echo $KUBERNETES_SERVICE_HOST
10.233.0.1

k get service -n default
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.233.0.1   <none>        443/TCP   372d

 

 

 

 

 


 

 

 

 

 

파드의 토큰 값과 클러스터의 서비스 어카운트 토큰 값이 다른데 어떻게 통신이 되는걸까?

 

먼저 파드의 토큰 값을 확인해본다.

k exec -ti -n somaz somaz-db-d7d785ff4-mwgfm -- cat /var/run/secrets/kubernetes.io/serviceaccount/token
eyJhbGciOiJSUzI1NiIsImtpZC...

 

 

 

그리고 클러스터의 서비스 어카운트 토큰 값을 확인한다.

$ k get secrets -n somaz
NAME                                               TYPE                                  DATA   AGE
default-token-7c6dm                                kubernetes.io/service-account-token   3      27d

$ k get secrets -n somaz default-token-7c6dm -o yaml | k neat
apiVersion: v1
data:
  ca.crt:...
  namespace: c29tYXoK # echo "somaz" | base64 -> c29tYXoK
  token: ZXlKaGJHY2lPaUpTVXpJMU5pSXNJbXRwWkNJNkltVlNSalpvYnpaR09UZER...

 

 

 

 

분명히 두개의 토큰 값은 다르다. 그러면 어떻게 통신하는 걸까?

 

이유는 다음과 같다.

 

실제로 두 토큰이 같아야 할 것처럼 보이지만, 쿠버네티스는 보안상의 이유로 서비스 계정 토큰을 자동으로 회전(rotate)한다. 서비스 계정 토큰이 탈취당하는 경우나 누출되는 경우를 대비하여 보안을 강화하기 위한 조치이다.

 

서비스 계정 토큰의 회전 주기는 쿠버네티스 클러스터 설정에 따라 다르다. 따라서, 생성 시점에 따라 파드의 토큰과 kubectl get secret 명령어로 조회하는 토큰이 다를 수 있다. 이것이 두 토큰이 다른 이유이다.

 

 

 

 

 

서비스 어카운트 토큰이 자동 회전하는지 확인

kubectl get serviceaccounts my-service-account -o yaml
  • 출력에서 secrets 필드가 없으면, 자동 회전 활성화됨.

 

 

 

자동 회전된 토큰을 가져오는 방법

kubectl describe secret $(kubectl get secret -n somaz | grep my-service-account | awk '{print $1}')
  • 토큰이 자동 회전되는지 정기적으로 확인하고, 갱신이 필요할 경우 이를 적용할 수 있음.

 

 

 

 

 

 


 

 

 

 

 

 

예제: Pod에서 서비스 어카운트 토큰을 사용하여 API 요청

TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
curl -X GET https://$KUBERNETES_SERVICE_HOST/api/v1/nodes \\
     --header "Authorization: Bearer $TOKEN" \\
     --insecure

 

 

설명

  • Pod 내부에서 API 서버에 직접 요청을 보내는 방법
  • `TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)` → Pod의 ServiceAccount Token을 읽음
  • curl -X GET → API 요청을 보내 클러스터 정보를 조회

 

위 요청이 실행되려면 해당 ServiceAccount에 적절한 권한(RBAC)이 필요함.

 

 

 

 


 

 

 

마무리 글: "Pod 내부 인증, ServiceAccount로 책임지자!"

 

ServiceAccount는 Kubernetes 클러스터 내부에서 실행되는

Pod가 안전하게 API 서버에 접근하도록 도와주는 핵심 리소스이다.

특히 1.24 이후 버전에서는 보안 강화를 위해 토큰 생성 및 마운트 방식에 변화가 생겼기 때문에,

이를 정확히 이해하고 대응하는 것이 중요하다.

 

 

자동 마운트를 비활성화하고 필요한 경우 수동 마운트를 사용하는 것, RBAC으로 최소 권한만 부여하는 것, 그리고 주기적인 토큰 회전 체크까 이 모든 것이 보안 사고를 예방하고 운영 안정성을 높이는 데 큰 역할을 한다.

 

 

지금 사용하는 클러스터에서 서비스 어카운트가 어떻게 설정되어 있는지, 어떤 권한이 있는지 꼭 한 번 점검해 보자.

 

앞으로 GitOps, Helm, CI/CD 파이프라인에서도 서비스 어카운트 기반 인증이 점점 더 중요해질 것이다.

 

 

 

 

 

 

 

 

 


Reference

https://kingofbackend.tistory.com/237

 

https://kubernetes.io/ko/docs/reference/access-authn-authz/service-accounts-admin/

 

https://www.bogotobogo.com/DevOps/Docker/Docker-Kubernetes-Service-Account.php

 

https://ssup2.github.io/theory_analysis/Kubernetes_Authentication_Service_Account/

728x90
반응형

'Container Orchestration > Kubernetes' 카테고리의 다른 글

Helm Chart 작성방법  (0) 2023.05.18
Helm 이란? (Kubernetes Package manager)  (2) 2023.05.16
Kubernetes Secret이란?  (0) 2023.05.09
MetalLB란?  (2) 2023.05.03
Kubernetes Resources(쿠버네티스 리소스)  (0) 2023.05.02