LabHub

쿠버네티스 운영 실무 · ServiceAccount 와 토큰 · 이론

ServiceAccount — 워크로드의 신원

LabHub 에서 이어서 보기

한 줄 요약

사람은 외부 인증 시스템으로 신원을 증명하지만, 파드는 ServiceAccount 로 증명한다. 그 증명서가 토큰이고, 요즘 토큰은 만료된다.

왜 이게 필요했나

클러스터 안에서 도는 프로그램도 API 를 호출한다. 오퍼레이터, 컨트롤러, 모니터링 에이전트, CI 러너가 모두 그렇다. 이들에게 사람 계정을 발급할 수는 없으므로 쿠버네티스는 워크로드 전용 신원인 ServiceAccount 를 둔다. 파드는 자기 SA 의 토큰을 파일로 마운트받고, apiserver 는 그 토큰으로 주체를 판별한다. 그다음 판정은 앞 모듈에서 배운 RBAC 가 한다. 인증(누구인가)과 인가(무엇을 할 수 있는가)는 별개라는 구조가 여기서 선명하게 보인다.

문제는 예전 방식이었다. 1.24 이전에는 SA 를 만들면 토큰이 담긴 Secret 이 자동으로 하나 생겼고, 그 토큰에는 만료가 없었다. 한 번 새어 나가면 영원히 유효한 자격증명이 클러스터마다 수십 개씩 굴러다녔다는 뜻이다. 로그에 찍히든 이미지 레이어에 남든 복사본을 추적할 방법도 없었다.

어떻게 동작하나

1.24 부터 기본이 바뀌었다. SA 를 만들어도 Secret 이 자동으로 생기지 않고, 파드에는 수명이 정해진 projected 토큰이 마운트된다. kubelet 이 만료 전에 알아서 갱신하므로 애플리케이션은 파일을 다시 읽기만 하면 된다.

토큰을 손으로 받을 때는 TokenRequest API 를 쓴다.

kubectl create token payments -n sa-lab --duration=3600s --audience=vault

이 토큰의 페이로드를 열어 보면 세 가지가 보인다. sub 는 주체(system:serviceaccount:<네임스페이스>:<이름>), aud이 토큰을 어디에 쓸 것인지, expiat 는 수명이다. aud 가 중요한 이유는 재사용 공격을 막기 때문이다. Vault 에 주려고 발급한 토큰이 apiserver 에도 통하면, 한 곳이 뚫렸을 때 다른 곳까지 열린다. 대상이 박힌 토큰은 그 대상에서만 유효하다.

파드 명세에 직접 쓰면 이렇게 된다.

volumes:  - name: vault-token    projected:      sources:        - serviceAccountToken:            audience: vault            expirationSeconds: 3600            path: vault-token

이 구조가 클라우드 워크로드 아이덴티티의 기반이다. 파드가 받은 이 짧은 수명의 토큰을 클라우드 STS 에 제시하면 임시 자격증명으로 바꿔 준다. 저장할 정적 키가 아예 사라진다.

반대 방향의 통제도 있다. 토큰이 필요 없는 파드에는 아예 주지 않는 것이다.

spec:  automountServiceAccountToken: false

이 필드는 파드에도, ServiceAccount 에도 달 수 있다. 어카운트 쪽에 걸면 그 어카운트를 쓰는 모든 파드에 기본 적용된다. 웹 서버처럼 API 를 전혀 호출하지 않는 워크로드는 토큰을 갖고 있을 이유가 없고, 컨테이너가 뚫렸을 때 공격자가 집어 갈 물건이 하나 줄어든다.

레거시 방식도 여전히 만들 수는 있다. kubernetes.io/service-account-token 타입 Secret 에 kubernetes.io/service-account.name 어노테이션을 달면 만료 없는 토큰이 생긴다. 클러스터 밖 도구가 TokenRequest 를 지원하지 않을 때의 마지막 수단이지만, 만료가 없다는 것은 폐기 절차를 사람이 100% 책임진다는 뜻이라 권장되지 않는다.

현장에서 만나는 모습

첫째, default 서비스 어카운트 남용. SA 를 지정하지 않은 파드는 네임스페이스의 default 를 쓴다. 그 결과 한 네임스페이스의 모든 파드가 같은 신원을 공유하게 되고, 권한을 조금이라도 주는 순간 그 권한이 전부에게 퍼진다. 파드마다 전용 SA 를 주는 것이 기본이다.

둘째, 권한은 여전히 RBAC 가 정한다. SA 를 만들었다고 아무 권한도 생기지 않는다. 반대로 편의로 걸어 둔 바인딩 하나가 워크로드에 과한 권한을 줄 수도 있다. --as 로 물어보는 습관이 여기서도 유효하다.

셋째, 토큰 파일 경로. 기본 마운트 경로는 /var/run/secrets/kubernetes.io/serviceaccount 다. 컨테이너 탈취 사고 분석에서 공격자가 가장 먼저 확인하는 경로이기도 하다. 사이드카나 디버그 컨테이너도 같은 것을 볼 수 있다는 점을 기억해야 한다.

다음 실습에서 할 것

전용 SA 를 만들어 파드에 붙이고 자동 마운트된 토큰 볼륨을 확인한다. 토큰을 끄는 두 가지 방법을 파드와 어카운트 양쪽에서 적용해 보고, TokenRequest 로 받은 토큰의 페이로드를 디코딩해 주체·대상·수명을 직접 읽는다. 대상이 지정된 projected 토큰을 명세로 쓰고, 레거시 장기 토큰 Secret 을 만들어 그 위험을 정리한 뒤, 마지막에 사용 현황 감사 보고서를 만든다.