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

사례6건

6건 중 1~5번째

  1. 증상

    2025년 9월 4일, CoreDNS 파드 하나가 OOMKilled 됐다.

    촉발

    CoreDNS 파드 하나가 OOMKilled 됐다.

    원인
    구성 요소
    기여 요인
    교훈

    장애는 Istio, kube-proxy, conntrack 각각의 동작이 겹쳐 생겼다. Istio 는 DNS 캐시를 공격적으로 갱신하며 클라이언트 포트를 다시 썼고, kube-proxy 는 conntrack 항목을 정리하지 않았고, conntrack 의 UDP 기본 타임아웃은 2분이었다. kube-proxy 쪽 문제는 쿠버네티스 v1.32 에서 고쳐졌다. 발표자는 Istio 가 아주 복잡한 일은 단순하게 만들지만 아주 단순한 일은 복잡하게 만든다고 평했다. 발표자는 또 Cilium 을 소개했다. Cilium 은 iptables 체인을 차례로 훑는 대신 맵으로 Service 를 찾고, eBPF 데이터 경로로 kube-proxy 를 완전히 대체할 수 있다.

  2. 증상

    운영 중 장애로 드러난 증상은 없다. 이 사례는 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나 다른 인그레스 컨트롤러로 옮겨야 한다.

  3. 증상

    운영 중 장애로 드러난 증상은 없다. 이 사례는 Cycode Labs가 Argo CD를 조사하다 찾은 취약점 CVE-2024-31989이고, 심각도 점수는 9.1이다.

    촉발

    클러스터 안의 권한 낮은 파드 하나가 kube-DNS로 Argo CD Redis 주소를 찾아 6379 포트로 암호 없이 접속할 수 있었다. Argo CD는 캐시에 든 매니페스트를 cacheEntryHash로 검증했는데, 이 해시는 비밀 키 없이 fnv64a로 만든 값이라 누구나 다시 계산할 수 있었다.

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

    Cycode Labs가 Argo CD의 쿠버네티스 구성 요소를 조사하다 찾았다. 2024년 1월 18일 GitHub Security Advisories로 Argo CD 팀에 알렸다.

    조치
    • Argo CD 팀이 2024년 5월 21일 Redis 서버 암호를 미리 정해 두는 2.11.1, 2.10.10, 2.9.15, 2.8.19 버전을 냈다. 사전 정보 없이는 Redis에 접속할 수 없게 됐지만, 문제를 일부만 막는 조치였다.
    교훈

    Argo CD를 2.11.1, 2.10.10, 2.9.15, 2.8.19 이상으로 올린다. argocd-redis-network-policy 네트워크 정책이 있고 켜져 있는지 확인한다. 네트워크 정책이 실제로 적용되려면 CNI가 제대로 설정돼 동작해야 한다. Argo CD 플러그인으로 Secret을 넣지 말고 컨트롤러 방식의 Secret 관리 도구를 써서 Secret이 Redis에 남지 않게 한다. 보안을 더 챙기려면 Argo CD를 다른 애플리케이션과 분리된 전용 클러스터에 둔다. 비밀 키 없이 만든 해시는 아무나 다시 계산할 수 있어서 무결성을 지켜 주지 못한다.

  4. 증상

    2020년 3월 16일 11:31 UTC에 SLO 기반 알림이 당직 엔지니어를 호출했다. 런던 리전 Hosted Prometheus 로 들어오는 쓰기의 약 20%가 실패하고 있었다. 문제는 이미 11분 전에 시작된 상태였다.

    촉발

    예약된 쿠버네티스 버전 업그레이드가 etcd 리더가 돌던 노드를 정상 종료 절차 없이 갑자기 종료했다.

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

    SLO 기반 알림이 11:31 UTC에 당직 엔지니어를 호출했다. 장애가 시작되고 11분이 지난 시점이었다. 오류율이 20% 수준이었기 때문에 이대로 두었다면 월간 SLA 를 넘기기까지 약 18시간이 걸렸을 것이다.

    조치
    • 11:32 UTC에 영향받은 distributor 를 재시작했고, 오류가 멈췄다.
    후속 조치
    • Cortex 의 etcd 클라이언트 설정에 gRPC keepalive 프로브를 켠다. DialKeepAliveTime 은 10초, DialKeepAliveTimeout 은 DialTimeout 의 두 배로 두고 PermitWithoutStream 을 true 로 한다.
    • 쿠버네티스 업그레이드 절차를 고쳐, etcd operator 가 관리하는 etcd 파드까지 포함해 파드와 머신을 정상 종료 절차로 내리게 한다.
    교훈

    노드가 죽으면 TCP 연결이 정상적으로 닫히지 않고 멈춘 채 남을 수 있다. etcd 클라이언트에 keepalive 를 설정하지 않으면 새 리더가 뽑힌 뒤에도 클라이언트는 죽은 연결을 계속 쓴다. 노드를 내리는 업그레이드 스크립트는 operator 가 관리하는 파드까지 정상 종료할 수 있어야 한다. 장애 대응과 복구, 사후 분석 동안 Grafana Loki 로 로그를 빠르게 뒤질 수 있어서 복구 시간이 줄었다.

  5. 증상

    팀의 Redis 의존 라이브러리를 쓰는 서비스들이 시작하자마자 죽는다는 보고가 운영 담당자에게 들어왔다. Spring Boot 로그를 보니 빈 초기화 중에 Redis 호스트 이름을 해석하지 못하고 있었다.

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

    Redis 의존 라이브러리를 쓰는 서비스 팀들이 시작 중에 서비스가 죽는다고 운영 담당자에게 보고하면서 문제가 처음 드러났다. 문제가 다시 나타났을 때는 팀이 Kibana 에서 호스트 이름 해석 실패를 보고 알았다.

    조치
    • 배포 매니페스트에 dnsConfig 블록을 넣어 ndots 를 4 로 낮췄다.
    • 라이브러리에서 잘못 설정된 최대 연결 풀 크기를 고쳐 새 버전을 내고 Redis 연결 수를 약 70% 줄였다.
    후속 조치
    • 이미지 라이브러리에 추가한 glibc 기반 Debian 베이스 이미지가 DNS 문제를 해결하는지 시험한다.
    • 더 새로운 Netty 버전을 쓰도록 의존성을 올린다.
    교훈

    쿠버네티스 기본값 ndots:5 에서는 리졸버가 점이 다섯 개보다 적은 외부 호스트 이름에 검색 도메인을 먼저 붙여 조회한다. 이 동작이 musl 리졸버의 NXDOMAIN 처리나 라이브러리의 DNS 리졸버 결함과 만나면 간헐적인 해석 실패가 된다. ndots 를 낮추면 대부분 서비스가 살아나지만 오래가는 해법은 아니다. 글쓴이는 이런 실패를 잡는 알림을 원했지만, 한 번씩 나는 DNS 실패마다 울리는 알림은 원하지 않는다고 했다.

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

이 6건에서 가장 많이 나오는 구성 요소: etcd, 인그레스 컨트롤러. kubegap 은 Claude Code 나 Codex 에서 쿠버네티스 클러스터를 읽기만 하며 이런 틈을 점검합니다. 점검 항목의 근거는 이 라이브러리와 같은 공개 보고서입니다.

원인별1

소프트웨어 결함6건

  1. IL-0104 Istio 의 공격적인 DNS 캐시 갱신과 클라이언트 포트 재사용, kube-proxy 가 종료된 파드의 conntrack 항목을 정리하지 않는 문제, 2분짜리 UDP conntrack 기본 타임아웃이 겹쳐 CoreDNS 파드가 OOMKilled 됐다.
  2. IL-0053 Ingress NGINX Controller의 어드미션 웹훅이 인증 없이 열려 있어, 조작한 Ingress 객체로 NGINX 설정을 주입해 원격 코드를 실행하고 클러스터의 모든 Secret을 읽을 수 있었다
    Wiz2025
  3. IL-0065 Argo CD의 Redis 캐시가 기본으로 암호 없이 클러스터 안 모든 파드에 열려 있어, 캐시된 매니페스트를 바꿔치기해 클러스터를 장악할 수 있었다
    Cycode2024
  4. IL-0190 쿠버네티스 업그레이드로 etcd 리더가 있던 노드가 갑자기 종료된 뒤, Cortex distributor 두 개가 옛 리더와 맺은 TCP 연결이 멈춘 채 그 연결을 계속 써서 런던 리전 Hosted Prometheus 쓰기가 12분 동안 일부 실패했다.
  5. IL-0050 ndots:5, Alpine 의 musl 리졸버, Netty 4.1.30 의 DNS 처리 문제가 겹쳐 쿠버네티스 서비스가 클러스터 밖 Redis 호스트 이름을 반복해서 해석하지 못했다
  6. IL-0072 etcd 쓰기 지연으로 API 서버가 오래된 데이터를 읽어 클러스터 상태가 두 가지로 번갈아 보였고, 주 운영 클러스터 전체가 약 4시간 멈췄다