서비스 계정
서비스 계정 (Service Accounts)
이 페이지는 Kubernetes의 ServiceAccount 객체를 소개해요. 서비스 계정이 어떻게 동작하는지, 사용 사례, 한계, 대안, 그리고 추가 지침을 위한 리소스 링크를 제공해요.
출처: 문서
본문
서비스 계정이란 무엇인가?
서비스 계정은 비인간(human) 계정 유형으로, Kubernetes에서 클러스터 안의 뚜렷한 신원(identity)을 제공해요. 애플리케이션 파드, 시스템 컴포넌트, 클러스터 안팎의 엔티티는 특정 ServiceAccount의 자격 증명을 사용해 그 ServiceAccount로 식별할 수 있어요. 이 신원은 API 서버에 인증하거나 신원 기반 보안 정책을 구현하는 등 다양한 상황에서 유용해요.
서비스 계정은 API 서버에 ServiceAccount 객체로 존재해요. 서비스 계정에는 다음 속성이 있어요.
- 네임스페이스 범위 (Namespaced): 각 서비스 계정은 Kubernetes 네임스페이스에 바인딩돼요. 모든 네임스페이스는 생성 시 기본(default) ServiceAccount를 받아요.
- 가벼움 (Lightweight): 서비스 계정은 클러스터에 존재하고 Kubernetes API에 정의돼요. 특정 작업을 활성화하기 위해 서비스 계정을 빠르게 만들 수 있어요.
- 이식성 (Portable): 복잡한 컨테이너화된 워크로드의 구성 번들은 시스템 컴포넌트에 대한 서비스 계정 정의를 포함할 수 있어요. 서비스 계정의 가벼운 특성과 네임스페이스 범위 신원 덕분에 구성이 이식 가능해져요.
서비스 계정은 클러스터에서 인증된 인간 사용자인 사용자 계정과 달라요. 기본적으로 사용자 계정은 Kubernetes API 서버에 존재하지 않아요. 대신 API 서버는 사용자 신원을 불투명한 데이터로 취급해요. 여러 방법으로 사용자 계정으로 인증할 수 있어요. 일부 Kubernetes 배포판은 사용자 계정을 API 서버에 표현하기 위한 사용자 정의 확장 API를 추가할 수도 있어요.
| 설명 | ServiceAccount | 사용자 또는 그룹 |
|---|---|---|
| 위치 | Kubernetes API (ServiceAccount 객체) | 외부 |
| 접근 제어 | Kubernetes RBAC 또는 기타 인가 메커니즘 | Kubernetes RBAC 또는 기타 신원 및 접근 관리 메커니즘 |
| 의도된 용도 | 워크로드, 자동화 | 사람 |
기본 서비스 계정 (Default service accounts)
클러스터를 만들면 Kubernetes는 클러스터의 모든 네임스페이스에 대해 default라는 이름의 ServiceAccount 객체를 자동으로 만들어요. 각 네임스페이스의 default 서비스 계정은 역할 기반 접근 제어(RBAC)가 활성화되어 있을 때 Kubernetes가 모든 인증된 주체에게 부여하는 기본 API 디스커버리 권한 외에는 기본적으로 아무 권한도 없어요. 네임스페이스에서 default ServiceAccount 객체를 삭제하면 컨트롤 플레인이 새 것으로 교체해요.
네임스페이스에 파드를 배포하면서 파드에 ServiceAccount를 수동으로 할당하지 않으면, Kubernetes는 그 네임스페이스의 기본 default ServiceAccount를 파드에 할당해요.
Kubernetes 서비스 계정의 사용 사례
일반적 지침으로, 다음 시나리오에서 신원을 제공하기 위해 서비스 계정을 사용할 수 있어요.
- 파드가 Kubernetes API 서버와 통신해야 하는 경우. 예를 들면: Secrets에 저장된 민감 정보에 대한 읽기 전용 접근 제공. 크로스 네임스페이스 접근 부여, 예를 들어
example네임스페이스의 파드가kube-node-lease네임스페이스의 Lease 객체를 읽고·나열하고·감시하도록 허용. - 파드가 외부 서비스와 통신해야 하는 경우. 예를 들어 워크로드 파드가 상용 클라우드 API에 대한 신원을 요구하고, 상용 프로바이더가 적절한 신뢰 관계를 구성하도록 허용하는 경우.
- imagePullSecret으로 프라이빗 이미지 레지스트리에 인증.
- 외부 서비스가 Kubernetes API 서버와 통신해야 하는 경우. 예를 들어 CI/CD 파이프라인의 일부로 클러스터에 인증.
- 클러스터에서 서로 다른 파드의 ServiceAccount 신원에 의존해 그 파드들을 서로 다른 컨텍스트로 그룹화하는 서드파티 보안 소프트웨어를 사용하는 경우.
서비스 계정 사용 방법
Kubernetes 서비스 계정을 사용하려면 다음을 수행해요.
- kubectl 같은 Kubernetes 클라이언트나 객체를 정의하는 매니페스트로 ServiceAccount 객체를 만들어요.
- RBAC 같은 인가 메커니즘으로 ServiceAccount 객체에 권한을 부여해요.
- 파드 생성 중에 ServiceAccount 객체를 파드에 할당해요. 외부 서비스의 신원을 사용한다면 ServiceAccount 토큰을 검색해 그 서비스에서 대신 사용해요.
지침은 파드용 서비스 계정 구성을 참고해요.
ServiceAccount에 권한 부여
내장 Kubernetes RBAC 메커니즘을 사용해 각 서비스 계정에 필요한 최소 권한을 부여할 수 있어요. 접근을 부여하는 role을 만들고, 그 role을 ServiceAccount에 바인딩해요. RBAC는 최소 권한 집합을 정의하게 해주므로 서비스 계정 권한이 최소 권한(least privilege) 원칙을 따르게 돼요. 그 서비스 계정을 사용하는 파드는 올바르게 동작하는 데 필요한 것보다 더 많은 권한을 얻지 않아요.
지침은 ServiceAccount 권한을 참고해요.
ServiceAccount로 크로스 네임스페이스 접근
RBAC를 사용해 한 네임스페이스의 서비스 계정이 클러스터의 다른 네임스페이스의 리소스에 작업을 수행하도록 허용할 수 있어요. 예를 들어 dev 네임스페이스에 서비스 계정과 파드가 있고, 파드가 maintenance 네임스페이스에서 실행 중인 Job을 볼 수 있게 하고 싶다고 해봐요. Job 객체 나열 권한을 부여하는 Role 객체를 만든 다음, maintenance 네임스페이스에 RoleBinding 객체를 만들어 Role을 ServiceAccount에 바인딩할 수 있어요. 이제 dev 네임스페이스의 파드는 그 서비스 계정을 사용해 maintenance 네임스페이스의 Job 객체를 나열할 수 있어요.
파드에 ServiceAccount 할당
파드에 ServiceAccount를 할당하려면 파드 사양에서 spec.serviceAccountName 필드를 설정해요. 그러면 Kubernetes가 그 ServiceAccount의 자격 증명을 자동으로 파드에 제공해요. v1.22 이상에서 Kubernetes는 TokenRequest API를 사용해 수명이 짧고 자동 회전되는 토큰을 얻어, 그 토큰을 projected volume으로 마운트해요.
기본적으로 Kubernetes는 할당된 ServiceAccount(기본 default든 지정한 커스텀 ServiceAccount든)의 자격 증명을 파드에 제공해요. 지정된 ServiceAccount나 기본 ServiceAccount의 자격 증명을 자동 주입하지 않으려면 파드 사양에서 automountServiceAccountToken 필드를 false로 설정해요.
1.22 이전 버전에서는 Kubernetes가 수명이 길고 정적인 토큰을 Secret으로 파드에 제공했어요.
수동으로 ServiceAccount 자격 증명 가져오기
ServiceAccount 자격 증명을 비표준 위치에 마운트하거나, API 서버가 아닌 대상을 위해 필요하다면 다음 방법 중 하나를 사용해요.
- TokenRequest API (권장): 내 애플리케이션 코드 내에서 수명이 짧은 서비스 계정 토큰을 요청해요. 토큰은 자동으로 만료되고 만료 시 회전할 수 있어요. Kubernetes를 모르는 레거시 애플리케이션이 있다면, 같은 파드 안의 사이드카 컨테이너를 사용해 이 토큰을 가져와 애플리케이션 워크로드에 제공할 수 있어요.
- 토큰 볼륨 프로젝션 (또한 권장): Kubernetes v1.20 이상에서 Pod 사양을 사용해 kubelet이 서비스 계정 토큰을 projected volume으로 파드에 추가하도록 지시해요. Projected 토큰은 자동으로 만료되고, kubelet이 만료 전에 토큰을 회전해요.
- 서비스 계정 토큰 시크릿 (비권장): 서비스 계정 토큰을 Kubernetes Secret으로 파드에 마운트할 수 있어요. 이 토큰은 만료되지 않고 회전도 하지 않아요. v1.24 이전 버전에서는 각 서비스 계정에 대해 영구 토큰이 자동으로 생성됐어요. 정적이고 수명이 긴 자격 증명과 관련된 위험 때문에, 특히 규모가 클 때 이 방법은 더 이상 권장되지 않아요.
LegacyServiceAccountTokenNoAutoGeneration기능 게이트(Kubernetes v1.24에서 v1.26까지 기본 활성화)는 Kubernetes가 ServiceAccount에 대해 이 토큰을 자동 생성하지 못하게 했어요. 이 기능 게이트는 v1.27에서 GA로 승격되어 제거됐어요. 무기한 서비스 계정 토큰을 수동으로 만들 수는 있지만, 보안 영향을 고려해야 해요.
서비스 계정 토큰의 노드 대상(오디언스) 제한
ServiceAccountNodeAudienceRestriction 기능 게이트가 활성화되면, NodeRestriction admission 플러그인이 kubelet이 TokenRequest API로 서비스 계정 토큰을 만들 때 요청할 수 있는 오디언스(audience)를 제한해요. 기본적으로 kubelet은 그 노드의 파드가 이미 참조하는 오디언스(projected 서비스 계정 토큰 볼륨이나 CSI 드라이버 토큰 요청을 통해)에 대해서만 토큰을 요청할 수 있어요. 관리자는 request-serviceaccounts-token-audience 동사가 있는 RBAC 규칙으로 kubelet에 추가 오디언스에 대한 접근을 부여할 수 있어요.
이 제한은 kubelet(노드 신원)에만 적용되며 TokenRequest API의 다른 호출자에는 영향을 주지 않아요. 자세한 내용과 RBAC 예시는 서비스 계정 토큰 오디언스 제한을 참고해요.
Kubernetes 클러스터 밖에서 실행되는 애플리케이션의 경우, Secret에 저장된 수명이 긴 ServiceAccount 토큰을 만드는 것을 고려할 수 있어요. 이것은 인증을 가능하게 하지만, Kubernetes 프로젝트는 이 접근 방식을 피할 것을 권장해요. 수명이 긴 베어러 토큰은 일단 공개되면 오용될 수 있으므로 보안 위험이에요. 대신 대안을 고려해요. 예를 들어 외부 애플리케이션은 잘 보호되는 프라이빗 키와 인증서로 인증하거나, 직접 구현한 인증 웹훅 같은 커스텀 메커니즘을 사용할 수 있어요. TokenRequest를 사용해 외부 애플리케이션용 수명이 짧은 토큰을 얻을 수도 있어요.
시크릿 접근 제한 (사용 중단됨)
Kubernetes는 ServiceAccount에 추가할 수 있는 kubernetes.io/enforce-mountable-secrets라는 어노테이션을 제공해요. 이 어노테이션을 적용하면 ServiceAccount의 시크릿은 지정된 유형의 리소스에만 마운트될 수 있어, 클러스터의 보안 상태를 강화해요.
매니페스트로 ServiceAccount에 어노테이션을 추가할 수 있어요.
apiVersion: v1
kind: ServiceAccount
metadata:
annotations:
kubernetes.io/enforce-mountable-secrets: "true"
name: my-serviceaccount
namespace: my-namespace
이 어노테이션이 "true"로 설정되면 Kubernetes 컨트롤 플레인은 이 ServiceAccount의 Secrets가 특정 마운트 제한을 받도록 보장해요.
- 파드에서 볼륨으로 마운트되는 각 Secret의 이름은 파드의 ServiceAccount의
secrets필드에 나타나야 해요. - 파드에서
envFrom으로 참조되는 각 Secret의 이름도 파드의 ServiceAccount의secrets필드에 나타나야 해요. - 파드에서
imagePullSecrets로 참조되는 각 Secret의 이름도 파드의 ServiceAccount의secrets필드에 나타나야 해요.
이런 제한을 이해하고 강제함으로써 클러스터 관리자는 더 타이트한 보안 프로파일을 유지하고, 시크릿이 적절한 리소스에만 접근되도록 보장할 수 있어요.
서비스 계정 자격 증명 인증
ServiceAccount는 서명된 JSON Web Token(JWT)을 사용해 Kubernetes API 서버와, 신뢰 관계가 존재하는 다른 시스템에 인증해요. 토큰이 발급된 방식(TokenRequest로 시간 제한 또는 Secret으로 레거시 메커니즘)에 따라 ServiceAccount 토큰은 만료 시간, 오디언스, 토큰이 유효해지기 시작하는 시간을 가질 수도 있어요. ServiceAccount로 행동하는 클라이언트가 Kubernetes API 서버와 통신을 시도하면, 클라이언트는 HTTP 요청에 Authorization: Bearer *** 헤더를 포함해요. API 서버는 그 베어러 토큰을 다음과 같이 검증해요.
- 토큰 서명 확인
- 토큰 만료 여부 확인
- 토큰 클레임의 객체 참조가 현재 유효한지 확인
- 토큰이 현재 유효한지 확인
- 오디언스 클레임 확인
TokenRequest API는 ServiceAccount에 대해 바운드(bound) 토큰을 생성해요. 이 바인딩은 파드처럼 그 ServiceAccount로 행동하는 클라이언트의 수명과 연결돼요. 바운드 파드 서비스 계정 토큰의 JWT 스키마와 페이로드 예시는 토큰 볼륨 프로젝션을 참고해요.
TokenRequest API로 발급된 토큰의 경우 API 서버는 그 ServiceAccount를 사용하는 특정 객체 참조가 여전히 존재하는지, 그 객체의 고유 ID로 일치시켜 확인해요. 파드에 Secret으로 마운트된 레거시 토큰의 경우 API 서버는 토큰을 Secret에 대해 검증해요.
인증 프로세스에 대한 자세한 내용은 인증을 참고해요.
자체 코드에서 서비스 계정 자격 증명 인증
Kubernetes 서비스 계정 자격 증명을 검증해야 하는 자체 서비스가 있다면 다음 방법을 사용할 수 있어요.
- TokenReview API (권장)
- OIDC 디스커버리
Kubernetes 프로젝트는 TokenReview API 사용을 권장해요. 이 방법은 Secrets, ServiceAccounts, Pods, Nodes 같은 API 객체에 바인딩된 토큰을 그 객체가 삭제될 때 무효화하기 때문이에요. 예를 들어 projected ServiceAccount 토큰이 포함된 파드를 삭제하면 클러스터가 그 토큰을 즉시 무효화하고 TokenReview가 즉시 실패해요. 대신 OIDC 검증을 사용하면 클라이언트는 토큰이 만료 타임스탬프에 도달할 때까지 그 토큰을 유효하다고 계속 취급해요.
애플리케이션은 항상 수용하는 오디언스를 정의하고, 토큰의 오디언스가 애플리케이션이 기대하는 오디언스와 일치하는지 확인해야 해요. 이렇게 하면 토큰의 범위를 최소화해 애플리케이션에서만 사용되고 다른 곳에서는 사용할 수 없게 돼요.
대안 (Alternatives)
- 다른 메커니즘으로 자체 토큰을 발급한 다음 웹훅 토큰 인증을 사용해 자체 검증 서비스로 베어러 토큰을 검증해요.
- 파드에 자체 신원을 제공해요. SPIFFE CSI driver 플러그인을 사용해 SPIFFE SVID를 X.509 인증서 쌍으로 파드에 제공하거나, Istio 같은 서비스 메시를 사용해 파드에 인증서를 제공해요. (이 항목은 Kubernetes 자체가 아닌 서드파티 프로젝트/제품을 연결해요.)
- 클러스터 밖에서 서비스 계정 토큰 없이 API 서버에 인증: 신원 공급자의 OpenID Connect(OIDC) 토큰을 받아들이도록 API 서버를 구성해요. 클라우드 프로바이더 같은 외부 IAM(Identity and Access Management) 서비스로 만든 서비스 계정이나 사용자 계정을 사용해 클러스터에 인증해요. 클라이언트 인증서로 CertificateSigningRequest API를 사용해요.
- kubelet이 이미지 레지스트리에서 자격 증명을 가져오도록 구성해요.
- 가상 Trusted Platform Module(TPM)에 접근하는 디바이스 플러그인을 사용해, 프라이빗 키로 인증할 수 있게 해요.
다음 단계
- 클러스터 관리자로서 ServiceAccount 관리 방법 배우기
- 파드에 ServiceAccount 할당 방법 배우기
- ServiceAccount API 참조 읽기