본문으로 건너뛰기

9.1 인증

kubectl 명령 하나가 실행될 때마다 apiserver는 그 요청이 누구에게서 왔는지부터 확인한다. 이 첫 단계가 인증(authentication)이다. 여기서 많은 입문자가 부딪히는 사실이 하나 있다. Kubernetes에는 사람 사용자를 나타내는 User라는 API 오브젝트가 없다. kubectl get users 같은 명령은 존재하지 않고, 사용자를 생성하는 API도 없다. Kubernetes는 사용자 계정 관리를 클러스터 바깥에 위임하고, apiserver는 요청에 실려 온 자격 증명을 검증해 사용자 이름과 그룹을 알아내는 일만 담당한다.

인증은 클러스터 바깥에 위임된다

apiserver에 도착한 요청은 세 단계를 통과한 뒤에야 etcd에 반영된다. 인증이 “누구인가"를, 인가가 “허용된 동작인가"를, 어드미션 제어가 “내용이 정책에 맞는가"를 판정한다.

    flowchart TD
  C["클라이언트<br/>(kubectl, Pod)"] --> A["인증: 누구인가"]
  A --> Z["인가: 허용되는가"]
  Z --> AD["어드미션 제어"]
  AD --> E[("etcd 저장")]
  

Kubernetes가 구분하는 요청 주체는 두 종류다. 사람 사용자(normal user)와 ServiceAccount다. ServiceAccount는 네임스페이스에 속하는 실제 API 오브젝트로 존재하며 9.3에서 다룬다. 사람 사용자는 클러스터 안에 대응하는 오브젝트가 없다. 클러스터 CA가 서명한 인증서를 가진 사람, 유효한 토큰을 제시한 사람, 외부 IdP가 신원을 보증한 사람이 곧 사용자다. 어떤 방식이든 검증에 성공하면 apiserver는 요청에 사용자 이름(username)과 그룹(groups) 목록을 부여하고 다음 단계로 넘긴다. 이후의 인가(RBAC)는 이 문자열들만 보고 판단한다.

인증 방식은 여러 개를 동시에 활성화하는 구성이 일반적이다. apiserver는 활성화된 인증기를 차례로 시도하고 하나라도 성공하면 인증을 끝낸다.

X.509 클라이언트 인증서

클러스터 CA가 서명한 클라이언트 인증서를 제시하면 apiserver는 인증서의 subject에서 신원을 읽는다. CN(Common Name)이 사용자 이름, O(Organization)가 그룹이 된다. O 필드는 여러 개를 넣어 한 사용자를 여러 그룹에 소속시킨다. 예를 들어 /CN=jane/O=dev-team/O=viewers로 서명된 인증서를 쓰는 요청은 사용자 jane, 그룹 dev-team과 viewers로 인증된다.

인증서 발급은 CertificateSigningRequest 리소스로 진행한다.

# 키와 CSR 생성
openssl genrsa -out jane.key 2048
openssl req -new -key jane.key -out jane.csr -subj "/CN=jane/O=dev-team"

# CSR 오브젝트 제출
kubectl apply -f - <<EOF
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
  name: jane
spec:
  request: $(base64 < jane.csr | tr -d '\n')
  signerName: kubernetes.io/kube-apiserver-client
  expirationSeconds: 86400        # 1일
  usages: ["client auth"]
EOF

# 관리자 승인 후 인증서 추출
kubectl certificate approve jane
kubectl get csr jane -o jsonpath='{.status.certificate}' | base64 -d > jane.crt
Kubernetes에는 인증서 폐기(revocation) 수단이 없다. 한번 서명된 인증서는 만료 전까지 유효하며, 유출돼도 CA를 통째로 교체하기 전에는 무효화하지 못한다. expirationSeconds를 짧게 설정하는 이유가 이것이다. 특히 그룹 system:masters는 apiserver에 하드코딩된 슈퍼유저 그룹이라 RBAC 설정과 무관하게 모든 권한을 가지므로, 이 그룹을 O에 넣은 인증서는 발급 자체를 피한다.

bearer 토큰과 OIDC

HTTP Authorization: Bearer <token> 헤더로 신원을 증명하는 방식이다. 가장 단순한 형태는 apiserver 기동 시 --token-auth-file로 지정하는 static token file인데, 토큰을 바꾸려면 apiserver 재시작이 필요해 테스트 용도에 가깝다. kubeadm join에 쓰이는 bootstrap token, Pod가 쓰는 ServiceAccount 토큰도 bearer 토큰의 일종이다.

사람 사용자를 조직 규모로 관리할 때는 OIDC(OpenID Connect) 연동이 표준에 가깝다. 사용자는 Keycloak, Dex, 클라우드 IAM 같은 외부 IdP에 로그인해 ID 토큰(JWT)을 받고, kubectl이 이 토큰을 요청에 첨부한다. apiserver는 IdP의 공개 키로 토큰의 서명과 발급자, 만료를 검증하고 토큰의 claim에서 사용자 이름과 그룹을 읽는다. 요청마다 IdP를 호출하는 구조가 아니라 서명 검증으로 끝나는 구조다.

apiserver 쪽 설정은 플래그 몇 개다.

--oidc-issuer-url=https://idp.example.com/realms/dev
--oidc-client-id=kubernetes
--oidc-username-claim=email
--oidc-groups-claim=groups

OIDC의 장점은 계정 수명 주기가 IdP에서 끝난다는 점이다. 퇴사자 계정 비활성화나 그룹 변경이 클러스터 설정과 무관하게 즉시 반영되고, 토큰 만료가 짧아 인증서의 폐기 불가 문제도 없다.

kubeconfig 해부

kubectl이 어느 서버에 어떤 자격 증명으로 접속할지는 kubeconfig 파일(기본 ~/.kube/config)이 결정한다. 구조는 세 목록과 포인터 하나다.

apiVersion: v1
kind: Config
clusters:                     # 접속 대상 apiserver 목록
- name: dev-cluster
  cluster:
    server: https://api.example.com:6443
    certificate-authority-data: <base64 CA>
users:                        # 제시할 자격 증명 목록
- name: jane
  user:
    client-certificate-data: <base64 인증서>
    client-key-data: <base64 개인 키>
contexts:                     # cluster + user + 기본 네임스페이스 조합
- name: jane@dev-cluster
  context:
    cluster: dev-cluster
    user: jane
    namespace: default
current-context: jane@dev-cluster

clusters는 서버 주소와 그 서버를 신뢰하기 위한 CA다. users는 인증서나 토큰, 또는 외부 명령으로 토큰을 받아 오는 exec 플러그인 같은 자격 증명이다. contexts가 둘을 짝지어 “이 클러스터에 이 신원으로 접속한다"를 정의하고, current-context가 지금 쓸 조합을 가리킨다.

kubectl config get-contexts
kubectl config use-context jane@dev-cluster

401과 403은 다른 단계의 실패다

인증 문제를 진단할 때는 두 상태 코드의 구분이 출발점이다.

코드실패한 단계의미
401 Unauthorized인증요청자가 누구인지 판별 실패
403 Forbidden인가신원은 확인됐으나 동작 불허

401이면 자격 증명 자체가 문제다. 만료된 인증서, 잘못된 토큰, kubeconfig의 user 항목 누락을 확인한다. 403이면 인증은 이미 성공했다. 에러 메시지에 apiserver가 식별한 사용자 이름이 그대로 나오므로, 의도한 신원으로 인증됐는지 먼저 읽고 나서 RBAC 권한을 확인한다. 403을 보고 인증서를 재발급하는 것은 방향이 틀린 대응이다.

정리

  • Kubernetes에 User 오브젝트는 없다. 사용자 관리는 인증서, 토큰, 외부 IdP에 위임되고, apiserver는 검증 결과로 사용자 이름과 그룹 문자열을 얻는다.
  • X.509 인증서는 CN이 사용자, O가 그룹이다. 폐기 수단이 없으므로 만료를 짧게 설정하고 system:masters를 O에 넣지 않는다.
  • 401은 인증 실패, 403은 인가 실패다. 403이 나오면 자격 증명이 아니라 RBAC을 확인한다.