Feb 11, 2026·6 min read·7 visits
The OpenShift GitOps operator acts as a 'Confused Deputy', allowing namespace admins to trick it into labeling their namespace as a critical system component. This label disables security constraints, allowing the attacker to launch privileged pods, mount the host filesystem, and seize control of the entire cluster.
A Critical privilege escalation vulnerability in the Red Hat OpenShift GitOps operator allows any user with Namespace Admin privileges to gain full Cluster Admin (Root) access. The flaw resides in the operator's metrics reconciliation logic, which blindly applies trusted system labels to user-controlled namespaces, bypassing critical security boundaries.
Kubernetes Operators are the tireless robots of the cloud-native world. We deploy them to automate complex tasks, trusting them with the keys to the kingdom (ClusterRole bindings) so they can manage resources on our behalf. Red Hat's OpenShift GitOps Operator is one such beast, designed to manage ArgoCD instances. It’s supposed to be a helpful butler, setting up monitoring and metrics so you can see pretty graphs of your deployments.
But here is the catch: to be helpful, the operator needs high privileges. And when a high-privileged component takes orders from a low-privileged user without checking credentials, we enter the danger zone of the "Confused Deputy." In CVE-2025-13888, this operator decides to help you enable metrics by handing you a "Get Out of Jail Free" card that works on the entire cluster security model.
This isn't a complex memory corruption bug or a race condition in the kernel. This is a logic flaw pure and simple. It is the digital equivalent of a bank vault guard who opens the door for anyone wearing a vest that says "Metrics Inspector," even if that vest is written in crayon on a napkin.
The vulnerability lies within the ArgoCDMetricsReconciler. Its job is straightforward: when a user creates an ArgoCD Custom Resource (CR), the reconciler ensures Prometheus can scrape it. To do this in the OpenShift ecosystem, the namespace containing the target usually needs a specific label: openshift.io/cluster-monitoring: "true".
Here is the problem: In OpenShift, that specific label is not just a sticky note for Prometheus. It is a VIP pass. The Cluster Monitoring Operator (CMO) and the Admission Controllers treat namespaces with this label as trusted, core infrastructure components. When a namespace wears this label, it is often exempted from restrictive Security Context Constraints (SCCs) and NetworkPolicies.
The developer of the GitOps operator assumed that if metrics were enabled, the label should be applied. They forgot to ask the most important question: "Is this actually a system namespace?" Because they skipped that check, any random developer with edit rights to a sandbox namespace could create an ArgoCD CR, trigger the operator, and suddenly elevate their humble sandbox to the same security tier as the openshift-apiserver.
Let's look at the crime scene in controllers/argocd/argocd_metrics_controller.go. The vulnerable code was shockingly naive. It effectively said: "If the user wants metrics, give them the cluster monitoring label."
Vulnerable Code Logic:
const clusterMonitoringLabel = "openshift.io/cluster-monitoring"
// ...
// If metrics are NOT disabled (default is enabled)
if argocd.Spec.Monitoring.DisableMetrics == nil || !*argocd.Spec.Monitoring.DisableMetrics {
// BLINDLY APPLY THE GOD-MODE LABEL
namespace.Labels[clusterMonitoringLabel] = "true"
err = r.Client.Update(ctx, &namespace)
}The fix, implemented in version 1.16.2, introduces a sanity check. It verifies if the namespace actually belongs to the system (starts with openshift-). If it's just a user namespace, it applies a harmless user-monitoring label instead.
Patched Code Logic:
if strings.HasPrefix(namespace.Name, "openshift-") {
// Only system namespaces get the VIP pass
monitoringLabel = clusterMonitoringLabel
} else {
// Peasants get the user label
monitoringLabel = userDefinedMonitoringLabel
}
namespace.Labels[monitoringLabel] = "true"This change (Commit bc6ac3e03d7c8b3db5d8f1770c868396a4c2dcef) effectively closes the loophole by ensuring that high-privilege labels are reserved for high-privilege namespaces.
So, how does an attacker weaponize this? You need access to a namespace with permissions to create an ArgoCD resource (often granted to Namespace Admins). Here is the kill chain:
The Setup: You are a developer in the dev-team-a namespace. You want to escape to the host node. You try to deploy a privileged pod, but OpenShift's SCC (Security Context Constraints) blocks you. You are stuck in a cage.
The Trigger: You define a minimal ArgoCD object. You don't even need a valid ArgoCD installation; you just need the CR to exist.
apiVersion: argoproj.io/v1alpha1
kind: ArgoCD
metadata:
name: trojan-horse
namespace: dev-team-a
spec:
monitoring:
enabled: true # The default, but let's be explicitThe Escalation: The GitOps Operator sees the CR. It helpfully updates your namespace metadata:
dev-team-a now has labels: { openshift.io/cluster-monitoring: "true" }.
The Execution: Now that your namespace is considered "System Infrastructure," you deploy a Pod with the privileged: true security context and mount the host root filesystem (/). The Admission Controllers, seeing the VIP label on the namespace, wave the pod through.
# Inside your new privileged pod
chroot /host
cat /etc/shadow
# or just steal the kubelet credentialsThis is a classic container breakout, enabled purely because the Operator painted a target on the namespace that the Admission Controllers interpreted as "Safe."
This is a Critical (9.1 CVSS) vulnerability because it breaks the fundamental promise of multi-tenancy. Kubernetes and OpenShift rely heavily on Namespace boundaries to keep tenants apart. If a tenant can unilaterally change the security classification of their own namespace, the isolation model collapses.
In a real-world scenario, this allows:
openshift-kube-apiserver).The vector is particularly dangerous because "Namespace Admin" is a very common role delegated to development leads. This bug turns every lead developer into a potential Cluster Admin.
The remediation is straightforward but urgent.
1. Patch: Upgrade the OpenShift GitOps Operator to version 1.16.2 or later immediately. This applies the logic fix preventing the label assignment.
2. Audit: The patch stops new bad labels, but you must check for existing damage. Run this query to find non-system namespaces masquerading as system components:
oc get ns -l openshift.io/cluster-monitoring=true --no-headers | grep -v "^openshift-"If that command returns any namespaces (like my-project or test-env), investigate immediately. Remove the label and audit the pods running in those namespaces for privileged flags.
3. Admission Control: For defense-in-depth, consider adding an OPA Gatekeeper or Kyverno policy that explicitly forbids the modification of the openshift.io/ label prefix by anyone other than the ClusterAdmin role, regardless of what an Operator tries to do.
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
OpenShift GitOps Operator Red Hat | < 1.16.2 | 1.16.2 |
| Attribute | Detail |
|---|---|
| CWE | CWE-266 (Incorrect Privilege Assignment) |
| CVSS | 9.1 (Critical) |
| Vector | CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H |
| Attack Vector | Network (via Kubernetes API) |
| Privileges Required | High (Namespace Admin) |
| Impact | Cluster Takeover / Root Access |
The product assigns privileges or access rights to an actor, but the assignment is incorrect, allowing access to resources that should be restricted.