Published on

Kubernetes 401 Unauthorized 원인별 해결 가이드

Authors

서버가 401 Unauthorized를 반환할 때 많은 분들이 곧바로 RBAC(403 Forbidden)부터 의심하지만, Kubernetes에서 401은 **"인증(Authentication) 실패"**에 더 가깝습니다. 즉, API 서버가 "당신이 누구인지"를 확인하지 못했거나(토큰/인증서 문제), 아예 인증 정보가 잘못 전달되고 있거나(잘못된 kubeconfig/컨텍스트/프록시), OIDC 플로우가 깨진 경우가 대부분입니다.

이 글은 kubectl 기준으로 가장 흔한 401 원인을 분류하고, 각각을 재현 가능한 점검 명령해결 절차로 정리합니다. EKS 같은 매니지드 환경에서 자주 발생하는 케이스도 포함합니다.

1) 401 vs 403: 먼저 구분해야 진단이 빨라진다

  • 401 Unauthorized: 인증 실패
    • 토큰/클라이언트 인증서가 없거나 잘못됨
    • 만료됨
    • OIDC/웹훅 인증이 실패
    • API 서버까지 가는 경로에서 Authorization 헤더가 제거/변조
  • 403 Forbidden: 인증은 됐지만 권한이 없음(RBAC/IAM AuthZ)

즉, 401이면 우선 **"내 요청이 어떤 사용자/서비스어카운트로 인증되고 있는가"**를 확인해야 합니다.

빠른 확인 체크

kubectl version --short
kubectl cluster-info
kubectl auth whoami 2>/dev/null || echo "(구버전 kubectl이면 미지원)"

# API 서버에 실제로 어떤 인증이 붙어 나가는지
kubectl get ns -v=8 2>&1 | sed -n '1,120p'

-v=8 로그에서 Authorization: Bearer ... 혹은 클라이언트 인증서 사용 여부를 확인할 수 있습니다(민감정보 출력 주의).

2) kubeconfig 컨텍스트/클러스터가 꼬여서 401

가장 흔한 실수는 다른 클러스터 컨텍스트로 요청을 보내거나, user 섹션이 깨져 인증이 빠지는 경우입니다.

증상

  • 특정 클러스터만 401
  • kubectl config current-context가 예상과 다름
  • KUBECONFIG 환경변수로 여러 파일을 합친 뒤 user/cluster 매핑이 깨짐

점검

kubectl config get-contexts
kubectl config current-context
kubectl config view --minify

# KUBECONFIG가 의도치 않게 설정됐는지
echo $KUBECONFIG

해결

  1. 올바른 컨텍스트로 전환
kubectl config use-context <expected-context>
  1. kubeconfig 재생성/재설정(EKS 예시)
aws eks update-kubeconfig --region ap-northeast-2 --name <cluster-name>

> EKS에서 인증이 엮인 이슈는 401이 아니라 403(AccessDenied)로도 자주 나타납니다. IRSA/IAM 쪽 권한 문제라면 아래 글도 함께 보세요: EKS IRSA인데 AccessDenied? OIDC·TrustPolicy·SA 점검

3) Exec 플러그인(aws-iam-authenticator, kubelogin 등) 실패로 401

kubeconfig의 users.user.exec는 kubectl이 외부 명령을 실행해 토큰을 받아오는 방식입니다. 이 실행이 실패하면 토큰이 비어 401이 납니다.

증상

  • error: exec plugin: invalid apiVersion 또는 실행 파일을 못 찾는 에러
  • 로컬에서는 되는데 CI에서만 401
  • AWS SSO/프로파일 만료 후 401

점검

# kubeconfig에서 exec 설정 확인
kubectl config view --minify -o jsonpath='{.users[0].user.exec}' ; echo

# 실제 exec 커맨드를 직접 실행해보기(예: aws eks get-token)
aws eks get-token --region <region> --cluster-name <cluster>

해결 포인트

  • kubectl 버전과 exec apiVersion 호환성 확인
  • CI 환경에 aws/kubelogin 바이너리 설치 및 PATH 확인
  • AWS SSO 사용 시 재로그인
aws sso login --profile <profile>
  • 프로파일/환경변수(AWS_PROFILE, AWS_REGION)가 의도대로 적용되는지 확인

4) 토큰 만료/시간 불일치(NTP)로 401

OIDC 또는 STS 기반 토큰은 만료가 빠르고, 노트북 절전/VM 시간 드리프트로도 실패합니다.

증상

  • 잠깐 잘 되다가 갑자기 401
  • token is expired 류 메시지(로그 레벨 높이면 보임)

점검

# 로컬 시간 확인
date

# (리눅스) NTP 동기화 상태
timedatectl status | sed -n '1,20p'

해결

  • NTP 동기화
  • 토큰 재발급(대부분 exec 플러그인 재실행으로 해결)
# EKS: kubectl 호출 시 자동으로 get-token이 재실행되지만,
# 캐시/자격증명이 만료된 경우 aws 로그인/자격증명 갱신이 필요
aws sts get-caller-identity

5) ServiceAccount 토큰/Pod 내부에서 401: 토큰이 없거나 audience가 다름

클러스터 내부 애플리케이션이 Kubernetes API를 호출할 때는 보통 다음을 사용합니다.

  • /var/run/secrets/kubernetes.io/serviceaccount/token
  • CA: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
  • API 서버: https://kubernetes.default.svc

원인 A: ServiceAccount 토큰 자동 마운트가 꺼짐

  • automountServiceAccountToken: false
  • 혹은 Pod spec에서 override

점검

kubectl get pod <pod> -o jsonpath='{.spec.automountServiceAccountToken}' ; echo
kubectl get sa <sa> -o jsonpath='{.automountServiceAccountToken}' ; echo

해결

Pod 또는 SA에 토큰 마운트 활성화.

apiVersion: v1
kind: ServiceAccount
metadata:
  name: app-sa
automountServiceAccountToken: true

원인 B: BoundServiceAccountTokenVolume + audience 불일치

최근 Kubernetes는 바운드 토큰(짧은 TTL, audience 포함)을 사용합니다. 특정 컴포넌트가 audience가 다른 토큰을 요구하면 401이 날 수 있습니다.

해결 방향

  • API 서버 호출이면 기본 audience로 충분한지 확인
  • 외부 시스템(예: Vault, 커스텀 API)이라면 해당 audience로 토큰 발급 필요
apiVersion: v1
kind: Pod
metadata:
  name: debug
spec:
  serviceAccountName: app-sa
  containers:
  - name: c
    image: curlimages/curl
    command: ["sleep","3600"]

Pod 내부에서 토큰을 확인하고 API 호출을 재현합니다.

kubectl exec -it debug -- sh
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
curl -sk -H "Authorization: Bearer $TOKEN" https://kubernetes.default.svc/api

6) 프록시/Ingress/API Gateway가 Authorization 헤더를 제거해서 401

Kubernetes API 서버 앞단에 프록시(사내 게이트웨이, NGINX, Envoy)나 L7 로드밸런서를 두는 구성에서, Authorization 헤더가 전달되지 않으면 API 서버는 당연히 401을 반환합니다.

증상

  • 직접 API 서버로 붙으면 정상인데, 특정 도메인/프록시 경유 시만 401
  • 브라우저/클라이언트에선 헤더가 있는데 서버엔 안 도착

점검

  • 프록시 설정에서 Authorization 헤더 전달/허용 여부 확인
  • CORS 사전요청(OPTIONS) 처리 확인(브라우저 기반일 때)

NGINX 예시(헤더 보존)

location / {
  proxy_pass https://kube-apiserver;
  proxy_set_header Authorization $http_authorization;
  proxy_set_header X-Forwarded-Proto $scheme;
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

gRPC/프록시 계층 이슈를 폭넓게 다룬 글이 필요하면 아래도 참고가 됩니다(401 자체보단 네트워크/프록시 계층 진단에 도움):

7) OIDC 인증(쿠버네티스 API 서버) 설정 불일치로 401

자체 구축 클러스터에서 API 서버에 OIDC를 붙인 경우, 아래 중 하나라도 불일치하면 401이 납니다.

  • --oidc-issuer-url
  • --oidc-client-id
  • --oidc-username-claim, --oidc-groups-claim
  • IdP의 JWKS/서명키 로테이션 반영 실패

증상

  • 특정 사용자/그룹만 401
  • IdP 키 로테이션 직후부터 401

점검/해결

  • API 서버 플래그와 IdP 설정 재검증
  • API 서버가 issuer의 .well-known/openid-configuration과 JWKS에 접근 가능한지 확인
  • 프록시/방화벽이 IdP로의 egress를 막지 않는지 확인

8) 인증서는 맞는데 CA/서버 주소가 바뀌어 "다른" API 서버에 붙는 경우

401은 보통 인증 실패지만, 실무에서 종종 클러스터 엔드포인트가 바뀌었는데 로컬 DNS/hosts/프록시가 예전 엔드포인트로 보내는 경우가 있습니다. 그 엔드포인트가 다른 시스템이면 401/404 등 엉뚱한 응답이 나옵니다.

점검

# kubeconfig의 server 값 확인
kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}' ; echo

# 실제로 어디로 연결되는지 DNS 확인
nslookup <apiserver-host>

해결

  • kubeconfig 재생성
  • 사내 DNS/프록시/hosts 정리

9) (EKS) aws-auth ConfigMap 매핑 누락은 보통 401이 아니라 403에 가깝다

EKS에서 IAM 주체를 Kubernetes 사용자로 매핑하는 aws-auth가 깨지면 대개는 403 Forbidden 성격으로 드러납니다. 하지만 환경/클라이언트에 따라 401처럼 보이는 메시지가 섞일 수 있어 혼동됩니다.

  • aws eks get-token은 성공하는데 kubectl get ns가 실패한다면
    • 인증(토큰 발급)은 성공
    • 권한 매핑/인가가 실패(대개 403)

이 경우는 401 트러블슈팅을 끝까지 했는데도 해결이 안 된다면, 다음을 추가로 확인하세요.

kubectl -n kube-system get configmap aws-auth -o yaml
kubectl auth can-i get pods -A

10) 재현 가능한 "401 디버그 루틴" (현장용 체크리스트)

아래 순서대로 보면 대부분 10분 내에 원인이 좁혀집니다.

  1. 컨텍스트 확인
kubectl config current-context
kubectl config view --minify
  1. 요청이 토큰/인증서를 실제로 싣고 나가는지 확인
kubectl get ns -v=8 2>&1 | sed -n '1,120p'
  1. exec 플러그인 토큰 발급이 되는지 확인
aws eks get-token --region <region> --cluster-name <cluster> | head
aws sts get-caller-identity
  1. 시간/NTP 확인
date
  1. 프록시/게이트웨이 경유 여부 확인
  • 회사 VPN, HTTP(S) 프록시 환경변수(HTTPS_PROXY)가 있는지
env | grep -i proxy
  1. Pod 내부 호출이면 SA 토큰 마운트 확인
kubectl get pod <pod> -o jsonpath='{.spec.serviceAccountName} {.spec.automountServiceAccountToken}' ; echo

마무리: 401은 "권한"이 아니라 "신원" 문제부터 보자

Kubernetes 401은 RBAC 이전 단계의 문제인 경우가 많습니다. 따라서 진단의 핵심은 다음 두 가지입니다.

  • kubeconfig/exec 플러그인을 통해 유효한 인증 정보가 생성되고 전달되는지
  • 프록시/네트워크/클러스터 설정으로 인해 인증 헤더가 손실되거나 OIDC 검증이 실패하는지

401을 403처럼 접근하면 시간을 크게 낭비합니다. 위 루틴대로 "컨텍스트 → 토큰 생성 → 토큰 전달 → 시간/프록시 → OIDC" 순으로 좁혀가면 대부분 빠르게 해결할 수 있습니다.

추가로 EKS에서 IRSA/IAM 연동 문제(403/AccessDenied)까지 함께 의심된다면 다음 글도 같이 보시면 전체 인증/인가 체인을 정리하는 데 도움이 됩니다.