Published on

EKS IRSA에서 AssumeRoleWithWebIdentity 0s 타임아웃 해결

Authors

서론

EKS에서 IRSA(IAM Roles for Service Accounts)를 붙인 파드가 AWS API를 호출할 때, 내부적으로는 STS의 AssumeRoleWithWebIdentity를 호출해 임시 자격 증명을 발급받습니다. 정상이라면 수십~수백 ms 내에 끝나지만, 현장에서는 로그에 AssumeRoleWithWebIdentity가 “0s timeout”처럼 보이며 즉시 실패하는 경우가 있습니다.

이 증상은 진짜로 0초 타임아웃이 걸렸다기보다, 대개 아래 중 하나로 인해 요청이 STS까지 도달하지 못했거나(네트워크/라우팅), TLS 핸드셰이크 전에 끊겼거나, 잘못된 엔드포인트/리전으로 즉시 실패하는 상황에서 자주 발생합니다.

이 글은 “권한(AccessDenied) 문제”가 아니라 연결/엔드포인트/환경변수/SDK 동작 관점에서, 빠르게 원인을 좁히고 확실하게 고치는 체크리스트를 제공합니다.

관련해서 파드에서 DNS는 되는데 HTTPS만 실패하는 네트워크 계열 이슈는 아래 글도 함께 보면 진단 속도가 빨라집니다.


증상 패턴 정리: “0s 타임아웃”이 의미하는 것

다음과 같은 로그/에러가 관찰됩니다.

  • 애플리케이션 로그(예: boto3/botocore)
    • Connect timeout on endpoint URL: "https://sts.<region>.amazonaws.com/"
    • Read timeout on endpoint URL...
    • SSLError 혹은 EOF occurred in violation of protocol
  • AWS SDK가 재시도를 거의 못 하고 즉시 실패(= 0초처럼 보임)
  • 동일 파드에서 DNS 조회는 되는데 curl https://sts...가 실패
  • 노드/서브넷/네임스페이스/특정 워크로드에서만 재현

핵심은 IRSA 자체(신뢰 정책, SA 어노테이션)가 맞아도 STS에 연결이 안 되면 AssumeRoleWithWebIdentity가 실패한다는 점입니다.


1) IRSA 구성 자체를 먼저 30초만에 확인(권한이 아니라 “연결 문제” 전제)

연결 문제를 파기 전에, IRSA가 정상 구성인지 최소 확인을 해두면 디버깅이 단순해집니다.

ServiceAccount 어노테이션

kubectl -n <ns> get sa <sa-name> -o yaml | yq '.metadata.annotations'

다음이 있어야 합니다.

  • eks.amazonaws.com/role-arn: arn:aws:iam::<account>:role/<role>

파드 환경 변수

IRSA가 적용된 파드에는 보통 아래가 주입됩니다.

  • AWS_ROLE_ARN
  • AWS_WEB_IDENTITY_TOKEN_FILE
kubectl -n <ns> exec -it <pod> -- sh -lc 'env | egrep "AWS_ROLE_ARN|AWS_WEB_IDENTITY_TOKEN_FILE|AWS_REGION|AWS_DEFAULT_REGION"'

토큰 파일도 실제로 존재해야 합니다.

kubectl -n <ns> exec -it <pod> -- sh -lc 'ls -l $AWS_WEB_IDENTITY_TOKEN_FILE && head -c 20 $AWS_WEB_IDENTITY_TOKEN_FILE && echo'

여기까지가 정상인데도 “0s 타임아웃”이면, 거의 확실히 네트워크/엔드포인트 문제로 넘어갑니다.


2) STS 엔드포인트 연결성부터 확인: 파드에서 curl로 재현

파드에서 STS로 HTTPS 요청이 가능한지 확인합니다.

kubectl -n <ns> exec -it <pod> -- sh -lc '
  apk add --no-cache curl 2>/dev/null || true
  curl -sv --connect-timeout 3 https://sts.${AWS_REGION:-ap-northeast-2}.amazonaws.com/ 2>&1 | tail -n +1
'

결과 해석

  • Could not resolve host: DNS 문제
  • Connection timed out: 라우팅/NACL/SecurityGroup/프록시/엔드포인트 문제
  • SSL 관련: MTU/프록시/TLS 중간장비/CA 번들 문제

특히 “DNS는 되는데 HTTPS만 실패”는 NAT, 라우팅 테이블, NACL, MTU(특히 1500/9001 혼재), VPC CNI SNAT에서 자주 터집니다. 이 케이스는 위 내부 링크 글의 체크리스트가 그대로 적용됩니다.


3) 가장 흔한 원인: 프라이빗 서브넷인데 NAT/STS VPC 엔드포인트가 없다

EKS 노드가 프라이빗 서브넷에 있고, 인터넷 egress가 NAT로 나가야 하는데 NAT가 없거나 라우팅이 깨지면 STS에 못 나갑니다.

확인 1: 노드가 있는 서브넷 라우팅

  • 프라이빗 서브넷 라우팅 테이블에 0.0.0.0/0 -> nat-...가 있는지
  • NAT Gateway가 실제로 같은 AZ에 있는지(교차 AZ도 되긴 하지만 비용/병목/장애 포인트)

확인 2: STS를 VPC 엔드포인트로 붙였는지

조직 정책상 인터넷 egress를 막는 경우, STS를 Interface VPC Endpoint(PrivateLink) 로 붙여야 합니다.

  • 서비스: com.amazonaws.<region>.sts
  • 타입: Interface
  • 서브넷: 워커 노드가 있는 서브넷들
  • 보안그룹: 노드/파드에서 443 인바운드 허용
  • Private DNS: 활성화 권장

엔드포인트를 만들었다면, 파드에서 DNS가 STS를 사설 IP로 해석하는지 확인합니다.

kubectl -n <ns> exec -it <pod> -- sh -lc '
  nslookup sts.${AWS_REGION:-ap-northeast-2}.amazonaws.com || true
'

사설 IP로 떨어지는데도 연결이 안 되면, 거의 항상 엔드포인트 SG/NACL 문제입니다.


4) “0s”처럼 보이게 만드는 SDK 설정: 프록시/리전/타임아웃

네트워크가 정상인데도 즉시 실패한다면, 애플리케이션 컨테이너의 환경변수/SDK 설정이 STS 호출을 망가뜨리는 경우가 있습니다.

(1) 잘못된 리전 또는 STS 엔드포인트 강제

IRSA는 기본적으로 STS 글로벌/리전 엔드포인트를 사용합니다. 그런데 다음이 섞이면 이상 증상이 납니다.

  • AWS_REGION, AWS_DEFAULT_REGION이 비어 있거나 엉뚱한 값
  • AWS_STS_REGIONAL_ENDPOINTS=regional 설정 + 리전 미설정
  • 커스텀 AWS_ENDPOINT_URL 류를 STS에까지 적용

권장: 파드에 리전을 명시하거나, 노드의 IMDS/SDK가 올바르게 리전을 잡도록 합니다.

env:
  - name: AWS_REGION
    value: ap-northeast-2
  - name: AWS_STS_REGIONAL_ENDPOINTS
    value: regional

(2) HTTP(S)_PROXY가 STS까지 가로채는 경우

기업망/프록시 환경에서 HTTPS_PROXY가 설정되어 있으면, STS 호출이 프록시로 향했다가 즉시 끊기며 “0s”처럼 보일 수 있습니다.

파드에서 확인:

kubectl -n <ns> exec -it <pod> -- sh -lc 'env | egrep -i "https?_proxy|no_proxy"'

해결:

  • NO_PROXYsts.<region>.amazonaws.com,.amazonaws.com,169.254.169.254 등을 추가
  • 또는 해당 워크로드에서 프록시 환경변수를 제거

(3) SDK 타임아웃이 너무 공격적(0~수십 ms)

간혹 공통 라이브러리에서 AWS SDK 타임아웃을 극단적으로 줄여둔 경우가 있습니다.

Python(boto3/botocore) 예시

import boto3
from botocore.config import Config

cfg = Config(
    connect_timeout=3,
    read_timeout=10,
    retries={"max_attempts": 5, "mode": "standard"},
)

sts = boto3.client("sts", config=cfg, region_name="ap-northeast-2")
print(sts.get_caller_identity())

Node.js(AWS SDK v3) 예시

import { STSClient, GetCallerIdentityCommand } from "@aws-sdk/client-sts";
import { NodeHttpHandler } from "@aws-sdk/node-http-handler";

const client = new STSClient({
  region: process.env.AWS_REGION || "ap-northeast-2",
  requestHandler: new NodeHttpHandler({
    connectionTimeout: 3000,
    socketTimeout: 10000,
  }),
  maxAttempts: 5,
});

const out = await client.send(new GetCallerIdentityCommand({}));
console.log(out);

타임아웃/재시도 설계 자체가 필요하다면, HTTP 클라이언트 관점의 재시도 패턴은 아래 글의 프레임을 그대로 가져와도 좋습니다.


5) MTU/경로 문제: TLS 핸드셰이크에서 끊기는 케이스

curl -v https://sts...에서 ClientHello 이후 멈추거나, SSL_ERROR_SYSCALL, EOF로 떨어지면 MTU/경로 문제를 의심합니다.

EKS에서 자주 나오는 패턴:

  • 노드 ENI MTU(예: 9001) vs 경로상 장비/터널 MTU(예: 1500) 불일치
  • 특정 서브넷/특정 AZ만 문제
  • DNS/HTTP(작은 패킷)는 되지만 HTTPS/TLS(큰 패킷, fragmentation 필요)는 실패

이 경우는 원인과 해결이 네트워크 레이어에 있으므로, 아래 글의 “DNS OK, HTTPS Fail” 섹션대로 SNAT, NACL, MTU, 경로 MTU discovery를 집중 점검하는 것이 빠릅니다.


6) VPC 엔드포인트를 쓴다면: Endpoint SG/NACL/Private DNS 3종 세트

STS Interface Endpoint를 붙였는데도 타임아웃이면, 대부분 아래 중 하나입니다.

(1) Endpoint 보안그룹 인바운드 443 누락

Interface Endpoint는 ENI이므로 SG가 붙습니다. 파드/노드가 속한 SG(또는 CIDR)에서 443 인바운드 허용이 필요합니다.

  • 인바운드: TCP 443 from node SG(권장) 또는 VPC CIDR
  • 아웃바운드: 기본 허용이면 보통 OK

(2) NACL이 에페메럴 포트를 막음

NACL에서 아웃바운드 443만 열어두고, 응답 트래픽(에페메럴 포트)을 막아버리면 “연결 시도만 하다 타임아웃”이 납니다.

  • 아웃바운드: 443 + ephemeral(1024-65535)
  • 인바운드: ephemeral 허용

(3) Private DNS 미설정

Private DNS가 꺼져 있으면 sts.<region>.amazonaws.com이 퍼블릭으로 해석되어 NAT로 나가려다 실패할 수 있습니다.


7) 빠른 진단용 원라이너: 파드에서 STS 호출을 최소로 재현

앱 코드가 복잡하면, 파드 안에서 AWS CLI로 바로 재현하는 게 빠릅니다.

kubectl -n <ns> exec -it <pod> -- sh -lc '
  aws --version || true
  aws sts get-caller-identity --region ${AWS_REGION:-ap-northeast-2} --debug 2>&1 | tail -n 80
'

여기서 --debug 로그에

  • 어떤 endpoint로 붙는지
  • 프록시를 타는지
  • connect/read timeout이 어디서 나는지

가 그대로 드러납니다.


8) “AccessDenied가 아닌데 STS가 안 된다”를 팀에 설명하는 포인트

IRSA 관련 이슈는 보통 IAM 정책부터 의심하지만, AssumeRoleWithWebIdentity는 STS 네트워크가 먼저입니다.

  • IAM 신뢰 정책이 완벽해도 STS에 못 붙으면 실패
  • 특히 프라이빗 클러스터/egress 제한/프록시 환경에서는 STS Interface Endpoint가 사실상 필수
  • “0s timeout”은 SDK가 빨리 포기했다기보다 연결 자체가 성립하지 않는 환경일 가능성이 큼

결론: 해결 우선순위(가장 빨리 먹히는 순)

  1. 파드에서 curl -v https://sts.<region>.amazonaws.com로 연결성 확인
  2. 프라이빗 서브넷이면 NAT 라우팅 또는 STS Interface Endpoint 구성 확인
  3. Endpoint 사용 시 Endpoint SG 443 / NACL ephemeral / Private DNS 점검
  4. 파드의 HTTPS_PROXY/NO_PROXY, AWS_REGION, AWS_STS_REGIONAL_ENDPOINTS 확인
  5. TLS 단계에서 끊기면 MTU/SNAT/NACL로 내려가서 원인 제거

위 순서대로 보면, “AssumeRoleWithWebIdentity 0s 타임아웃” 류는 대부분 1~2시간 내에 원인까지 도달합니다.