Reporter: ihopenre-eng
Published: July 2026
최근 제가 OpenChoreo 프로젝트에서 보고한 취약점이 GitHub Security Advisory(CVE-2026-73842)로 공개되었습니다.
이번 취약점은 Cluster Gateway의 내부 API가 호출자를 인증하지 않는 설계 문제에서 시작됩니다. 내부 네트워크에서 Gateway에 접근할 수 있는 환경이라면 Kubernetes API로 전달되는 요청에 대해 별도의 인증이나 권한 검사가 이루어지지 않아 Secret 조회, 워크로드 변경, Pod 내부 명령 실행과 같은 동작이 가능했습니다.
이번 글에서는 취약점을 분석하면서 어떤 코드가 원인이 되었는지, 그리고 이러한 설계가 실제로 어떤 영향을 미칠 수 있는지 순서대로 살펴보겠습니다.

코드를 보기 전에 먼저 OpenChoreo의 구조를 간단히 이해하면 이후 내용을 따라가기 훨씬 쉽습니다.
OpenChoreo는 Control Plane이 여러 Data Plane Kubernetes Cluster를 관리하는 구조입니다. 이때 Cluster Gateway는 Control Plane과 Data Plane 사이에서 Kubernetes API 요청을 전달하는 역할을 합니다.
즉, Gateway는 여러 클러스터를 연결하는 핵심 구성 요소라고 볼 수 있습니다.

이 구조를 이해한 상태에서 자연스럽게 이런 질문이 생겼습니다.
"Gateway는 내부 요청을 어떻게 인증할까?"
이 질문을 시작으로 코드를 따라가 보았습니다.

코드를 보면 /api/proxy/, /api/exec/, /api/wirelogs/ 세 가지 Internal API가 등록되어 있습니다.
internalMux.HandleFunc("/api/proxy/", ...)
internalMux.HandleFunc("/api/exec/", ...)
internalMux.HandleFunc("/api/wirelogs/", ...)
여기서 가장 궁금했던 점은 "이 요청을 누가 인증하는가?"였습니다.
일반적으로 이러한 관리용 API는 인증 미들웨어를 거치거나, 클라이언트 인증서를 검증하는 코드가 먼저 등장합니다.
하지만 코드를 계속 따라가도 그런 처리를 찾을 수 없었습니다.
오히려 눈에 띈 것은 TLS 설정이었습니다.

tlsConfig := &tls.Config{
ClientAuth: tls.RequestClientCert,
}
처음에는 이 설정을 보고 "클라이언트 인증서를 사용해 접근을 제어하는구나."라고 생각했습니다. 하지만 RequestClientCert는 이름 그대로 클라이언트 인증서를 요청(Request) 할 뿐, 인증서가 반드시 있어야 하는 것도 아니고 유효한지까지 검증하는 설정도 아닙니다.
즉, TLS 연결은 암호화되지만 이 연결을 요청한 클라이언트가 신뢰할 수 있는 대상인지까지는 확인하지 않습니다.
그래서 자연스럽게 다음 단계로 넘어갔습니다.
"그렇다면 Handler 내부에서 별도로 인증을 수행하지 않을까?"
이번에는 handleHTTPProxy(), handleExec(), handleWirelogs()를 하나씩 따라가 보았습니다.

관리용 API라면 일반적으로 Handler에 들어오기 전에 인증 미들웨어를 거치거나, Handler 내부에서 클라이언트 인증서나 토큰을 검증하는 코드가 존재합니다.
예를 들어 다음과 같은 처리를 기대했습니다.
r.TLS.PeerCertificates를 이용한 클라이언트 인증서 검증
JWT 또는 Access Token 검증
호출자의 권한(Role) 확인
특정 Data Plane에 접근 가능한 사용자 여부 확인
하지만 handleHTTPProxy()를 비롯한 관련 Handler에서는 이러한 인증 과정을 찾을 수 없었습니다.
즉, 요청이 Internal API에 도달하면 호출자가 누구인지 확인하는 절차 없이 그대로 다음 단계로 전달되고 있었습니다.
여기서 한 가지 의문이 생겼습니다.
"인증은 없더라도 최소한 요청 자체는 엄격하게 제한하고 있지 않을까?"
이 질문을 확인하기 위해 이번에는 요청을 검사하는 RequestValidator를 분석해 보았습니다.
handleHTTPProxy()를 따라가다 보면 최종적으로 RequestValidator를 통해 요청을 검사하는 것을 확인할 수 있습니다.
처음에는 이 Validator가 이름 그대로 읽기 전용(Read-only) 요청만 허용할 것으로 예상했습니다. 관리용 프록시라면 일반적으로 GET이나 일부 조회 API만 허용하고, 리소스를 변경하는 요청은 차단하는 것이 일반적이기 때문입니다.
그래서 Validator가 어떤 HTTP Method를 허용하는지 먼저 살펴보았습니다.

코드를 확인한 결과 예상과는 조금 달랐습니다.
GET뿐만 아니라 POST, PUT, PATCH, DELETE까지 모두 허용하고 있었습니다.
즉, 이 프록시는 단순히 Kubernetes API를 조회하는 용도가 아니라 리소스를 생성하거나 수정, 삭제하는 요청도 그대로 전달할 수 있는 구조였습니다.
반면 클라이언트 라이브러리에는 해당 프록시가 "Read-only"라는 설명이 포함되어 있었기 때문에, 실제 구현은 Client Library의 설명보다 더 넓은 범위의 요청을 허용하고 있었습니다.
다음으로 확인한 부분은 어떤 Kubernetes API 경로를 차단하는지였습니다.
Validator에는 일부 민감한 API를 차단하기 위한 Block List가 존재합니다.

/api/v1/namespaces/kube-system/secrets
처음에는 모든 Secret API가 차단될 것으로 예상했지만, 실제로는 kube-system 네임스페이스의 Secret만 예외적으로 차단하고 있었습니다.
즉,
/api/v1/namespaces/default/secrets
/api/v1/namespaces/production/secrets
/api/v1/namespaces/tenant-a/secrets
와 같은 일반 Tenant Namespace의 Secret 요청은 Validator를 통과할 수 있었습니다.
이러한 Secret에는 다음과 같은 민감한 정보가 저장되는 경우가 많습니다.
Database 계정 정보
Cloud Access Key
TLS Private Key
API Token
Kubernetes ServiceAccount Token
만약 공격자가 이러한 Secret을 읽을 수 있다면, 단순히 Kubernetes 리소스를 조회하는 수준을 넘어 다른 시스템으로의 추가적인 공격도 가능해질 수 있습니다.
지금까지 확인한 내용을 정리하면 다음과 같습니다.
Internal API에 대한 호출자 인증이 없다.
Data Plane에 대한 권한 검사가 없다.
쓰기(Write) 요청도 허용된다.
Tenant Namespace의 Secret 조회도 가능하다.
이 네 가지가 결합되면서 Gateway는 Kubernetes API 요청을 중계하는 과정에서
호출자 인증이나 권한 검사를 수행하지 않는 구조였습니다.
공격자가 Internal API에 접근할 수 있는 환경이라면 다음과 같은 작업을 수행할 수 있습니다.
Secret 조회
Deployment 생성 및 삭제
ConfigMap 수정
Service 변경
Pod 내부 명령 실행(/api/exec/)
특히 Cluster Gateway는 여러 Data Plane을 관리하는 구조이기 때문에, 하나의 Gateway를 통해 여러 Kubernetes Cluster에 영향을 줄 수 있다는 점도 중요한 부분입니다.

이번 취약점은 실제 Kubernetes 환경을 구성하지 않더라도 RequestValidator만으로 동작을 확인할 수 있었습니다.
이를 위해 Validator를 대상으로 간단한 단위 테스트를 작성했습니다.
아래 테스트는 Validator가 어떤 요청을 허용하는지 확인하기 위해 작성한 PoC입니다.

테스트에서는 다음과 같은 항목을 확인했습니다.
Tenant Namespace Secret 조회 요청이 허용되는지
DELETE 요청이 허용되는지
PATCH 요청이 허용되는지
모든 테스트는 예상한 결과대로 통과했으며, Validator가 이러한 요청을 차단하지 않는다는 것을 확인할 수 있었습니다.
이후 실제 요청 흐름을 분석해 보니, Validator를 통과한 요청은 Gateway를 거쳐 Data Plane Agent로 전달되고, 최종적으로 Kubernetes API Server에서 그대로 실행되는 구조였습니다.
이번 취약점은 하나의 코드 실수라기보다는 여러 보안 검증이 동시에 빠져 있었던 설계상의 문제에 가까웠습니다.
Gateway는 내부 관리용 서비스라는 이유로 호출자를 신뢰하고 있었지만, 현대적인 클라우드 환경에서는 내부 네트워크 역시 항상 신뢰할 수 있다고 가정하기 어렵습니다.
따라서 관리용 API라고 하더라도 다음과 같은 보안 계층은 반드시 필요합니다.
호출자 인증(Authentication)
대상 리소스에 대한 권한 검증(Authorization)
최소 권한 원칙(Least Privilege)
네트워크 접근 제어(Network Policy)
이번 분석을 통해 다시 한 번 느낀 점은, "내부 서비스이기 때문에 안전하다"는 가정은 더 이상 유효하지 않다는 것입니다. Zero Trust 환경에서는 내부 API 역시 외부 서비스와 동일한 수준의 인증과 권한 검사를 적용해야 하며, 그렇지 않을 경우 하나의 Gateway가 전체 Kubernetes 환경의 공격 지점이 될 수 있습니다.