- Published on
EKS AWS Load Balancer Controller 설치 후 403 해결
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서론
EKS에서 AWS Load Balancer Controller(이하 LBC)를 설치하고 Ingress를 붙였는데, 기대했던 200/302가 아니라 403 Forbidden이 떨어지는 경우가 생각보다 흔합니다. 문제는 “403”이라는 결과가 너무 포괄적이라는 점입니다.
- ALB가 내는 403인지(예: WAF, listener rule, fixed-response)
- 애플리케이션이 내는 403인지(예: 인증/인가 미들웨어)
- LBC가 리소스를 제대로 만들지 못해 엉뚱한 대상/규칙으로 라우팅되는지(권한/어노테이션/타겟그룹)
이 글은 LBC 설치 직후 또는 Ingress 적용 직후에 맞닥뜨리는 403을 “어디서 발생했는지”부터 분리하고, 가장 빈도가 높은 원인들을 로그/명령어 기반으로 끝까지 해결하는 체크리스트로 구성했습니다.
1) 403의 “발생 지점”부터 확정하기
403을 해결하는 가장 빠른 방법은 “누가 403을 반환했는지”를 확정하는 것입니다. 같은 403이라도 해결책이 완전히 달라집니다.
1.1 ALB/WAF가 반환하는 403인지 확인
가장 먼저 브라우저가 아니라 curl -v로 헤더를 봅니다.
curl -vk https://example.your-domain.com/ \
-H 'Host: example.your-domain.com'
아래 힌트가 있으면 ALB/WAF 쪽일 확률이 큽니다.
- 응답 헤더에
server: awselb/2.0가 보임 - 본문이 AWS 고정 에러 페이지 형태
- WAF를 붙였다면
x-amzn-ErrorType,x-amzn-RequestId등 단서가 보이기도 함
만약 WAF가 원인이라면, LBC 자체 문제가 아니라 WAF 규칙/로그 분석이 핵심입니다. 이 케이스는 아래 글이 가장 직접적입니다.
1.2 애플리케이션이 반환하는 403인지 확인
ALB를 통과해 Pod까지 갔는데 앱이 403을 내는 경우도 많습니다.
- 응답 헤더에 앱 프레임워크 흔적(예:
server: nginx,x-powered-by,set-cookie등) - 동일 경로를 Pod IP로 직접 호출하면 같은 403이 재현됨
이 경우는 Ingress/ALB가 아니라 인증/인가 설정, JWT/세션, 프록시 헤더(X-Forwarded-For/Proto) 처리 등을 봐야 합니다.
1.3 LBC가 “엉뚱한 규칙”을 만들어 403이 나는지 확인
ALB는 listener rule에 fixed-response 403을 걸 수 있습니다. 의도치 않게 기본 규칙이 403으로 남아 있거나, host/path 매칭이 실패해 기본 규칙으로 떨어지면 403이 발생합니다.
다음 순서로 확인합니다.
- Ingress가 의도한 host/path를 갖고 있는지
- ALB listener rule이 Ingress spec과 일치하는지
- rule 우선순위(priority) 충돌이 없는지
kubectl get ingress -A -o wide
kubectl describe ingress -n <ns> <ingress-name>
2) Controller 로그로 “권한/리소스 생성 실패”부터 제거
LBC 설치 직후 403이 뜬다면, 의외로 컨트롤러가 필요한 AWS 리소스를 제대로 만들지 못한 상태에서 기존 ALB/리스너가 남아 기본 403을 내는 경우가 있습니다. 따라서 먼저 컨트롤러 로그를 봅니다.
kubectl -n kube-system get deploy | grep -i load-balancer
kubectl -n kube-system logs deploy/aws-load-balancer-controller --tail=200
로그에서 특히 자주 보이는 패턴:
AccessDenied/UnauthorizedOperation→ IAM 권한/IRSA 문제failed to reconcile→ 어노테이션 값 오류, 서브넷 태그, SG 참조 문제WebIdentityErr/AssumeRoleWithWebIdentity실패 → IRSA 신뢰 정책/서비스어카운트 연결 문제
IRSA가 얽힌 AccessDenied는 403과 동반되는 대표 케이스입니다. 아래 글이 디버깅 흐름을 잘 정리합니다.
2.1 IRSA가 제대로 붙었는지(서비스 어카운트/어노테이션)
LBC는 보통 IRSA를 사용합니다. 서비스 어카운트에 role ARN이 붙었는지 확인합니다.
kubectl -n kube-system get sa aws-load-balancer-controller -o yaml | sed -n '1,120p'
기대값(예시):
metadata:
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::<ACCOUNT_ID>:role/AWSLoadBalancerControllerRole
그리고 파드 환경변수/토큰 마운트가 정상인지도 봅니다.
kubectl -n kube-system get pod -l app.kubernetes.io/name=aws-load-balancer-controller
kubectl -n kube-system describe pod <controller-pod>
2.2 IAM 정책이 최신/충분한지
LBC는 버전에 따라 필요한 권한이 달라질 수 있습니다. “예전에 쓰던 정책”을 그대로 재사용하면 일부 API에서 AccessDenied가 나며 리소스가 반쯤 생성되고, 최종적으로 트래픽이 기본 403으로 떨어질 수 있습니다.
권장 흐름:
- 사용 중인 LBC 버전 확인
- AWS 공식 문서의 해당 버전 정책(JSON)으로 업데이트
- 최소한 다음 계열 권한이 막히지 않는지 확인
elasticloadbalancing:*(특히 listener/rule/targetgroup)ec2:Describe*,ec2:CreateSecurityGroup,ec2:AuthorizeSecurityGroupIngressiam:CreateServiceLinkedRole(필요 시)waf-regional:*/wafv2:*(WAF 연동 시)
3) Ingress 어노테이션 실수로 인한 “기본 403” 패턴
권한이 정상인데도 403이면, 다음으로 흔한 원인이 Ingress 어노테이션/스펙 불일치입니다.
3.1 ingressClassName / ingress.class 불일치
클러스터에 NGINX Ingress, ALB Ingress 등 여러 컨트롤러가 공존하면, Ingress가 원치 않는 컨트롤러에 잡히거나 아무도 잡지 않는 경우가 있습니다. 그 결과 ALB 규칙이 기대와 다르게 남아 403이 날 수 있습니다.
Ingress에 명시적으로 지정하세요.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app
namespace: default
spec:
ingressClassName: alb
rules:
- host: example.your-domain.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app-svc
port:
number: 80
(구형 클러스터/리소스에서는 kubernetes.io/ingress.class: alb 어노테이션을 쓰기도 하지만, 가능하면 spec.ingressClassName를 권장합니다.)
3.2 host/path가 실제 요청과 매칭되지 않아 기본 규칙으로 떨어짐
예를 들어 Ingress에 host를 example.com으로 걸어놓고, 실제 요청은 alb-dns-name.amazonaws.com으로 때리면 host 매칭이 실패합니다. 그러면 listener 기본 규칙(종종 fixed 404/403)에 걸립니다.
해결:
- Route53/CNAME 또는 테스트 시
curl -H 'Host: ...'로 host를 맞추기 - Ingress에 host를 비우고(와일드카드) 테스트 후 점진적으로 고정
curl -vk https://<ALB_DNS_NAME>/ -H 'Host: example.your-domain.com'
3.3 action/조건 어노테이션 오타로 fixed-response가 생성되는 경우
LBC는 alb.ingress.kubernetes.io/actions.<name> 같은 고급 어노테이션으로 redirect/fixed-response를 만들 수 있습니다. JSON이 깨지거나 serviceName 참조가 틀리면 의도치 않게 fixed-response가 남을 수 있습니다.
Ingress 이벤트를 확인하세요.
kubectl describe ingress -n <ns> <ingress-name> | sed -n '/Events:/,$p'
4) “ALB는 정상인데 403”일 때: WAF, 인증, 헤더
컨트롤러/규칙이 정상인데도 403이면, 실제로는 보안 계층에서 막는 경우가 많습니다.
4.1 WAF 연동 시: 차단 로그부터 본다
WAF가 붙어 있다면 추측하지 말고 로그로 확정하는 게 가장 빠릅니다. 특히 다음 조건에서 WAF 403이 자주 발생합니다.
Host헤더가 예상과 다름(테스트 도메인/ALB DNS로 접근)- 특정 User-Agent/국가/IP 대역 차단
- Body size/JSON 스키마/SQLi/XSS 매칭
WAF 403은 아래 글의 흐름대로 보면 대부분 해결됩니다.
4.2 앱 인증/인가가 “프록시 뒤”에서 깨지는 경우
ALB 뒤에서 앱이 403을 내는 흔한 이유:
- HTTPS 종료 지점이 바뀌면서 앱이
X-Forwarded-Proto를 못 읽고 CSRF/redirect 정책이 틀어짐 - 허용 IP를
X-Forwarded-For가 아니라 소스 IP로 판단(ALB IP로 보임) - OIDC/SSO 콜백 URL이 도메인 불일치로 거부
이건 애플리케이션/미들웨어 설정 영역이라 프레임워크별로 다르지만, 공통적으로는 Forwarded 헤더를 신뢰하도록 설정하고, 외부 도메인/스킴을 정확히 맞추는 것이 핵심입니다.
5) NLB/타겟그룹 상태 이상이 403으로 “보이게” 만드는 경우
엄밀히 말해 타겟이 죽으면 502/504가 더 흔하지만, 환경에 따라 기본 규칙/커스텀 에러 처리로 403처럼 관측될 때도 있습니다(특히 CloudFront/WAF/게이트웨이 계층이 앞에 있을 때).
타겟그룹 헬스와 보안그룹/Pod readiness를 함께 점검하세요.
6) 재현 가능한 “10분 진단 체크리스트”
마지막으로, 현장에서 바로 쓸 수 있게 순서를 고정합니다.
- curl로 403 주체 확인
server: awselb/2.0인지, 앱 헤더인지
- Ingress 매칭 확인
ingressClassName: alb- host/path가 실제 요청과 일치하는지
- Controller 로그 확인
kubectl -n kube-system logs deploy/aws-load-balancer-controller- AccessDenied/AssumeRole 실패 여부
- IRSA 확인
- SA에
eks.amazonaws.com/role-arn존재 - 신뢰 정책(OIDC provider, sub/sa) 일치
- SA에
- Listener rule 확인(기본 규칙 403 여부)
- host/path 미매칭으로 기본 규칙으로 떨어지는지
- WAF 사용 시 로그로 차단 근거 확인
7) 예시: 가장 흔한 403 케이스 2가지와 해결
케이스 A: ALB DNS로 접속하니 403(Host 미매칭)
- 증상:
https://<alb-dns>/는 403,curl -H 'Host: my.domain'는 200 - 원인: Ingress rule이
host: my.domain인데 요청 Host가 ALB DNS라 매칭 실패 - 해결: Route53 연결 후 도메인으로 접속하거나, 테스트 시 Host 헤더를 강제로 맞춤
curl -vk https://<alb-dns-name>/ -H 'Host: my.domain'
케이스 B: Controller 로그에 AccessDenied, ALB 규칙이 반쪽
- 증상: Ingress는 생성됐지만 listener rule/targetgroup 생성이 실패하고 기본 403만 반환
- 원인: IRSA/IAM policy 부족
- 해결: LBC 버전에 맞는 IAM policy 적용 + SA role-arn 재확인 + 파드 재시작
kubectl -n kube-system rollout restart deploy/aws-load-balancer-controller
kubectl -n kube-system logs deploy/aws-load-balancer-controller --tail=200
결론
EKS에서 AWS Load Balancer Controller 설치 후 마주치는 403은 “네트워크가 막혔다”기보다, 대개 (1) Host/Path 매칭 실패로 기본 규칙으로 떨어지거나, (2) IRSA/IAM 권한 문제로 리소스가 완성되지 않거나, (3) WAF/인증 계층에서 의도대로 차단되는 경우가 대부분입니다.
403을 보면 곧바로 설정을 뜯어고치기보다, 먼저 curl -v로 403의 발신자를 특정하고(ALB/WAF vs 앱), 다음으로 controller 로그와 Ingress 이벤트를 기반으로 원인을 좁히면 재발 방지까지 포함해 빠르게 정리할 수 있습니다.