Repository: kumahq/kuma
Pull request #11732 (2024-10-09 08:48:33)

chore(transparent-proxy): deprecated code cleanup and small improvements
description in progress

## Motivation

<!-- Why are we doing this change -->

## Implementation information

<!-- Explain how this was done and potentially alternatives considered and discarded -->

## Supporting documentation

<!-- Is there a MADR? An Issue? A related PR? -->

Fix #XX

<!--
> Changelog: skip
-->
<!--
Uncomment the above section to explicitly set a [`> Changelog:` entry here](https://github.com/kumahq/kuma/blob/master/CONTRIBUTING.md#submitting-a-patch)?
-->



Patch:
diff --git a/.github/workflows/ci-stability.yaml b/.github/workflows/ci-stability.yaml
index c5345bbd4..c55b78d1c 100644
--- a/.github/workflows/ci-stability.yaml
+++ b/.github/workflows/ci-stability.yaml
@@ -20,15 +20,22 @@ jobs:
   trigger-ci:
     runs-on: ubuntu-latest
     steps:
+      - name: Generate GitHub app token
+        id: github-app-token
+        uses: actions/create-github-app-token@5d869da34e18e7287c1daad50e0b8ea0f506ce69 # v1.11.0
+        with:
+          app-id: ${{ secrets.APP_ID }}
+          private-key: ${{ secrets.APP_PRIVATE_KEY }}
       - name: Checkout repository
         uses: actions/checkout@v3
         with:
           fetch-depth: 0
+          token: ${{ steps.github-app-token.outputs.token }}
       - name: Get open pull requests and save to file
         run: |
           gh pr list --json number,labels > open_prs.json
         env:
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+          GITHUB_TOKEN: ${{ steps.github-app-token.outputs.token }}
       - name: Process PRs
         id: process_prs
         run: |
@@ -40,7 +47,7 @@ jobs:
           echo "pr_numbers_with_verify_stability=$pr_numbers_with_verify_stability" >> $GITHUB_OUTPUT
           echo "pr_numbers_with_verify_stability_merge_master=$pr_numbers_with_verify_stability_merge_master" >> $GITHUB_OUTPUT
         env:
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+          GITHUB_TOKEN: ${{ steps.github-app-token.outputs.token }}
       - name: Merge master branch (if applicable) and push a single commit
         if: steps.process_prs.outputs.pr_numbers_with_verify_stability != ''
         run: |
@@ -71,4 +78,4 @@ jobs:
             git push origin $pr_branch
           done
         env:
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+          GITHUB_TOKEN: ${{ steps.github-app-token.outputs.token }}
diff --git a/DEVELOPER.md b/DEVELOPER.md
index 4ee921284..061373973 100644
--- a/DEVELOPER.md
+++ b/DEVELOPER.md
@@ -16,6 +16,8 @@ for any tools/libraries that you may be missing.
 - [`unzip`](http://infozip.sourceforge.net/UnZip.html)
 - [`make`](https://www.gnu.org/software/make/)
 - [`go`](https://golang.org/)
+- [`jq`](https://jqlang.github.io/jq/download/)
+- [`yq`](https://mikefarah.gitbook.io/yq)
 - [`clang-format`](https://clang.llvm.org/docs/ClangFormat.html) # normally included in
   system's clang package with some exceptions, please check with the following command:
   ```bash
diff --git a/app/kumactl/cmd/install/install_transparent_proxy.go b/app/kumactl/cmd/install/install_transparent_proxy.go
index 16b4240a6..95b216a5a 100644
--- a/app/kumactl/cmd/install/install_transparent_proxy.go
+++ b/app/kumactl/cmd/install/install_transparent_proxy.go
@@ -237,7 +237,6 @@ runuser -u kuma-dp -- \
 	cmd.Flags().BoolVar(&cfg.Redirect.DNS.Enabled, flagRedirectDNS, cfg.Redirect.DNS.Enabled, "redirect only DNS requests targeted to the servers listed in /etc/resolv.conf to a specified port")
 	cmd.Flags().BoolVar(&cfg.Redirect.DNS.CaptureAll, flagRedirectAllDNSTraffic, cfg.Redirect.DNS.CaptureAll, "redirect all DNS traffic to a specified port, unlike --redirect-dns this will not be limited to the dns servers identified in /etc/resolve.conf")
 	cmd.Flags().Var(&cfg.Redirect.DNS.Port, "redirect-dns-port", "the port where the DNS agent is listening")
-	cmd.Flags().StringVar(&cfg.Redirect.DNS.UpstreamTargetChain, "redirect-dns-upstream-target-chain", cfg.Redirect.DNS.UpstreamTargetChain, "(optional) the iptables chain where the upstream DNS requests should be directed to. It is only applied for IP V4. Use with care.")
 	cmd.Flags().BoolVar(&cfg.StoreFirewalld, "store-firewalld", cfg.StoreFirewalld, "store the iptables changes with firewalld")
 	cmd.Flags().BoolVar(
 		&cfg.Redirect.DNS.SkipConntrackZoneSplit,
@@ -262,7 +261,7 @@ runuser -u kuma-dp -- \
 	cmd.Flags().StringArrayVar(&cfg.Redirect.VNet.Networks, "vnet", cfg.Redirect.VNet.Networks, "virtual networks in a format of interfaceNameRegex:CIDR split by ':' where interface name doesn't have to be exact name e.g. docker0:172.17.0.0/16, br+:172.18.0.0/16, iface:::1/64")
 	cmd.Flags().UintVar(&cfg.Wait, "wait", cfg.Wait, "specify the amount of time, in seconds, that the application should wait for the xtables exclusive lock before exiting. If the lock is not available within the specified time, the application will exit with an error")
 	cmd.Flags().UintVar(&cfg.WaitInterval, "wait-interval", cfg.WaitInterval, "flag can be used to specify the amount of time, in microseconds, that iptables should wait between each iteration of the lock acquisition loop. This can be useful if the xtables lock is being held by another application for a long time, and you want to reduce the amount of CPU that iptables uses while waiting for the lock")
-	cmd.Flags().IntVar(&cfg.Retry.MaxRetries, "max-retries", cfg.Retry.MaxRetries, "flag can be used to specify the maximum number of times to retry an installation before giving up")
+	cmd.Flags().UintVar(&cfg.Retry.MaxRetries, "max-retries", cfg.Retry.MaxRetries, "flag can be used to specify the maximum number of times to retry an installation before giving up")
 	cmd.Flags().Var(&cfg.Retry.SleepBetweenRetries, "sleep-between-retries", "flag can be used to specify the amount of time to sleep between retries")
 
 	cmd.Flags().BoolVar(&cfg.Log.Enabled, "iptables-logs", cfg.Log.Enabled, "enable logs for iptables rules using the LOG chain. This option activates kernel logging for packets matching the rules, where details about the IP/IPv6 headers are logged. This information can be accessed via dmesg(1) or syslog.")
@@ -305,7 +304,6 @@ runuser -u kuma-dp -- \
 	cmd.Flags().StringVar(&configValue, flagTransparentProxyConfig, configValue, "transparent proxy configuration provided in YAML or JSON format")
 	cmd.Flags().StringVar(&configFile, flagTransparentProxyConfigFile, configFile, "path to the file containing the transparent proxy configuration in YAML or JSON format")
 
-	_ = cmd.Flags().MarkDeprecated("redirect-dns-upstream-target-chain", "This flag has no effect anymore. Will be removed in 2.9.x version")
 	_ = cmd.Flags().MarkDeprecated("kuma-dp-uid", "please use --kuma-dp-user, which accepts both UIDs and usernames")
 
 	// Manually define the `--help` flag since we are handling flag parsing ourselves due to
diff --git a/app/kumactl/data/install/k8s/demo/demo.yaml b/app/kumactl/data/install/k8s/demo/demo.yaml
index 4853d821c..a19231c2b 100644
--- a/app/kumactl/data/install/k8s/demo/demo.yaml
+++ b/app/kumactl/data/install/k8s/demo/demo.yaml
@@ -39,6 +39,8 @@ kind: Service
 metadata:
   name: redis
   namespace: {{ .Namespace }}
+  labels:
+    app: redis
 spec:
   selector:
     app: redis
@@ -82,6 +84,8 @@ kind: Service
 metadata:
   name: demo-app
   namespace: {{ .Namespace }}
+  labels:
+    app: demo-app
 spec:
   selector:
     app: demo-app
diff --git a/app/kumactl/data/install/k8s/demo/gateway.yaml b/app/kumactl/data/install/k8s/demo/gateway.yaml
index 1950ffa74..816f0c4b0 100644
--- a/app/kumactl/data/install/k8s/demo/gateway.yaml
+++ b/app/kumactl/data/install/k8s/demo/gateway.yaml
@@ -13,7 +13,7 @@ spec:
       protocol: HTTP
   selectors:
   - match:
-      kuma.io/service: demo-app-gateway_kuma-demo_svc
+      kuma.io/service: demo-app-gateway_{{ .Namespace }}_svc
 ---
 apiVersion: kuma.io/v1alpha1
 kind: MeshHTTPRoute
@@ -28,17 +28,17 @@ spec:
     kind: MeshGateway
     name: demo-app
   to:
-    - targetRef:
-        kind: Mesh
-      rules:
-        - matches:
-            - path:
-                type: PathPrefix
-                value: "/"
-          default:
-            backendRefs:
-              - kind: MeshService
-                name: demo-app_{{ .Namespace }}_svc_5000
+  - targetRef:
+      kind: Mesh
+    rules:
+    - default:
+        backendRefs:
+        - kind: MeshService
+          name: demo-app_{{ .Namespace }}_svc_5000
+      matches:
+      - path:
+          type: PathPrefix
+          value: /
 ---
 apiVersion: kuma.io/v1alpha1
 kind: MeshGatewayInstance
diff --git a/mk/dependencies/deps.lock b/mk/dependencies/deps.lock
index 031e9e497..41dec2053 100644
--- a/mk/dependencies/deps.lock
+++ b/mk/dependencies/deps.lock
@@ -1,1 +1,1 @@
-67e5a6e147d569852851535e91196922ade8162d
+85edd35bf66665ac4e9f4fd54fdd06d202a510c3
diff --git a/mk/dev.mk b/mk/dev.mk
index 80debd183..6c078b442 100644
--- a/mk/dev.mk
+++ b/mk/dev.mk
@@ -130,10 +130,12 @@ dev/sync-demo:
 	rm app/kumactl/data/install/k8s/demo/*.yaml
 	curl -s --fail https://raw.githubusercontent.com/kumahq/kuma-counter-demo/master/demo.yaml | \
 		sed 's/"local"/"{{ .Zone }}"/g' | \
-		sed 's/\([^/]\)kuma-demo/\1{{ .Namespace }}/g' \
+		sed 's/\([^/]\)kuma-demo/\1{{ .Namespace }}/g' | \
+		sed 's/\([^/]\)kuma-system/\1{{ .SystemNamespace }}/g' \
 		> app/kumactl/data/install/k8s/demo/demo.yaml
 	curl -s --fail https://raw.githubusercontent.com/kumahq/kuma-counter-demo/master/gateway.yaml | \
-		sed 's/\([^/]\)kuma-demo/\1{{ .Namespace }}/g' \
+		sed 's/\([^/]\)kuma-demo/\1{{ .Namespace }}/g' | \
+		sed 's/\([^/]\)kuma-system/\1{{ .SystemNamespace }}/g' \
 		> app/kumactl/data/install/k8s/demo/gateway.yaml
 
 .PHONY: dev/set-kuma-helm-repo
diff --git a/pkg/core/resources/apis/meshmultizoneservice/api/v1alpha1/helpers.go b/pkg/core/resources/apis/meshmultizoneservice/api/v1alpha1/helpers.go
index 7f6498375..760808586 100644
--- a/pkg/core/resources/apis/meshmultizoneservice/api/v1alpha1/helpers.go
+++ b/pkg/core/resources/apis/meshmultizoneservice/api/v1alpha1/helpers.go
@@ -26,7 +26,7 @@ func (t *MeshMultiZoneServiceResource) AllocateVIP(vip string) {
 	})
 }
 
-func (m *MeshMultiZoneServiceResource) FindPort(port uint32) (Port, bool) {
+func (m *MeshMultiZoneServiceResource) findPort(port uint32) (Port, bool) {
 	for _, p := range m.Spec.Ports {
 		if p.Port == port {
 			return p, true
@@ -35,6 +35,13 @@ func (m *MeshMultiZoneServiceResource) FindPort(port uint32) (Port, bool) {
 	return Port{}, false
 }
 
+func (m *MeshMultiZoneServiceResource) FindSectionNameByPort(port uint32) (string, bool) {
+	if port, found := m.findPort(port); found {
+		return port.GetName(), true
+	}
+	return "", false
+}
+
 func (m *MeshMultiZoneServiceResource) FindPortByName(name string) (Port, bool) {
 	for _, p := range m.Spec.Ports {
 		if p.Name == name {
diff --git a/pkg/core/resources/apis/meshservice/api/v1alpha1/helpers.go b/pkg/core/resources/apis/meshservice/api/v1alpha1/helpers.go
index 920c865ce..ffdcc1351 100644
--- a/pkg/core/resources/apis/meshservice/api/v1alpha1/helpers.go
+++ b/pkg/core/resources/apis/meshservice/api/v1alpha1/helpers.go
@@ -15,7 +15,7 @@ func (m *MeshServiceResource) DestinationName(port uint32) string {
 	return fmt.Sprintf("%s_%s_%s_%s_msvc_%d", id.Mesh, id.Name, id.Namespace, id.Zone, port)
 }
 
-func (m *MeshServiceResource) FindPort(port uint32) (Port, bool) {
+func (m *MeshServiceResource) findPort(port uint32) (Port, bool) {
 	for _, p := range m.Spec.Ports {
 		if p.Port == port {
 			return p, true
@@ -24,6 +24,13 @@ func (m *MeshServiceResource) FindPort(port uint32) (Port, bool) {
 	return Port{}, false
 }
 
+func (m *MeshServiceResource) FindSectionNameByPort(port uint32) (string, bool) {
+	if port, found := m.findPort(port); found {
+		return port.GetName(), true
+	}
+	return "", false
+}
+
 func (m *MeshServiceResource) FindPortByName(name string) (Port, bool) {
 	for _, p := range m.Spec.Ports {
 		if p.Name == name {
diff --git a/pkg/plugins/runtime/k8s/plugin.go b/pkg/plugins/runtime/k8s/plugin.go
index 1beb5ae84..5feeb9439 100644
--- a/pkg/plugins/runtime/k8s/plugin.go
+++ b/pkg/plugins/runtime/k8s/plugin.go
@@ -81,7 +81,7 @@ func addControllers(mgr kube_ctrl.Manager, rt core_runtime.Runtime, converter k8
 	if err := addServiceReconciler(mgr); err != nil {
 		return err
 	}
-	if err := addMeshServiceReconciler(mgr, converter); err != nil {
+	if err := addMeshServiceReconciler(mgr, rt, converter); err != nil {
 		return err
 	}
 	if err := addMeshReconciler(mgr, rt); err != nil {
@@ -138,7 +138,10 @@ func addServiceReconciler(mgr kube_ctrl.Manager) error {
 	return reconciler.SetupWithManager(mgr)
 }
 
-func addMeshServiceReconciler(mgr kube_ctrl.Manager, converter k8s_common.Converter) error {
+func addMeshServiceReconciler(mgr kube_ctrl.Manager, rt core_runtime.Runtime, converter k8s_common.Converter) error {
+	if rt.Config().Mode == config_core.Global {
+		return nil
+	}
 	reconciler := &k8s_controllers.MeshServiceReconciler{
 		Client:            mgr.GetClient(),
 		Log:               core.Log.WithName("controllers").WithName("MeshService"),
diff --git a/pkg/transparentproxy/config/config.go b/pkg/transparentproxy/config/config.go
index 9ae84f276..cc5748915 100644
--- a/pkg/transparentproxy/config/config.go
+++ b/pkg/transparentproxy/config/config.go
@@ -210,9 +210,6 @@ type DNS struct {
 	CaptureAll             bool   `json:"captureAll" split_words:"true"`             // KUMA_TRANSPARENT_PROXY_REDIRECT_DNS_CAPTURE_ALL
 	SkipConntrackZoneSplit bool   `json:"skipConntrackZoneSplit" split_words:"true"` // KUMA_TRANSPARENT_PROXY_REDIRECT_DNS_SKIP_CONNTRACK_ZONE_SPLIT
 	ResolvConfigPath       string `json:"resolvConfigPath" split_words:"true"`       // KUMA_TRANSPARENT_PROXY_REDIRECT_DNS_RESOLV_CONFIG_PATH
-	// The iptables chain where the upstream DNS requests should be directed to.
-	// It is only applied for IP V4. Use with care. (default "RETURN")
-	UpstreamTargetChain string `json:"-" ignored:"true"`
 }
 
 type InitializedDNS struct {
@@ -486,13 +483,25 @@ type Log struct {
 type Retry struct {
 	// MaxRetries specifies the number of retries after the initial attempt.
 	// A value of 0 means no retries, and only the initial attempt will be made.
-	MaxRetries int `json:"maxRetries" split_words:"true"` // KUMA_TRANSPARENT_PROXY_RETRY_MAX_RETRIES
+	MaxRetries uint `json:"maxRetries" split_words:"true"` // KUMA_TRANSPARENT_PROXY_RETRY_MAX_RETRIES
 	// SleepBetweenRetries defines the duration to wait between retry attempts.
 	// This delay helps in situations where immediate retries may not be
 	// beneficial, allowing time for transient issues to resolve.
 	SleepBetweenRetries config_types.Duration `json:"sleepBetweenRetries" split_words:"true"` // KUMA_TRANSPARENT_PROXY_RETRY_SLEEP_BETWEEN_RETRIES
 }
 
+type InitializedRetry struct {
+	MaxRetries          int
+	SleepBetweenRetries time.Duration
+}
+
+func (c Retry) Initialize() InitializedRetry {
+	return InitializedRetry{
+		MaxRetries:          int(c.MaxRetries),
+		SleepBetweenRetries: c.SleepBetweenRetries.Duration,
+	}
+}
+
 // Comments struct contains the configuration for iptables rule comments.
 // It includes an option to enable or disable comments.
 type Comments struct {
@@ -816,12 +825,7 @@ func (c Config) Initialize(ctx context.Context) (InitializedConfig, error) {
 		return InitializedConfig{}, err
 	}
 
-	l := Logger{
-		stdout: c.RuntimeStdout,
-		stderr: c.RuntimeStderr,
-		maxTry: c.Retry.MaxRetries + 1,
-	}
-
+	l := Logger{stdout: c.RuntimeStdout, stderr: c.RuntimeStderr}
 	loggerIPv4 := l.WithPrefix(consts.IptablesCommandByFamily[consts.IPv4])
 	loggerIPv6 := l.WithPrefix(consts.IptablesCommandByFamily[consts.IPv6])
 
@@ -909,28 +913,35 @@ func (c Config) Initialize(ctx context.Context) (InitializedConfig, error) {
 
 func DefaultConfig() Config {
 	return Config{
-		KumaDPUser: "",
+		KumaDPUser:   "",
+		IPFamilyMode: IPFamilyModeDualStack,
 		Redirect: Redirect{
 			NamePrefix: consts.IptablesChainsPrefix,
 			Inbound: TrafficFlow{
-				Enabled:           true,
-				Port:              Port(consts.DefaultRedirectInbountPort),
-				ChainName:         "INBOUND",
-				RedirectChainName: "INBOUND_REDIRECT",
-				ExcludePorts:      Ports{},
-				IncludePorts:      Ports{},
+				Enabled:                       true,
+				Port:                          Port(consts.DefaultRedirectInbountPort),
+				ChainName:                     "INBOUND",
+				RedirectChainName:             "INBOUND_REDIRECT",
+				IncludePorts:                  Ports{},
+				ExcludePorts:                  Ports{},
+				ExcludePortsForUIDs:           []string{},
+				ExcludePortsForIPs:            []string{},
+				InsertRedirectInsteadOfAppend: false,
 			},
 			Outbound: TrafficFlow{
-				Enabled:           true,
-				Port:              Port(consts.DefaultRedirectOutboundPort),
-				ChainName:         "OUTBOUND",
-				RedirectChainName: "OUTBOUND_REDIRECT",
-				ExcludePorts:      Ports{},
-				IncludePorts:      Ports{},
+				Enabled:                       true,
+				Port:                          Port(consts.DefaultRedirectOutboundPort),
+				ChainName:                     "OUTBOUND",
+				RedirectChainName:             "OUTBOUND_REDIRECT",
+				IncludePorts:                  Ports{},
+				ExcludePorts:                  Ports{},
+				ExcludePortsForUIDs:           []string{},
+				ExcludePortsForIPs:            []string{},
+				InsertRedirectInsteadOfAppend: false,
 			},
 			DNS: DNS{
-				Port:                   Port(consts.DefaultRedirectDNSPort),
 				Enabled:                false,
+				Port:                   Port(consts.DefaultRedirectDNSPort),
 				CaptureAll:             false,
 				SkipConntrackZoneSplit: false,
 				ResolvConfigPath:       "/etc/resolv.conf",
@@ -940,16 +951,14 @@ func DefaultConfig() Config {
 			},
 		},
 		Ebpf: Ebpf{
-			Enabled:            false,
-			CgroupPath:         "/sys/fs/cgroup",
-			BPFFSPath:          "/run/kuma/bpf",
-			ProgramsSourcePath: "/tmp/kuma-ebpf",
+			Enabled:              false,
+			InstanceIP:           "",
+			InstanceIPEnvVarName: "",
+			BPFFSPath:            "/run/kuma/bpf",
+			CgroupPath:           "/sys/fs/cgroup",
+			ProgramsSourcePath:   "/tmp/kuma-ebpf",
+			TCAttachIface:        "",
 		},
-		DropInvalidPackets: false,
-		RuntimeStdout:      os.Stdout,
-		RuntimeStderr:      os.Stderr,
-		Verbose:            false,
-		DryRun:             false,
 		Log: Log{
 			Enabled: false,
 			Level:   consts.LogLevelDebug,
@@ -962,12 +971,16 @@ func DefaultConfig() Config {
 			MaxRetries:          4,
 			SleepBetweenRetries: config_types.Duration{Duration: 2 * time.Second},
 		},
-		Executables: NewExecutables(),
 		Comments: Comments{
 			Disabled: false,
 		},
-		IPFamilyMode:   IPFamilyModeDualStack,
-		StoreFirewalld: false,
-		CNIMode:        false,
+		Executables:        NewExecutables(),
+		RuntimeStdout:      os.Stdout,
+		RuntimeStderr:      os.Stderr,
+		DropInvalidPackets: false,
+		StoreFirewalld:     false,
+		CNIMode:            false,
+		Verbose:            false,
+		DryRun:             false,
 	}
 }
diff --git a/pkg/transparentproxy/config/config_executables.go b/pkg/transparentproxy/config/config_executables.go
index 65fa63e3e..bf201ff25 100644
--- a/pkg/transparentproxy/config/config_executables.go
+++ b/pkg/transparentproxy/config/config_executables.go
@@ -204,6 +204,8 @@ func (c ExecutablesIPvX) Initialize(
 		return InitializedExecutablesIPvX{}, errors.Wrap(err, "functionality verification failed")
 	}
 
+	retry := cfg.Retry.Initialize()
+
 	return InitializedExecutablesIPvX{
 		Iptables:        iptables,
 		IptablesSave:    iptablesSave,
@@ -212,8 +214,8 @@ func (c ExecutablesIPvX) Initialize(
 
 		mode: mode,
 
-		retry:  cfg.Retry,
-		logger: l,
+		retry:  retry,
+		logger: l.WithMaxTry(retry.MaxRetries + 1),
 	}, nil
 }
 
@@ -225,7 +227,7 @@ type InitializedExecutablesIPvX struct {
 
 	mode consts.IptablesMode
 
-	retry  Retry
+	retry  InitializedRetry
 	logger Logger
 }
 
@@ -265,7 +267,7 @@ func (c InitializedExecutablesIPvX) restore(
 				c.logger.InfoTry("will try again in", c.retry.SleepBetweenRetries)
 			}
 
-			time.Sleep(c.retry.SleepBetweenRetries.Duration)
+			time.Sleep(c.retry.SleepBetweenRetries)
 		}
 	}
 
diff --git a/pkg/transparentproxy/config/config_logger.go b/pkg/transparentproxy/config/config_logger.go
index 605889e2a..644f330fa 100644
--- a/pkg/transparentproxy/config/config_logger.go
+++ b/pkg/transparentproxy/config/config_logger.go
@@ -26,13 +26,13 @@ type Logger struct {
 // WithPrefix returns a new Logger instance with the specified prefix added to
 // the default prefixes.
 func (l Logger) WithPrefix(prefix string) Logger {
-	return Logger{
-		stdout:          l.stdout,
-		stderr:          l.stderr,
-		maxTry:          l.maxTry,
-		try:             l.try,
-		defaultPrefixes: append(l.defaultPrefixes, prefix),
-	}
+	l.defaultPrefixes = append(l.defaultPrefixes, prefix)
+	return l
+}
+
+func (l Logger) WithMaxTry(maxTry int) Logger {
+	l.maxTry = maxTry
+	return l
 }
 
 // tryPrefix generates a logging prefix based on the current retry attempt.
diff --git a/pkg/transparentproxy/consts/consts.go b/pkg/transparentproxy/consts/consts.go
index 453ef3265..34eaeeb4d 100644
--- a/pkg/transparentproxy/consts/consts.go
+++ b/pkg/transparentproxy/consts/consts.go
@@ -38,17 +38,12 @@ var IPTypeMap = map[bool]string{
 	IPv6: "IPv6",
 }
 
-// Default ports used for iptables redirection
+// Ports used for iptables redirection
 const (
 	DefaultRedirectInbountPort  uint16 = 15006
 	DefaultRedirectOutboundPort uint16 = 15001
 	DefaultRedirectDNSPort      uint16 = 15053
-)
-
-const (
-	DNSPort             uint16 = 53
-	OutputLogPrefix            = "OUTPUT:"
-	PreroutingLogPrefix        = "PREROUTING:"
+	DNSPort                     uint16 = 53
 )
 
 var InboundPassthroughSourceAddress = map[bool]net.IPNet{
diff --git a/pkg/transparentproxy/iptables/builder/builder_table_nat.go b/pkg/transparentproxy/iptables/builder/builder_table_nat.go
index 115ecd138..f2e97d190 100644
--- a/pkg/transparentproxy/iptables/builder/builder_table_nat.go
+++ b/pkg/transparentproxy/iptables/builder/builder_table_nat.go
@@ -175,7 +175,7 @@ func buildMeshOutbound(cfg config.InitializedConfigIPvX) *Chain {
 				rules.
 					NewAppendRule(
 						Protocol(Tcp(DestinationPort(consts.DNSPort))),
-						Jump(ToPort(cfg.Redirect.DNS.Port)),
+						Jump(Redirect(ToPort(cfg.Redirect.DNS.Port))),
 					).
 					WithCommentf("redirect all DNS requests sent via TCP to kuma-dp DNS proxy (listening on port %d)", cfg.Redirect.DNS.Port),
 			)
@@ -186,7 +186,7 @@ func buildMeshOutbound(cfg config.InitializedConfigIPvX) *Chain {
 						NewAppendRule(
 							Destination(dnsIp),
 							Protocol(Tcp(DestinationPort(consts.DNSPort))),
-							Jump(ToPort(cfg.Redirect.DNS.Port)),
+							Jump(Redirect(ToPort(cfg.Redirect.DNS.Port))),
 						).
 						WithCommentf("redirect DNS requests sent via TCP to %s to kuma-dp DNS proxy (listening on port %d)", dnsIp, cfg.Redirect.DNS.Port),
 				)
@@ -235,7 +235,7 @@ func buildMeshRedirect(cfg config.InitializedTrafficFlow) *Chain {
 		rules.
 			NewAppendRule(
 				Protocol(Tcp()),
-				Jump(ToPort(cfg.Port)),
+				Jump(Redirect(ToPort(cfg.Port))),
 			).
 			WithCommentf("redirect TCP traffic to envoy (port %d)", cfg.Port),
 	)
@@ -245,7 +245,7 @@ func addOutputRules(cfg config.InitializedConfigIPvX, nat *tables.NatTable) {
 	if cfg.Log.Enabled {
 		nat.Output().AddRules(
 			rules.
-				NewInsertRule(Jump(Log(consts.OutputLogPrefix, cfg.Log.Level))).
+				NewInsertRule(Jump(Log(LogPrefix(consts.ChainOutput), LogLevel(cfg.Log.Level)))).
 				WithComment("log matching packets using kernel logging"),
 		)
 	}
@@ -304,7 +304,7 @@ func addOutputRules(cfg config.InitializedConfigIPvX, nat *tables.NatTable) {
 				rules.
 					NewInsertRule(
 						Protocol(Udp(DestinationPort(consts.DNSPort))),
-						Jump(ToPort(cfg.Redirect.DNS.Port)),
+						Jump(Redirect(ToPort(cfg.Redirect.DNS.Port))),
 					).
 					WithCommentf("redirect all DNS requests to the kuma-dp DNS proxy (listening on port %d)", cfg.Redirect.DNS.Port),
 			)
@@ -315,7 +315,7 @@ func addOutputRules(cfg config.InitializedConfigIPvX, nat *tables.NatTable) {
 						NewInsertRule(
 							Destination(dnsIp),
 							Protocol(Udp(DestinationPort(consts.DNSPort))),
-							Jump(ToPort(cfg.Redirect.DNS.Port)),
+							Jump(Redirect(ToPort(cfg.Redirect.DNS.Port))),
 						).
 						WithCommentf("redirect DNS requests to %s to the kuma-dp DNS proxy (listening on port %d)", dnsIp, cfg.Redirect.DNS.Port),
 				)
@@ -341,7 +341,7 @@ func addPreroutingRules(cfg config.InitializedConfigIPvX, nat *tables.NatTable)
 	if cfg.Log.Enabled {
 		nat.Prerouting().AddRules(
 			rules.
-				NewAppendRule(Jump(Log(consts.PreroutingLogPrefix, cfg.Log.Level))).
+				NewAppendRule(Jump(Log(LogPrefix(consts.ChainPrerouting), LogLevel(cfg.Log.Level)))).
 				WithComment("log matching packets using kernel logging"),
 		)
 	}
@@ -365,7 +365,7 @@ func addPreroutingRules(cfg config.InitializedConfigIPvX, nat *tables.NatTable)
 					InInterface(iface),
 					Match(MatchUdp()),
 					Protocol(Udp(DestinationPort(consts.DNSPort))),
-					Jump(ToPort(cfg.Redirect.DNS.Port)),
+					Jump(Redirect(ToPort(cfg.Redirect.DNS.Port))),
 				).
 				WithCommentf("redirect DNS requests on interface %s to the kuma-dp DNS proxy (listening on port %d)", iface, cfg.Redirect.DNS.Port),
 			rules.
@@ -373,7 +373,7 @@ func addPreroutingRules(cfg config.InitializedConfigIPvX, nat *tables.NatTable)
 					NotDestination(cfg.Redirect.VNet.InterfaceCIDRs[iface]),
 					InInterface(iface),
 					Protocol(Tcp()),
-					Jump(ToPort(cfg.Redirect.Outbound.Port)),
+					Jump(Redirect(ToPort(cfg.Redirect.Outbound.Port))),
 				).
 				WithCommentf("redirect TCP traffic on interface %s, excluding destination %s, to the envoy's outbound passthrough port %d", iface, cfg.Redirect.VNet.InterfaceCIDRs[iface], cfg.Redirect.Outbound.Port),
 		)
diff --git a/pkg/transparentproxy/iptables/parameters/jump.go b/pkg/transparentproxy/iptables/parameters/jump.go
index 96849863b..e97e515ef 100644
--- a/pkg/transparentproxy/iptables/parameters/jump.go
+++ b/pkg/transparentproxy/iptables/parameters/jump.go
@@ -1,28 +1,16 @@
 package parameters
 
-import (
-	"strconv"
-)
+type JumpParameter WrappingParameter
 
-var _ ParameterBuilder = &JumpParameter{}
-
-type JumpParameter struct {
-	parameters []string
-}
-
-func (p *JumpParameter) Build(bool) []string {
-	return p.parameters
-}
-
-func (p *JumpParameter) Negate() ParameterBuilder {
-	return p
+func newJumpParameter(param string, params ...string) *JumpParameter {
+	return (*JumpParameter)(NewWrappingParameter(param, params...))
 }
 
 func Jump(parameter *JumpParameter) *Parameter {
 	return &Parameter{
 		long:       "--jump",
 		short:      "-j",
-		parameters: []ParameterBuilder{parameter},
+		parameters: []ParameterBuilder{(*WrappingParameter)(parameter)},
 	}
 }
 
@@ -39,51 +27,13 @@ func JumpConditional(
 }
 
 func ToUserDefinedChain(chainName string) *JumpParameter {
-	return &JumpParameter{parameters: []string{chainName}}
-}
-
-func ToPort[T ~uint16](port T) *JumpParameter {
-	return &JumpParameter{parameters: []string{
-		"REDIRECT",
-		"--to-ports",
-		strconv.Itoa(int(port)),
-	}}
+	return newJumpParameter(chainName)
 }
 
 func Return() *JumpParameter {
-	return &JumpParameter{parameters: []string{"RETURN"}}
+	return newJumpParameter("RETURN")
 }
 
 func Drop() *JumpParameter {
-	return &JumpParameter{parameters: []string{"DROP"}}
-}
-
-// Turn on kernel logging of matching packets. When this option is set for
-// a rule, the Linux kernel will print some information on all matching packets
-// (like most IP header fields) via the kernel log (where it can be read with
-// dmesg or syslogd(8)). This is a "non-terminating target", i.e. rule traversal
-// continues at the next rule. So if you want to LOG the packets you refuse, use
-// two separate rules with the same matching criteria, first using target LOG
-// then DROP (or REJECT).
-// ref. iptables(8) > LOG
-// levels:
-//
-//	0 - EMERGENCY	- system is unusable
-//	1 - ALERT		- action must be taken immediately
-//	2 - CRITICAL	- critical conditions
-//	3 - ERR			- error conditions
-//	4 - WARNING		- warning conditions
-//	5 - NOTICE		- normal but significant condition
-//	6 - INFO		- informational
-//	7 - DEBUG		- debug-level messages
-//
-// ref. https://git.netfilter.org/iptables/tree/extensions/libebt_log.c#n27
-func Log(prefix string, level uint16) *JumpParameter {
-	return &JumpParameter{
-		parameters: []string{
-			"LOG",
-			"--log-prefix", prefix,
-			"--log-level", strconv.Itoa(int(level)),
-		},
-	}
+	return newJumpParameter("DROP")
 }
diff --git a/pkg/transparentproxy/iptables/parameters/jump_ct.go b/pkg/transparentproxy/iptables/parameters/jump_ct.go
index ad5de14ab..3bfea1783 100644
--- a/pkg/transparentproxy/iptables/parameters/jump_ct.go
+++ b/pkg/transparentproxy/iptables/parameters/jump_ct.go
@@ -1,29 +1,20 @@
 package parameters
 
-type CtParameter struct {
-	name  string
-	value string
-}
+type CtParameter WrappingParameter
 
-func (p *CtParameter) Build() []string {
-	return []string{p.name, p.value}
+func newCtParameter(param string, params ...string) *CtParameter {
+	return (*CtParameter)(NewWrappingParameter(param, params...))
 }
 
-func Ct(ctParameters ...*CtParameter) *JumpParameter {
-	parameters := []string{"CT"}
-
-	for _, parameter := range ctParameters {
-		parameters = append(parameters, parameter.Build()...)
+func Ct(params ...*CtParameter) *JumpParameter {
+	var parameters []string
+	for _, param := range params {
+		parameters = append(parameters, param.parameters...)
 	}
 
-	return &JumpParameter{
-		parameters: parameters,
-	}
+	return newJumpParameter("CT", parameters...)
 }
 
 func Zone(id string) *CtParameter {
-	return &CtParameter{
-		name:  "--zone",
-		value: id,
-	}
+	return newCtParameter("--zone", id)
 }
diff --git a/pkg/transparentproxy/iptables/parameters/jump_log.go b/pkg/transparentproxy/iptables/parameters/jump_log.go
new file mode 100644
index 000000000..7259d2c65
--- /dev/null
+++ b/pkg/transparentproxy/iptables/parameters/jump_log.go
@@ -0,0 +1,49 @@
+package parameters
+
+import (
+	"fmt"
+)
+
+type LogParameter WrappingParameter
+
+func newLogParameter(param string, params ...string) *LogParameter {
+	return (*LogParameter)(NewWrappingParameter(param, params...))
+}
+
+// Turn on kernel logging of matching packets. When this option is set for
+// a rule, the Linux kernel will print some information on all matching packets
+// (like most IP header fields) via the kernel log (where it can be read with
+// dmesg or syslogd(8)). This is a "non-terminating target", i.e. rule traversal
+// continues at the next rule. So if you want to LOG the packets you refuse, use
+// two separate rules with the same matching criteria, first using target LOG
+// then DROP (or REJECT).
+// ref. iptables(8) > LOG
+func Log(params ...*LogParameter) *JumpParameter {
+	var parameters []string
+	for _, param := range params {
+		parameters = append(parameters, param.parameters...)
+	}
+
+	return newJumpParameter("LOG", parameters...)
+}
+
+// Valid levels:
+//
+//	0 - EMERGENCY	- system is unusable
+//	1 - ALERT		- action must be taken immediately
+//	2 - CRITICAL	- critical conditions
+//	3 - ERR			- error conditions
+//	4 - WARNING		- warning conditions
+//	5 - NOTICE		- normal but significant condition
+//	6 - INFO		- informational
+//	7 - DEBUG		- debug-level messages
+//
+// ref. https://git.netfilter.org/iptables/tree/extensions/libebt_log.c#n27
+func LogLevel(level uint16) *LogParameter {
+	return newLogParameter("--log-level", fmt.Sprintf("%d", level))
+}
+
+// LogPrefix returns a --log-prefix parameter with prefix ending with ":"
+func LogPrefix(prefix string) *LogParameter {
+	return newLogParameter("--log-prefix", prefix+":")
+}
diff --git a/pkg/transparentproxy/iptables/parameters/jump_redirect.go b/pkg/transparentproxy/iptables/parameters/jump_redirect.go
new file mode 100644
index 000000000..fe5c28dea
--- /dev/null
+++ b/pkg/transparentproxy/iptables/parameters/jump_redirect.go
@@ -0,0 +1,19 @@
+package parameters
+
+import (
+	"fmt"
+)
+
+type RedirectParameter WrappingParameter
+
+func newRedirectParameter(param string, params ...string) *RedirectParameter {
+	return (*RedirectParameter)(NewWrappingParameter(param, params...))
+}
+
+func Redirect(parameter *RedirectParameter) *JumpParameter {
+	return newJumpParameter("REDIRECT", parameter.parameters...)
+}
+
+func ToPort[T ~uint16](port T) *RedirectParameter {
+	return newRedirectParameter("--to-ports", fmt.Sprintf("%d", port))
+}
diff --git a/pkg/transparentproxy/iptables/parameters/parameters.go b/pkg/transparentproxy/iptables/parameters/parameters.go
index a5e3148bd..0141d9be1 100644
--- a/pkg/transparentproxy/iptables/parameters/parameters.go
+++ b/pkg/transparentproxy/iptables/parameters/parameters.go
@@ -137,3 +137,24 @@ func (p *SimpleParameter) Build(verbose bool) []string {
 func (p *SimpleParameter) Negate() ParameterBuilder {
 	return p
 }
+
+var _ ParameterBuilder = &WrappingParameter{}
+
+// WrappingParameter is a utility type that encapsulates a slice of strings,
+// allowing for flexible parameter handling in scenarios where only raw string
+// arguments are needed
+type WrappingParameter struct {
+	parameters []string
+}
+
+func NewWrappingParameter(param string, params ...string) *WrappingParameter {
+	return &WrappingParameter{parameters: append([]string{param}, params...)}
+}
+
+func (p *WrappingParameter) Build(bool) []string {
+	return p.parameters
+}
+
+func (p *WrappingParameter) Negate() ParameterBuilder {
+	return p
+}
diff --git a/pkg/xds/context/context.go b/pkg/xds/context/context.go
index dbe8b7949..bf055d95e 100644
--- a/pkg/xds/context/context.go
+++ b/pkg/xds/context/context.go
@@ -151,6 +151,9 @@ func (mc *MeshContext) GetReachableBackends(dataplane *core_mesh.DataplaneResour
 	for _, reachableBackend := range dataplane.Spec.Networking.TransparentProxying.GetReachableBackends().GetRefs() {
 		if len(reachableBackend.Labels) > 0 {
 			for _, tri := range mc.resolveResourceIdentifiersForLabels(reachableBackend.Kind, reachableBackend.Labels) {
+				if port := reachableBackend.Port; port != nil {
+					tri.SectionName = mc.getSectionName(tri.ResourceType, tri.ResourceIdentifier, reachableBackend.Port.GetValue())
+				}
 				reachableBackends[tri] = true
 			}
 		} else {
@@ -162,7 +165,7 @@ func (mc *MeshContext) GetReachableBackends(dataplane *core_mesh.DataplaneResour
 				}),
 			}
 			if port := reachableBackend.Port; port != nil {
-				key.SectionName = fmt.Sprintf("%d", port.GetValue())
+				key.SectionName = mc.getSectionName(key.ResourceType, key.ResourceIdentifier, reachableBackend.Port.GetValue())
 			}
 			reachableBackends[key] = true
 		}
@@ -185,6 +188,31 @@ func (mc *MeshContext) resolveResourceIdentifiersForLabels(kind string, labels m
 	return result
 }
 
+func (mc *MeshContext) getSectionName(kind core_model.ResourceType, key core_model.ResourceIdentifier, port uint32) string {
+	switch kind {
+	case meshservice_api.MeshServiceType:
+		ms, found := mc.MeshServiceByIdentifier[key]
+		if !found {
+			return fmt.Sprintf("%d", port)
+		}
+		if sectionName, portFound := ms.FindSectionNameByPort(port); portFound {
+			return sectionName
+		}
+		return fmt.Sprintf("%d", port)
+	case meshmzservice_api.MeshMultiZoneServiceType:
+		mmzs, found := mc.MeshMultiZoneServiceByIdentifier[key]
+		if !found {
+			return fmt.Sprintf("%d", port)
+		}
+		if sectionName, portFound := mmzs.FindSectionNameByPort(port); portFound {
+			return sectionName
+		}
+		return fmt.Sprintf("%d", port)
+	default:
+		return fmt.Sprintf("%d", port)
+	}
+}
+
 func (mc *MeshContext) getResourceNamesForLabels(kind string, labels map[string]string) map[core_model.ResourceIdentifier]int {
 	reachable := map[core_model.ResourceIdentifier]int{}
 	for label, value := range labels {


Test patch:
diff --git a/.github/workflows/_test.yaml b/.github/workflows/_test.yaml
index 4ba0d3656..c89705abd 100644
--- a/.github/workflows/_test.yaml
+++ b/.github/workflows/_test.yaml
@@ -51,7 +51,7 @@ jobs:
               "test_e2e": {
                 "target": [""],
                 "k8sVersion": ["kindIpv6", "${{ env.K8S_MIN_VERSION }}", "${{ env.K8S_MAX_VERSION }}"],
-                "arch": ["arm64"],
+                "arch": ["amd64"],
                 "parallelism": [4],
                 "cniNetworkPlugin": ["flannel"],
                 "sidecarContainers": [""]
diff --git a/app/kumactl/cmd/install/testdata/install-demo.defaults.golden.yaml b/app/kumactl/cmd/install/testdata/install-demo.defaults.golden.yaml
index 348b6006f..af059d4f6 100644
--- a/app/kumactl/cmd/install/testdata/install-demo.defaults.golden.yaml
+++ b/app/kumactl/cmd/install/testdata/install-demo.defaults.golden.yaml
@@ -12,6 +12,8 @@ kind: Service
 metadata:
   name: redis
   namespace: kuma-demo
+  labels:
+    app: redis
 spec:
   selector:
     app: redis
@@ -24,6 +26,8 @@ kind: Service
 metadata:
   name: demo-app
   namespace: kuma-demo
+  labels:
+    app: demo-app
 spec:
   selector:
     app: demo-app
@@ -130,14 +134,14 @@ spec:
     kind: MeshGateway
     name: demo-app
   to:
-    - targetRef:
-        kind: Mesh
-      rules:
-        - matches:
-            - path:
-                type: PathPrefix
-                value: "/"
-          default:
-            backendRefs:
-              - kind: MeshService
-                name: demo-app_kuma-demo_svc_5000
+  - targetRef:
+      kind: Mesh
+    rules:
+    - default:
+        backendRefs:
+        - kind: MeshService
+          name: demo-app_kuma-demo_svc_5000
+      matches:
+      - path:
+          type: PathPrefix
+          value: /
diff --git a/app/kumactl/cmd/install/testdata/install-demo.overrides.golden.yaml b/app/kumactl/cmd/install/testdata/install-demo.overrides.golden.yaml
index 8bcc6872f..9ec91ebf3 100644
--- a/app/kumactl/cmd/install/testdata/install-demo.overrides.golden.yaml
+++ b/app/kumactl/cmd/install/testdata/install-demo.overrides.golden.yaml
@@ -12,6 +12,8 @@ kind: Service
 metadata:
   name: redis
   namespace: not-kuma-demo
+  labels:
+    app: redis
 spec:
   selector:
     app: redis
@@ -24,6 +26,8 @@ kind: Service
 metadata:
   name: demo-app
   namespace: not-kuma-demo
+  labels:
+    app: demo-app
 spec:
   selector:
     app: demo-app
@@ -106,7 +110,7 @@ spec:
       protocol: HTTP
   selectors:
   - match:
-      kuma.io/service: demo-app-gateway_kuma-demo_svc
+      kuma.io/service: demo-app-gateway_not-kuma-demo_svc
 ---
 apiVersion: kuma.io/v1alpha1
 kind: MeshGatewayInstance
@@ -130,14 +134,14 @@ spec:
     kind: MeshGateway
     name: demo-app
   to:
-    - targetRef:
-        kind: Mesh
-      rules:
-        - matches:
-            - path:
-                type: PathPrefix
-                value: "/"
-          default:
-            backendRefs:
-              - kind: MeshService
-                name: demo-app_not-kuma-demo_svc_5000
+  - targetRef:
+      kind: Mesh
+    rules:
+    - default:
+        backendRefs:
+        - kind: MeshService
+          name: demo-app_not-kuma-demo_svc_5000
+      matches:
+      - path:
+          type: PathPrefix
+          value: /
diff --git a/app/kumactl/cmd/install/testdata/install-demo.without-gateway.golden.yaml b/app/kumactl/cmd/install/testdata/install-demo.without-gateway.golden.yaml
index 1a5b7d6c7..f89ca0a68 100644
--- a/app/kumactl/cmd/install/testdata/install-demo.without-gateway.golden.yaml
+++ b/app/kumactl/cmd/install/testdata/install-demo.without-gateway.golden.yaml
@@ -12,6 +12,8 @@ kind: Service
 metadata:
   name: redis
   namespace: kuma-demo
+  labels:
+    app: redis
 spec:
   selector:
     app: redis
@@ -24,6 +26,8 @@ kind: Service
 metadata:
   name: demo-app
   namespace: kuma-demo
+  labels:
+    app: demo-app
 spec:
   selector:
     app: demo-app
diff --git a/docs/guides/e2e-test-tips.md b/docs/guides/e2e-test-tips.md
index f4d890465..c6b26ff3d 100644
--- a/docs/guides/e2e-test-tips.md
+++ b/docs/guides/e2e-test-tips.md
@@ -2,11 +2,6 @@
 
 E2E tests are less stable and slower than unit tests therefore it's useful to know some tricks
 
-## Mac M1 and ARM support
-
-Currently, we don't support ARM architecture when building docker images which we rely on for e2e tests.
-This is tracked in [#3252](https://github.com/kumahq/kuma/issues/3252) and [#237](https://github.com/kumahq/kuma/issues/237).
-
 ## Using a VM
 
 If you are developing on a laptop e2e tests might be almost impossible to run.
@@ -28,7 +23,7 @@ When e2e tests are executed here are the steps
 K3D is faster than KIND and it is a default tool to run E2E tests. However, not all tests run with it.
 To use KIND in E2E tests add `K8S_CLUSTER_TOOL=kind`
 
-```
+```bash
 make test/e2e K8S_CLUSTER_TOOL=kind
 ```
 
@@ -39,7 +34,7 @@ Regular `make test/e2e` will execute all the tests in the project.
 If you want to execute single test you can change the code from `It()` to `FIt()` or `Describe()` to `FDescribe()` and then use `E2E_PKG_LIST`
 
 Example:
-```
+```bash
 FIt("should access service locally and remotely", func() {
 ...
 
@@ -49,15 +44,15 @@ make test/e2e E2E_PKG_LIST=./test/e2e/deploy/...
 ## Running e2e tests one at a time
 
 When you run `make test/e2e`, the Docker infrastructure will get torn down if any tests fail.
-In the case of failing tests, it can be useful to run one suit at a time, while leaving the Docker containers in place.
+In the case of failing tests, it can be useful to run one suite at a time, while leaving the Docker containers in place.
 
 To do this, first create the test environment:
-```
+```bash
 make images test/e2e/k8s/start
 ```
 
 Now you can run each test suite (exiting on any failures):
-```
+```bash
 (
     set -e
     for t in $(go list ./test/e2e/...); do
@@ -77,13 +72,13 @@ When you use `make test/e2e/debug` and the test fails, execution will immediatel
 ## Decide which Kubernetes clusters will be created
 
 If you know you are running only universal tests you can skip creating Kubernetes clusters by setting
-```
+```bash
 make test/e2e/debug K8SCLUSTERS= E2E_PKG_LIST=./test/e2e/trafficpermission/universal/...
 ```
 
 or if the test need only 1 Kuberenetes cluster do this
 
-```
+```bash
 make test/e2e/debug K8SCLUSTERS=kuma-1 E2E_PKG_LIST=./test/e2e/trafficpermission/universal/...
 ```
 
@@ -93,14 +88,14 @@ make test/e2e/debug K8SCLUSTERS=kuma-1 E2E_PKG_LIST=./test/e2e/trafficpermission
 
 Running `make test/e2e/debug` can intentionally leave resources if test fails. Clean them up with   
 
-```
+```bash
 make k3d/stop/all && docker stop $(docker ps -aq) # omit $ for fish
 ```
 
 ### Tests failing because of disk pressure
 
 From time to time when running tests your docker environment can run out of space. You will see k8s events like this:
-```
+```bash
 Warning  FailedScheduling  63s (x1 over 2m15s)  default-scheduler  0/1 nodes are available: 
 1 node(s) had taint {node.kubernetes.io/disk-pressure:}, that the pod didn't tolerate.
 ```
@@ -117,7 +112,7 @@ docker system prune --volumes --all
 The Kuma build has an optional `dev/envrc` target that generates a `.envrc` file to set the `$CI_TOOLS_DIR` and `$KUBECONFIG` environment variables.
 This is useful to keeping the Kuma CI tools installation tidy in your Kuma workspace, and for conveniently accessing the Kind clusters that are provisioned by the e2e tests.
 
-```
+```bash
 $ make dev/envrc
 direnv: loading ~/upstream/konghq/kuma/.envrc
 direnv: export +CI_TOOLS_DIR +KUBECONFIG
diff --git a/pkg/core/resources/apis/mesh/dataplane_validator_test.go b/pkg/core/resources/apis/mesh/dataplane_validator_test.go
index 5959bef50..b3b156e2c 100644
--- a/pkg/core/resources/apis/mesh/dataplane_validator_test.go
+++ b/pkg/core/resources/apis/mesh/dataplane_validator_test.go
@@ -330,6 +330,7 @@ var _ = Describe("Dataplane", func() {
                   refs:
                   - kind: MeshService
                     name: a
+                    port: 9090
                   - kind: MeshExternalService
                     name: es
                     namespace: es1
diff --git a/test/e2e_env/multizone/reachablebackends/reachablebackends_meshservices.go b/test/e2e_env/multizone/reachablebackends/reachablebackends_meshservices.go
index 865376e7c..18fd416f9 100644
--- a/test/e2e_env/multizone/reachablebackends/reachablebackends_meshservices.go
+++ b/test/e2e_env/multizone/reachablebackends/reachablebackends_meshservices.go
@@ -22,6 +22,7 @@ func MeshServicesWithReachableBackendsOption() {
           kuma.io/display-name: other-zone-test-server
       - kind: MeshService
         name: local-test-server
+        port: 80
 `
 
 	mesh := fmt.Sprintf(`
diff --git a/test/framework/k8s_controlplane.go b/test/framework/k8s_controlplane.go
index 3334458cf..8a95b8d06 100644
--- a/test/framework/k8s_controlplane.go
+++ b/test/framework/k8s_controlplane.go
@@ -79,21 +79,19 @@ func (c *K8sControlPlane) GetKubectlOptions(namespace ...string) *k8s.KubectlOpt
 }
 
 func (c *K8sControlPlane) PortForwardKumaCP() {
-	kumaCpPods := c.GetKumaCPPods()
-	// There could be multiple pods still starting so pick one that's available already
-	for i := range kumaCpPods {
-		if k8s.IsPodAvailable(&kumaCpPods[i]) {
-			c.portFwd.apiServerTunnel = k8s.NewTunnel(c.GetKubectlOptions(Config.KumaNamespace), k8s.ResourceTypePod, kumaCpPods[i].Name, 0, 5681)
-			c.portFwd.apiServerTunnel.ForwardPort(c.t)
-			c.portFwd.ApiServerEndpoint = c.portFwd.apiServerTunnel.Endpoint()
-
-			c.madsFwd.apiServerTunnel = k8s.NewTunnel(c.GetKubectlOptions(Config.KumaNamespace), k8s.ResourceTypePod, kumaCpPods[i].Name, 0, 5676)
-			c.madsFwd.apiServerTunnel.ForwardPort(c.t)
-			c.madsFwd.ApiServerEndpoint = c.madsFwd.apiServerTunnel.Endpoint()
-			return
-		}
+	kumaCpSvc := c.GetKumaCPSvc()
+	if k8s.IsServiceAvailable(&kumaCpSvc) {
+		c.portFwd.apiServerTunnel = k8s.NewTunnel(c.GetKubectlOptions(Config.KumaNamespace), k8s.ResourceTypeService, kumaCpSvc.Name, 0, 5681)
+		c.portFwd.apiServerTunnel.ForwardPort(c.t)
+		c.portFwd.ApiServerEndpoint = c.portFwd.apiServerTunnel.Endpoint()
+
+		c.madsFwd.apiServerTunnel = k8s.NewTunnel(c.GetKubectlOptions(Config.KumaNamespace), k8s.ResourceTypeService, kumaCpSvc.Name, 0, 5676)
+		c.madsFwd.apiServerTunnel.ForwardPort(c.t)
+		c.madsFwd.ApiServerEndpoint = c.madsFwd.apiServerTunnel.Endpoint()
+		return
 	}
-	c.t.Fatalf("Failed finding an available pod, allPods: %v", kumaCpPods)
+
+	c.t.Fatalf("Failed finding an available service, service: %v", kumaCpSvc)
 }
 
 func (c *K8sControlPlane) ClosePortForwards() {