🇰🇷 한글 설명
Flux Operator의 웹 UI 인증 코드에 권한 상승 취약점이 존재하여 공격자가 Kubernetes RBAC 대상을 우회하고 운영자 서비스 계정의 특권으로 API 요청을 실행할 수 있다.
💡 쉬운 설명
Flux Operator의 웹 UI 인증 코드에 취약점이 있어 공격자가 시스템의 권한을 상승시킬 수 있다. 이는 OIDC 제공자가 특정 클레임을 발급하지 않거나 사용자 지정 CEL 표현식이 빈 값으로 평가될 때 발생한다. 공격자는 운영자 서비스 계정의 자격으로 API 요청을 실행할 수 있다.
🇺🇸 원문 (English)
The Flux Operator is a Kubernetes CRD controller that manages the lifecycle of CNCF Flux CD and the ControlPlane enterprise distribution. Starting in version 0.36.0 and prior to version 0.40.0, a privilege escalation vulnerability exists in the Flux Operator Web UI authentication code that allows an attacker to bypass Kubernetes RBAC impersonation and execute API requests with the operator's service account privileges. In order to be vulnerable, cluster admins must configure the Flux Operator with an OIDC provider that issues tokens lacking the expected claims (e.g., `email`, `groups`), or configure custom CEL expressions that can evaluate to empty values. After OIDC token claims are processed through CEL expressions, there is no validation that the resulting `username` and `groups` values are non-empty. When both values are empty, the Kubernetes client-go library does not add impersonation headers to API requests, causing them to be executed with the flux-operator service account's credentials instead of the authenticated user's limited permissions. This can result in privilege escalation, data exposure, and/or information disclosure. Version 0.40.0 patches the issue.
📍 영향받는 시스템
Flux Operator 버전 0.36.0부터 0.40.0 이전 버전
⚔️ 공격 벡터
OIDC 토큰 클레임을 CEL 표현식으로 처리하여 사용자 이름과 그룹이 빈 값으로 평가될 때 발생한다. 공격자는 Kubernetes 클라이언트 라이브러리가 임시화 헤더를 추가하지 않아 운영자 서비스 계정의 자격으로 API 요청을 실행할 수 있다.
🛡️ 대응 방법
Flux Operator를 버전 0.40.0 이상으로 업그레이드하고 OIDC 제공자가 적절한 클레임을 발급하는지 확인한다. 또한 사용자 지정 CEL 표현식이 빈 값으로 평가되지 않도록 확인한다.
🔧 검증 코드
이 코드는 로컬 시스템의 Flux Operator 버전을 확인하여 취약점 여부를 판단한다. Kubernetes 클라이언트가 설치되어 있어야 하며 Flux Operator가 flux-system 네임스페이스에 배포되어 있어야 한다. 코드를 실행하기 전에 필요한 권한과 의존성을 확인한다.
python -c "import subprocess; output = subprocess.check_output(['kubectl', 'get', 'deployment', '-n', 'flux-system', 'flux-operator']).decode('utf-8'); version = output.split('\n')[1].split()[1]; if version >= '0.40.0': print('{\"result\": \"\uac80\uc6d0\", \"reason\": \"Flux Operator \uacb0\uc81c \uc5c8\uc9c0 0.40.0\uacb0 \uc81c\"}') else: print('{\"result\": \"\uc99d\uc5c0\", \"reason\": \"Flux Operator \uacb0\uc81c \uc5c8\uc9c0 0.40.0\uc6d0 \uc81c\"}')"