VYPR
High severityOSV Advisory· Published Jan 26, 2026· Updated Jan 27, 2026

Skipper Ingress Controller Allows Unauthorized Access to Internal Services via ExternalName

CVE-2026-24470

Description

Skipper is an HTTP router and reverse proxy for service composition. Prior to version 0.24.0, when running Skipper as an Ingress controller, users with permissions to create an Ingress and a Service of type ExternalName can create routes that enable them to use Skipper's network access to reach internal services. Version 0.24.0 disables Kubernetes ExternalName by default. As a workaround, developers can allow list targets of an ExternalName and allow list via regular expressions.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
github.com/zalando/skipperGo
< 0.24.00.24.0

Affected products

1

Patches

1
a4c87ce029a5

security: disable kubernetes external name by default (#3842)

https://github.com/zalando/skipperSandor SzücsJan 26, 2026via ghsa
17 files changed · +109 10
  • config/config.go+3 0 modified
    @@ -190,6 +190,7 @@ type Config struct {
     	KubernetesAnnotationPredicates                       []kubernetes.AnnotationPredicates  `yaml:"-"`
     	KubernetesAnnotationFiltersAppend                    []kubernetes.AnnotationFilters     `yaml:"-"`
     	KubernetesEastWestRangePredicates                    []*eskip.Predicate                 `yaml:"-"`
    +	EnableKubernetesExternalNames                        bool                               `yaml:"enable-kubernetes-external-names"`
     	KubernetesOnlyAllowedExternalNames                   bool                               `yaml:"kubernetes-only-allowed-external-names"`
     	KubernetesAllowedExternalNames                       regexpListFlag                     `yaml:"kubernetes-allowed-external-names"`
     	KubernetesRedisServiceNamespace                      string                             `yaml:"kubernetes-redis-service-namespace"`
    @@ -522,6 +523,7 @@ func NewConfig() *Config {
     	flag.Var(&cfg.KubernetesAnnotationFiltersAppendString, "kubernetes-annotation-filters-append", "configures filters appended to non east-west routes of annotated resources. E.g. -kubernetes-annotation-filters-append='zone-a=true=foo() -> bar()' will add 'foo() -> bar()' filters to all non east-west routes of ingress or routegroup annotated with 'zone-a: true'. For east-west routes use -kubernetes-east-west-range-annotation-filters-append.")
     	flag.Var(&cfg.KubernetesEastWestRangeAnnotationPredicatesString, "kubernetes-east-west-range-annotation-predicates", "similar to -kubernetes-annotation-predicates configures predicates appended to east-west routes of annotated resources. See also -kubernetes-east-west-range-domains.")
     	flag.Var(&cfg.KubernetesEastWestRangeAnnotationFiltersAppendString, "kubernetes-east-west-range-annotation-filters-append", "similar to -kubernetes-annotation-filters-append configures filters appended to east-west routes of annotated resources. See also -kubernetes-east-west-range-domains.")
    +	flag.BoolVar(&cfg.EnableKubernetesExternalNames, "enable-kubernetes-external-names", false, "only if enabled we allow to use external name services as backends in Ingress")
     	flag.BoolVar(&cfg.KubernetesOnlyAllowedExternalNames, "kubernetes-only-allowed-external-names", false, "only accept external name services, route group network backends and route group explicit LB endpoints from an allow list defined by zero or more -kubernetes-allowed-external-name flags")
     	flag.Var(&cfg.KubernetesAllowedExternalNames, "kubernetes-allowed-external-name", "set zero or more regular expressions from which at least one should be matched by the external name services, route group network addresses and explicit endpoints domain names")
     	flag.StringVar(&cfg.KubernetesRedisServiceNamespace, "kubernetes-redis-service-namespace", "", "Sets namespace for redis to be used to lookup endpoints")
    @@ -948,6 +950,7 @@ func (c *Config) ToOptions() skipper.Options {
     		KubernetesEastWestRangeAnnotationFiltersAppend: c.KubernetesEastWestRangeAnnotationFiltersAppend,
     		KubernetesAnnotationPredicates:                 c.KubernetesAnnotationPredicates,
     		KubernetesAnnotationFiltersAppend:              c.KubernetesAnnotationFiltersAppend,
    +		EnableKubernetesExternalNames:                  c.EnableKubernetesExternalNames,
     		KubernetesOnlyAllowedExternalNames:             c.KubernetesOnlyAllowedExternalNames,
     		KubernetesAllowedExternalNames:                 c.KubernetesAllowedExternalNames,
     		KubernetesRedisServiceNamespace:                c.KubernetesRedisServiceNamespace,
    
  • dataclients/kubernetes/ingress.go+8 2 modified
    @@ -37,6 +37,7 @@ type ingressContext struct {
     	annotationPredicate string
     	annotationBackend   string
     	forwardBackendURL   string
    +	enableExternalNames bool
     	extraRoutes         []*eskip.Route
     	backendWeights      map[string]float64
     	pathMode            PathMode
    @@ -58,6 +59,7 @@ type ingress struct {
     	provideHTTPSRedirect                           bool
     	disableCatchAllRoutes                          bool
     	forceKubernetesService                         bool
    +	enableExternalNames                            bool
     	backendTrafficAlgorithm                        BackendTrafficAlgorithm
     	defaultLoadBalancerAlgorithm                   string
     	forwardBackendURL                              string
    @@ -67,9 +69,12 @@ type ingress struct {
     	kubernetesEastWestRangeAnnotationFiltersAppend []AnnotationFilters
     }
     
    -var nonWord = regexp.MustCompile(`\W`)
    +var (
    +	nonWord = regexp.MustCompile(`\W`)
     
    -var errNotAllowedExternalName = errors.New("ingress with not allowed external name service")
    +	errNotEnabledExternalName = errors.New("ingress is not enabled to reference external name service")
    +	errNotAllowedExternalName = errors.New("ingress with not allowed external name service")
    +)
     
     func (ic *ingressContext) addHostRoute(host string, route *eskip.Route) {
     	ic.applyBackend(route)
    @@ -102,6 +107,7 @@ func newIngress(o Options) *ingress {
     		kubernetesEastWestDomain:                       o.KubernetesEastWestDomain,
     		eastWestRangeDomains:                           o.KubernetesEastWestRangeDomains,
     		eastWestRangePredicates:                        o.KubernetesEastWestRangePredicates,
    +		enableExternalNames:                            o.EnableExternalNames,
     		allowedExternalNames:                           o.AllowedExternalNames,
     		forceKubernetesService:                         o.ForceKubernetesService,
     		backendTrafficAlgorithm:                        o.BackendTrafficAlgorithm,
    
  • dataclients/kubernetes/ingressv1.go+16 3 modified
    @@ -109,7 +109,10 @@ func convertPathRuleV1(
     			ic.logger.Errorf("Failed to find target port for service %s, but %d endpoints exist", svcName, len(eps))
     		}
     	} else if svc.Spec.Type == "ExternalName" {
    -		return externalNameRoute(ns, name, host, hostRegexp, svc, servicePort, allowedExternalNames)
    +		if ic.enableExternalNames {
    +			return externalNameRoute(ns, name, host, hostRegexp, svc, servicePort, allowedExternalNames)
    +		}
    +		return nil, errNotEnabledExternalName
     	} else if forceKubernetesService {
     		eps = []string{serviceNameBackend(svcName, ns, servicePort)}
     	} else {
    @@ -185,6 +188,11 @@ func (ing *ingress) addEndpointsRuleV1(ic *ingressContext, host string, prule *d
     			return nil
     		}
     
    +		if errors.Is(err, errNotEnabledExternalName) {
    +			ic.logger.Infof("Not enabled to reference external name from ingress: %s/%s", meta.Namespace, meta.Name)
    +			return nil
    +		}
    +
     		// Ingress status field does not support errors
     		return fmt.Errorf("error while getting service: %w", err)
     	}
    @@ -359,8 +367,12 @@ func (ing *ingress) convertDefaultBackendV1(
     		ic.logger.Errorf("Failed to find target port %v, %s, for service %s add shuntroute: %v", svc.Spec.Ports, svcPort, svcName, err)
     		err = nil
     	} else if svc.Spec.Type == "ExternalName" {
    -		r, err := externalNameRoute(ns, name, "default", nil, svc, servicePort, ing.allowedExternalNames)
    -		return r, err == nil, err
    +		if ic.enableExternalNames {
    +			r, err := externalNameRoute(ns, name, "default", nil, svc, servicePort, ing.allowedExternalNames)
    +			return r, err == nil, err
    +		}
    +		return nil, false, errNotEnabledExternalName
    +
     	} else if forceKubernetesService {
     		eps = []string{serviceNameBackend(svcName, ns, servicePort)}
     	} else {
    @@ -435,6 +447,7 @@ func (ing *ingress) ingressV1Route(
     		annotationPredicate: annotationPredicate(i.Metadata),
     		annotationBackend:   annotationBackendString(i.Metadata),
     		forwardBackendURL:   ing.forwardBackendURL,
    +		enableExternalNames: ing.enableExternalNames,
     		extraRoutes:         extraRoutes(i.Metadata),
     		backendWeights:      backendWeights(i.Metadata, logger),
     		pathMode:            pathMode(i.Metadata, ing.pathMode, logger),
    
  • dataclients/kubernetes/kube.go+4 0 modified
    @@ -233,6 +233,10 @@ type Options struct {
     	// (using tracingTag filter) should be added to all routes
     	BackendNameTracingTag bool
     
    +	// EnableExternalNames enables the integration of Kubernetes
    +	// Service type ExternalName as backends in Ingress.
    +	EnableExternalNames bool
    +
     	// OnlyAllowedExternalNames will enable validation of ingress external names and route groups network
     	// backend addresses, explicit LB endpoints validation against the list of patterns in
     	// AllowedExternalNames.
    
  • dataclients/kubernetes/kubernetestest/fixtures.go+2 0 modified
    @@ -41,6 +41,7 @@ type kubeOptionsParser struct {
     	HTTPSRedirectCode                              int                               `yaml:"httpsRedirectCode"`
     	DisableCatchAllRoutes                          bool                              `yaml:"disableCatchAllRoutes"`
     	BackendNameTracingTag                          bool                              `yaml:"backendNameTracingTag"`
    +	EnableExternalNames                            bool                              `yaml:"enableExternalNames"`
     	OnlyAllowedExternalNames                       bool                              `yaml:"onlyAllowedExternalNames"`
     	AllowedExternalNames                           []string                          `yaml:"allowedExternalNames"`
     	IngressClass                                   string                            `yaml:"kubernetes-ingress-class"`
    @@ -263,6 +264,7 @@ func testFixture(t *testing.T, f fixtureSet) {
     			t.Fatal(err)
     		}
     
    +		o.EnableExternalNames = kop.EnableExternalNames
     		o.OnlyAllowedExternalNames = kop.OnlyAllowedExternalNames
     		o.AllowedExternalNames = aen
     	}
    
  • dataclients/kubernetes/testdata/ingressV1/external-name/all-allowed.kube+1 0 modified
    @@ -1,3 +1,4 @@
    +enableExternalNames: true
     onlyAllowedExternalNames: true
     allowedExternalNames:
     - ^external1[.]example[.]org$
    
  • dataclients/kubernetes/testdata/ingressV1/external-name/external-name-validation-not-enabled.kube+1 0 added
    @@ -0,0 +1 @@
    +enableExternalNames: true
    
  • dataclients/kubernetes/testdata/ingressV1/external-name/none-allowed.kube+1 0 modified
    @@ -1 +1,2 @@
    +enableExternalNames: true
     onlyAllowedExternalNames: true
    
  • dataclients/kubernetes/testdata/ingressV1/external-name/not-allowed.eskip+0 0 added
  • dataclients/kubernetes/testdata/ingressV1/external-name/not-allowed.kube+5 0 added
    @@ -0,0 +1,5 @@
    +enableExternalNames: false
    +onlyAllowedExternalNames: true
    +allowedExternalNames:
    +- ^external1[.]example[.]org$
    +- ^external2[.]example[.]org$
    
  • dataclients/kubernetes/testdata/ingressV1/external-name/not-allowed.yaml+54 0 added
    @@ -0,0 +1,54 @@
    +apiVersion: networking.k8s.io/v1
    +kind: Ingress
    +metadata:
    +  name: myapp
    +  namespace: default
    +spec:
    +  rules:
    +  - host: example.org
    +    http:
    +      paths:
    +      - path: /one
    +        pathType: ImplementationSpecific
    +        backend:
    +          service:
    +            name: external1
    +            port:
    +              name: ext
    +      - path: /two
    +        pathType: ImplementationSpecific
    +        backend:
    +          service:
    +            name: external2
    +            port:
    +              name: ext
    +---
    +apiVersion: v1
    +kind: Service
    +metadata:
    +  labels:
    +    application: myapp
    +  name: external1
    +spec:
    +  type: ExternalName
    +  externalName: external1.example.org
    +  ports:
    +  - name: ext
    +    port: 443
    +    protocol: TCP
    +    targetPort: 443
    +---
    +apiVersion: v1
    +kind: Service
    +metadata:
    +  labels:
    +    application: myapp
    +  name: external2
    +spec:
    +  type: ExternalName
    +  externalName: external2.example.org
    +  ports:
    +  - name: ext
    +    port: 443
    +    protocol: TCP
    +    targetPort: 443
    
  • dataclients/kubernetes/testdata/ingressV1/external-name/some-allowed.kube+1 0 modified
    @@ -1,3 +1,4 @@
    +enableExternalNames: true
     onlyAllowedExternalNames: true
     allowedExternalNames:
     - ^external1[.]example[.]org$
    
  • dataclients/kubernetes/testdata/ingressV1/ingress-data/ing-with-externalname-svc.kube+2 0 added
    @@ -0,0 +1,2 @@
    +enableExternalNames: true
    +
    
  • docs/kubernetes/external-addresses.md+5 3 modified
    @@ -1,15 +1,15 @@
     # External Addresses (External Name)
     
    -In Kubernetes, it is possible to define services with external names (type=ExternalName). For ingress objects,
    -Skipper supports these services, and generates routes from the ingress objects that reference one or more
    +In Kubernetes, it is possible to define services with external names (type=ExternalName). For Ingress objects,
    +Skipper supports these services, if enabled by `-enable-kubernetes-external-names`. Skipper generates routes from the Ingress objects that reference one or more
     external name service, that will have a backend pointing to the network address defined by the specified
     service.
     
     Route groups don't support services of type ExternalName, but they support network backends, and even LB
     backends with explicit endpoints with custom endpoint addresses. This way, it is possible to achieve the same
     with route groups.
     
    -For both the ingress objects and the route groups, the accepted external addresses must be explicitly allowed by
    +For both the Ingress objects and the route groups, the accepted external addresses must be explicitly allowed by
     listing regexp expressions of which at least one must be matched by the domain name of these addresses. The
     allow list is a startup option, defined via command line flags or in the configuration file. Enforcing this
     list happens only in the Kubernetes Ingress mode of Skipper.
    @@ -20,6 +20,7 @@ For compatibility reasons, the validation needs to be enabled with an explicit t
     
     ```sh
     skipper -kubernetes \
    +-enable-kubernetes-external-names \
     -kubernetes-only-allowed-external-names \
     -kubernetes-allowed-external-name "^one[.]example[.]org$" \
     -kubernetes-allowed-external-name "^two[.]example[.]org$"
    @@ -30,6 +31,7 @@ skipper -kubernetes \
     For compatibility reasons, the validation needs to be enabled with an explicit toggle:
     
     ```yaml
    +enable-kubernetes-external-names: true
     kubernetes-only-allowed-external-names: true
     kubernetes-allowed-external-names:
     - ^one[.]example[.]org$
    
  • docs/kubernetes/ingress-usage.md+1 1 modified
    @@ -37,7 +37,7 @@ Service type | supported | workaround
     --- | --- | ---
     ClusterIP | yes | ---
     NodePort | yes | ---
    -ExternalName | yes | ---
    +ExternalName | yes (enable by `-enable-kubernetes-external-names` | ---
     LoadBalancer | no | it should not, because Kubernetes cloud-controller-manager will maintain it
     
     
    
  • skipper.go+4 0 modified
    @@ -293,6 +293,9 @@ type Options struct {
     	// KubernetesAnnotationFiltersAppend sets filters to append for each annotation key and value
     	KubernetesAnnotationFiltersAppend []kubernetes.AnnotationFilters
     
    +	// EnableKubernetesExternalNames enables to use Kubernetes service type ExternalName as backend in Ingress and RouteGroup.
    +	EnableKubernetesExternalNames bool
    +
     	// KubernetesOnlyAllowedExternalNames will enable validation of ingress external names and route groups network
     	// backend addresses, explicit LB endpoints validation against the list of patterns in
     	// AllowedExternalNames.
    @@ -1034,6 +1037,7 @@ type Options struct {
     func (o *Options) KubernetesDataClientOptions() kubernetes.Options {
     	return kubernetes.Options{
     		AllowedExternalNames:                           o.KubernetesAllowedExternalNames,
    +		EnableExternalNames:                            o.EnableKubernetesExternalNames,
     		BackendNameTracingTag:                          o.OpenTracingBackendNameTag,
     		DefaultFiltersDir:                              o.DefaultFiltersDir,
     		KubernetesInCluster:                            o.KubernetesInCluster,
    
  • VERSION+1 1 modified
    @@ -1 +1 @@
    -v0.23
    +v0.24
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

6

News mentions

0

No linked articles in our index yet.