- Published on
Kubernetes 401 Unauthorized 원인별 해결 가이드
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서버가 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
해결
- 올바른 컨텍스트로 전환
kubectl config use-context <expected-context>
- 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분 내에 원인이 좁혀집니다.
- 컨텍스트 확인
kubectl config current-context
kubectl config view --minify
- 요청이 토큰/인증서를 실제로 싣고 나가는지 확인
kubectl get ns -v=8 2>&1 | sed -n '1,120p'
- exec 플러그인 토큰 발급이 되는지 확인
aws eks get-token --region <region> --cluster-name <cluster> | head
aws sts get-caller-identity
- 시간/NTP 확인
date
- 프록시/게이트웨이 경유 여부 확인
- 회사 VPN, HTTP(S) 프록시 환경변수(
HTTPS_PROXY)가 있는지
env | grep -i proxy
- 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)까지 함께 의심된다면 다음 글도 같이 보시면 전체 인증/인가 체인을 정리하는 데 도움이 됩니다.