본문 바로가기
클라우드/쿠버네티스

[Kubernetes] "내 파드의 신분증이 바뀌었다?" Service Account와 Projected Volume 완벽 해부 🆔

by gasbugs 2025. 12. 19.

안녕하세요! 오늘은 쿠버네티스 보안의 기본이자 핵심인 Service Account(서비스 어카운트)에 대해 깊이 파헤쳐 보려 합니다.

단순히 "파드(Pod)에 권한 줄 때 쓰는 거 아니야?"라고 알고 계셨다면, 오늘 포스팅을 통해 조금 더 깊은 내용을 가져가실 수 있습니다. 특히 최근 쿠버네티스 버전에서 표준이 된 Projected VolumeBound Token 개념은 보안 측면에서 매우 중요하기 때문에 꼭 이해하고 넘어가야 합니다.

10분만 투자해서 쿠버네티스 인증 구조의 핵심을 마스터해 보세요! 🚀

 


1. Service Account(SA): 파드를 위한 신분증 🪪

우리가 회사 건물에 들어갈 때 사원증을 찍듯이, 쿠버네티스 클러스터 내부에서 파드(Pod) 내의 애플리케이션이 API 서버와 소통하려면 신분증이 필요합니다. 이것이 바로 Service Account입니다.

👤 User Account vs. 🤖 Service Account

  • User Account: 사람(Human)을 위한 계정입니다. (관리자, 개발자 등). 주로 전역적(Global)으로 관리되며, AWS IAM이나 Google 계정 등 외부 시스템과 연결됩니다.
  • Service Account: 프로세스(Bot)를 위한 계정입니다. 네임스페이스(Namespace)에 종속된다는 점이 가장 큰 특징입니다.

파드를 생성할 때 별도로 지정하지 않으면, 쿠버네티스는 자동으로 해당 네임스페이스의 default 서비스 어카운트를 할당합니다.


2. 과거의 방식: "영원히 썩지 않는 열쇠" (Legacy) 🗝️

예전(Legacy) 방식에서는 서비스 어카운트를 생성하면 자동으로 Secret(시크릿) 리소스가 만들어졌습니다. 그리고 이 시크릿 안에는 만료 기한이 없는(No Expiration) 토큰이 들어있었죠.

이 방식은 치명적인 단점이 있었습니다.

  1. 보안 취약: 토큰을 한 번 탈취당하면, 관리자가 수동으로 폐기하기 전까지 해커가 영원히 사용할 수 있습니다.
  2. 관리 불편: 토큰을 교체(Rotation) 하려면 시크릿을 지우고 다시 만드는 번거로운 작업이 필요했습니다.

그래서 쿠버네티스 커뮤니티는 Projected Volume이라는 새로운 기술을 도입하게 됩니다.


3. 현재의 표준: Projected Volume과 "살아있는 토큰" ✨

최신 쿠버네티스에서는 토큰을 정적인 Secret에 저장하지 않습니다. 대신 Projected Volume을 이용해 파드가 생성되는 그 순간에 API 서버로부터 "임시 토큰"을 발급받아 파드에 꽂아줍니다.

📽️ 왜 이름이 "Projected(투영된)" 인가요?

일반적인 볼륨 마운트가 하나의 저장소를 연결하는 것이라면, Projected Volume여러 소스(Source)를 한 곳의 디렉터리에 모아서(투영해서) 보여주는 기술이기 때문입니다.

보통 다음 세 가지 정보를 한 폴더(/var/run/secrets/kubernetes.io/serviceaccount)에 섞어서 보여줍니다.

  1. serviceAccountToken: 인증을 위한 JWT 토큰
  2. configMap (kube-root-ca.crt): API 서버 신뢰를 위한 인증서
  3. downwardAPI: 파드의 네임스페이스 정보

4. 핵심 기술: Bound Service Account Token 🔗

Projected Volume을 통해 주입되는 토큰은 Bound Token이라고 부릅니다. 어딘가에 강력하게 "묶여(Bound)" 있기 때문입니다. 이 개념이 보안의 핵심입니다.

① Time Bound (시간 제한) ⏳

이 토큰은 유효기간이 있습니다 (기본 1시간).

  • Legacy: 유효기간 없음.
  • Modern: expirationSeconds가 설정됨.

② Object Bound (파드 종속) 📦

이 토큰은 발급된 특정 파드(Pod UID)와 운명을 같이 합니다.

  • 파드가 삭제되면 토큰도 즉시 무효화됩니다.
  • 공격자가 토큰을 복사해서 다른 파드에서 쓰려고 해도, 토큰 내부의 정보와 실행 환경이 일치하지 않으면 거부될 수 있습니다.

③ Audience Bound (사용처 제한) 🎯

토큰에 aud(Audience) 필드를 설정할 수 있습니다.

  • "이 토큰은 AWS 인증용이야"라고 발급받았다면, 이 토큰으로 쿠버네티스 API 서버를 해킹하려 해도 "어? 넌 AWS용이잖아?" 하고 거부합니다.
  • Istio 같은 서비스 메시가 이 기능을 적극 활용하여 서비스 간 인증을 수행합니다.

5. YAML 파일 뜯어보기 📝

여러분의 파드 YAML 파일 맨 아래쪽을 보면, kubelet이 자동으로 추가한 다음과 같은 설정을 볼 수 있습니다.

YAML

volumes:
  - name: kube-api-access-xxxxx
    projected:
      defaultMode: 420
      sources:
      - serviceAccountToken:
          expirationSeconds: 3607  # 👈 1시간 뒤 만료!
          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

이 설정 덕분에 파드 내부의 애플리케이션은 /var/run/secrets/kubernetes.io/serviceaccount/token 파일을 읽어서 안전하게 통신할 수 있는 것입니다.


6. 토큰이 만료되면 앱은 죽나요? (Token Rotation) 🔄

"1시간 뒤에 만료된다면, 내 서비스도 1시간 뒤에 멈추나요?"

아니요! 걱정하지 마세요.

Kubelet이 아주 부지런하게 움직입니다.

  1. 토큰 만료 시간이 다가오면 Kubelet이 자동으로 API 서버에 새 토큰을 요청합니다.
  2. 그리고 파드 내부에 마운트 된 token 파일을 새로운 토큰 값으로 덮어쓰기(Update) 합니다.
  3. 애플리케이션(대부분의 K8s 클라이언트 라이브러리)은 주기적으로 파일을 다시 읽어서 토큰을 갱신합니다.

⚠️ 주의할 점: 직접 애플리케이션을 개발한다면, 토큰 파일을 처음에만 읽고 끝내는 것이 아니라 주기적으로 다시 읽는 로직(Reload)을 구현해야 합니다. (Go 클라이언트 등 표준 라이브러리는 이미 구현되어 있습니다.)


7. 정리하며 🎁

오늘 내용을 세 줄로 요약해 보겠습니다.

  1. Service Account는 파드가 쿠버네티스 API와 대화하기 위한 신분증이다.
  2. 보안을 위해 Projected Volume 방식을 사용하여, 수명이 짧고 자동 갱신되는 토큰(Bound Token)을 사용한다.
  3. 이 토큰은 파드와 운명을 같이하며, 특정 파드에 묶여(Bound) 있어 탈취되어도 위험이 적다.

이제 kubectl get pod -o yaml을 쳤을 때 나오는 복잡한 projected 구문이 두렵지 않으시죠? 이것은 여러분의 클러스터를 안전하게 지키기 위한 쿠버네티스의 세심한 배려랍니다. 🛡️

오늘도 안전하고 즐거운 쿠버네티스 생활 되세요!