Repository: traefik/traefik (language: go)

Issue:
Internal Node IPs for NodePort services feature breaks namespace scoped Traefik deployments
### Welcome!

- [X] Yes, I've searched similar issues on [GitHub](https://github.com/traefik/traefik/issues) and didn't find any.
- [X] Yes, I've searched similar issues on the [Traefik community forum](https://community.traefik.io) and didn't find any.

### What did you do?

I'm running my Traefik deployments on Kubernetres in a fully name-spaced way. All Traefik RBAC permissions are configured using `Roles` and `RoleBindings`. The K8s cluster is owned by an infrastructure team and host multiple namespaces for multiple teams/clients. Until now I was able to run and configure Traefik permissions without any support from the infra team. 

With the [Internal Node IPs for NodePort services](https://github.com/traefik/traefik/pull/10278) feature introduced in Treafik 3.1 I no longer can do it. Traefik 3.1 requires cluster level permissions to list/watch nodes. This makes both me (as I need to ask/deploy an external team to manage/deploy my Traefik configuration) and the infrastructure team (that operate at a big scale and don't want to be bothered with an application level configurations) unhappy.

As stated in the original PR comment by @rtribotte nodes are not namespaced so I fully understand why it was implemented this way. But the possibility to run Traefik in a fully namespaced way is/was a cool and useful feature that, I think, should be kept. 
 
@rtribotte are you open to refactoring the code to fetch node resources in the service namespace or other solution that will allow for fully namespaced Traefik setups?

Also the documentation for [3.0 -> 3.1 migration](https://doc.traefik.io/traefik/migration/v3/) is missing any information about the new permissions requirements 

### What did you see instead?

Traefik 3.1 can't run in Kubernetes without cluster level permissions for listing nodes

### What version of Traefik are you using?

```
traefik version
Version:      3.1.0
Codename:     comte
Go version:   go1.22.5
Built:        2024-07-15T14:44:04Z
OS/Arch:      linux/amd64
```

### What is your environment & configuration?

Traefik RBAC configuration:

```yaml
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: traefik
  namespace: {{ .Release.Namespace }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: traefik-service-discovery
  namespace: {{ .Release.Namespace }}
rules:
  - apiGroups: [""]
    resources: ["endpoints", "pods", "secrets", "services","nodes"]
    verbs: ["get", "watch", "list"]
  - apiGroups: ["extensions", "networking.k8s.io"]
    resources: ["ingresses", "ingressclasses"]
    verbs: ["get", "list", "watch"]
  - apiGroups: ["traefik.io", "traefik.containo.us"]
    resources: ["ingressroutes", "ingressroutetcps", "ingressrouteudps", "middlewares", "middlewaretcps", "tlsoptions", "tlsstores", "traefikservices", "serverstransports", "serverstransporttcps"]
    verbs: ["get", "list", "watch"]
  - apiGroups: ["discovery.k8s.io"]
    resources: ["endpointslices"]
    verbs: ["watch", "list"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: traefik-role-binding
  namespace: {{ .Release.Namespace }}
subjects:
  - kind: ServiceAccount
    name: traefik
    namespace: {{ .Release.Namespace }}
roleRef:
  kind: Role
  name: traefik-service-discovery
  apiGroup: rbac.authorization.k8s.io
```



### If applicable, please paste the log output in DEBUG level

_No response_


Discussion / hints:

Fix patch:
diff --git a/docs/content/migration/v2.md b/docs/content/migration/v2.md
index aa803fc814..0e56a8feed 100644
--- a/docs/content/migration/v2.md
+++ b/docs/content/migration/v2.md
@@ -455,7 +455,7 @@ To enable HTTP/3 on an EntryPoint, please check out the [HTTP/3 configuration](.
 
 In `v2.6`, the [Kubernetes Gateway API provider](../providers/kubernetes-gateway.md) now only supports the version [v1alpha2](https://gateway-api.sigs.k8s.io/v1alpha2/guides/) of the specification and 
 [route namespaces](https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1beta1.RouteNamespaces) selectors, which requires Traefik to fetch and watch the cluster namespaces.
-Therefore, the [RBAC](../reference/dynamic-configuration/kubernetes-gateway.md#rbac) and [CRD](../reference/dynamic-configuration/kubernetes-gateway.md#definitions) definitions must be updated.
+Therefore, the RBAC and CRD definitions must be updated.
 
 ## v2.6.0 to v2.6.1
 
diff --git a/docs/content/migration/v3.md b/docs/content/migration/v3.md
index 4529a89a44..01a237173a 100644
--- a/docs/content/migration/v3.md
+++ b/docs/content/migration/v3.md
@@ -10,9 +10,11 @@ description: "Learn the steps needed to migrate to new Traefik Proxy v3 versions
 ### Kubernetes Provider RBACs
 
 Starting with v3.1, the Kubernetes Providers now use the [EndpointSlices API](https://kubernetes.io/docs/concepts/services-networking/endpoint-slices/) (Kubernetes >=v1.21) to discover service endpoint addresses.
+It also brings NodePort load-balancing which requires Nodes resources lookup.
 
-Therefore, in the corresponding RBACs (see [KubernetesIngress](../routing/providers/kubernetes-ingress.md#configuration-example), [KubernetesCRD](../reference/dynamic-configuration/kubernetes-crd.md#rbac), and [KubernetesGateway](../reference/dynamic-configuration/kubernetes-gateway.md#rbac) provider RBACs), 
-the `endpoints` right has to be removed and the following `endpointslices` right has to be added.
+Therefore, in the corresponding RBACs (see [KubernetesIngress](../routing/providers/kubernetes-ingress.md#configuration-example), [KubernetesCRD](../reference/dynamic-configuration/kubernetes-crd.md#rbac), and [KubernetesGateway](../reference/dynamic-configuration/kubernetes-gateway-rbac.yml) provider RBACs):
+
+- the `endpoints` right has to be removed and the following `endpointslices` right has to be added:
 
 ```yaml
   ... 
@@ -26,6 +28,21 @@ the `endpoints` right has to be removed and the following `endpointslices` right
   ...
 ```
 
+- the `nodes` right has to be added:
+
+```yaml
+  ...
+  - apiGroups:
+      - ""
+    resources:
+      - nodes
+    verbs:
+      - get
+      - list
+      - watch
+  ...
+```
+
 #### Gateway API: KubernetesGateway Provider
 
 In v3.1, the KubernetesGateway Provider is no longer an experimental feature.
@@ -51,3 +68,10 @@ It can be enabled without the associated `experimental.kubernetesgateway` option
 
 The `kubernetesgateway` option should be removed from the experimental section of the static configuration.
 To configure `kubernetesgateway`, please check out the [KubernetesGateway Provider documentation](../providers/kubernetes-gateway.md).
+
+## v3.1.0 to v3.1.1
+
+### IngressClass Lookup
+
+The Kubernetes Ingress provider option `disableIngressClassLookup` has been deprecated in v3.1.1, and will be removed in the next major version.
+Please use the `disableClusterScopeResources` option instead to avoid cluster scope resources discovery (IngressClass, Nodes).
diff --git a/docs/content/providers/kubernetes-ingress.md b/docs/content/providers/kubernetes-ingress.md
index 2da41808d2..20e7457c94 100644
--- a/docs/content/providers/kubernetes-ingress.md
+++ b/docs/content/providers/kubernetes-ingress.md
@@ -287,6 +287,11 @@ providers:
 
 _Optional, Default: false_
 
+??? warning "Deprecated"
+
+    The Kubernetes Ingress provider option `disableIngressClassLookup` has been deprecated in v3.1, and will be removed in the next major version.
+	Please use the `disableClusterScopeResources` option instead.
+
 If the parameter is set to `true`,
 Traefik will not discover IngressClasses in the cluster.
 By doing so, it alleviates the requirement of giving Traefik the rights to look IngressClasses up.
@@ -312,6 +317,33 @@ providers:
 --providers.kubernetesingress.disableingressclasslookup=true
 ```
 
+### `disableClusterScopeResources`
+
+_Optional, Default: false_
+
+When this parameter is set to `true`,
+Traefik will not discover cluster scope resources (`IngressClass` and `Nodes`).
+By doing so, it alleviates the requirement of giving Traefik the rights to look up for cluster resources.
+Furthermore, Traefik will not handle Ingresses with IngressClass references, therefore such Ingresses will be ignored (please note that annotations are not affected by this option).
+This will also prevent from using the `NodePortLB` options on services.
+
+```yaml tab="File (YAML)"
+providers:
+  kubernetesIngress:
+    disableClusterScopeResources: true
+    # ...
+```
+
+```toml tab="File (TOML)"
+[providers.kubernetesIngress]
+  disableClusterScopeResources = true
+  # ...
+```
+
+```bash tab="CLI"
+--providers.kubernetesingress.disableClusterScopeResources=true
+```
+
 ### `ingressEndpoint`
 
 #### `hostname`
diff --git a/docs/content/reference/static-configuration/cli-ref.md b/docs/content/reference/static-configuration/cli-ref.md
index a261ffa27c..cd245706d0 100644
--- a/docs/content/reference/static-configuration/cli-ref.md
+++ b/docs/content/reference/static-configuration/cli-ref.md
@@ -726,6 +726,9 @@ Allow ExternalName services. (Default: ```false```)
 `--providers.kubernetescrd.certauthfilepath`:  
 Kubernetes certificate authority file path (not needed for in-cluster client).
 
+`--providers.kubernetescrd.disableclusterscoperesources`:  
+Disables the lookup of cluster scope resources (incompatible with IngressClasses and NodePortLB enabled services). (Default: ```false```)
+
 `--providers.kubernetescrd.endpoint`:  
 Kubernetes server endpoint (required for external cluster client).
 
@@ -798,8 +801,11 @@ Allow ExternalName services. (Default: ```false```)
 `--providers.kubernetesingress.certauthfilepath`:  
 Kubernetes certificate authority file path (not needed for in-cluster client).
 
+`--providers.kubernetesingress.disableclusterscoperesources`:  
+Disables the lookup of cluster scope resources (incompatible with IngressClasses and NodePortLB enabled services). (Default: ```false```)
+
 `--providers.kubernetesingress.disableingressclasslookup`:  
-Disables the lookup of IngressClasses. (Default: ```false```)
+Disables the lookup of IngressClasses (Deprecated, please use DisableClusterScopeResources). (Default: ```false```)
 
 `--providers.kubernetesingress.endpoint`:  
 Kubernetes server endpoint (required for external cluster client).
diff --git a/docs/content/reference/static-configuration/env-ref.md b/docs/content/reference/static-configuration/env-ref.md
index 43882e52f0..02873474bb 100644
--- a/docs/content/reference/static-configuration/env-ref.md
+++ b/docs/content/reference/static-configuration/env-ref.md
@@ -726,6 +726,9 @@ Allow ExternalName services. (Default: ```false```)
 `TRAEFIK_PROVIDERS_KUBERNETESCRD_CERTAUTHFILEPATH`:  
 Kubernetes certificate authority file path (not needed for in-cluster client).
 
+`TRAEFIK_PROVIDERS_KUBERNETESCRD_DISABLECLUSTERSCOPERESOURCES`:  
+Disables the lookup of cluster scope resources (incompatible with IngressClasses and NodePortLB enabled services). (Default: ```false```)
+
 `TRAEFIK_PROVIDERS_KUBERNETESCRD_ENDPOINT`:  
 Kubernetes server endpoint (required for external cluster client).
 
@@ -798,8 +801,11 @@ Allow ExternalName services. (Default: ```false```)
 `TRAEFIK_PROVIDERS_KUBERNETESINGRESS_CERTAUTHFILEPATH`:  
 Kubernetes certificate authority file path (not needed for in-cluster client).
 
+`TRAEFIK_PROVIDERS_KUBERNETESINGRESS_DISABLECLUSTERSCOPERESOURCES`:  
+Disables the lookup of cluster scope resources (incompatible with IngressClasses and NodePortLB enabled services). (Default: ```false```)
+
 `TRAEFIK_PROVIDERS_KUBERNETESINGRESS_DISABLEINGRESSCLASSLOOKUP`:  
-Disables the lookup of IngressClasses. (Default: ```false```)
+Disables the lookup of IngressClasses (Deprecated, please use DisableClusterScopeResources). (Default: ```false```)
 
 `TRAEFIK_PROVIDERS_KUBERNETESINGRESS_ENDPOINT`:  
 Kubernetes server endpoint (required for external cluster client).
diff --git a/docs/content/reference/static-configuration/file.toml b/docs/content/reference/static-configuration/file.toml
index f171b9e862..8a033d2f29 100644
--- a/docs/content/reference/static-configuration/file.toml
+++ b/docs/content/reference/static-configuration/file.toml
@@ -124,6 +124,7 @@
     allowEmptyServices = true
     allowExternalNameServices = true
     disableIngressClassLookup = true
+    disableClusterScopeResources = true
     nativeLBByDefault = true
     [providers.kubernetesIngress.ingressEndpoint]
       ip = "foobar"
@@ -141,6 +142,7 @@
     throttleDuration = "42s"
     allowEmptyServices = true
     nativeLBByDefault = true
+    disableClusterScopeResources = true
   [providers.kubernetesGateway]
     endpoint = "foobar"
     token = "foobar"
diff --git a/docs/content/reference/static-configuration/file.yaml b/docs/content/reference/static-configuration/file.yaml
index 1c053dfafb..d8a9b13624 100644
--- a/docs/content/reference/static-configuration/file.yaml
+++ b/docs/content/reference/static-configuration/file.yaml
@@ -141,6 +141,7 @@ providers:
     allowEmptyServices: true
     allowExternalNameServices: true
     disableIngressClassLookup: true
+    disableClusterScopeResources: true
     nativeLBByDefault: true
   kubernetesCRD:
     endpoint: foobar
@@ -156,6 +157,7 @@ providers:
     throttleDuration: 42s
     allowEmptyServices: true
     nativeLBByDefault: true
+    disableClusterScopeResources: true
   kubernetesGateway:
     endpoint: foobar
     token: foobar
diff --git a/pkg/cli/deprecation.go b/pkg/cli/deprecation.go
index 60bb0adac7..0e7af5d05a 100644
--- a/pkg/cli/deprecation.go
+++ b/pkg/cli/deprecation.go
@@ -204,16 +204,17 @@ func (c *configuration) deprecationNotice(logger zerolog.Logger) bool {
 }
 
 type providers struct {
-	Docker        *docker        `json:"docker,omitempty" toml:"docker,omitempty" yaml:"docker,omitempty" label:"allowEmpty" file:"allowEmpty"`
-	Swarm         *swarm         `json:"swarm,omitempty" toml:"swarm,omitempty" yaml:"swarm,omitempty" label:"allowEmpty" file:"allowEmpty"`
-	Consul        *consul        `json:"consul,omitempty" toml:"consul,omitempty" yaml:"consul,omitempty" label:"allowEmpty" file:"allowEmpty"`
-	ConsulCatalog *consulCatalog `json:"consulCatalog,omitempty" toml:"consulCatalog,omitempty" yaml:"consulCatalog,omitempty" label:"allowEmpty" file:"allowEmpty"`
-	Nomad         *nomad         `json:"nomad,omitempty" toml:"nomad,omitempty" yaml:"nomad,omitempty" label:"allowEmpty" file:"allowEmpty"`
-	Marathon      map[string]any `json:"marathon,omitempty" toml:"marathon,omitempty" yaml:"marathon,omitempty" label:"allowEmpty" file:"allowEmpty"`
-	Rancher       map[string]any `json:"rancher,omitempty" toml:"rancher,omitempty" yaml:"rancher,omitempty" label:"allowEmpty" file:"allowEmpty"`
-	ETCD          *etcd          `json:"etcd,omitempty" toml:"etcd,omitempty" yaml:"etcd,omitempty" label:"allowEmpty" file:"allowEmpty"`
-	Redis         *redis         `json:"redis,omitempty" toml:"redis,omitempty" yaml:"redis,omitempty" label:"allowEmpty" file:"allowEmpty"`
-	HTTP          *http          `json:"http,omitempty" toml:"http,omitempty" yaml:"http,omitempty" label:"allowEmpty" file:"allowEmpty"`
+	Docker            *docker        `json:"docker,omitempty" toml:"docker,omitempty" yaml:"docker,omitempty" label:"allowEmpty" file:"allowEmpty"`
+	Swarm             *swarm         `json:"swarm,omitempty" toml:"swarm,omitempty" yaml:"swarm,omitempty" label:"allowEmpty" file:"allowEmpty"`
+	Consul            *consul        `json:"consul,omitempty" toml:"consul,omitempty" yaml:"consul,omitempty" label:"allowEmpty" file:"allowEmpty"`
+	ConsulCatalog     *consulCatalog `json:"consulCatalog,omitempty" toml:"consulCatalog,omitempty" yaml:"consulCatalog,omitempty" label:"allowEmpty" file:"allowEmpty"`
+	Nomad             *nomad         `json:"nomad,omitempty" toml:"nomad,omitempty" yaml:"nomad,omitempty" label:"allowEmpty" file:"allowEmpty"`
+	Marathon          map[string]any `json:"marathon,omitempty" toml:"marathon,omitempty" yaml:"marathon,omitempty" label:"allowEmpty" file:"allowEmpty"`
+	Rancher           map[string]any `json:"rancher,omitempty" toml:"rancher,omitempty" yaml:"rancher,omitempty" label:"allowEmpty" file:"allowEmpty"`
+	ETCD              *etcd          `json:"etcd,omitempty" toml:"etcd,omitempty" yaml:"etcd,omitempty" label:"allowEmpty" file:"allowEmpty"`
+	Redis             *redis         `json:"redis,omitempty" toml:"redis,omitempty" yaml:"redis,omitempty" label:"allowEmpty" file:"allowEmpty"`
+	HTTP              *http          `json:"http,omitempty" toml:"http,omitempty" yaml:"http,omitempty" label:"allowEmpty" file:"allowEmpty"`
+	KubernetesIngress *ingress       `json:"kubernetesIngress,omitempty" toml:"kubernetesIngress,omitempty" yaml:"kubernetesIngress,omitempty" file:"allowEmpty"`
 }
 
 func (p *providers) deprecationNotice(logger zerolog.Logger) bool {
@@ -243,6 +244,7 @@ func (p *providers) deprecationNotice(logger zerolog.Logger) bool {
 	etcdIncompatible := p.ETCD.deprecationNotice(logger)
 	redisIncompatible := p.Redis.deprecationNotice(logger)
 	httpIncompatible := p.HTTP.deprecationNotice(logger)
+	p.KubernetesIngress.deprecationNotice(logger)
 	return incompatible ||
 		dockerIncompatible ||
 		consulIncompatible ||
@@ -457,6 +459,22 @@ func (h *http) deprecationNotice(logger zerolog.Logger) bool {
 	return incompatible
 }
 
+type ingress struct {
+	DisableIngressClassLookup *bool `json:"disableIngressClassLookup,omitempty" toml:"disableIngressClassLookup,omitempty" yaml:"disableIngressClassLookup,omitempty"`
+}
+
+func (i *ingress) deprecationNotice(logger zerolog.Logger) {
+	if i == nil {
+		return
+	}
+
+	if i.DisableIngressClassLookup != nil {
+		logger.Error().Msg("Kubernetes Ingress provider `disableIngressClassLookup` option has been deprecated in v3.1, and will be removed in the next major version." +
+			"Please use the `disableClusterScopeResources` option instead." +
+			"For more information please read the migration guide: https://doc.traefik.io/traefik/v3.1/migration/v3/#ingressclasslookup")
+	}
+}
+
 type experimental struct {
 	HTTP3             *bool `json:"http3,omitempty" toml:"http3,omitempty" yaml:"http3,omitempty"`
 	KubernetesGateway *bool `json:"kubernetesGateway,omitempty" toml:"kubernetesGateway,omitempty" yaml:"kubernetesGateway,omitempty"`
diff --git a/pkg/provider/kubernetes/crd/client.go b/pkg/provider/kubernetes/crd/client.go
index a52f76497a..f82852e9ff 100644
--- a/pkg/provider/kubernetes/crd/client.go
+++ b/pkg/provider/kubernetes/crd/client.go
@@ -57,10 +57,12 @@ type clientWrapper struct {
 	csCrd  traefikclientset.Interface
 	csKube kclientset.Interface
 
-	factoryClusterScope kinformers.SharedInformerFactory
-	factoriesCrd        map[string]traefikinformers.SharedInformerFactory
-	factoriesKube       map[string]kinformers.SharedInformerFactory
-	factoriesSecret     map[string]kinformers.SharedInformerFactory
+	clusterScopeFactory         kinformers.SharedInformerFactory
+	disableClusterScopeInformer bool
+
+	factoriesCrd    map[string]traefikinformers.SharedInformerFactory
+	factoriesKube   map[string]kinformers.SharedInformerFactory
+	factoriesSecret map[string]kinformers.SharedInformerFactory
 
 	labelSelector string
 
@@ -237,18 +239,11 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (<
 		c.factoriesSecret[ns] = factorySecret
 	}
 
-	c.factoryClusterScope = kinformers.NewSharedInformerFactory(c.csKube, resyncPeriod)
-	_, err := c.factoryClusterScope.Core().V1().Nodes().Informer().AddEventHandler(eventHandler)
-	if err != nil {
-		return nil, err
-	}
-
 	for _, ns := range namespaces {
 		c.factoriesCrd[ns].Start(stopCh)
 		c.factoriesKube[ns].Start(stopCh)
 		c.factoriesSecret[ns].Start(stopCh)
 	}
-	c.factoryClusterScope.Start(stopCh)
 
 	for _, ns := range namespaces {
 		for t, ok := range c.factoriesCrd[ns].WaitForCacheSync(stopCh) {
@@ -270,9 +265,19 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (<
 		}
 	}
 
-	for t, ok := range c.factoryClusterScope.WaitForCacheSync(stopCh) {
-		if !ok {
-			return nil, fmt.Errorf("timed out waiting for controller caches to sync %s", t.String())
+	if !c.disableClusterScopeInformer {
+		c.clusterScopeFactory = kinformers.NewSharedInformerFactory(c.csKube, resyncPeriod)
+		_, err := c.clusterScopeFactory.Core().V1().Nodes().Informer().AddEventHandler(eventHandler)
+		if err != nil {
+			return nil, err
+		}
+
+		c.clusterScopeFactory.Start(stopCh)
+
+		for t, ok := range c.clusterScopeFactory.WaitForCacheSync(stopCh) {
+			if !ok {
+				return nil, fmt.Errorf("timed out waiting for controller caches to sync %s", t.String())
+			}
 		}
 	}
 
@@ -474,7 +479,7 @@ func (c *clientWrapper) GetSecret(namespace, name string) (*corev1.Secret, bool,
 }
 
 func (c *clientWrapper) GetNodes() ([]*corev1.Node, bool, error) {
-	nodes, err := c.factoryClusterScope.Core().V1().Nodes().Lister().List(labels.Everything())
+	nodes, err := c.clusterScopeFactory.Core().V1().Nodes().Lister().List(labels.Everything())
 	exist, err := translateNotFoundError(err)
 	return nodes, exist, err
 }
diff --git a/pkg/provider/kubernetes/crd/kubernetes.go b/pkg/provider/kubernetes/crd/kubernetes.go
index 89efa35026..752ba88a7b 100644
--- a/pkg/provider/kubernetes/crd/kubernetes.go
+++ b/pkg/provider/kubernetes/crd/kubernetes.go
@@ -50,17 +50,18 @@ const (
 
 // Provider holds configurations of the provider.
 type Provider struct {
-	Endpoint                  string              `description:"Kubernetes server endpoint (required for external cluster client)." json:"endpoint,omitempty" toml:"endpoint,omitempty" yaml:"endpoint,omitempty"`
-	Token                     types.FileOrContent `description:"Kubernetes bearer token (not needed for in-cluster client). It accepts either a token value or a file path to the token." json:"token,omitempty" toml:"token,omitempty" yaml:"token,omitempty" loggable:"false"`
-	CertAuthFilePath          string              `description:"Kubernetes certificate authority file path (not needed for in-cluster client)." json:"certAuthFilePath,omitempty" toml:"certAuthFilePath,omitempty" yaml:"certAuthFilePath,omitempty"`
-	Namespaces                []string            `description:"Kubernetes namespaces." json:"namespaces,omitempty" toml:"namespaces,omitempty" yaml:"namespaces,omitempty" export:"true"`
-	AllowCrossNamespace       bool                `description:"Allow cross namespace resource reference." json:"allowCrossNamespace,omitempty" toml:"allowCrossNamespace,omitempty" yaml:"allowCrossNamespace,omitempty" export:"true"`
-	AllowExternalNameServices bool                `description:"Allow ExternalName services." json:"allowExternalNameServices,omitempty" toml:"allowExternalNameServices,omitempty" yaml:"allowExternalNameServices,omitempty" export:"true"`
-	LabelSelector             string              `description:"Kubernetes label selector to use." json:"labelSelector,omitempty" toml:"labelSelector,omitempty" yaml:"labelSelector,omitempty" export:"true"`
-	IngressClass              string              `description:"Value of kubernetes.io/ingress.class annotation to watch for." json:"ingressClass,omitempty" toml:"ingressClass,omitempty" yaml:"ingressClass,omitempty" export:"true"`
-	ThrottleDuration          ptypes.Duration     `description:"Ingress refresh throttle duration" json:"throttleDuration,omitempty" toml:"throttleDuration,omitempty" yaml:"throttleDuration,omitempty" export:"true"`
-	AllowEmptyServices        bool                `description:"Allow the creation of services without endpoints." json:"allowEmptyServices,omitempty" toml:"allowEmptyServices,omitempty" yaml:"allowEmptyServices,omitempty" export:"true"`
-	NativeLBByDefault         bool                `description:"Defines whether to use Native Kubernetes load-balancing mode by default." json:"nativeLBByDefault,omitempty" toml:"nativeLBByDefault,omitempty" yaml:"nativeLBByDefault,omitempty" export:"true"`
+	Endpoint                     string              `description:"Kubernetes server endpoint (required for external cluster client)." json:"endpoint,omitempty" toml:"endpoint,omitempty" yaml:"endpoint,omitempty"`
+	Token                        types.FileOrContent `description:"Kubernetes bearer token (not needed for in-cluster client). It accepts either a token value or a file path to the token." json:"token,omitempty" toml:"token,omitempty" yaml:"token,omitempty" loggable:"false"`
+	CertAuthFilePath             string              `description:"Kubernetes certificate authority file path (not needed for in-cluster client)." json:"certAuthFilePath,omitempty" toml:"certAuthFilePath,omitempty" yaml:"certAuthFilePath,omitempty"`
+	Namespaces                   []string            `description:"Kubernetes namespaces." json:"namespaces,omitempty" toml:"namespaces,omitempty" yaml:"namespaces,omitempty" export:"true"`
+	AllowCrossNamespace          bool                `description:"Allow cross namespace resource reference." json:"allowCrossNamespace,omitempty" toml:"allowCrossNamespace,omitempty" yaml:"allowCrossNamespace,omitempty" export:"true"`
+	AllowExternalNameServices    bool                `description:"Allow ExternalName services." json:"allowExternalNameServices,omitempty" toml:"allowExternalNameServices,omitempty" yaml:"allowExternalNameServices,omitempty" export:"true"`
+	LabelSelector                string              `description:"Kubernetes label selector to use." json:"labelSelector,omitempty" toml:"labelSelector,omitempty" yaml:"labelSelector,omitempty" export:"true"`
+	IngressClass                 string              `description:"Value of kubernetes.io/ingress.class annotation to watch for." json:"ingressClass,omitempty" toml:"ingressClass,omitempty" yaml:"ingressClass,omitempty" export:"true"`
+	ThrottleDuration             ptypes.Duration     `description:"Ingress refresh throttle duration" json:"throttleDuration,omitempty" toml:"throttleDuration,omitempty" yaml:"throttleDuration,omitempty" export:"true"`
+	AllowEmptyServices           bool                `description:"Allow the creation of services without endpoints." json:"allowEmptyServices,omitempty" toml:"allowEmptyServices,omitempty" yaml:"allowEmptyServices,omitempty" export:"true"`
+	NativeLBByDefault            bool                `description:"Defines whether to use Native Kubernetes load-balancing mode by default." json:"nativeLBByDefault,omitempty" toml:"nativeLBByDefault,omitempty" yaml:"nativeLBByDefault,omitempty" export:"true"`
+	DisableClusterScopeResources bool                `description:"Disables the lookup of cluster scope resources (incompatible with IngressClasses and NodePortLB enabled services)." json:"disableClusterScopeResources,omitempty" toml:"disableClusterScopeResources,omitempty" yaml:"disableClusterScopeResources,omitempty" export:"true"`
 
 	lastConfiguration safe.Safe
 
@@ -112,6 +113,7 @@ func (p *Provider) newK8sClient(ctx context.Context) (*clientWrapper, error) {
 	}
 
 	client.labelSelector = p.LabelSelector
+	client.disableClusterScopeInformer = p.DisableClusterScopeResources
 	return client, nil
 }
 
diff --git a/pkg/provider/kubernetes/crd/kubernetes_http.go b/pkg/provider/kubernetes/crd/kubernetes_http.go
index 85c9e0ab57..d81a020585 100644
--- a/pkg/provider/kubernetes/crd/kubernetes_http.go
+++ b/pkg/provider/kubernetes/crd/kubernetes_http.go
@@ -51,11 +51,12 @@ func (p *Provider) loadIngressRouteConfiguration(ctx context.Context, client Cli
 		}
 
 		cb := configBuilder{
-			client:                    client,
-			allowCrossNamespace:       p.AllowCrossNamespace,
-			allowExternalNameServices: p.AllowExternalNameServices,
-			allowEmptyServices:        p.AllowEmptyServices,
-			NativeLBByDefault:         p.NativeLBByDefault,
+			client:                       client,
+			allowCrossNamespace:          p.AllowCrossNamespace,
+			allowExternalNameServices:    p.AllowExternalNameServices,
+			allowEmptyServices:           p.AllowEmptyServices,
+			nativeLBByDefault:            p.NativeLBByDefault,
+			disableClusterScopeResources: p.DisableClusterScopeResources,
 		}
 
 		for _, route := range ingressRoute.Spec.Routes {
@@ -199,11 +200,12 @@ func (p *Provider) makeMiddlewareKeys(ctx context.Context, ingRouteNamespace str
 }
 
 type configBuilder struct {
-	client                    Client
-	allowCrossNamespace       bool
-	allowExternalNameServices bool
-	allowEmptyServices        bool
-	NativeLBByDefault         bool
+	client                       Client
+	allowCrossNamespace          bool
+	allowExternalNameServices    bool
+	allowEmptyServices           bool
+	nativeLBByDefault            bool
+	disableClusterScopeResources bool
 }
 
 // buildTraefikService creates the configuration for the traefik service defined in tService,
@@ -428,7 +430,7 @@ func (c configBuilder) loadServers(parentNamespace string, svc traefikv1alpha1.L
 		return []dynamic.Server{{URL: fmt.Sprintf("%s://%s", protocol, hostPort)}}, nil
 	}
 
-	nativeLB := c.NativeLBByDefault
+	nativeLB := c.nativeLBByDefault
 	if svc.NativeLB != nil {
 		nativeLB = *svc.NativeLB
 	}
@@ -448,6 +450,10 @@ func (c configBuilder) loadServers(parentNamespace string, svc traefikv1alpha1.L
 
 	var servers []dynamic.Server
 	if service.Spec.Type == corev1.ServiceTypeNodePort && svc.NodePortLB {
+		if c.disableClusterScopeResources {
+			return nil, errors.New("nodes lookup is disabled")
+		}
+
 		nodes, nodesExists, nodesErr := c.client.GetNodes()
 		if nodesErr != nil {
 			return nil, nodesErr
diff --git a/pkg/provider/kubernetes/crd/kubernetes_tcp.go b/pkg/provider/kubernetes/crd/kubernetes_tcp.go
index 5cc35343df..63795c0a32 100644
--- a/pkg/provider/kubernetes/crd/kubernetes_tcp.go
+++ b/pkg/provider/kubernetes/crd/kubernetes_tcp.go
@@ -239,6 +239,10 @@ func (p *Provider) loadTCPServers(client Client, namespace string, svc traefikv1
 
 	var servers []dynamic.TCPServer
 	if service.Spec.Type == corev1.ServiceTypeNodePort && svc.NodePortLB {
+		if p.DisableClusterScopeResources {
+			return nil, errors.New("nodes lookup is disabled")
+		}
+
 		nodes, nodesExists, nodesErr := client.GetNodes()
 		if nodesErr != nil {
 			return nil, nodesErr
diff --git a/pkg/provider/kubernetes/crd/kubernetes_udp.go b/pkg/provider/kubernetes/crd/kubernetes_udp.go
index 8ab7a38380..5b0e01e786 100644
--- a/pkg/provider/kubernetes/crd/kubernetes_udp.go
+++ b/pkg/provider/kubernetes/crd/kubernetes_udp.go
@@ -123,6 +123,10 @@ func (p *Provider) loadUDPServers(client Client, namespace string, svc traefikv1
 
 	var servers []dynamic.UDPServer
 	if service.Spec.Type == corev1.ServiceTypeNodePort && svc.NodePortLB {
+		if p.DisableClusterScopeResources {
+			return nil, errors.New("nodes lookup is disabled")
+		}
+
 		nodes, nodesExists, nodesErr := client.GetNodes()
 		if nodesErr != nil {
 			return nil, nodesErr
diff --git a/pkg/provider/kubernetes/ingress/client.go b/pkg/provider/kubernetes/ingress/client.go
index 0e16aea9eb..7eaff4307b 100644
--- a/pkg/provider/kubernetes/ingress/client.go
+++ b/pkg/provider/kubernetes/ingress/client.go
@@ -49,14 +49,14 @@ type Client interface {
 
 type clientWrapper struct {
 	clientset                   kclientset.Interface
-	factoryClusterScope         kinformers.SharedInformerFactory
+	clusterScopeFactory         kinformers.SharedInformerFactory
 	factoriesKube               map[string]kinformers.SharedInformerFactory
 	factoriesSecret             map[string]kinformers.SharedInformerFactory
 	factoriesIngress            map[string]kinformers.SharedInformerFactory
-	clusterFactory              kinformers.SharedInformerFactory
 	ingressLabelSelector        string
 	isNamespaceAll              bool
-	disableIngressClassInformer bool
+	disableIngressClassInformer bool // Deprecated.
+	disableClusterScopeInformer bool
 	watchedNamespaces           []string
 	serverVersion               *version.Version
 }
@@ -201,58 +201,52 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (<
 		c.factoriesSecret[ns] = factorySecret
 	}
 
-	c.factoryClusterScope = kinformers.NewSharedInformerFactory(c.clientset, resyncPeriod)
-	_, err = c.factoryClusterScope.Core().V1().Nodes().Informer().AddEventHandler(eventHandler)
-	if err != nil {
-		return nil, err
-	}
-
 	for _, ns := range namespaces {
 		c.factoriesIngress[ns].Start(stopCh)
 		c.factoriesKube[ns].Start(stopCh)
 		c.factoriesSecret[ns].Start(stopCh)
 	}
-	c.factoryClusterScope.Start(stopCh)
 
 	for _, ns := range namespaces {
-		for typ, ok := range c.factoriesIngress[ns].WaitForCacheSync(stopCh) {
+		for t, ok := range c.factoriesIngress[ns].WaitForCacheSync(stopCh) {
 			if !ok {
-				return nil, fmt.Errorf("timed out waiting for controller caches to sync %s in namespace %q", typ, ns)
+				return nil, fmt.Errorf("timed out waiting for controller caches to sync %s in namespace %q", t.String(), ns)
 			}
 		}
 
-		for typ, ok := range c.factoriesKube[ns].WaitForCacheSync(stopCh) {
+		for t, ok := range c.factoriesKube[ns].WaitForCacheSync(stopCh) {
 			if !ok {
-				return nil, fmt.Errorf("timed out waiting for controller caches to sync %s in namespace %q", typ, ns)
+				return nil, fmt.Errorf("timed out waiting for controller caches to sync %s in namespace %q", t.String(), ns)
 			}
 		}
 
-		for typ, ok := range c.factoriesSecret[ns].WaitForCacheSync(stopCh) {
+		for t, ok := range c.factoriesSecret[ns].WaitForCacheSync(stopCh) {
 			if !ok {
-				return nil, fmt.Errorf("timed out waiting for controller caches to sync %s in namespace %q", typ, ns)
+				return nil, fmt.Errorf("timed out waiting for controller caches to sync %s in namespace %q", t.String(), ns)
 			}
 		}
 	}
 
-	for t, ok := range c.factoryClusterScope.WaitForCacheSync(stopCh) {
-		if !ok {
-			return nil, fmt.Errorf("timed out waiting for controller caches to sync %s", t.String())
-		}
-	}
-
-	if !c.disableIngressClassInformer {
-		c.clusterFactory = kinformers.NewSharedInformerFactoryWithOptions(c.clientset, resyncPeriod)
+	if !c.disableIngressClassInformer || !c.disableClusterScopeInformer {
+		c.clusterScopeFactory = kinformers.NewSharedInformerFactory(c.clientset, resyncPeriod)
 
-		_, err = c.clusterFactory.Networking().V1().IngressClasses().Informer().AddEventHandler(eventHandler)
+		_, err = c.clusterScopeFactory.Networking().V1().IngressClasses().Informer().AddEventHandler(eventHandler)
 		if err != nil {
 			return nil, err
 		}
 
-		c.clusterFactory.Start(stopCh)
+		if !c.disableClusterScopeInformer {
+			_, err = c.clusterScopeFactory.Core().V1().Nodes().Informer().AddEventHandler(eventHandler)
+			if err != nil {
+				return nil, err
+			}
+		}
+
+		c.clusterScopeFactory.Start(stopCh)
 
-		for typ, ok := range c.clusterFactory.WaitForCacheSync(stopCh) {
+		for t, ok := range c.clusterScopeFactory.WaitForCacheSync(stopCh) {
 			if !ok {
-				return nil, fmt.Errorf("timed out waiting for controller caches to sync %s", typ)
+				return nil, fmt.Errorf("timed out waiting for controller caches to sync %s", t.String())
 			}
 		}
 	}
@@ -370,18 +364,18 @@ func (c *clientWrapper) GetSecret(namespace, name string) (*corev1.Secret, bool,
 }
 
 func (c *clientWrapper) GetNodes() ([]*corev1.Node, bool, error) {
-	nodes, err := c.factoryClusterScope.Core().V1().Nodes().Lister().List(labels.Everything())
+	nodes, err := c.clusterScopeFactory.Core().V1().Nodes().Lister().List(labels.Everything())
 	exist, err := translateNotFoundError(err)
 	return nodes, exist, err
 }
 
 func (c *clientWrapper) GetIngressClasses() ([]*netv1.IngressClass, error) {
-	if c.clusterFactory == nil {
+	if c.clusterScopeFactory == nil {
 		return nil, errors.New("cluster factory not loaded")
 	}
 
 	var ics []*netv1.IngressClass
-	ingressClasses, err := c.clusterFactory.Networking().V1().IngressClasses().Lister().List(labels.Everything())
+	ingressClasses, err := c.clusterScopeFactory.Networking().V1().IngressClasses().Lister().List(labels.Everything())
 	if err != nil {
 		return nil, err
 	}
diff --git a/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-node-port-lb-cluster-scope-disabled.yml b/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-node-port-lb-cluster-scope-disabled.yml
new file mode 100644
index 0000000000..46e360bf6a
--- /dev/null
+++ b/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-node-port-lb-cluster-scope-disabled.yml
@@ -0,0 +1,45 @@
+kind: Ingress
+apiVersion: networking.k8s.io/v1
+metadata:
+  name: ""
+  namespace: testing
+
+spec:
+  rules:
+  - host: traefik.tchouk
+    http:
+      paths:
+      - path: /bar
+        backend:
+          service:
+            name: service1
+            port:
+              number: 8080
+        pathType: Prefix
+
+---
+kind: Service
+apiVersion: v1
+metadata:
+  name: service1
+  namespace: testing
+  annotations:
+    traefik.ingress.kubernetes.io/service.nodeportlb: "true"
+
+spec:
+  ports:
+    - port: 8080
+      nodePort: 32456
+  clusterIP: 10.0.0.1
+  type: NodePort
+  externalName: traefik.wtf
+
+---
+kind: Node
+apiVersion: v1
+metadata:
+  name: traefik-node
+status:
+  addresses:
+    - type: InternalIP
+      address: 172.16.4.4
diff --git a/pkg/provider/kubernetes/ingress/kubernetes.go b/pkg/provider/kubernetes/ingress/kubernetes.go
index 7ae9c6b8bf..490271b8c9 100644
--- a/pkg/provider/kubernetes/ingress/kubernetes.go
+++ b/pkg/provider/kubernetes/ingress/kubernetes.go
@@ -51,8 +51,10 @@ type Provider struct {
 	ThrottleDuration          ptypes.Duration     `description:"Ingress refresh throttle duration" json:"throttleDuration,omitempty" toml:"throttleDuration,omitempty" yaml:"throttleDuration,omitempty" export:"true"`
 	AllowEmptyServices        bool                `description:"Allow creation of services without endpoints." json:"allowEmptyServices,omitempty" toml:"allowEmptyServices,omitempty" yaml:"allowEmptyServices,omitempty" export:"true"`
 	AllowExternalNameServices bool                `description:"Allow ExternalName services." json:"allowExternalNameServices,omitempty" toml:"allowExternalNameServices,omitempty" yaml:"allowExternalNameServices,omitempty" export:"true"`
-	DisableIngressClassLookup bool                `description:"Disables the lookup of IngressClasses." json:"disableIngressClassLookup,omitempty" toml:"disableIngressClassLookup,omitempty" yaml:"disableIngressClassLookup,omitempty" export:"true"`
-	NativeLBByDefault         bool                `description:"Defines whether to use Native Kubernetes load-balancing mode by default." json:"nativeLBByDefault,omitempty" toml:"nativeLBByDefault,omitempty" yaml:"nativeLBByDefault,omitempty" export:"true"`
+	// Deprecated: please use DisableClusterScopeResources.
+	DisableIngressClassLookup    bool `description:"Disables the lookup of IngressClasses (Deprecated, please use DisableClusterScopeResources)." json:"disableIngressClassLookup,omitempty" toml:"disableIngressClassLookup,omitempty" yaml:"disableIngressClassLookup,omitempty" export:"true"`
+	DisableClusterScopeResources bool `description:"Disables the lookup of cluster scope resources (incompatible with IngressClasses and NodePortLB enabled services)." json:"disableClusterScopeResources,omitempty" toml:"disableClusterScopeResources,omitempty" yaml:"disableClusterScopeResources,omitempty" export:"true"`
+	NativeLBByDefault            bool `description:"Defines whether to use Native Kubernetes load-balancing mode by default." json:"nativeLBByDefault,omitempty" toml:"nativeLBByDefault,omitempty" yaml:"nativeLBByDefault,omitempty" export:"true"`
 
 	lastConfiguration safe.Safe
 
@@ -114,7 +116,8 @@ func (p *Provider) newK8sClient(ctx context.Context) (*clientWrapper, error) {
 	}
 
 	cl.ingressLabelSelector = p.LabelSelector
-	cl.disableIngressClassInformer = p.DisableIngressClassLookup
+	cl.disableIngressClassInformer = p.DisableIngressClassLookup || p.DisableClusterScopeResources
+	cl.disableClusterScopeInformer = p.DisableClusterScopeResources
 	return cl, nil
 }
 
@@ -212,7 +215,6 @@ func (p *Provider) loadConfigurationFromIngresses(ctx context.Context, client Cl
 			Middlewares: map[string]*dynamic.Middleware{},
 			Services:    map[string]*dynamic.Service{},
 		},
-		TCP: &dynamic.TCPConfiguration{},
 	}
 
 	var ingressClasses []*netv1.IngressClass
@@ -591,6 +593,10 @@ func (p *Provider) loadService(client Client, namespace string, backend netv1.In
 		}
 
 		if svcConfig.Service.NodePortLB && service.Spec.Type == corev1.ServiceTypeNodePort {
+			if p.DisableClusterScopeResources {
+				return nil, errors.New("nodes lookup is disabled")
+			}
+
 			nodes, nodesExists, nodesErr := client.GetNodes()
 			if nodesErr != nil {
 				return nil, nodesErr


Test patch:
diff --git a/pkg/provider/kubernetes/crd/kubernetes_test.go b/pkg/provider/kubernetes/crd/kubernetes_test.go
index 8f9f5c2c5b..49b7bc6869 100644
--- a/pkg/provider/kubernetes/crd/kubernetes_test.go
+++ b/pkg/provider/kubernetes/crd/kubernetes_test.go
@@ -7446,10 +7446,10 @@ func TestNativeLB(t *testing.T) {
 
 func TestNodePortLB(t *testing.T) {
 	testCases := []struct {
-		desc         string
-		ingressClass string
-		paths        []string
-		expected     *dynamic.Configuration
+		desc                string
+		paths               []string
+		disableClusterScope bool
+		expected            *dynamic.Configuration
 	}{
 		{
 			desc: "Empty",
@@ -7594,6 +7594,90 @@ func TestNodePortLB(t *testing.T) {
 				TLS: &dynamic.TLSConfiguration{},
 			},
 		},
+		{
+			desc:                "HTTP with node port LB, cluster scope resources disabled",
+			paths:               []string{"services.yml", "with_node_port_lb.yml"},
+			disableClusterScope: true,
+			expected: &dynamic.Configuration{
+				UDP: &dynamic.UDPConfiguration{
+					Routers:  map[string]*dynamic.UDPRouter{},
+					Services: map[string]*dynamic.UDPService{},
+				},
+				TCP: &dynamic.TCPConfiguration{
+					ServersTransports: map[string]*dynamic.TCPServersTransport{},
+					Routers:           map[string]*dynamic.TCPRouter{},
+					Middlewares:       map[string]*dynamic.TCPMiddleware{},
+					Services:          map[string]*dynamic.TCPService{},
+				},
+				HTTP: &dynamic.HTTPConfiguration{
+					ServersTransports: map[string]*dynamic.ServersTransport{},
+					Routers:           map[string]*dynamic.Router{},
+					Middlewares:       map[string]*dynamic.Middleware{},
+					Services:          map[string]*dynamic.Service{},
+				},
+				TLS: &dynamic.TLSConfiguration{},
+			},
+		},
+
+		{
+			desc:                "TCP with native Service LB, cluster scope resources disabled",
+			paths:               []string{"tcp/services.yml", "tcp/with_node_port_service_lb.yml"},
+			disableClusterScope: true,
+			expected: &dynamic.Configuration{
+				UDP: &dynamic.UDPConfiguration{
+					Routers:  map[string]*dynamic.UDPRouter{},
+					Services: map[string]*dynamic.UDPService{},
+				},
+				TCP: &dynamic.TCPConfiguration{
+					ServersTransports: map[string]*dynamic.TCPServersTransport{},
+					Routers: map[string]*dynamic.TCPRouter{
+						"default-test.route-fdd3e9338e47a45efefc": {
+							EntryPoints: []string{"foo"},
+							Service:     "default-test.route-fdd3e9338e47a45efefc",
+							Rule:        "HostSNI(`foo.com`)",
+						},
+					},
+					Middlewares: map[string]*dynamic.TCPMiddleware{},
+					Services:    map[string]*dynamic.TCPService{},
+				},
+				HTTP: &dynamic.HTTPConfiguration{
+					ServersTransports: map[string]*dynamic.ServersTransport{},
+					Routers:           map[string]*dynamic.Router{},
+					Middlewares:       map[string]*dynamic.Middleware{},
+					Services:          map[string]*dynamic.Service{},
+				},
+				TLS: &dynamic.TLSConfiguration{},
+			},
+		},
+		{
+			desc:                "UDP with native Service LB, cluster scope resources disabled",
+			paths:               []string{"udp/services.yml", "udp/with_node_port_service_lb.yml"},
+			disableClusterScope: true,
+			expected: &dynamic.Configuration{
+				UDP: &dynamic.UDPConfiguration{
+					Routers: map[string]*dynamic.UDPRouter{
+						"default-test.route-0": {
+							EntryPoints: []string{"foo"},
+							Service:     "default-test.route-0",
+						},
+					},
+					Services: map[string]*dynamic.UDPService{},
+				},
+				TCP: &dynamic.TCPConfiguration{
+					ServersTransports: map[string]*dynamic.TCPServersTransport{},
+					Routers:           map[string]*dynamic.TCPRouter{},
+					Middlewares:       map[string]*dynamic.TCPMiddleware{},
+					Services:          map[string]*dynamic.TCPService{},
+				},
+				HTTP: &dynamic.HTTPConfiguration{
+					ServersTransports: map[string]*dynamic.ServersTransport{},
+					Routers:           map[string]*dynamic.Router{},
+					Middlewares:       map[string]*dynamic.Middleware{},
+					Services:          map[string]*dynamic.Service{},
+				},
+				TLS: &dynamic.TLSConfiguration{},
+			},
+		},
 	}
 
 	for _, test := range testCases {
@@ -7617,7 +7701,9 @@ func TestNodePortLB(t *testing.T) {
 				<-eventCh
 			}
 
-			p := Provider{}
+			p := Provider{
+				DisableClusterScopeResources: test.disableClusterScope,
+			}
 
 			conf := p.loadConfigurationFromCRD(context.Background(), client)
 			assert.Equal(t, test.expected, conf)
diff --git a/pkg/provider/kubernetes/ingress/kubernetes_test.go b/pkg/provider/kubernetes/ingress/kubernetes_test.go
index c7c5a67eb3..5a4e3dd2b7 100644
--- a/pkg/provider/kubernetes/ingress/kubernetes_test.go
+++ b/pkg/provider/kubernetes/ingress/kubernetes_test.go
@@ -35,7 +35,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 		{
 			desc: "Empty ingresses",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Routers:     map[string]*dynamic.Router{},
 					Middlewares: map[string]*dynamic.Middleware{},
@@ -46,7 +45,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 		{
 			desc: "Ingress one rule host only",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Routers:     map[string]*dynamic.Router{},
 					Middlewares: map[string]*dynamic.Middleware{},
@@ -57,7 +55,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 		{
 			desc: "Ingress with a basic rule on one path",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers: map[string]*dynamic.Router{
@@ -90,7 +87,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 		{
 			desc: "Ingress with annotations",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers: map[string]*dynamic.Router{
@@ -148,7 +144,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 		{
 			desc: "Ingress with two different rules with one path",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers: map[string]*dynamic.Router{
@@ -185,7 +180,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 		{
 			desc: "Ingress with conflicting routers on host",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers: map[string]*dynamic.Router{
@@ -222,7 +216,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 		{
 			desc: "Ingress with conflicting routers on path",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers: map[string]*dynamic.Router{
@@ -259,7 +252,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 		{
 			desc: "Ingress one rule with two paths",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers: map[string]*dynamic.Router{
@@ -296,7 +288,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 		{
 			desc: "Ingress one rule with one path and one host",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers: map[string]*dynamic.Router{
@@ -329,7 +320,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 		{
 			desc: "Ingress with one host without path",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers: map[string]*dynamic.Router{
@@ -359,7 +349,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 		{
 			desc: "Ingress one rule with one host and two paths",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers: map[string]*dynamic.Router{
@@ -396,7 +385,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 		{
 			desc: "Ingress Two rules with one host and one path",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers: map[string]*dynamic.Router{
@@ -433,7 +421,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 		{
 			desc: "Ingress with two services",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers: map[string]*dynamic.Router{
@@ -487,7 +474,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 			desc:               "Ingress with one service without endpoints subset",
 			allowEmptyServices: true,
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers: map[string]*dynamic.Router{
@@ -512,7 +498,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 		{
 			desc: "Ingress with one service without endpoint",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers:     map[string]*dynamic.Router{},
@@ -523,7 +508,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 		{
 			desc: "Single Service Ingress (without any rules)",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers: map[string]*dynamic.Router{
@@ -558,7 +542,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 		{
 			desc: "Ingress with port value in backend and no pod replica",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers: map[string]*dynamic.Router{
@@ -591,7 +574,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 		{
 			desc: "Ingress with port name in backend and no pod replica",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers: map[string]*dynamic.Router{
@@ -624,7 +606,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 		{
 			desc: "Ingress with port name in backend and 2 pod replica",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers: map[string]*dynamic.Router{
@@ -657,7 +638,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 		{
 			desc: "Ingress with two paths using same service and different port name",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers: map[string]*dynamic.Router{
@@ -710,7 +690,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 		{
 			desc: "Ingress with a named port matching subset of service pods",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers: map[string]*dynamic.Router{
@@ -743,7 +722,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 		{
 			desc: "2 ingresses in different namespace with same service name",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers: map[string]*dynamic.Router{
@@ -796,7 +774,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 		{
 			desc: "Ingress with unknown service port name",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers:     map[string]*dynamic.Router{},
@@ -807,7 +784,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 		{
 			desc: "Ingress with unknown service port",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers:     map[string]*dynamic.Router{},
@@ -818,7 +794,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 		{
 			desc: "Ingress with port invalid for one service",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers: map[string]*dynamic.Router{
@@ -848,7 +823,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 		{
 			desc: "TLS support",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers: map[string]*dynamic.Router{
@@ -889,7 +863,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 		{
 			desc: "Ingress with a basic rule on one path with https (port == 443)",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers: map[string]*dynamic.Router{
@@ -922,7 +895,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 		{
 			desc: "Ingress with a basic rule on one path with https (portname == https)",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers: map[string]*dynamic.Router{
@@ -955,7 +927,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 		{
 			desc: "Ingress with a basic rule on one path with https (portname starts with https)",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 
@@ -989,7 +960,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 		{
 			desc: "Double Single Service Ingress",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers: map[string]*dynamic.Router{
@@ -1024,7 +994,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 		{
 			desc: "Ingress with default traefik ingressClass",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers: map[string]*dynamic.Router{
@@ -1054,7 +1023,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 		{
 			desc: "Ingress without provider traefik ingressClass and unknown annotation",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers:     map[string]*dynamic.Router{},
@@ -1066,7 +1034,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 			desc:         "Ingress with non matching provider traefik ingressClass and annotation",
 			ingressClass: "tchouk",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers:     map[string]*dynamic.Router{},
@@ -1078,7 +1045,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 			desc:         "Ingress with ingressClass without annotation",
 			ingressClass: "tchouk",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers:     map[string]*dynamic.Router{},
@@ -1090,7 +1056,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 			desc:         "Ingress with ingressClass without annotation",
 			ingressClass: "toto",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers:     map[string]*dynamic.Router{},
@@ -1101,7 +1066,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 		{
 			desc: "Ingress with wildcard host",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers: map[string]*dynamic.Router{
@@ -1133,7 +1097,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 		{
 			desc: "Ingress with multiple ingressClasses",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers: map[string]*dynamic.Router{
@@ -1168,7 +1131,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 			desc:         "Ingress with ingressClasses filter",
 			ingressClass: "traefik-lb2",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers: map[string]*dynamic.Router{
@@ -1198,7 +1160,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 		{
 			desc: "Ingress with prefix pathType",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers: map[string]*dynamic.Router{
@@ -1228,7 +1189,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 		{
 			desc: "Ingress with empty pathType",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers: map[string]*dynamic.Router{
@@ -1258,7 +1218,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 		{
 			desc: "Ingress with exact pathType",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers: map[string]*dynamic.Router{
@@ -1288,7 +1247,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 		{
 			desc: "Ingress with implementationSpecific pathType",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers: map[string]*dynamic.Router{
@@ -1318,7 +1276,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 		{
 			desc: "Ingress with ingress annotation",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers: map[string]*dynamic.Router{
@@ -1351,7 +1308,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 			desc:                      "Ingress with ingress annotation",
 			disableIngressClassLookup: true,
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers: map[string]*dynamic.Router{
@@ -1381,7 +1337,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 		{
 			desc: "Ingress with ingressClass",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers: map[string]*dynamic.Router{
@@ -1414,7 +1369,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 			desc:                      "Ingress with ingressClass",
 			disableIngressClassLookup: true,
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers:     map[string]*dynamic.Router{},
@@ -1425,7 +1379,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 		{
 			desc: "Ingress with named port",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers: map[string]*dynamic.Router{
@@ -1455,7 +1408,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 		{
 			desc: "Ingress with missing ingressClass",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers:     map[string]*dynamic.Router{},
@@ -1466,7 +1418,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
 		{
 			desc: "Ingress with defaultbackend",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers: map[string]*dynamic.Router{
@@ -1524,7 +1475,6 @@ func TestLoadConfigurationFromIngressesWithExternalNameServices(t *testing.T) {
 		{
 			desc: "Ingress with service with externalName",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers:     map[string]*dynamic.Router{},
@@ -1536,7 +1486,6 @@ func TestLoadConfigurationFromIngressesWithExternalNameServices(t *testing.T) {
 			desc:                      "Ingress with service with externalName enabled",
 			allowExternalNameServices: true,
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers: map[string]*dynamic.Router{
@@ -1566,7 +1515,6 @@ func TestLoadConfigurationFromIngressesWithExternalNameServices(t *testing.T) {
 		{
 			desc: "Ingress with IPv6 endpoints",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers: map[string]*dynamic.Router{
@@ -1597,7 +1545,6 @@ func TestLoadConfigurationFromIngressesWithExternalNameServices(t *testing.T) {
 			desc:                      "Ingress with IPv6 endpoints externalname enabled",
 			allowExternalNameServices: true,
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers: map[string]*dynamic.Router{
@@ -1650,7 +1597,6 @@ func TestLoadConfigurationFromIngressesWithNativeLB(t *testing.T) {
 		{
 			desc: "Ingress with native service lb",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers: map[string]*dynamic.Router{
@@ -1693,14 +1639,13 @@ func TestLoadConfigurationFromIngressesWithNativeLB(t *testing.T) {
 
 func TestLoadConfigurationFromIngressesWithNodePortLB(t *testing.T) {
 	testCases := []struct {
-		desc         string
-		ingressClass string
-		expected     *dynamic.Configuration
+		desc                 string
+		clusterScopeDisabled bool
+		expected             *dynamic.Configuration
 	}{
 		{
 			desc: "Ingress with node port lb",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers: map[string]*dynamic.Router{
@@ -1725,6 +1670,17 @@ func TestLoadConfigurationFromIngressesWithNodePortLB(t *testing.T) {
 				},
 			},
 		},
+		{
+			desc:                 "Ingress with node port lb cluster scope disabled",
+			clusterScopeDisabled: true,
+			expected: &dynamic.Configuration{
+				HTTP: &dynamic.HTTPConfiguration{
+					Middlewares: map[string]*dynamic.Middleware{},
+					Routers:     map[string]*dynamic.Router{},
+					Services:    map[string]*dynamic.Service{},
+				},
+			},
+		},
 	}
 
 	for _, test := range testCases {
@@ -1733,7 +1689,7 @@ func TestLoadConfigurationFromIngressesWithNodePortLB(t *testing.T) {
 
 			clientMock := newClientMock(generateTestFilename(test.desc))
 
-			p := Provider{IngressClass: test.ingressClass}
+			p := Provider{DisableClusterScopeResources: test.clusterScopeDisabled}
 			conf := p.loadConfigurationFromIngresses(context.Background(), clientMock)
 
 			assert.Equal(t, test.expected, conf)
@@ -1927,7 +1883,6 @@ func TestLoadConfigurationFromIngressesWithNativeLBByDefault(t *testing.T) {
 		{
 			desc: "Ingress with native service lb",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers: map[string]*dynamic.Router{
@@ -1955,7 +1910,6 @@ func TestLoadConfigurationFromIngressesWithNativeLBByDefault(t *testing.T) {
 		{
 			desc: "Ingress with native lb by default",
 			expected: &dynamic.Configuration{
-				TCP: &dynamic.TCPConfiguration{},
 				HTTP: &dynamic.HTTPConfiguration{
 					Middlewares: map[string]*dynamic.Middleware{},
 					Routers: map[string]*dynamic.Router{