ServiceAccount

개요

서비스 어카운트(SA)는 사람이 아닌, 클러스터 리소스로서 만들어진 계정 리소스이다.
가령 파드의 앱, 시스템 컴포넌트, 클러스터 안팎에서 이 서아의 자격 증명서를 서아로서 식별되기 위해 쓸 수 있다.
신원 베이스의 보안 정책을 구현할 때나 api 서버 인증에서 유용하다.

특징 - 유정 계정과의 차이

다음의 특징을 가진다.[1]

서비스어카운트는 클러스터에서 인간으로서 인증되는 유저 계정과 다르다.
일단 위의 특징들도 유저 계정과 분명히 다른 지점이고, 추가적으로 더 정리하자면,

용례

서비스 어카운트는 기본적으로 api 서버와 통신하기 위해 사용된다.
그런데 다른 용도로도 조금씩 사용될 수 있다!

서비스 어카운트 토큰

서비스 어카운트는 API 서버에 인증될 때 서비스 어카운트 토큰이라는 것을 사용한다.
이 토큰은 API 서버에서 만들어 제공되는 JWT 토큰으로, 서비스어카운트는 통신 시 헤더에 Authorization: Bearer <token>와 같은 식으로 사용한다.

그래서 청중 대상이 있고, 만료 시간이 있고, 유효 시간이 있는 것이다.
API 서버는 서비스 어카운트 토큰으로 들어온 요청에 대해 다음의 인증 과정을 거친다.

보통 서비스어카운트는 파드에 부여되는 개념이긴 하다.
그러나 엄밀하게 파드에서 사용될 것으로 용도가 국한된 것은 절대 아니다.
이 토큰은 여러 리소스에 부착될 수 있다.

구성

그렇다면 이 토큰은 어떻게 구성돼있을까?

{
  # 청중
  "aud": [
    "https://my-audience.example.com"
  ],
  # 만료시간
  "exp": 1729605240,
  # 발급된 시간
  "iat": 1729601640,
  # 발급 주체
  "iss": "https://my-cluster.example.com",
  # 토큰 고유 식별자
  "jti": "aed34954-b33a-4142-b1ec-389d6bbb4936",
  # 토큰이 유효화되는 시작 시간. Not BeFore
  "nbf": 1729601640,
  # 토큰의 주체
  "sub": "system:serviceaccount:my-namespace:my-serviceaccount"
  # 해당 토큰에 대해 추가적으로 넣어주는 정보
  "kubernetes.io": {
    "namespace": "my-namespace",
    "serviceaccount": {
      "name": "my-serviceaccount",
      "uid": "14ee3fa4-a7e2-420f-9f9a-dbc4507c3798"
    },
	# 노드와 파드 정보는 이 토큰이 파드에 마운팅됐을 때만 들어간다.
    "node": {
      "name": "my-node",
      "uid": "646e7c5e-32d6-4d42-9dbd-e504e6cbe6b1"
    },
    "pod": {
      "name": "my-pod",
      "uid": "5e0bd49b-f040-43b0-99b7-22765a53f7f3"
    },
  },
}

토큰의 클레임 부분은 이렇게 생겼다.
(Kubernetes v1.33 - Octarine 기준)

토큰 생성

서비스 어카운트 리소스를 만든다고 그냥 토큰이 갑자기 뿅 생기는 게 아니다.
크게는 두 가지 방식이 있는데, 한 쪽은 레거시 방식이라 추천되지 않는다.

TokenRequest API

1.22 버전 이후, 서비스 어카운트 토큰 생성은 TokenRequest API를 통해 이뤄진다.

POST /api/v1/namespaces/{namespace}/serviceaccounts/{name}/token

이건 말 그대로 서비스 어카운트의 토큰을 발행하는 API로, kubectl create token {SA}와 같은 식으로도 만들 수 있다.
이 토큰은 수명이 있어 만기되면 알아서 무효화되는데, 직접 api를 쏴서 받은 토큰을 알아서 갱신해야 한다.

다른 방식으로는 시크릿에 토큰을 주입하는 방식이다.

파드에 볼륨으로 마운팅된다는 토큰이 바로 이 API를 활용한다.[3]

volumes:
  - name: kube-api-access-<random-suffix>
    projected:
      sources:
        - serviceAccountToken:
            expirationSeconds: 3607
            path: token 

파드 스펙에 .spec.serviceAccountName을 넣어주면 api 서버에서는 해당 서비스 어카운트에 대한 토큰을 발급한 후 projected 볼륨으로 마운팅해서 넣어준다.

조금 더 구체적으로 토큰이 부여되는 과정을 파헤쳐보자.

참고로 1.22버전 이전에는 수명을 가지지 않은 토큰을 넣어줬다고 한다.

시크릿 주입

apiVersion: v1
kind: Secret
metadata:
  name: test
  annotations:
    kubernetes.io/service-account.name: test
type: kubernetes.io/service-account-token

서비스 어카운트 토큰을 담은 시크릿을 만드는 식으로 생성하는 것도 가능하다!
위처럼 시크릿을 만들면 해당 시크릿은 서비스 어카운트 토큰을 담은 채로 생성된다.

그러나 이 방식은 레거시로, 매우 위험하다.
왜냐하면 이렇게 생성된 토큰은 TokenRequest API를 거치지 않은 이전의 방식으로 생성되며 수명이 존재하지 않는 영구 토큰이기 때문이다.
1.24 버전까지는 서비스 어카운트를 생성하면 이 시크릿이 자동으로 만들어졌다고 한다.

클러스터 외부에서는 이렇게 지속시간이 긴 토큰이 필요할 수도 있을 것이다.
그럴 때 최후의 방법 정도로 고려해보자.
차라리 서비스 어카운트 인증이 아니라 다른 인증 방식을 사용하는 게 대부분의 경우에서 더 나은 선택일 것이다.
인증 웹훅을 쓰거나, 잘 보호된 개인키와 인증서를 써도 되고, 굳이 서비스 어카운트 토큰을 쓰고 싶으면 주기적으로 TokenRequest API를 사용하는 로직을 넣던가 하는 게 좋다.

apiVersion: v1
kind: ServiceAccount
metadata:
  name: build-robot
  namespace: default
secrets:
  - name: build-robot-secret

참고로 이렇게 서비스 어카운트를 생성할 때 같이 만들어지는 시크릿을 명시하는 방식도 가능하다.[4]

토큰 검증

직접 토큰의 유효성을 검증하고 싶은 너드들은 TokenReview API를 쓰면 된다.[5]
아니면 OIDC 디스커버리를 쓰는 것도 방법이다.
전자가 추천되는데, 파드나 다른 것에 바인딩된 토큰이 알아서 무효화되기 때문이다.
가령 파드에서 서아 토큰을 볼륨으로 둔 상태에서 이를 지우면, 토큰 리뷰는 바로 실패해준다.
oidc는 토큰의 만기 시간에 맞춰서 유효하다고 답을 내릴 가능성이 높다.
어플리케이션은 반드시 여기에서 항상 청중을 정의해야 한다.
그리고 그 청중이 토큰과 맞아야 한다.
이건 토큰의 영역을 최소화해준다.
그래서 앱에서만 사용될 수 있도록 해줄 것이다.

대안

토큰을 다른 매커니즘으로 발행하고, 웹훅 토큰 인증 과정을 통해 베어러 토큰으로 쓸 수도 있다.
파드의 고유 신원을 제공해주는 방법도 있다.
https://cert-manager.io 나, Istio같은 서비스 메쉬를 쓰는 것도 방법이다.

api서버가 oidc을 허용하게 하는 방법이 있다.
iam으로 하는 방법도 있다.
CertificateSigningRequest api를 이용하는 방법도 있다.

kubelet에서 프라이빗 레지스트리에서 접근할 때는 아예 처음부터 증명서 설정을 박아넣는 방법이 있다.
https://kubernetes.io/docs/tasks/administer-cluster/kubelet-credential-provider/#configuring-the-kubelet

Trusted Platform Module을 위해 디바이스 플러그인을 쓸 수있다.

관련 문서

이름 noteType created
ServiceAccount knowledge 2025-01-13
T-각 네임스페이스 서비스 어카운트에 권한 바인딩 topic/temp 2025-03-16
T-서비스 어카운트 토큰은 어떻게 인증되는가 topic/temp 2025-03-16

참고

  annotations:
    kubernetes.io/enforce-mountable-secrets: "true"

서비스 어카운트에 이런 어노테이션을 붙일 수 있다.
과거엔 이렇게 하면 서비스 어카운트의 시크릿이 파드에 마운팅되지 못하도록 할 수 있었다.
근데 지금은 마운팅이 싫다면 그냥 다른 네임스페이스로 격리할 것이 권장된다.


  1. https://kubernetes.io/docs/reference/access-authn-authz/service-accounts-admin/ ↩︎

  2. https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#add-imagepullsecrets-to-a-service-account ↩︎

  3. https://kubernetes.io/docs/reference/kubernetes-api/authentication-resources/token-request-v1/ ↩︎

  4. https://kubernetes.io/docs/reference/access-authn-authz/service-accounts-admin/#auto-generated-legacy-serviceaccount-token-clean-up ↩︎

  5. https://kubernetes.io/docs/reference/kubernetes-api/authentication-resources/token-review-v1/ ↩︎