Incident Library
  • 원인
  • 구성 요소
  • 증상
  • 기여 요인
  • 사례

사례3건

3건 중 1~3번째

  1. 증상

    운영 중 장애로 드러난 증상은 없다. 이 사례는 Wiz Research가 Ingress NGINX Controller의 어드미션 컨트롤러 코드를 검토하다 찾은 취약점들이고, 이 취약점들을 묶어 IngressNightmare라고 부른다. CVSS v3.1 기본 점수는 9.8이다.

    촉발

    인증 없는 어드미션 웹훅에 악성 Ingress 객체를 담은 AdmissionReview 요청을 보내면 공격이 시작된다. 어드미션 컨트롤러는 받은 Ingress 객체로 임시 NGINX 설정 파일을 만들고 nginx -t로 검사한다. 이때 어노테이션 값과 객체 UID가 제대로 걸러지지 않은 채 설정 파일에 들어갔다.

    원인
    구성 요소
    기여 요인
    감지

    Wiz Research가 어드미션 컨트롤러 코드를 검토하다 찾았다. 2024년 12월 31일 CVE-2025-1974와 CVE-2025-24514를 쿠버네티스에 보고했고, 2025년 1월 2일 CVE-2025-1097을, 1월 21일 CVE-2025-1098을 보고했다. 일반 공개는 2025년 3월 24일이었다.

    조치
    • 쿠버네티스가 2025년 2월 7일 CVE-2025-1098, CVE-2025-1097, CVE-2025-24514 주입 취약점을 고친 내부 패치를 만들었다.
    • 쿠버네티스가 어드미션 컨트롤러에서 NGINX 설정 검사를 빼 CVE-2025-1974를 막았고, 2025년 2월 20일 이를 Wiz에 알렸다.
    • 수정이 들어간 Ingress NGINX Controller 1.12.1과 1.11.5가 나왔다.
    교훈

    Ingress NGINX Controller를 최신 버전으로 올리고, 어드미션 웹훅 엔드포인트를 외부에 노출하지 않는다. 바로 올릴 수 없다면 네트워크 정책으로 쿠버네티스 API 서버만 어드미션 컨트롤러에 닿게 하거나 어드미션 컨트롤러를 잠시 끈다. Helm으로 설치했다면 controller.admissionWebhooks.enabled=false 로 다시 설치하고, 직접 설치했다면 ingress-nginx-admission ValidatingWebhookConfiguration을 지우고 컨트롤러의 --validating-webhook 인자를 뺀다. 업그레이드한 뒤에는 어드미션 컨트롤러를 다시 켠다. 어드미션 컨트롤러는 흔히 놓치는 공격 지점이다. 인증 없이 도는 웹 서버이면서 권한이 높고, 클러스터 안 어느 파드에서나 닿는다. 공격자가 컨테이너 탈출이나 SSRF로 파드 네트워크에 들어오는 일은 생각보다 쉽다. Wiz는 최소 권한 설계가 없어 공격이 클러스터 장악까지 이어졌다고 지적했다. 걸러지지 않은 사용자 입력을 nginx -t로 검사하는 것 자체도 위험하다고 봤다. 2025년 11월 갱신된 글에 따르면 ingress-nginx는 2026년 3월에 유지보수를 끝내고 지원을 종료한다. 그러니 Gateway API나 다른 인그레스 컨트롤러로 옮겨야 한다.

  2. 증상

    Jetstack 은 europe-west1 에 있는 GKE 리전 클러스터의 마스터를 업그레이드하고 있었다. 이 개발 클러스터는 여러 팀이 낮 동안 앱을 시험하는 데 쓴다. GKE Terraform 파이프라인으로 업그레이드를 시작했는데, 마스터 업그레이드가 20분으로 잡아 둔 Terraform 타임아웃 안에 끝나지 않았다. GKE 콘솔에는 클러스터가 여전히 업그레이드 중으로 나왔다.

    촉발

    마스터 업그레이드 중 두 번째 마스터 인스턴스의 API 서버가 ca-registration PostStartHook 을 실행하면서 kube-system 의 extension-apiserver-authentication ConfigMap 을 갱신하려 했다. 이 요청은 설정해 둔 OPA 검증 웹훅의 백엔드가 응답하지 않아 타임아웃 났다.

    원인
    구성 요소
    기여 요인
    감지

    GKE Terraform 파이프라인으로 시작한 마스터 업그레이드가 20분으로 잡아 둔 Terraform 타임아웃 안에 끝나지 않은 것이 첫 신호였다. 파이프라인을 다시 돌리자 kube-apiserver 가 unhealthy 라는 오류가 났다. 원인은 Google Support 의 도움을 받아 찾았다.

    조치
    • API 서버에 간헐적으로 접근될 때를 이용해 OPA 의 ValidatingAdmissionWebhook 리소스를 지워 클러스터를 복구했다.
    후속 조치
    • OPA 의 ValidatingAdmissionWebhook 이 정책이 적용되고 개발 팀이 접근하는 네임스페이스만 보도록 범위를 좁혔다.
    • 웹훅을 정책이 실제로 검증하는 Ingress 와 Service 리소스에만 켰다.
    • OPA 가 응답하지 않으면 재시작되도록 liveness probe 를 더하고 문서를 고쳤다.
    • 이 동작을 개선하자는 쿠버네티스 업스트림 이슈가 아직 열려 있어, 그 진행을 지켜본다.
    교훈

    API 서버 응답 시간에 알림을 걸어 두었다면, OPA 웹훅을 처음 배포한 뒤 모든 CREATE 와 UPDATE 요청의 응답 시간이 늘어난 것을 알아챘을 수도 있다. 모든 워크로드에 프로브를 설정하는 것이 중요하다. 팀은 OPA 배포가 너무 간단해 보여서 Helm 차트를 쓰지 않았는데, 그 차트에는 어드미션 컨트롤러 컨테이너의 livenessProbe 를 포함해 튜토리얼 기본 설정에 없는 조정이 여럿 들어 있었다. 노드 자동 복구를 끄는 것도 검토했지만 끄지 않기로 했다. 같은 문제를 겪은 것은 Jetstack 이 처음이 아니며, 관련 업스트림 이슈는 아직 열려 있다.

  3. 증상

    사용자들이 작업이 시작되지 않는다고 알려 왔다. 평소에는 모니터링이 먼저 잡아내는 편이었지만 이번에는 사용자 보고가 먼저였다. 처음부터 이미지 풀과 관련된 흔적이 보였다.

    촉발

    애플리케이션 하나가 읽어야 하는 버킷의 권한을 바꾸면서 그 애플리케이션이 시작하지 못하게 됐다. 이 애플리케이션은 약 1,000대 노드에서 도는 DaemonSet이었고 크래시 루프에 빠졌다. imagePullPolicy가 Always여서 재시작할 때마다 노드에 이미 있는 이미지를 다시 받았다.

    원인
    구성 요소
    기여 요인
    감지

    사용자들이 작업이 시작되지 않는다고 알려 와서 알았다. 모니터링보다 사용자 보고가 먼저였다. 팀은 파드 상태, 이미지 풀 지표, 레지스트리 응답 코드를 차례로 확인해 429 속도 제한을 찾았다.

    조치
    • 영향을 받은 NAT 인스턴스를 교체해 레지스트리로 나가는 출발지 IP를 바꿨다.
    • Cloud SQL 접속에 쓰던 고정 IP 설정과 방화벽 규칙을 다시 맞춰 데이터베이스 연결을 되살렸다.
    후속 조치
    • latest 같은 유동 이미지 태그를 막는 어드미션 웹훅을 만들어 워크로드 리소스와 파드에 걸었다.
    • 웹훅이 클러스터가 만드는 파드까지 거부하자, 파드 객체에서는 빼고 워크로드 객체에만 적용하도록 바꿨다.
    교훈

    imagePullPolicy가 Always인 DaemonSet이 크래시 루프에 빠지면 노드 수만큼 이미지 풀이 반복된다. 레지스트리 트래픽이 NAT 몇 대로 모이면 출발지 IP 몇 개가 속도 제한에 걸려 리전 전체가 이미지를 받지 못한다. NAT를 바꾸면 그 IP를 허용해 둔 외부 서비스 연결도 함께 끊긴다. 파드에 거는 어드미션 웹훅은 사용자가 아니라 컨트롤러가 만드는 파드까지 막는다. 발표자들은 대규모 클러스터에서는 DaemonSet을 특히 조심하라고 했다. DaemonSet이 기대는 DNS, Vault, 이미지 저장소도 모두 망가뜨려 본 적이 있다고 했다. 수천 대 단위로 무언가를 다루면 그만큼 망가뜨리기 쉽다는 것이다. kubelet 아래에도 인프라가 있다는 것을 잊지 말라고도 했다.

내 클러스터에도 있는지 확인하기

이 3건에서 가장 많이 나오는 구성 요소: 어드미션 웹훅, 애플리케이션 데이터베이스. kubegap 은 Claude Code 나 Codex 에서 쿠버네티스 클러스터를 읽기만 하며 이런 틈을 점검합니다. 점검 항목의 근거는 이 라이브러리와 같은 공개 보고서입니다.

원인별3