Repository: kumahq/kuma
Pull request #3361 (2021-11-25 09:38:44)

chore(kuma-dp) send reports to tls endpoint
### Summary

Instead of sending data as a raw bytes to tcp endpoint, now they
will be sent to TLS protected endpoint

### Full changelog

no changelog

### Issues resolved

no issues resolved

### Documentation

no documentation changes

### Testing

- [ ] Unit tests
- [ ] E2E tests
- [x] Manual testing on Universal
- [ ] Manual testing on Kubernetes 

### Backwards compatibility

- [x] Add `backport-to-stable` label if the code is backwards compatible. Otherwise, list breaking changes.


codecov-commenter: # [Codecov](https://codecov.io/gh/kumahq/kuma/pull/3361?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq) Report
> Merging [#3361](https://codecov.io/gh/kumahq/kuma/pull/3361?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq) (f71386f) into [master](https://codecov.io/gh/kumahq/kuma/commit/4da705aa5984864e4d2a5225214da981fe744c28?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq) (4da705a) will **decrease** coverage by `0.03%`.
> The diff coverage is `0.00%`.

[![Impacted file tree graph](https://codecov.io/gh/kumahq/kuma/pull/3361/graphs/tree.svg?width=650&height=150&src=pr&token=D7Yvqs6rwV&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq)](https://codecov.io/gh/kumahq/kuma/pull/3361?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq)

```diff
@@            Coverage Diff             @@
##           master    #3361      +/-   ##
==========================================
- Coverage   52.27%   52.24%   -0.04%     
==========================================
  Files         929      929              
  Lines       54452    54453       +1     
==========================================
- Hits        28463    28447      -16     
- Misses      23774    23776       +2     
- Partials     2215     2230      +15     
```


| [Impacted Files](https://codecov.io/gh/kumahq/kuma/pull/3361?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq) | Coverage Δ | |
|---|---|---|
| [pkg/core/runtime/reports/reports.go](https://codecov.io/gh/kumahq/kuma/pull/3361/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq#diff-cGtnL2NvcmUvcnVudGltZS9yZXBvcnRzL3JlcG9ydHMuZ28=) | `12.61% <0.00%> (-0.12%)` | :arrow_down: |
| [pkg/defaults/mesh.go](https://codecov.io/gh/kumahq/kuma/pull/3361/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq#diff-cGtnL2RlZmF1bHRzL21lc2guZ28=) | `64.28% <0.00%> (-21.43%)` | :arrow_down: |
| [pkg/defaults/mesh/signing\_key.go](https://codecov.io/gh/kumahq/kuma/pull/3361/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq#diff-cGtnL2RlZmF1bHRzL21lc2gvc2lnbmluZ19rZXkuZ28=) | `64.70% <0.00%> (-11.77%)` | :arrow_down: |
| [pkg/test/grpc/clientstream.go](https://codecov.io/gh/kumahq/kuma/pull/3361/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq#diff-cGtnL3Rlc3QvZ3JwYy9jbGllbnRzdHJlYW0uZ28=) | `84.00% <0.00%> (-8.00%)` | :arrow_down: |
| [pkg/core/runtime/component/component.go](https://codecov.io/gh/kumahq/kuma/pull/3361/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq#diff-cGtnL2NvcmUvcnVudGltZS9jb21wb25lbnQvY29tcG9uZW50Lmdv) | `81.13% <0.00%> (-7.55%)` | :arrow_down: |
| [pkg/kds/client/sink.go](https://codecov.io/gh/kumahq/kuma/pull/3361/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq#diff-cGtnL2tkcy9jbGllbnQvc2luay5nbw==) | `54.05% <0.00%> (-5.41%)` | :arrow_down: |
| [pkg/defaults/components.go](https://codecov.io/gh/kumahq/kuma/pull/3361/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq#diff-cGtnL2RlZmF1bHRzL2NvbXBvbmVudHMuZ28=) | `88.23% <0.00%> (-3.93%)` | :arrow_down: |
| [pkg/defaults/mesh/mesh.go](https://codecov.io/gh/kumahq/kuma/pull/3361/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq#diff-cGtnL2RlZmF1bHRzL21lc2gvbWVzaC5nbw==) | `74.07% <0.00%> (-3.71%)` | :arrow_down: |
| [pkg/core/resources/manager/cache.go](https://codecov.io/gh/kumahq/kuma/pull/3361/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq#diff-cGtnL2NvcmUvcmVzb3VyY2VzL21hbmFnZXIvY2FjaGUuZ28=) | `83.11% <0.00%> (-2.60%)` | :arrow_down: |
| [pkg/mads/v1/client/client.go](https://codecov.io/gh/kumahq/kuma/pull/3361/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq#diff-cGtnL21hZHMvdjEvY2xpZW50L2NsaWVudC5nbw==) | `41.25% <0.00%> (-2.50%)` | :arrow_down: |
| ... and [8 more](https://codecov.io/gh/kumahq/kuma/pull/3361/diff?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/kumahq/kuma/pull/3361?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/kumahq/kuma/pull/3361?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq). Last update [4da705a...f71386f](https://codecov.io/gh/kumahq/kuma/pull/3361?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq).


Patch:
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 502a94442..cafe29884 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -20,8 +20,10 @@ Fix #XXX
 - [ ] Unit tests
 - [ ] E2E tests
 - [ ] Manual testing on Universal
-- [ ] Manual testing on Kubernetes 
+- [ ] Manual testing on Kubernetes
 
 ### Backwards compatibility
 
+- [ ] Update [`UPGRADE.md`](UPGRADE.md) with any steps users will need to take
+      when upgrading.
 - [ ] Add `backport-to-stable` label if the code is backwards compatible. Otherwise, list breaking changes.
diff --git a/RELEASE.md b/RELEASE.md
index 0522bffd6..bc7017c7c 100644
--- a/RELEASE.md
+++ b/RELEASE.md
@@ -27,6 +27,8 @@ Below you can find the checklist of all the work that has to be performed when c
 To release a new version of Kuma follow these steps:
 
 - [ ] Update the `CHANGELOG.md`. Use `make changelog` as a helper.
+- [ ] Double-check that `UPGRADE.md` is updated with any task that maybe required.
+- [ ] In case of Envoy version change update the compatibility matrix.
 - [ ] Double-check that all the new changes have been documented on the [kuma.io website](https://github.com/kumahq/kuma-website) by opening up a documentation PR with the new content for the new version of Kuma.
 - [ ] Manually bump the `version` and `appVersion` of the Helm chart
 - [ ] Create a new Git tag for the release using the [make-release-tag.sh](./tools/release/make-release-tag.sh) script. This script takes 2 arguments, the name of the previous release tag and the name of the new release tag to create and create an annotated tag containing a summary of the commits in the release.
@@ -43,3 +45,4 @@ To release a new version of Kuma follow these steps:
 - [ ] Post the content on the Kong blog.
 - [ ] In preparation for the email newsletter announcement, include any reminder for upcoming community events like meetups or community calls to the content, and send the email to the Kuma newsletter.
 - [ ] Announce the release on the community channels like Slack and Twitter.
+- [ ] Add issues to upgrade to the latest version of each non dependabot dependency (most notably dependencies in the helm chart like grafana, loki, jaeger...)
diff --git a/UPGRADE.md b/UPGRADE.md
index 3894e9a0b..117756029 100644
--- a/UPGRADE.md
+++ b/UPGRADE.md
@@ -6,6 +6,8 @@ with `x.y.z` being the version you are planning to upgrade to.
 If such a section does not exist, the upgrade you want to perform
 does not have any particular instructions.
 
+## Upcoming release
+
 ## Upgrade to `1.4.0`
 
 Starting with this version, the default API server authentication method is user
diff --git a/deployments/charts/kuma/templates/_helpers.tpl b/deployments/charts/kuma/templates/_helpers.tpl
index 5521637c9..a973180a1 100644
--- a/deployments/charts/kuma/templates/_helpers.tpl
+++ b/deployments/charts/kuma/templates/_helpers.tpl
@@ -160,6 +160,8 @@ env:
 {{- end }}
 - name: KUMA_API_SERVER_AUTHN_LOCALHOST_IS_ADMIN
   value: "false"
+- name: KUMA_RUNTIME_KUBERNETES_SERVICE_ACCOUNT_NAME
+  value: "system:serviceaccount:{{ .Release.Namespace }}:{{ include "kuma.name" . }}-control-plane"
 {{- end }}
 
 {{/*
diff --git a/pkg/config/app/kuma-cp/kuma-cp.defaults.yaml b/pkg/config/app/kuma-cp/kuma-cp.defaults.yaml
index 39711cc9f..4ccadc364 100644
--- a/pkg/config/app/kuma-cp/kuma-cp.defaults.yaml
+++ b/pkg/config/app/kuma-cp/kuma-cp.defaults.yaml
@@ -153,7 +153,9 @@ runtime:
   # Kubernetes-specific configuration
   kubernetes:
     # Service name of the Kuma Control Plane. It is used to point Kuma DP to proper URL.
-    controlPlaneServiceName: kuma-control-plane
+    controlPlaneServiceName: kuma-control-plane # ENV: KUMA_RUNTIME_KUBERNETES_CONTROL_PLANE_SERVICE_NAME
+    # Name of Service Account that is used to run the Control Plane
+    serviceAccountName: "system:serviceaccount:kuma-system:kuma-control-plane" # ENV: KUMA_RUNTIME_KUBERNETES_SERVICE_ACCOUNT_NAME
     # Admission WebHook Server configuration
     admissionServer:
       # Address the Admission WebHook Server should be listening on
diff --git a/pkg/config/plugins/runtime/k8s/config.go b/pkg/config/plugins/runtime/k8s/config.go
index e5a882f94..37ee2f5f9 100644
--- a/pkg/config/plugins/runtime/k8s/config.go
+++ b/pkg/config/plugins/runtime/k8s/config.go
@@ -16,6 +16,7 @@ func DefaultKubernetesRuntimeConfig() *KubernetesRuntimeConfig {
 			Port: 5443,
 		},
 		ControlPlaneServiceName: "kuma-control-plane",
+		ServiceAccountName:      "system:serviceaccount:kuma-system:kuma-control-plane",
 		Injector: Injector{
 			CNIEnabled:           false,
 			VirtualProbesEnabled: true,
@@ -87,6 +88,8 @@ type KubernetesRuntimeConfig struct {
 	// marshaled objects will be stored in the cache. If equal to 0s then
 	// cache is turned off
 	MarshalingCacheExpirationTime time.Duration `yaml:"marshalingCacheExpirationTime" envconfig:"kuma_runtime_kubernetes_marshaling_cache_expiration_time"`
+	// Name of Service Account that is used to run the Control Plane
+	ServiceAccountName string `yaml:"serviceAccountName,omitempty" envconfig:"kuma_runtime_kubernetes_service_account_name"`
 	// ControlPlaneServiceName defines service name of the Kuma control plane. It is used to point Kuma DP to proper URL.
 	ControlPlaneServiceName string `yaml:"controlPlaneServiceName,omitempty" envconfig:"kuma_runtime_kubernetes_control_plane_service_name"`
 }
diff --git a/pkg/core/bootstrap/bootstrap.go b/pkg/core/bootstrap/bootstrap.go
index b4bd4d6b9..403b92576 100644
--- a/pkg/core/bootstrap/bootstrap.go
+++ b/pkg/core/bootstrap/bootstrap.go
@@ -100,7 +100,17 @@ func buildRuntime(appCtx context.Context, cfg kuma_cp.Config) (core_runtime.Runt
 	builder.WithLeaderInfo(leaderInfoComponent)
 
 	builder.WithLookupIP(lookup.CachedLookupIP(net.LookupIP, cfg.General.DNSCacheTTL))
-	builder.WithEnvoyAdminClient(admin.NewEnvoyAdminClient(builder.ResourceManager(), builder.Config()))
+	envoyAdminClient, err := admin.NewEnvoyAdminClient(
+		builder.ResourceManager(),
+		builder.CaManagers(),
+		builder.Config().DpServer.TlsCertFile,
+		builder.Config().DpServer.TlsKeyFile,
+		builder.Config().Runtime.Kubernetes.Injector.SidecarContainer.AdminPort,
+	)
+	if err != nil {
+		return nil, err
+	}
+	builder.WithEnvoyAdminClient(envoyAdminClient)
 	builder.WithAPIManager(customization.NewAPIList())
 	builder.WithXDSHooks(&xds_hooks.Hooks{})
 	builder.WithCAProvider(secrets.NewCaProvider(builder.CaManagers()))
diff --git a/pkg/core/runtime/reports/reports.go b/pkg/core/runtime/reports/reports.go
index 9cd955a3e..10de3e29a 100644
--- a/pkg/core/runtime/reports/reports.go
+++ b/pkg/core/runtime/reports/reports.go
@@ -2,6 +2,7 @@ package reports
 
 import (
 	"context"
+	"crypto/tls"
 	"fmt"
 	"net"
 	"os"
@@ -24,7 +25,7 @@ import (
 const (
 	pingInterval = 3600
 	pingHost     = "kong-hf.konghq.com"
-	pingPort     = 61831
+	pingPort     = 61832
 )
 
 var (
@@ -158,8 +159,9 @@ func (b *reportsBuffer) dispatch(rt core_runtime.Runtime, host string, port int,
 		return err
 	}
 
-	conn, err := net.Dial("tcp", net.JoinHostPort(host,
-		strconv.FormatUint(uint64(port), 10)))
+	conf := &tls.Config{}
+	conn, err := tls.Dial("tcp", net.JoinHostPort(host,
+		strconv.FormatUint(uint64(port), 10)), conf)
 	if err != nil {
 		return err
 	}
diff --git a/pkg/defaults/mesh/mesh.go b/pkg/defaults/mesh/mesh.go
index 76a5f2428..20eb7be01 100644
--- a/pkg/defaults/mesh/mesh.go
+++ b/pkg/defaults/mesh/mesh.go
@@ -83,15 +83,5 @@ func EnsureDefaultMeshResources(resManager manager.ResourceManager, meshName str
 	} else {
 		log.Info("default Signing Key already exist", "mesh", meshName, "name", issuer.SigningKeyResourceKey(issuer.DataplaneTokenPrefix, meshName).Name)
 	}
-
-	created, err = ensureEnvoyAdminClientSigningKey(resManager, meshName)
-	if err != nil {
-		return errors.Wrap(err, "could not create default Envoy Admin Client Token Signing Key")
-	}
-	if created {
-		log.Info("default Signing Key created", "mesh", meshName, "name", issuer.SigningKeyResourceKey(issuer.EnvoyAdminClientTokenPrefix, meshName).Name)
-	} else {
-		log.Info("default Signing Key already exist", "mesh", meshName, "name", issuer.SigningKeyResourceKey(issuer.EnvoyAdminClientTokenPrefix, meshName).Name)
-	}
 	return nil
 }
diff --git a/pkg/defaults/mesh/signing_key.go b/pkg/defaults/mesh/signing_key.go
index fef4cb1d4..cada578c2 100644
--- a/pkg/defaults/mesh/signing_key.go
+++ b/pkg/defaults/mesh/signing_key.go
@@ -14,10 +14,6 @@ func ensureDataplaneTokenSigningKey(resManager manager.ResourceManager, meshName
 	return ensureSigningKeyForPrefix(resManager, meshName, issuer.DataplaneTokenPrefix)
 }
 
-func ensureEnvoyAdminClientSigningKey(resManager manager.ResourceManager, meshName string) (created bool, err error) {
-	return ensureSigningKeyForPrefix(resManager, meshName, issuer.EnvoyAdminClientTokenPrefix)
-}
-
 func ensureSigningKeyForPrefix(resManager manager.ResourceManager, meshName, prefix string) (created bool, err error) {
 	signingKey, err := issuer.CreateSigningKey()
 	if err != nil {
diff --git a/pkg/envoy/admin/client.go b/pkg/envoy/admin/client.go
index 64f4d5499..fc38eb22b 100644
--- a/pkg/envoy/admin/client.go
+++ b/pkg/envoy/admin/client.go
@@ -1,12 +1,12 @@
 package admin
 
 import (
-	"bytes"
-	"crypto/hmac"
-	"crypto/sha256"
+	"context"
 	"crypto/tls"
-	"encoding/hex"
+	"crypto/x509"
+	"encoding/pem"
 	"fmt"
+	"io"
 	"net"
 	"net/http"
 	"strconv"
@@ -14,103 +14,153 @@ import (
 
 	"github.com/pkg/errors"
 
-	kuma_cp "github.com/kumahq/kuma/pkg/config/app/kuma-cp"
+	"github.com/kumahq/kuma/pkg/core/ca"
 	core_mesh "github.com/kumahq/kuma/pkg/core/resources/apis/mesh"
 	"github.com/kumahq/kuma/pkg/core/resources/manager"
-	"github.com/kumahq/kuma/pkg/tokens/builtin/issuer"
+	"github.com/kumahq/kuma/pkg/core/resources/model"
+	core_store "github.com/kumahq/kuma/pkg/core/resources/store"
+	util_tls "github.com/kumahq/kuma/pkg/tls"
+	xds_tls "github.com/kumahq/kuma/pkg/xds/envoy/tls"
 )
 
 type EnvoyAdminClient interface {
-	GenerateAPIToken(dataplane *core_mesh.DataplaneResource) (string, error)
 	PostQuit(dataplane *core_mesh.DataplaneResource) error
 }
 
 type envoyAdminClient struct {
 	rm         manager.ResourceManager
-	cfg        kuma_cp.Config
-	httpClient *http.Client
-	scheme     string
+	caManagers ca.Managers
+	clientCert tls.Certificate
+	adminPort  uint32
 }
 
-func NewEnvoyAdminClient(rm manager.ResourceManager, cfg kuma_cp.Config) EnvoyAdminClient {
-	return &envoyAdminClient{
-		rm:  rm,
-		cfg: cfg,
-		httpClient: &http.Client{
-			Transport: &http.Transport{
-				Dial: (&net.Dialer{
-					Timeout: 3 * time.Second,
-				}).Dial,
-				TLSHandshakeTimeout: 3 * time.Second,
-				TLSClientConfig: &tls.Config{
-					// we do not want to explicitly verify the https
-					InsecureSkipVerify: true,
-				},
-			},
-			Timeout: 5 * time.Second,
-		},
-		scheme: "https",
+func NewEnvoyAdminClient(rm manager.ResourceManager, caManagers ca.Managers, clientCertPath, clientKeyPath string, adminPort uint32) (EnvoyAdminClient, error) {
+	cert, err := tls.LoadX509KeyPair(clientCertPath, clientKeyPath)
+	if err != nil {
+		return nil, err
 	}
-}
 
-const (
-	quitquitquit = "quitquitquit"
-)
-
-func (a *envoyAdminClient) GenerateAPIToken(dataplane *core_mesh.DataplaneResource) (string, error) {
-	mesh := dataplane.Meta.GetMesh()
-	key, err := a.getOrCreateSigningKey(mesh)
-	if err != nil {
-		return "", err
+	client := &envoyAdminClient{
+		rm:         rm,
+		caManagers: caManagers,
+		clientCert: cert,
+		adminPort:  adminPort,
 	}
+	return client, nil
+}
 
-	mac := hmac.New(sha256.New, []byte(key))
-	_, err = mac.Write([]byte(dataplane.Meta.GetName()))
+// Envoy admin API endpoint is secured in two possible ways
+// 1) When mTLS on the mesh is disabled, we pass autogenerated self signed cert just to have TLS in place.
+// 2) When mTLS on the mesh is enabled, we are protecting the endpoint with enabled mTLS backend.
+//
+// Regardless of which CA is used to protect Admin API endpoint, Envoy will always require certs from CP which are the same certs as DP server.
+func (a *envoyAdminClient) buildHTTPClient(dataplane *core_mesh.DataplaneResource) (*http.Client, error) {
+	caCertPool, err := a.caCertPoolOfMeshMTLS(dataplane.Meta.GetMesh())
 	if err != nil {
-		return "", err
+		return nil, err
 	}
 
-	return hex.EncodeToString(mac.Sum(nil)), nil
+	c := &http.Client{
+		Transport: &http.Transport{
+			Dial: (&net.Dialer{
+				Timeout: 3 * time.Second,
+			}).Dial,
+			TLSHandshakeTimeout: 3 * time.Second,
+			TLSClientConfig: &tls.Config{
+				VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
+					if caCertPool == nil {
+						// It means we that admin endpoint is protected with 1) option. We skip extra verification of cert
+						return nil
+					}
+					// verify CA against the Mesh CA
+					if err := util_tls.VerifyOnlyCA(caCertPool)(rawCerts, verifiedChains); err != nil {
+						return err
+					}
+
+					// Verify SPIFFE to see if we are connecting to the right DP
+					cert, _ := x509.ParseCertificate(rawCerts[0]) // ignore error because cert was parsed already
+					dpSpiffe := xds_tls.ServiceSpiffeID(dataplane.Meta.GetMesh(), dataplane.Spec.GetIdentifyingService())
+					for _, uri := range cert.URIs {
+						if uri.String() == dpSpiffe {
+							return nil
+						}
+					}
+					return errors.Errorf("could not find expected URI SAN %s", dpSpiffe)
+				},
+				// We disable builtin verification because
+				// 1) In first case, we don't have stable self-signed cert between instances of CP and we don't want to operate them.
+				// 2) it expects hostname or IP in cert instead of SPIFFE URI SAN, so we cannot use builtin verification
+				//
+				// Also keep in mind that on this very moment we are not sending sensitive data to the DP.
+				InsecureSkipVerify: true,
+				Certificates:       []tls.Certificate{a.clientCert},
+			},
+		},
+		Timeout: 5 * time.Second,
+	}
+	return c, err
 }
 
-func (a *envoyAdminClient) getOrCreateSigningKey(mesh string) (string, error) {
-	key, err := issuer.GetSigningKey(a.rm, issuer.DataplaneTokenPrefix, mesh)
+func (a *envoyAdminClient) caCertPoolOfMeshMTLS(mesh string) (*x509.CertPool, error) {
+	meshRes := core_mesh.NewMeshResource()
+	err := a.rm.Get(context.Background(), meshRes, core_store.GetByKey(mesh, model.NoMesh))
 	if err != nil {
-		return "", errors.Wrap(err, "unable to retrieve the signing key")
+		return nil, err
 	}
-
-	return string(key), nil
+	backend := meshRes.GetEnabledCertificateAuthorityBackend()
+	if backend == nil {
+		return nil, nil
+	}
+	caManager, ok := a.caManagers[backend.Type]
+	if !ok {
+		return nil, errors.Errorf("cannot find CA Manager for type %s", backend.Type)
+	}
+	rootCerts, err := caManager.GetRootCert(context.Background(), mesh, backend)
+	if err != nil {
+		return nil, err
+	}
+	certPool := x509.NewCertPool()
+	for _, certPEM := range rootCerts {
+		block, _ := pem.Decode(certPEM)
+		cert, err := x509.ParseCertificate(block.Bytes)
+		if err != nil {
+			return nil, err
+		}
+		certPool.AddCert(cert)
+	}
+	return certPool, nil
 }
 
+const (
+	quitquitquit = "quitquitquit"
+)
+
 func (a *envoyAdminClient) adminAddress(dataplane *core_mesh.DataplaneResource) string {
 	ip := dataplane.GetIP()
 	// TODO: this will work perfectly fine with K8s, but will fail for Universal
 	// The real allocated admin port is part of the DP metadata, but it is attached to a particular CP,
 	// so we can not reliably use that. A better approach would be to include the admin port
 	// in the DataplaneInsights.
-	portUint := a.cfg.Runtime.Kubernetes.Injector.SidecarContainer.AdminPort
-
-	return net.JoinHostPort(ip, strconv.FormatUint(uint64(portUint), 10))
+	return net.JoinHostPort(ip, strconv.FormatUint(uint64(a.adminPort), 10))
 }
 
 func (a *envoyAdminClient) PostQuit(dataplane *core_mesh.DataplaneResource) error {
-	token, err := a.GenerateAPIToken(dataplane)
+	httpClient, err := a.buildHTTPClient(dataplane)
 	if err != nil {
 		return err
 	}
 
-	body := bytes.NewReader([]byte(""))
-	url := fmt.Sprintf("%s://%s/%s", a.scheme, a.adminAddress(dataplane), quitquitquit)
-
-	request, err := http.NewRequest("POST", url, body)
+	url := fmt.Sprintf("https://%s/%s", a.adminAddress(dataplane), quitquitquit)
+	request, err := http.NewRequest("POST", url, nil)
 	if err != nil {
 		return err
 	}
 
-	request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
-
 	// Envoy will not send back any response, so do we not check the response
-	response, err := a.httpClient.Do(request)
+	response, err := httpClient.Do(request)
+	if errors.Is(err, io.EOF) {
+		return nil // Envoy may not respond correctly for this request because it already started the shut-down process.
+	}
 	if err != nil {
 		return errors.Wrapf(err, "unable to send POST to %s", quitquitquit)
 	}
diff --git a/pkg/plugins/runtime/k8s/plugin.go b/pkg/plugins/runtime/k8s/plugin.go
index 87571da31..5ee5b46de 100644
--- a/pkg/plugins/runtime/k8s/plugin.go
+++ b/pkg/plugins/runtime/k8s/plugin.go
@@ -234,7 +234,7 @@ func addValidators(mgr kube_ctrl.Manager, rt core_runtime.Runtime, converter k8s
 		return errors.Errorf("could not find composite validator in the extensions context")
 	}
 
-	handler := k8s_webhooks.NewValidatingWebhook(converter, core_registry.Global(), k8s_registry.Global(), rt.Config().Mode, rt.Config().Store.Kubernetes.SystemNamespace)
+	handler := k8s_webhooks.NewValidatingWebhook(converter, core_registry.Global(), k8s_registry.Global(), rt.Config().Mode, rt.Config().Runtime.Kubernetes.ServiceAccountName)
 	composite.AddValidator(handler)
 
 	k8sMeshValidator := k8s_webhooks.NewMeshValidatorWebhook(rt.MeshValidator(), converter, rt.ResourceManager())
diff --git a/pkg/plugins/runtime/k8s/webhooks/validation.go b/pkg/plugins/runtime/k8s/webhooks/validation.go
index ab170ad39..eba4ea468 100644
--- a/pkg/plugins/runtime/k8s/webhooks/validation.go
+++ b/pkg/plugins/runtime/k8s/webhooks/validation.go
@@ -24,23 +24,23 @@ import (
 	"github.com/kumahq/kuma/pkg/version"
 )
 
-func NewValidatingWebhook(converter k8s_common.Converter, coreRegistry core_registry.TypeRegistry, k8sRegistry k8s_registry.TypeRegistry, mode core.CpMode, systemNamespace string) k8s_common.AdmissionValidator {
+func NewValidatingWebhook(converter k8s_common.Converter, coreRegistry core_registry.TypeRegistry, k8sRegistry k8s_registry.TypeRegistry, mode core.CpMode, cpServiceAccountName string) k8s_common.AdmissionValidator {
 	return &validatingHandler{
-		coreRegistry:    coreRegistry,
-		k8sRegistry:     k8sRegistry,
-		converter:       converter,
-		mode:            mode,
-		systemNamespace: systemNamespace,
+		coreRegistry:         coreRegistry,
+		k8sRegistry:          k8sRegistry,
+		converter:            converter,
+		mode:                 mode,
+		cpServiceAccountName: cpServiceAccountName,
 	}
 }
 
 type validatingHandler struct {
-	coreRegistry    core_registry.TypeRegistry
-	k8sRegistry     k8s_registry.TypeRegistry
-	converter       k8s_common.Converter
-	decoder         *admission.Decoder
-	mode            core.CpMode
-	systemNamespace string
+	coreRegistry         core_registry.TypeRegistry
+	k8sRegistry          k8s_registry.TypeRegistry
+	converter            k8s_common.Converter
+	decoder              *admission.Decoder
+	mode                 core.CpMode
+	cpServiceAccountName string
 }
 
 func (h *validatingHandler) InjectDecoder(d *admission.Decoder) error {
@@ -104,7 +104,7 @@ func (h *validatingHandler) decode(req admission.Request) (core_model.Resource,
 
 // Note that this func does not validate ConfigMap and Secret since this webhook does not support those
 func (h *validatingHandler) isOperationAllowed(resType core_model.ResourceType, userInfo authenticationv1.UserInfo, op v1.Operation) admission.Response {
-	if isKumaServiceAccount(userInfo, h.systemNamespace) {
+	if userInfo.Username == h.cpServiceAccountName {
 		// Assume this means sync from another zone. Not security; protecting user from self.
 		return admission.Allowed("")
 	}
@@ -149,15 +149,6 @@ func syncErrorResponse(resType core_model.ResourceType, cpMode core.CpMode, op v
 	}
 }
 
-func isKumaServiceAccount(userInfo authenticationv1.UserInfo, systemNamespace string) bool {
-	elms := strings.Split(userInfo.Username, ":")
-	// system:serviceaccount:<namespace>:kuma-control-plane
-	if len(elms) == 4 && elms[2] == systemNamespace {
-		return true
-	}
-	return false
-}
-
 // validateResourceLocation validates if resources that suppose to be applied on Global are applied on Global and other way around
 func (h *validatingHandler) validateResourceLocation(resType core_model.ResourceType) admission.Response {
 	if err := system.ValidateLocation(resType, h.mode); err != nil {
diff --git a/pkg/tls/verify_only_ca.go b/pkg/tls/verify_only_ca.go
new file mode 100644
index 000000000..c30c184f7
--- /dev/null
+++ b/pkg/tls/verify_only_ca.go
@@ -0,0 +1,41 @@
+package tls
+
+import (
+	"crypto/x509"
+	"time"
+
+	"github.com/pkg/errors"
+)
+
+func VerifyOnlyCA(caPool *x509.CertPool) func([][]byte, [][]*x509.Certificate) error {
+	return func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
+		// Code copy/pasted and adapted from
+		// https://github.com/golang/go/blob/81555cb4f3521b53f9de4ce15f64b77cc9df61b9/src/crypto/tls/handshake_client.go#L327-L344, but adapted to skip the hostname verification.
+		// See https://github.com/golang/go/issues/21971#issuecomment-412836078.
+
+		// If this is the first handshake on a connection, process and
+		// (optionally) verify the server's certificates.
+		certs := make([]*x509.Certificate, len(rawCerts))
+		for i, asn1Data := range rawCerts {
+			cert, err := x509.ParseCertificate(asn1Data)
+			if err != nil {
+				return errors.Wrap(err, "tls: failed to parse certificate from server")
+			}
+			certs[i] = cert
+		}
+
+		opts := x509.VerifyOptions{
+			Roots:         caPool,
+			CurrentTime:   time.Now(),
+			DNSName:       "", // <- skip hostname verification
+			Intermediates: x509.NewCertPool(),
+		}
+
+		// Skip the first cert because it's the leaf. All others are intermediates.
+		for _, cert := range certs[:1] {
+			opts.Intermediates.AddCert(cert)
+		}
+		_, err := certs[0].Verify(opts)
+		return err
+	}
+}
diff --git a/pkg/tokens/builtin/issuer/signing_key.go b/pkg/tokens/builtin/issuer/signing_key.go
index af8a4bb77..98496f8c9 100644
--- a/pkg/tokens/builtin/issuer/signing_key.go
+++ b/pkg/tokens/builtin/issuer/signing_key.go
@@ -18,8 +18,7 @@ import (
 )
 
 const (
-	DataplaneTokenPrefix        = "dataplane-token"
-	EnvoyAdminClientTokenPrefix = "envoy-admin-client-token"
+	DataplaneTokenPrefix = "dataplane-token"
 )
 
 func SigningKeyNotFound(meshName string) error {
diff --git a/pkg/xds/bootstrap/template_v3.go b/pkg/xds/bootstrap/template_v3.go
index 9e9505fcd..b53bdb258 100644
--- a/pkg/xds/bootstrap/template_v3.go
+++ b/pkg/xds/bootstrap/template_v3.go
@@ -13,6 +13,7 @@ import (
 	envoy_type_matcher_v3 "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3"
 
 	util_proto "github.com/kumahq/kuma/pkg/util/proto"
+	"github.com/kumahq/kuma/pkg/xds/envoy/tls"
 )
 
 func genConfig(parameters configParameters) (*envoy_bootstrap_v3.Bootstrap, error) {
@@ -103,6 +104,25 @@ func genConfig(parameters configParameters) (*envoy_bootstrap_v3.Bootstrap, erro
 			},
 		},
 		StaticResources: &envoy_bootstrap_v3.Bootstrap_StaticResources{
+			Secrets: []*envoy_tls.Secret{
+				{
+					Name: tls.CpValidationCtx,
+					Type: &envoy_tls.Secret_ValidationContext{
+						ValidationContext: &envoy_tls.CertificateValidationContext{
+							MatchSubjectAltNames: []*envoy_type_matcher_v3.StringMatcher{
+								{
+									MatchPattern: &envoy_type_matcher_v3.StringMatcher_Exact{Exact: parameters.XdsHost},
+								},
+							},
+							TrustedCa: &envoy_core_v3.DataSource{
+								Specifier: &envoy_core_v3.DataSource_InlineBytes{
+									InlineBytes: parameters.CertBytes,
+								},
+							},
+						},
+					},
+				},
+			},
 			Clusters: []*envoy_cluster_v3.Cluster{
 				{
 					// TODO does timeout and keepAlive make sense on this as it uses unix domain sockets?
@@ -185,24 +205,13 @@ func genConfig(parameters configParameters) (*envoy_bootstrap_v3.Bootstrap, erro
 					TlsParams: &envoy_tls.TlsParameters{
 						TlsMinimumProtocolVersion: envoy_tls.TlsParameters_TLSv1_2,
 					},
-					ValidationContextType: &envoy_tls.CommonTlsContext_ValidationContext{
-						ValidationContext: &envoy_tls.CertificateValidationContext{
-							MatchSubjectAltNames: []*envoy_type_matcher_v3.StringMatcher{
-								{
-									MatchPattern: &envoy_type_matcher_v3.StringMatcher_Exact{Exact: parameters.XdsHost},
-								},
-							},
+					ValidationContextType: &envoy_tls.CommonTlsContext_ValidationContextSdsSecretConfig{
+						ValidationContextSdsSecretConfig: &envoy_tls.SdsSecretConfig{
+							Name: tls.CpValidationCtx,
 						},
 					},
 				},
 			}
-			if parameters.CertBytes != nil {
-				transport.CommonTlsContext.GetValidationContext().TrustedCa = &envoy_core_v3.DataSource{
-					Specifier: &envoy_core_v3.DataSource_InlineBytes{
-						InlineBytes: parameters.CertBytes,
-					},
-				}
-			}
 			any, err := util_proto.MarshalAnyDeterministic(transport)
 			if err != nil {
 				return nil, err
diff --git a/pkg/xds/envoy/listeners/filter_chain_configurers.go b/pkg/xds/envoy/listeners/filter_chain_configurers.go
index 6d77eaba3..065834cea 100644
--- a/pkg/xds/envoy/listeners/filter_chain_configurers.go
+++ b/pkg/xds/envoy/listeners/filter_chain_configurers.go
@@ -9,7 +9,6 @@ import (
 	mesh_proto "github.com/kumahq/kuma/api/mesh/v1alpha1"
 	core_mesh "github.com/kumahq/kuma/pkg/core/resources/apis/mesh"
 	core_xds "github.com/kumahq/kuma/pkg/core/xds"
-	"github.com/kumahq/kuma/pkg/tls"
 	util_proto "github.com/kumahq/kuma/pkg/util/proto"
 	xds_context "github.com/kumahq/kuma/pkg/xds/context"
 	envoy_common "github.com/kumahq/kuma/pkg/xds/envoy"
@@ -41,16 +40,14 @@ func StaticEndpoints(virtualHostName string, paths []*envoy_common.StaticEndpoin
 	})
 }
 
-func StaticTlsEndpoints(virtualHostName string, keyPair *tls.KeyPair, paths []*envoy_common.StaticEndpointPath) FilterChainBuilderOpt {
-	return AddFilterChainConfigurer(&v3.StaticEndpointsConfigurer{
-		VirtualHostName: virtualHostName,
-		Paths:           paths,
-		KeyPair:         keyPair,
+func ServerSideMTLS(ctx xds_context.Context) FilterChainBuilderOpt {
+	return AddFilterChainConfigurer(&v3.ServerSideMTLSConfigurer{
+		Ctx: ctx,
 	})
 }
 
-func ServerSideMTLS(ctx xds_context.Context) FilterChainBuilderOpt {
-	return AddFilterChainConfigurer(&v3.ServerSideMTLSConfigurer{
+func ServerSideMTLSWithCP(ctx xds_context.Context) FilterChainBuilderOpt {
+	return AddFilterChainConfigurer(&v3.ServerSideMTLSWithCPConfigurer{
 		Ctx: ctx,
 	})
 }
diff --git a/pkg/xds/envoy/listeners/v3/server_mtls_with_cp_configurer.go b/pkg/xds/envoy/listeners/v3/server_mtls_with_cp_configurer.go
new file mode 100644
index 000000000..40f798463
--- /dev/null
+++ b/pkg/xds/envoy/listeners/v3/server_mtls_with_cp_configurer.go
@@ -0,0 +1,48 @@
+package v3
+
+import (
+	envoy_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
+	envoy_listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
+	envoy_extensions_transport_sockets_tls_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
+
+	util_proto "github.com/kumahq/kuma/pkg/util/proto"
+	xds_context "github.com/kumahq/kuma/pkg/xds/context"
+	xds_tls "github.com/kumahq/kuma/pkg/xds/envoy/tls"
+	tls "github.com/kumahq/kuma/pkg/xds/envoy/tls/v3"
+)
+
+type ServerSideMTLSWithCPConfigurer struct {
+	Ctx xds_context.Context
+}
+
+var _ FilterChainConfigurer = &ServerSideMTLSWithCPConfigurer{}
+
+func (c *ServerSideMTLSWithCPConfigurer) Configure(filterChain *envoy_listener.FilterChain) error {
+	tlsContext, err := tls.CreateDownstreamTlsContext(c.Ctx)
+	if err != nil {
+		return err
+	}
+	if tlsContext == nil { // if mTLS is not enabled, fallback on self-signed certs
+		tlsContext = tls.StaticDownstreamTlsContext(c.Ctx.ControlPlane.AdminProxyKeyPair)
+	}
+
+	// require certs that were presented when DP connects to the CP
+	tlsContext.RequireClientCertificate = util_proto.Bool(true)
+	tlsContext.CommonTlsContext.ValidationContextType = &envoy_extensions_transport_sockets_tls_v3.CommonTlsContext_ValidationContextSdsSecretConfig{
+		ValidationContextSdsSecretConfig: &envoy_extensions_transport_sockets_tls_v3.SdsSecretConfig{
+			Name: xds_tls.CpValidationCtx,
+		},
+	}
+
+	pbst, err := util_proto.MarshalAnyDeterministic(tlsContext)
+	if err != nil {
+		return err
+	}
+	filterChain.TransportSocket = &envoy_core.TransportSocket{
+		Name: "envoy.transport_sockets.tls",
+		ConfigType: &envoy_core.TransportSocket_TypedConfig{
+			TypedConfig: pbst,
+		},
+	}
+	return nil
+}
diff --git a/pkg/xds/envoy/listeners/v3/static_endpoints_configurer.go b/pkg/xds/envoy/listeners/v3/static_endpoints_configurer.go
index fc2de80a4..c2e8df879 100644
--- a/pkg/xds/envoy/listeners/v3/static_endpoints_configurer.go
+++ b/pkg/xds/envoy/listeners/v3/static_endpoints_configurer.go
@@ -1,22 +1,18 @@
 package v3
 
 import (
-	envoy_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
 	envoy_listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
 	envoy_route "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
 	envoy_hcm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
 
-	"github.com/kumahq/kuma/pkg/tls"
 	util_proto "github.com/kumahq/kuma/pkg/util/proto"
 	util_xds "github.com/kumahq/kuma/pkg/util/xds"
 	envoy_common "github.com/kumahq/kuma/pkg/xds/envoy"
-	xds_tls "github.com/kumahq/kuma/pkg/xds/envoy/tls/v3"
 )
 
 type StaticEndpointsConfigurer struct {
 	VirtualHostName string
 	Paths           []*envoy_common.StaticEndpointPath
-	KeyPair         *tls.KeyPair
 }
 
 var _ FilterChainConfigurer = &StaticEndpointsConfigurer{}
@@ -78,21 +74,5 @@ func (c *StaticEndpointsConfigurer) Configure(filterChain *envoy_listener.Filter
 			TypedConfig: pbst,
 		},
 	})
-
-	if c.KeyPair != nil {
-		tlsContext := xds_tls.StaticDownstreamTlsContext(c.KeyPair)
-		pbst, err = util_proto.MarshalAnyDeterministic(tlsContext)
-		if err != nil {
-			return err
-		}
-
-		filterChain.TransportSocket = &envoy_core.TransportSocket{
-			Name: "envoy.transport_sockets.tls",
-			ConfigType: &envoy_core.TransportSocket_TypedConfig{
-				TypedConfig: pbst,
-			},
-		}
-	}
-
 	return nil
 }
diff --git a/pkg/xds/envoy/tls/tls.go b/pkg/xds/envoy/tls/tls.go
index c21d38d4f..18460b3ba 100644
--- a/pkg/xds/envoy/tls/tls.go
+++ b/pkg/xds/envoy/tls/tls.go
@@ -7,6 +7,7 @@ import (
 const (
 	MeshCaResource       = "mesh_ca"
 	IdentityCertResource = "identity_cert"
+	CpValidationCtx      = "cp_validation_ctx"
 )
 
 // KumaALPNProtocols are set for UpstreamTlsContext to show that mTLS is created by mesh.
diff --git a/pkg/xds/generator/admin_proxy_generator.go b/pkg/xds/generator/admin_proxy_generator.go
index abf60b1e5..5db226c6d 100644
--- a/pkg/xds/generator/admin_proxy_generator.go
+++ b/pkg/xds/generator/admin_proxy_generator.go
@@ -1,10 +1,9 @@
 package generator
 
 import (
-	"fmt"
-
-	"github.com/pkg/errors"
+	"github.com/Masterminds/semver/v3"
 
+	mesh_proto "github.com/kumahq/kuma/api/mesh/v1alpha1"
 	core_xds "github.com/kumahq/kuma/pkg/core/xds"
 	xds_context "github.com/kumahq/kuma/pkg/xds/context"
 	envoy_common "github.com/kumahq/kuma/pkg/xds/envoy"
@@ -35,6 +34,19 @@ var staticTlsEndpointPaths = []*envoy_common.StaticEndpointPath{
 type AdminProxyGenerator struct {
 }
 
+// backwards compatibility with 1.3.x
+var HasCPValidationCtxInBootstrap = func(version *mesh_proto.Version) (bool, error) {
+	if version.GetKumaDp().GetVersion() == "" { // mostly for tests but also for very old version of Kuma
+		return false, nil
+	}
+
+	semverVer, err := semver.NewVersion(version.KumaDp.GetVersion())
+	if err != nil {
+		return false, err
+	}
+	return !semverVer.LessThan(semver.MustParse("1.4.0")), nil
+}
+
 func (g AdminProxyGenerator) Generate(ctx xds_context.Context, proxy *core_xds.Proxy) (*core_xds.ResourceSet, error) {
 	if proxy.Metadata.GetAdminPort() == 0 {
 		// It's not possible to export Admin endpoints if Envoy Admin API has not been enabled on that dataplane.
@@ -70,20 +82,18 @@ func (g AdminProxyGenerator) Generate(ctx xds_context.Context, proxy *core_xds.P
 				Configure(envoy_listeners.StaticEndpoints(envoy_names.GetAdminListenerName(), staticEndpointPaths)),
 			),
 		}
-		if proxy.Dataplane != nil {
+		hasCpValidationCtx, err := HasCPValidationCtxInBootstrap(proxy.Metadata.Version)
+		if err != nil {
+			return nil, err
+		}
+		if proxy.Dataplane != nil && hasCpValidationCtx {
 			for _, se := range staticTlsEndpointPaths {
 				se.ClusterName = envoyAdminClusterName
-
-				token, err := ctx.EnvoyAdminClient.GenerateAPIToken(proxy.Dataplane)
-				if err != nil {
-					return nil, errors.Wrapf(err, "unable to generate the API token")
-				}
-				se.Header = "Authorization"
-				se.HeaderExactMatch = fmt.Sprintf("Bearer %s", token)
 			}
 			filterChains = append(filterChains, envoy_listeners.FilterChain(envoy_listeners.NewFilterChainBuilder(proxy.APIVersion).
 				Configure(envoy_listeners.MatchTransportProtocol("tls")).
-				Configure(envoy_listeners.StaticTlsEndpoints(envoy_names.GetAdminListenerName(), ctx.ControlPlane.AdminProxyKeyPair, staticTlsEndpointPaths)),
+				Configure(envoy_listeners.StaticEndpoints(envoy_names.GetAdminListenerName(), staticTlsEndpointPaths)).
+				Configure(envoy_listeners.ServerSideMTLSWithCP(ctx)),
 			))
 		}
 		listener, err := envoy_listeners.NewListenerBuilder(proxy.APIVersion).


Test patch:
diff --git a/app/kumactl/cmd/install/testdata/install-control-plane.cni-enabled.golden.yaml b/app/kumactl/cmd/install/testdata/install-control-plane.cni-enabled.golden.yaml
index f9b80471a..ce4f0e774 100644
--- a/app/kumactl/cmd/install/testdata/install-control-plane.cni-enabled.golden.yaml
+++ b/app/kumactl/cmd/install/testdata/install-control-plane.cni-enabled.golden.yaml
@@ -1140,6 +1140,8 @@ spec:
               value: "true"
             - name: KUMA_RUNTIME_KUBERNETES_INJECTOR_SIDECAR_CONTAINER_IMAGE
               value: "docker.io/kumahq/kuma-dp:0.0.1"
+            - name: KUMA_RUNTIME_KUBERNETES_SERVICE_ACCOUNT_NAME
+              value: "system:serviceaccount:kuma-system:kuma-control-plane"
             - name: KUMA_STORE_KUBERNETES_SYSTEM_NAMESPACE
               value: "kuma-system"
             - name: KUMA_STORE_TYPE
diff --git a/app/kumactl/cmd/install/testdata/install-control-plane.defaults.golden.yaml b/app/kumactl/cmd/install/testdata/install-control-plane.defaults.golden.yaml
index e3aa9d281..a67d30d34 100644
--- a/app/kumactl/cmd/install/testdata/install-control-plane.defaults.golden.yaml
+++ b/app/kumactl/cmd/install/testdata/install-control-plane.defaults.golden.yaml
@@ -965,6 +965,8 @@ spec:
               value: "false"
             - name: KUMA_RUNTIME_KUBERNETES_INJECTOR_SIDECAR_CONTAINER_IMAGE
               value: "docker.io/kumahq/kuma-dp:0.0.1"
+            - name: KUMA_RUNTIME_KUBERNETES_SERVICE_ACCOUNT_NAME
+              value: "system:serviceaccount:kuma-system:kuma-control-plane"
             - name: KUMA_STORE_KUBERNETES_SYSTEM_NAMESPACE
               value: "kuma-system"
             - name: KUMA_STORE_TYPE
diff --git a/app/kumactl/cmd/install/testdata/install-control-plane.global.golden.yaml b/app/kumactl/cmd/install/testdata/install-control-plane.global.golden.yaml
index 03a44015e..a934b0ec7 100644
--- a/app/kumactl/cmd/install/testdata/install-control-plane.global.golden.yaml
+++ b/app/kumactl/cmd/install/testdata/install-control-plane.global.golden.yaml
@@ -974,6 +974,8 @@ spec:
               value: "false"
             - name: KUMA_RUNTIME_KUBERNETES_INJECTOR_SIDECAR_CONTAINER_IMAGE
               value: "docker.io/kumahq/kuma-dp:0.0.1"
+            - name: KUMA_RUNTIME_KUBERNETES_SERVICE_ACCOUNT_NAME
+              value: "system:serviceaccount:kuma-system:kuma-control-plane"
             - name: KUMA_STORE_KUBERNETES_SYSTEM_NAMESPACE
               value: "kuma-system"
             - name: KUMA_STORE_TYPE
diff --git a/app/kumactl/cmd/install/testdata/install-control-plane.override-env-vars.golden.yaml b/app/kumactl/cmd/install/testdata/install-control-plane.override-env-vars.golden.yaml
index 069cced3f..321735c8f 100644
--- a/app/kumactl/cmd/install/testdata/install-control-plane.override-env-vars.golden.yaml
+++ b/app/kumactl/cmd/install/testdata/install-control-plane.override-env-vars.golden.yaml
@@ -965,6 +965,8 @@ spec:
               value: "false"
             - name: KUMA_RUNTIME_KUBERNETES_INJECTOR_SIDECAR_CONTAINER_IMAGE
               value: "docker.io/kumahq/kuma-dp:0.0.1"
+            - name: KUMA_RUNTIME_KUBERNETES_SERVICE_ACCOUNT_NAME
+              value: "system:serviceaccount:kuma-system:kuma-control-plane"
             - name: KUMA_STORE_KUBERNETES_SYSTEM_NAMESPACE
               value: "kuma-system"
             - name: KUMA_STORE_TYPE
diff --git a/app/kumactl/cmd/install/testdata/install-control-plane.overrides.golden.yaml b/app/kumactl/cmd/install/testdata/install-control-plane.overrides.golden.yaml
index 17c5d51c0..5d3759493 100644
--- a/app/kumactl/cmd/install/testdata/install-control-plane.overrides.golden.yaml
+++ b/app/kumactl/cmd/install/testdata/install-control-plane.overrides.golden.yaml
@@ -985,6 +985,8 @@ spec:
               value: "false"
             - name: KUMA_RUNTIME_KUBERNETES_INJECTOR_SIDECAR_CONTAINER_IMAGE
               value: "kuma-ci/kuma-dp:greatest"
+            - name: KUMA_RUNTIME_KUBERNETES_SERVICE_ACCOUNT_NAME
+              value: "system:serviceaccount:kuma:kuma-control-plane"
             - name: KUMA_STORE_KUBERNETES_SYSTEM_NAMESPACE
               value: "kuma"
             - name: KUMA_STORE_TYPE
diff --git a/app/kumactl/cmd/install/testdata/install-control-plane.with-ingress.golden.yaml b/app/kumactl/cmd/install/testdata/install-control-plane.with-ingress.golden.yaml
index 1b92fff21..2aca84919 100644
--- a/app/kumactl/cmd/install/testdata/install-control-plane.with-ingress.golden.yaml
+++ b/app/kumactl/cmd/install/testdata/install-control-plane.with-ingress.golden.yaml
@@ -994,6 +994,8 @@ spec:
               value: "false"
             - name: KUMA_RUNTIME_KUBERNETES_INJECTOR_SIDECAR_CONTAINER_IMAGE
               value: "docker.io/kumahq/kuma-dp:0.0.1"
+            - name: KUMA_RUNTIME_KUBERNETES_SERVICE_ACCOUNT_NAME
+              value: "system:serviceaccount:kuma-system:kuma-control-plane"
             - name: KUMA_STORE_KUBERNETES_SYSTEM_NAMESPACE
               value: "kuma-system"
             - name: KUMA_STORE_TYPE
diff --git a/app/kumactl/cmd/install/testdata/install-control-plane.zone.golden.yaml b/app/kumactl/cmd/install/testdata/install-control-plane.zone.golden.yaml
index 7044b2668..fad3b6bbb 100644
--- a/app/kumactl/cmd/install/testdata/install-control-plane.zone.golden.yaml
+++ b/app/kumactl/cmd/install/testdata/install-control-plane.zone.golden.yaml
@@ -973,6 +973,8 @@ spec:
               value: "false"
             - name: KUMA_RUNTIME_KUBERNETES_INJECTOR_SIDECAR_CONTAINER_IMAGE
               value: "docker.io/kumahq/kuma-dp:0.0.1"
+            - name: KUMA_RUNTIME_KUBERNETES_SERVICE_ACCOUNT_NAME
+              value: "system:serviceaccount:kuma-system:kuma-control-plane"
             - name: KUMA_STORE_KUBERNETES_SYSTEM_NAMESPACE
               value: "kuma-system"
             - name: KUMA_STORE_TYPE
diff --git a/pkg/api-server/config_ws_test.go b/pkg/api-server/config_ws_test.go
index 39f3cd0a6..efd95ec35 100644
--- a/pkg/api-server/config_ws_test.go
+++ b/pkg/api-server/config_ws_test.go
@@ -187,6 +187,7 @@ var _ = Describe("Config WS", func() {
 				"port": 5443
 			  },
 			  "controlPlaneServiceName": "kuma-control-plane",
+			  "serviceAccountName": "system:serviceaccount:kuma-system:kuma-control-plane",
 			  "injector": {
 				"caCertFile": "",
 				"builtinDNS": {
diff --git a/pkg/config/loader_test.go b/pkg/config/loader_test.go
index 37075eb60..e25c70063 100644
--- a/pkg/config/loader_test.go
+++ b/pkg/config/loader_test.go
@@ -139,6 +139,7 @@ var _ = Describe("Config loader", func() {
 			Expect(cfg.MonitoringAssignmentServer.ApiVersions).To(ContainElements("v1"))
 
 			Expect(cfg.Runtime.Kubernetes.ControlPlaneServiceName).To(Equal("custom-control-plane"))
+			Expect(cfg.Runtime.Kubernetes.ServiceAccountName).To(Equal("custom-sa"))
 
 			Expect(cfg.Runtime.Kubernetes.AdmissionServer.Address).To(Equal("127.0.0.2"))
 			Expect(cfg.Runtime.Kubernetes.AdmissionServer.Port).To(Equal(uint32(9443)))
@@ -317,6 +318,7 @@ runtime:
   universal:
     dataplaneCleanupAge: 1h
   kubernetes:
+    serviceAccountName: custom-sa
     controlPlaneServiceName: custom-control-plane
     admissionServer:
       address: 127.0.0.2
@@ -505,6 +507,7 @@ access:
 				"KUMA_MONITORING_ASSIGNMENT_SERVER_ASSIGNMENT_REFRESH_INTERVAL":                            "12s",
 				"KUMA_REPORTS_ENABLED":                                                                     "false",
 				"KUMA_RUNTIME_KUBERNETES_CONTROL_PLANE_SERVICE_NAME":                                       "custom-control-plane",
+				"KUMA_RUNTIME_KUBERNETES_SERVICE_ACCOUNT_NAME":                                             "custom-sa",
 				"KUMA_RUNTIME_KUBERNETES_ADMISSION_SERVER_ADDRESS":                                         "127.0.0.2",
 				"KUMA_RUNTIME_KUBERNETES_ADMISSION_SERVER_PORT":                                            "9443",
 				"KUMA_RUNTIME_KUBERNETES_ADMISSION_SERVER_CERT_DIR":                                        "/var/run/secrets/kuma.io/kuma-admission-server/tls-cert",
diff --git a/pkg/config/plugins/runtime/k8s/testdata/default-config.golden.yaml b/pkg/config/plugins/runtime/k8s/testdata/default-config.golden.yaml
index b6856b048..0bbc816ff 100644
--- a/pkg/config/plugins/runtime/k8s/testdata/default-config.golden.yaml
+++ b/pkg/config/plugins/runtime/k8s/testdata/default-config.golden.yaml
@@ -48,4 +48,5 @@ injector:
     enabled: true
     port: 15053
 marshalingCacheExpirationTime: 5m0s
+serviceAccountName: system:serviceaccount:kuma-system:kuma-control-plane
 controlPlaneServiceName: kuma-control-plane
diff --git a/pkg/core/managers/apis/mesh/mesh_manager_test.go b/pkg/core/managers/apis/mesh/mesh_manager_test.go
index 4917cd89d..eb9e1798b 100644
--- a/pkg/core/managers/apis/mesh/mesh_manager_test.go
+++ b/pkg/core/managers/apis/mesh/mesh_manager_test.go
@@ -107,10 +107,6 @@ var _ = Describe("Mesh Manager", func() {
 			// and Dataplane Token Signing Key for the mesh exists
 			err = secretManager.Get(context.Background(), system.NewSecretResource(), store.GetBy(issuer.SigningKeyResourceKey(issuer.DataplaneTokenPrefix, meshName)))
 			Expect(err).ToNot(HaveOccurred())
-
-			// and Envoy Admin Client Signing Key for the mesh exists
-			err = secretManager.Get(context.Background(), system.NewSecretResource(), store.GetBy(issuer.SigningKeyResourceKey(issuer.EnvoyAdminClientTokenPrefix, meshName)))
-			Expect(err).ToNot(HaveOccurred())
 		})
 
 		Describe("should set default values for Prometheus settings", func() {
@@ -231,7 +227,7 @@ var _ = Describe("Mesh Manager", func() {
 			secrets = &system.SecretResourceList{}
 			err = secretManager.List(context.Background(), secrets, store.ListByMesh("demo-2"))
 			Expect(err).ToNot(HaveOccurred())
-			Expect(secrets.Items).To(HaveLen(2)) // two default signing keys
+			Expect(secrets.Items).To(HaveLen(1)) // default signing key
 		})
 
 		It("should not delete Mesh if there are Dataplanes attached", func() {
diff --git a/pkg/defaults/mesh/mesh_test.go b/pkg/defaults/mesh/mesh_test.go
index 6d2284219..e2c96b66c 100644
--- a/pkg/defaults/mesh/mesh_test.go
+++ b/pkg/defaults/mesh/mesh_test.go
@@ -48,10 +48,6 @@ var _ = Describe("EnsureDefaultMeshResources", func() {
 		// and Dataplane Token Signing Key for the mesh exists
 		err = resManager.Get(context.Background(), system.NewSecretResource(), core_store.GetBy(issuer.SigningKeyResourceKey(issuer.DataplaneTokenPrefix, model.DefaultMesh)))
 		Expect(err).ToNot(HaveOccurred())
-
-		// and Envoy Admin Client Signing Key for the mesh exists
-		err = resManager.Get(context.Background(), system.NewSecretResource(), core_store.GetBy(issuer.SigningKeyResourceKey(issuer.EnvoyAdminClientTokenPrefix, model.DefaultMesh)))
-		Expect(err).ToNot(HaveOccurred())
 	})
 
 	It("should ignore subsequent calls to EnsureDefaultMeshResources", func() {
@@ -74,7 +70,5 @@ var _ = Describe("EnsureDefaultMeshResources", func() {
 		Expect(err).ToNot(HaveOccurred())
 		err = resManager.Get(context.Background(), system.NewSecretResource(), core_store.GetBy(issuer.SigningKeyResourceKey(issuer.DataplaneTokenPrefix, model.DefaultMesh)))
 		Expect(err).ToNot(HaveOccurred())
-		err = resManager.Get(context.Background(), system.NewSecretResource(), core_store.GetBy(issuer.SigningKeyResourceKey(issuer.EnvoyAdminClientTokenPrefix, model.DefaultMesh)))
-		Expect(err).ToNot(HaveOccurred())
 	})
 })
diff --git a/pkg/envoy/admin/admin_suite_test.go b/pkg/envoy/admin/admin_suite_test.go
deleted file mode 100644
index fda88bb0d..000000000
--- a/pkg/envoy/admin/admin_suite_test.go
+++ /dev/null
@@ -1,58 +0,0 @@
-package admin_test
-
-import (
-	"context"
-	"testing"
-
-	. "github.com/onsi/ginkgo"
-	. "github.com/onsi/gomega"
-
-	kuma_cp "github.com/kumahq/kuma/pkg/config/app/kuma-cp"
-	core_mesh "github.com/kumahq/kuma/pkg/core/resources/apis/mesh"
-	"github.com/kumahq/kuma/pkg/core/resources/model"
-	core_store "github.com/kumahq/kuma/pkg/core/resources/store"
-	"github.com/kumahq/kuma/pkg/defaults/mesh"
-	"github.com/kumahq/kuma/pkg/envoy/admin"
-	"github.com/kumahq/kuma/pkg/test"
-	"github.com/kumahq/kuma/pkg/test/runtime"
-)
-
-func TestEnvoyAdmin(t *testing.T) {
-	test.RunSpecs(t, "EnvoyAdmin Suite")
-}
-
-const (
-	testMesh    = "test-mesh"
-	anotherMesh = "another-mesh"
-)
-
-var eac admin.EnvoyAdminClient
-
-var _ = BeforeSuite(func() {
-
-	// setup the runtime
-	cfg := kuma_cp.DefaultConfig()
-	builder, err := runtime.BuilderFor(context.Background(), cfg)
-	Expect(err).ToNot(HaveOccurred())
-	runtime, err := builder.Build()
-	Expect(err).ToNot(HaveOccurred())
-	resManager := runtime.ResourceManager()
-	Expect(resManager).ToNot(BeNil())
-
-	// create mesh defaults
-	err = resManager.Create(context.Background(), core_mesh.NewMeshResource(), core_store.CreateByKey(testMesh, model.NoMesh))
-	Expect(err).ToNot(HaveOccurred())
-
-	err = mesh.EnsureDefaultMeshResources(runtime.ResourceManager(), testMesh)
-	Expect(err).ToNot(HaveOccurred())
-
-	err = resManager.Create(context.Background(), core_mesh.NewMeshResource(), core_store.CreateByKey(anotherMesh, model.NoMesh))
-	Expect(err).ToNot(HaveOccurred())
-
-	err = mesh.EnsureDefaultMeshResources(runtime.ResourceManager(), anotherMesh)
-	Expect(err).ToNot(HaveOccurred())
-
-	// setup the Envoy Admin Client
-	eac = admin.NewEnvoyAdminClient(resManager, runtime.Config())
-	Expect(eac).ToNot(BeNil())
-})
diff --git a/pkg/envoy/admin/client_test.go b/pkg/envoy/admin/client_test.go
deleted file mode 100644
index 6a96afce5..000000000
--- a/pkg/envoy/admin/client_test.go
+++ /dev/null
@@ -1,83 +0,0 @@
-package admin_test
-
-import (
-	. "github.com/onsi/ginkgo"
-	. "github.com/onsi/gomega"
-
-	core_mesh "github.com/kumahq/kuma/pkg/core/resources/apis/mesh"
-	test_model "github.com/kumahq/kuma/pkg/test/resources/model"
-)
-
-var _ = Describe("EnvoyAdminClient", func() {
-
-	Describe("GenerateAPIToken()", func() {
-		It("create and fetch same token for dp/mesh", func() {
-			// when
-			token1, err := eac.GenerateAPIToken(&core_mesh.DataplaneResource{
-				Meta: &test_model.ResourceMeta{
-					Name: "dp-1",
-					Mesh: testMesh,
-				},
-			})
-			Expect(err).ToNot(HaveOccurred())
-
-			// and
-			token2, err := eac.GenerateAPIToken(&core_mesh.DataplaneResource{
-				Meta: &test_model.ResourceMeta{
-					Name: "dp-1",
-					Mesh: testMesh,
-				},
-			})
-			Expect(err).ToNot(HaveOccurred())
-
-			// then
-			Expect(token1).To(Equal(token2))
-		})
-
-		It("two dps in same mesh", func() {
-			// when
-			token1, err := eac.GenerateAPIToken(&core_mesh.DataplaneResource{
-				Meta: &test_model.ResourceMeta{
-					Name: "dp-1",
-					Mesh: testMesh,
-				},
-			})
-			Expect(err).ToNot(HaveOccurred())
-
-			// and
-			token2, err := eac.GenerateAPIToken(&core_mesh.DataplaneResource{
-				Meta: &test_model.ResourceMeta{
-					Name: "dp-2",
-					Mesh: testMesh,
-				},
-			})
-			Expect(err).ToNot(HaveOccurred())
-
-			// then
-			Expect(token1).ToNot(Equal(token2))
-		})
-
-		It("two dps in two meshes", func() {
-			// when
-			token1, err := eac.GenerateAPIToken(&core_mesh.DataplaneResource{
-				Meta: &test_model.ResourceMeta{
-					Name: "dp-1",
-					Mesh: testMesh,
-				},
-			})
-			Expect(err).ToNot(HaveOccurred())
-
-			// and
-			token2, err := eac.GenerateAPIToken(&core_mesh.DataplaneResource{
-				Meta: &test_model.ResourceMeta{
-					Name: "dp-1",
-					Mesh: anotherMesh,
-				},
-			})
-			Expect(err).ToNot(HaveOccurred())
-
-			// then
-			Expect(token1).ToNot(Equal(token2))
-		})
-	})
-})
diff --git a/pkg/plugins/runtime/k8s/webhooks/validation_test.go b/pkg/plugins/runtime/k8s/webhooks/validation_test.go
index ff9aa9bbb..53c9520a6 100644
--- a/pkg/plugins/runtime/k8s/webhooks/validation_test.go
+++ b/pkg/plugins/runtime/k8s/webhooks/validation_test.go
@@ -44,7 +44,7 @@ var _ = Describe("Validation", func() {
 		func(given testCase) {
 			// given
 			webhook := &kube_admission.Webhook{
-				Handler: webhooks.NewValidatingWebhook(converter, core_registry.Global(), k8s_registry.Global(), given.mode, "kuma-system"),
+				Handler: webhooks.NewValidatingWebhook(converter, core_registry.Global(), k8s_registry.Global(), given.mode, "system:serviceaccount:kuma-system:kuma-control-plane"),
 			}
 			Expect(webhook.InjectScheme(scheme)).To(Succeed())
 
@@ -128,7 +128,7 @@ var _ = Describe("Validation", func() {
 		Entry("should pass validation for synced policy from Global to Zone", testCase{
 			mode:        core.Zone,
 			objTemplate: &mesh_proto.TrafficRoute{},
-			username:    "system:serviceaccount:kuma-system:mesh",
+			username:    "system:serviceaccount:kuma-system:kuma-control-plane",
 			obj: `
             {
               "apiVersion":"kuma.io/v1alpha1",
@@ -491,7 +491,7 @@ var _ = Describe("Validation", func() {
 		Entry("should fail validation on missing mesh object", testCase{
 			mode:        core.Zone,
 			objTemplate: &mesh_proto.TrafficRoute{},
-			username:    "system:serviceaccount:kuma-system:mesh",
+			username:    "system:serviceaccount:kuma-system:kuma-control-plane",
 			obj: `
             {
               "apiVersion":"kuma.io/v1alpha1",
diff --git a/pkg/xds/bootstrap/testdata/bootstrap.k8s.golden.yaml b/pkg/xds/bootstrap/testdata/bootstrap.k8s.golden.yaml
index 2527ce19a..a71059375 100644
--- a/pkg/xds/bootstrap/testdata/bootstrap.k8s.golden.yaml
+++ b/pkg/xds/bootstrap/testdata/bootstrap.k8s.golden.yaml
@@ -85,11 +85,8 @@ staticResources:
         commonTlsContext:
           tlsParams:
             tlsMinimumProtocolVersion: TLSv1_2
-          validationContext:
-            matchSubjectAltNames:
-            - exact: localhost
-            trustedCa:
-              inlineBytes: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURNekNDQWh1Z0F3SUJBZ0lRRGhsSW5mc1hZSGFtS04rMjlxblF2ekFOQmdrcWhraUc5dzBCQVFzRkFEQVAKTVEwd0N3WURWUVFERXdScmRXMWhNQjRYRFRJeE1EUXdNakV3TWpJeU5sb1hEVE14TURNek1URXdNakl5TmxvdwpEekVOTUFzR0ExVUVBeE1FYTNWdFlUQ0NBU0l3RFFZSktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0NnZ0VCCkFMNEdHZytlMk83ZUExMkYwRjZ2MnJyOGoyaVZTRktlcG5adEwxNWxyQ2RzNmxxSzUwc1hXT3c4UEtacDJpaEEKWEpWVFNaekthc3lMRFRBUjlWWVFqVHBFNTI2RXp2dGR0aFNhZ2YzMlFXVyt3WTZMTXBFZGV4S09PQ3gyc2U1NQpSZDk3TDMzeVlQZmdYMTVPWWxpSFBEMDU2ampob3RITGROMmxweTcrU1REdlF5Um5YQXU3M1lrWTM3RWQ0aEk0CnQvVjZzb0h5RUdOY0RobTlwNWZCR3F6MG5qQmJRa3AybFRZNS9rajQycUI3UTZyQ00ydGJQc0VNb29lQUF3NW0KaHlZNHhqMHRQOXVjcWxVejhnYys2bzhIRE5zdDhOZUpYWmt0V24rQ095dGpyL056R2dTMjJrdlNEcGhpc0pvdApvMEZ5b0lPZEF0eEMxcXhYWFIrWHVVVUNBd0VBQWFPQmlqQ0JoekFPQmdOVkhROEJBZjhFQkFNQ0FxUXdIUVlEClZSMGxCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQk1BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0hRWUQKVlIwT0JCWUVGS1JMa2dJelgvT2pLdzlpZGVwdVEvUk10VCtBTUNZR0ExVWRFUVFmTUIyQ0NXeHZZMkZzYUc5egpkSWNRL1FDaEl3QUFBQUFBQUFBQUFBQUFBVEFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBUHM1eUpaaG9ZbEdXCkNwQThkU0lTaXZNOC84aUJOUTNmVndQNjNmdDBFSkxNVkd1MlJGWjQvVUFKL3JVUFNHTjh4aFhTazUrMWQ1NmEKL2thSDlyWDBIYVJJSEhseEE3aVBVS3hBajQ0eDlMS21xUEhUb0wzWGxXWTFBWHp2aWNXOWQrR00yRmFRZWUrSQpsZWFxTGJ6MEFadmxudTI3MVoxQ2VhQUN1VTlHbGp1anZ5aVRURTluYUhVRXF2SGdTcFB0aWxKYWx5SjUveklsClo5RjArVVd0M1RPWU1zNWcrU0N0ME13SFROYmlzYm1ld3BjRkZKemp0Mmt2dHJjOXQ5ZGtGODF4aGNTMTl3N3EKaDFBZVAzUlJsTGw3YnY5RUFWWEVtSWF2aWgvMjlQQTNaU3krcGJZTlc3ak5KSGpNUTRoUTBFK3hjQ2F6VS9PNAp5cFdHYWFudlBnPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
+          validationContextSdsSecretConfig:
+            name: cp_validation_ctx
         sni: localhost
     type: STRICT_DNS
     upstreamConnectionOptions:
@@ -97,6 +94,13 @@ staticResources:
         keepaliveInterval: 10
         keepaliveProbes: 3
         keepaliveTime: 10
+  secrets:
+  - name: cp_validation_ctx
+    validationContext:
+      matchSubjectAltNames:
+      - exact: localhost
+      trustedCa:
+        inlineBytes: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURNekNDQWh1Z0F3SUJBZ0lRRGhsSW5mc1hZSGFtS04rMjlxblF2ekFOQmdrcWhraUc5dzBCQVFzRkFEQVAKTVEwd0N3WURWUVFERXdScmRXMWhNQjRYRFRJeE1EUXdNakV3TWpJeU5sb1hEVE14TURNek1URXdNakl5TmxvdwpEekVOTUFzR0ExVUVBeE1FYTNWdFlUQ0NBU0l3RFFZSktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0NnZ0VCCkFMNEdHZytlMk83ZUExMkYwRjZ2MnJyOGoyaVZTRktlcG5adEwxNWxyQ2RzNmxxSzUwc1hXT3c4UEtacDJpaEEKWEpWVFNaekthc3lMRFRBUjlWWVFqVHBFNTI2RXp2dGR0aFNhZ2YzMlFXVyt3WTZMTXBFZGV4S09PQ3gyc2U1NQpSZDk3TDMzeVlQZmdYMTVPWWxpSFBEMDU2ampob3RITGROMmxweTcrU1REdlF5Um5YQXU3M1lrWTM3RWQ0aEk0CnQvVjZzb0h5RUdOY0RobTlwNWZCR3F6MG5qQmJRa3AybFRZNS9rajQycUI3UTZyQ00ydGJQc0VNb29lQUF3NW0KaHlZNHhqMHRQOXVjcWxVejhnYys2bzhIRE5zdDhOZUpYWmt0V24rQ095dGpyL056R2dTMjJrdlNEcGhpc0pvdApvMEZ5b0lPZEF0eEMxcXhYWFIrWHVVVUNBd0VBQWFPQmlqQ0JoekFPQmdOVkhROEJBZjhFQkFNQ0FxUXdIUVlEClZSMGxCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQk1BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0hRWUQKVlIwT0JCWUVGS1JMa2dJelgvT2pLdzlpZGVwdVEvUk10VCtBTUNZR0ExVWRFUVFmTUIyQ0NXeHZZMkZzYUc5egpkSWNRL1FDaEl3QUFBQUFBQUFBQUFBQUFBVEFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBUHM1eUpaaG9ZbEdXCkNwQThkU0lTaXZNOC84aUJOUTNmVndQNjNmdDBFSkxNVkd1MlJGWjQvVUFKL3JVUFNHTjh4aFhTazUrMWQ1NmEKL2thSDlyWDBIYVJJSEhseEE3aVBVS3hBajQ0eDlMS21xUEhUb0wzWGxXWTFBWHp2aWNXOWQrR00yRmFRZWUrSQpsZWFxTGJ6MEFadmxudTI3MVoxQ2VhQUN1VTlHbGp1anZ5aVRURTluYUhVRXF2SGdTcFB0aWxKYWx5SjUveklsClo5RjArVVd0M1RPWU1zNWcrU0N0ME13SFROYmlzYm1ld3BjRkZKemp0Mmt2dHJjOXQ5ZGtGODF4aGNTMTl3N3EKaDFBZVAzUlJsTGw3YnY5RUFWWEVtSWF2aWgvMjlQQTNaU3krcGJZTlc3ak5KSGpNUTRoUTBFK3hjQ2F6VS9PNAp5cFdHYWFudlBnPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
 statsConfig:
   statsTags:
   - regex: ^grpc\.((.+)\.)
diff --git a/pkg/xds/bootstrap/testdata/bootstrap.overridden.golden.yaml b/pkg/xds/bootstrap/testdata/bootstrap.overridden.golden.yaml
index e4cd4d2e3..44849f1ef 100644
--- a/pkg/xds/bootstrap/testdata/bootstrap.overridden.golden.yaml
+++ b/pkg/xds/bootstrap/testdata/bootstrap.overridden.golden.yaml
@@ -92,11 +92,8 @@ staticResources:
         commonTlsContext:
           tlsParams:
             tlsMinimumProtocolVersion: TLSv1_2
-          validationContext:
-            matchSubjectAltNames:
-            - exact: localhost
-            trustedCa:
-              inlineBytes: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURNekNDQWh1Z0F3SUJBZ0lRRGhsSW5mc1hZSGFtS04rMjlxblF2ekFOQmdrcWhraUc5dzBCQVFzRkFEQVAKTVEwd0N3WURWUVFERXdScmRXMWhNQjRYRFRJeE1EUXdNakV3TWpJeU5sb1hEVE14TURNek1URXdNakl5TmxvdwpEekVOTUFzR0ExVUVBeE1FYTNWdFlUQ0NBU0l3RFFZSktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0NnZ0VCCkFMNEdHZytlMk83ZUExMkYwRjZ2MnJyOGoyaVZTRktlcG5adEwxNWxyQ2RzNmxxSzUwc1hXT3c4UEtacDJpaEEKWEpWVFNaekthc3lMRFRBUjlWWVFqVHBFNTI2RXp2dGR0aFNhZ2YzMlFXVyt3WTZMTXBFZGV4S09PQ3gyc2U1NQpSZDk3TDMzeVlQZmdYMTVPWWxpSFBEMDU2ampob3RITGROMmxweTcrU1REdlF5Um5YQXU3M1lrWTM3RWQ0aEk0CnQvVjZzb0h5RUdOY0RobTlwNWZCR3F6MG5qQmJRa3AybFRZNS9rajQycUI3UTZyQ00ydGJQc0VNb29lQUF3NW0KaHlZNHhqMHRQOXVjcWxVejhnYys2bzhIRE5zdDhOZUpYWmt0V24rQ095dGpyL056R2dTMjJrdlNEcGhpc0pvdApvMEZ5b0lPZEF0eEMxcXhYWFIrWHVVVUNBd0VBQWFPQmlqQ0JoekFPQmdOVkhROEJBZjhFQkFNQ0FxUXdIUVlEClZSMGxCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQk1BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0hRWUQKVlIwT0JCWUVGS1JMa2dJelgvT2pLdzlpZGVwdVEvUk10VCtBTUNZR0ExVWRFUVFmTUIyQ0NXeHZZMkZzYUc5egpkSWNRL1FDaEl3QUFBQUFBQUFBQUFBQUFBVEFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBUHM1eUpaaG9ZbEdXCkNwQThkU0lTaXZNOC84aUJOUTNmVndQNjNmdDBFSkxNVkd1MlJGWjQvVUFKL3JVUFNHTjh4aFhTazUrMWQ1NmEKL2thSDlyWDBIYVJJSEhseEE3aVBVS3hBajQ0eDlMS21xUEhUb0wzWGxXWTFBWHp2aWNXOWQrR00yRmFRZWUrSQpsZWFxTGJ6MEFadmxudTI3MVoxQ2VhQUN1VTlHbGp1anZ5aVRURTluYUhVRXF2SGdTcFB0aWxKYWx5SjUveklsClo5RjArVVd0M1RPWU1zNWcrU0N0ME13SFROYmlzYm1ld3BjRkZKemp0Mmt2dHJjOXQ5ZGtGODF4aGNTMTl3N3EKaDFBZVAzUlJsTGw3YnY5RUFWWEVtSWF2aWgvMjlQQTNaU3krcGJZTlc3ak5KSGpNUTRoUTBFK3hjQ2F6VS9PNAp5cFdHYWFudlBnPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
+          validationContextSdsSecretConfig:
+            name: cp_validation_ctx
         sni: localhost
     type: STRICT_DNS
     upstreamConnectionOptions:
@@ -104,6 +101,13 @@ staticResources:
         keepaliveInterval: 10
         keepaliveProbes: 3
         keepaliveTime: 10
+  secrets:
+  - name: cp_validation_ctx
+    validationContext:
+      matchSubjectAltNames:
+      - exact: localhost
+      trustedCa:
+        inlineBytes: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURNekNDQWh1Z0F3SUJBZ0lRRGhsSW5mc1hZSGFtS04rMjlxblF2ekFOQmdrcWhraUc5dzBCQVFzRkFEQVAKTVEwd0N3WURWUVFERXdScmRXMWhNQjRYRFRJeE1EUXdNakV3TWpJeU5sb1hEVE14TURNek1URXdNakl5TmxvdwpEekVOTUFzR0ExVUVBeE1FYTNWdFlUQ0NBU0l3RFFZSktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0NnZ0VCCkFMNEdHZytlMk83ZUExMkYwRjZ2MnJyOGoyaVZTRktlcG5adEwxNWxyQ2RzNmxxSzUwc1hXT3c4UEtacDJpaEEKWEpWVFNaekthc3lMRFRBUjlWWVFqVHBFNTI2RXp2dGR0aFNhZ2YzMlFXVyt3WTZMTXBFZGV4S09PQ3gyc2U1NQpSZDk3TDMzeVlQZmdYMTVPWWxpSFBEMDU2ampob3RITGROMmxweTcrU1REdlF5Um5YQXU3M1lrWTM3RWQ0aEk0CnQvVjZzb0h5RUdOY0RobTlwNWZCR3F6MG5qQmJRa3AybFRZNS9rajQycUI3UTZyQ00ydGJQc0VNb29lQUF3NW0KaHlZNHhqMHRQOXVjcWxVejhnYys2bzhIRE5zdDhOZUpYWmt0V24rQ095dGpyL056R2dTMjJrdlNEcGhpc0pvdApvMEZ5b0lPZEF0eEMxcXhYWFIrWHVVVUNBd0VBQWFPQmlqQ0JoekFPQmdOVkhROEJBZjhFQkFNQ0FxUXdIUVlEClZSMGxCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQk1BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0hRWUQKVlIwT0JCWUVGS1JMa2dJelgvT2pLdzlpZGVwdVEvUk10VCtBTUNZR0ExVWRFUVFmTUIyQ0NXeHZZMkZzYUc5egpkSWNRL1FDaEl3QUFBQUFBQUFBQUFBQUFBVEFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBUHM1eUpaaG9ZbEdXCkNwQThkU0lTaXZNOC84aUJOUTNmVndQNjNmdDBFSkxNVkd1MlJGWjQvVUFKL3JVUFNHTjh4aFhTazUrMWQ1NmEKL2thSDlyWDBIYVJJSEhseEE3aVBVS3hBajQ0eDlMS21xUEhUb0wzWGxXWTFBWHp2aWNXOWQrR00yRmFRZWUrSQpsZWFxTGJ6MEFadmxudTI3MVoxQ2VhQUN1VTlHbGp1anZ5aVRURTluYUhVRXF2SGdTcFB0aWxKYWx5SjUveklsClo5RjArVVd0M1RPWU1zNWcrU0N0ME13SFROYmlzYm1ld3BjRkZKemp0Mmt2dHJjOXQ5ZGtGODF4aGNTMTl3N3EKaDFBZVAzUlJsTGw3YnY5RUFWWEVtSWF2aWgvMjlQQTNaU3krcGJZTlc3ak5KSGpNUTRoUTBFK3hjQ2F6VS9PNAp5cFdHYWFudlBnPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
 statsConfig:
   statsTags:
   - regex: ^grpc\.((.+)\.)
diff --git a/pkg/xds/bootstrap/testdata/bootstrap.universal.golden.yaml b/pkg/xds/bootstrap/testdata/bootstrap.universal.golden.yaml
index d05d54c52..cf4d250bf 100644
--- a/pkg/xds/bootstrap/testdata/bootstrap.universal.golden.yaml
+++ b/pkg/xds/bootstrap/testdata/bootstrap.universal.golden.yaml
@@ -85,11 +85,8 @@ staticResources:
         commonTlsContext:
           tlsParams:
             tlsMinimumProtocolVersion: TLSv1_2
-          validationContext:
-            matchSubjectAltNames:
-            - exact: localhost
-            trustedCa:
-              inlineBytes: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURNekNDQWh1Z0F3SUJBZ0lRRGhsSW5mc1hZSGFtS04rMjlxblF2ekFOQmdrcWhraUc5dzBCQVFzRkFEQVAKTVEwd0N3WURWUVFERXdScmRXMWhNQjRYRFRJeE1EUXdNakV3TWpJeU5sb1hEVE14TURNek1URXdNakl5TmxvdwpEekVOTUFzR0ExVUVBeE1FYTNWdFlUQ0NBU0l3RFFZSktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0NnZ0VCCkFMNEdHZytlMk83ZUExMkYwRjZ2MnJyOGoyaVZTRktlcG5adEwxNWxyQ2RzNmxxSzUwc1hXT3c4UEtacDJpaEEKWEpWVFNaekthc3lMRFRBUjlWWVFqVHBFNTI2RXp2dGR0aFNhZ2YzMlFXVyt3WTZMTXBFZGV4S09PQ3gyc2U1NQpSZDk3TDMzeVlQZmdYMTVPWWxpSFBEMDU2ampob3RITGROMmxweTcrU1REdlF5Um5YQXU3M1lrWTM3RWQ0aEk0CnQvVjZzb0h5RUdOY0RobTlwNWZCR3F6MG5qQmJRa3AybFRZNS9rajQycUI3UTZyQ00ydGJQc0VNb29lQUF3NW0KaHlZNHhqMHRQOXVjcWxVejhnYys2bzhIRE5zdDhOZUpYWmt0V24rQ095dGpyL056R2dTMjJrdlNEcGhpc0pvdApvMEZ5b0lPZEF0eEMxcXhYWFIrWHVVVUNBd0VBQWFPQmlqQ0JoekFPQmdOVkhROEJBZjhFQkFNQ0FxUXdIUVlEClZSMGxCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQk1BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0hRWUQKVlIwT0JCWUVGS1JMa2dJelgvT2pLdzlpZGVwdVEvUk10VCtBTUNZR0ExVWRFUVFmTUIyQ0NXeHZZMkZzYUc5egpkSWNRL1FDaEl3QUFBQUFBQUFBQUFBQUFBVEFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBUHM1eUpaaG9ZbEdXCkNwQThkU0lTaXZNOC84aUJOUTNmVndQNjNmdDBFSkxNVkd1MlJGWjQvVUFKL3JVUFNHTjh4aFhTazUrMWQ1NmEKL2thSDlyWDBIYVJJSEhseEE3aVBVS3hBajQ0eDlMS21xUEhUb0wzWGxXWTFBWHp2aWNXOWQrR00yRmFRZWUrSQpsZWFxTGJ6MEFadmxudTI3MVoxQ2VhQUN1VTlHbGp1anZ5aVRURTluYUhVRXF2SGdTcFB0aWxKYWx5SjUveklsClo5RjArVVd0M1RPWU1zNWcrU0N0ME13SFROYmlzYm1ld3BjRkZKemp0Mmt2dHJjOXQ5ZGtGODF4aGNTMTl3N3EKaDFBZVAzUlJsTGw3YnY5RUFWWEVtSWF2aWgvMjlQQTNaU3krcGJZTlc3ak5KSGpNUTRoUTBFK3hjQ2F6VS9PNAp5cFdHYWFudlBnPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
+          validationContextSdsSecretConfig:
+            name: cp_validation_ctx
         sni: localhost
     type: STRICT_DNS
     upstreamConnectionOptions:
@@ -97,6 +94,13 @@ staticResources:
         keepaliveInterval: 10
         keepaliveProbes: 3
         keepaliveTime: 10
+  secrets:
+  - name: cp_validation_ctx
+    validationContext:
+      matchSubjectAltNames:
+      - exact: localhost
+      trustedCa:
+        inlineBytes: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURNekNDQWh1Z0F3SUJBZ0lRRGhsSW5mc1hZSGFtS04rMjlxblF2ekFOQmdrcWhraUc5dzBCQVFzRkFEQVAKTVEwd0N3WURWUVFERXdScmRXMWhNQjRYRFRJeE1EUXdNakV3TWpJeU5sb1hEVE14TURNek1URXdNakl5TmxvdwpEekVOTUFzR0ExVUVBeE1FYTNWdFlUQ0NBU0l3RFFZSktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0NnZ0VCCkFMNEdHZytlMk83ZUExMkYwRjZ2MnJyOGoyaVZTRktlcG5adEwxNWxyQ2RzNmxxSzUwc1hXT3c4UEtacDJpaEEKWEpWVFNaekthc3lMRFRBUjlWWVFqVHBFNTI2RXp2dGR0aFNhZ2YzMlFXVyt3WTZMTXBFZGV4S09PQ3gyc2U1NQpSZDk3TDMzeVlQZmdYMTVPWWxpSFBEMDU2ampob3RITGROMmxweTcrU1REdlF5Um5YQXU3M1lrWTM3RWQ0aEk0CnQvVjZzb0h5RUdOY0RobTlwNWZCR3F6MG5qQmJRa3AybFRZNS9rajQycUI3UTZyQ00ydGJQc0VNb29lQUF3NW0KaHlZNHhqMHRQOXVjcWxVejhnYys2bzhIRE5zdDhOZUpYWmt0V24rQ095dGpyL056R2dTMjJrdlNEcGhpc0pvdApvMEZ5b0lPZEF0eEMxcXhYWFIrWHVVVUNBd0VBQWFPQmlqQ0JoekFPQmdOVkhROEJBZjhFQkFNQ0FxUXdIUVlEClZSMGxCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQk1BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0hRWUQKVlIwT0JCWUVGS1JMa2dJelgvT2pLdzlpZGVwdVEvUk10VCtBTUNZR0ExVWRFUVFmTUIyQ0NXeHZZMkZzYUc5egpkSWNRL1FDaEl3QUFBQUFBQUFBQUFBQUFBVEFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBUHM1eUpaaG9ZbEdXCkNwQThkU0lTaXZNOC84aUJOUTNmVndQNjNmdDBFSkxNVkd1MlJGWjQvVUFKL3JVUFNHTjh4aFhTazUrMWQ1NmEKL2thSDlyWDBIYVJJSEhseEE3aVBVS3hBajQ0eDlMS21xUEhUb0wzWGxXWTFBWHp2aWNXOWQrR00yRmFRZWUrSQpsZWFxTGJ6MEFadmxudTI3MVoxQ2VhQUN1VTlHbGp1anZ5aVRURTluYUhVRXF2SGdTcFB0aWxKYWx5SjUveklsClo5RjArVVd0M1RPWU1zNWcrU0N0ME13SFROYmlzYm1ld3BjRkZKemp0Mmt2dHJjOXQ5ZGtGODF4aGNTMTl3N3EKaDFBZVAzUlJsTGw3YnY5RUFWWEVtSWF2aWgvMjlQQTNaU3krcGJZTlc3ak5KSGpNUTRoUTBFK3hjQ2F6VS9PNAp5cFdHYWFudlBnPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
 statsConfig:
   statsTags:
   - regex: ^grpc\.((.+)\.)
diff --git a/pkg/xds/bootstrap/testdata/generator.custom-config-minimal-request.golden.yaml b/pkg/xds/bootstrap/testdata/generator.custom-config-minimal-request.golden.yaml
index 780b60fb1..ee967287a 100644
--- a/pkg/xds/bootstrap/testdata/generator.custom-config-minimal-request.golden.yaml
+++ b/pkg/xds/bootstrap/testdata/generator.custom-config-minimal-request.golden.yaml
@@ -86,11 +86,8 @@ staticResources:
         commonTlsContext:
           tlsParams:
             tlsMinimumProtocolVersion: TLSv1_2
-          validationContext:
-            matchSubjectAltNames:
-            - exact: localhost
-            trustedCa:
-              inlineBytes: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURNekNDQWh1Z0F3SUJBZ0lRRGhsSW5mc1hZSGFtS04rMjlxblF2ekFOQmdrcWhraUc5dzBCQVFzRkFEQVAKTVEwd0N3WURWUVFERXdScmRXMWhNQjRYRFRJeE1EUXdNakV3TWpJeU5sb1hEVE14TURNek1URXdNakl5TmxvdwpEekVOTUFzR0ExVUVBeE1FYTNWdFlUQ0NBU0l3RFFZSktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0NnZ0VCCkFMNEdHZytlMk83ZUExMkYwRjZ2MnJyOGoyaVZTRktlcG5adEwxNWxyQ2RzNmxxSzUwc1hXT3c4UEtacDJpaEEKWEpWVFNaekthc3lMRFRBUjlWWVFqVHBFNTI2RXp2dGR0aFNhZ2YzMlFXVyt3WTZMTXBFZGV4S09PQ3gyc2U1NQpSZDk3TDMzeVlQZmdYMTVPWWxpSFBEMDU2ampob3RITGROMmxweTcrU1REdlF5Um5YQXU3M1lrWTM3RWQ0aEk0CnQvVjZzb0h5RUdOY0RobTlwNWZCR3F6MG5qQmJRa3AybFRZNS9rajQycUI3UTZyQ00ydGJQc0VNb29lQUF3NW0KaHlZNHhqMHRQOXVjcWxVejhnYys2bzhIRE5zdDhOZUpYWmt0V24rQ095dGpyL056R2dTMjJrdlNEcGhpc0pvdApvMEZ5b0lPZEF0eEMxcXhYWFIrWHVVVUNBd0VBQWFPQmlqQ0JoekFPQmdOVkhROEJBZjhFQkFNQ0FxUXdIUVlEClZSMGxCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQk1BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0hRWUQKVlIwT0JCWUVGS1JMa2dJelgvT2pLdzlpZGVwdVEvUk10VCtBTUNZR0ExVWRFUVFmTUIyQ0NXeHZZMkZzYUc5egpkSWNRL1FDaEl3QUFBQUFBQUFBQUFBQUFBVEFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBUHM1eUpaaG9ZbEdXCkNwQThkU0lTaXZNOC84aUJOUTNmVndQNjNmdDBFSkxNVkd1MlJGWjQvVUFKL3JVUFNHTjh4aFhTazUrMWQ1NmEKL2thSDlyWDBIYVJJSEhseEE3aVBVS3hBajQ0eDlMS21xUEhUb0wzWGxXWTFBWHp2aWNXOWQrR00yRmFRZWUrSQpsZWFxTGJ6MEFadmxudTI3MVoxQ2VhQUN1VTlHbGp1anZ5aVRURTluYUhVRXF2SGdTcFB0aWxKYWx5SjUveklsClo5RjArVVd0M1RPWU1zNWcrU0N0ME13SFROYmlzYm1ld3BjRkZKemp0Mmt2dHJjOXQ5ZGtGODF4aGNTMTl3N3EKaDFBZVAzUlJsTGw3YnY5RUFWWEVtSWF2aWgvMjlQQTNaU3krcGJZTlc3ak5KSGpNUTRoUTBFK3hjQ2F6VS9PNAp5cFdHYWFudlBnPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
+          validationContextSdsSecretConfig:
+            name: cp_validation_ctx
         sni: localhost
     type: STRICT_DNS
     upstreamConnectionOptions:
@@ -98,6 +95,13 @@ staticResources:
         keepaliveInterval: 10
         keepaliveProbes: 3
         keepaliveTime: 10
+  secrets:
+  - name: cp_validation_ctx
+    validationContext:
+      matchSubjectAltNames:
+      - exact: localhost
+      trustedCa:
+        inlineBytes: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURNekNDQWh1Z0F3SUJBZ0lRRGhsSW5mc1hZSGFtS04rMjlxblF2ekFOQmdrcWhraUc5dzBCQVFzRkFEQVAKTVEwd0N3WURWUVFERXdScmRXMWhNQjRYRFRJeE1EUXdNakV3TWpJeU5sb1hEVE14TURNek1URXdNakl5TmxvdwpEekVOTUFzR0ExVUVBeE1FYTNWdFlUQ0NBU0l3RFFZSktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0NnZ0VCCkFMNEdHZytlMk83ZUExMkYwRjZ2MnJyOGoyaVZTRktlcG5adEwxNWxyQ2RzNmxxSzUwc1hXT3c4UEtacDJpaEEKWEpWVFNaekthc3lMRFRBUjlWWVFqVHBFNTI2RXp2dGR0aFNhZ2YzMlFXVyt3WTZMTXBFZGV4S09PQ3gyc2U1NQpSZDk3TDMzeVlQZmdYMTVPWWxpSFBEMDU2ampob3RITGROMmxweTcrU1REdlF5Um5YQXU3M1lrWTM3RWQ0aEk0CnQvVjZzb0h5RUdOY0RobTlwNWZCR3F6MG5qQmJRa3AybFRZNS9rajQycUI3UTZyQ00ydGJQc0VNb29lQUF3NW0KaHlZNHhqMHRQOXVjcWxVejhnYys2bzhIRE5zdDhOZUpYWmt0V24rQ095dGpyL056R2dTMjJrdlNEcGhpc0pvdApvMEZ5b0lPZEF0eEMxcXhYWFIrWHVVVUNBd0VBQWFPQmlqQ0JoekFPQmdOVkhROEJBZjhFQkFNQ0FxUXdIUVlEClZSMGxCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQk1BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0hRWUQKVlIwT0JCWUVGS1JMa2dJelgvT2pLdzlpZGVwdVEvUk10VCtBTUNZR0ExVWRFUVFmTUIyQ0NXeHZZMkZzYUc5egpkSWNRL1FDaEl3QUFBQUFBQUFBQUFBQUFBVEFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBUHM1eUpaaG9ZbEdXCkNwQThkU0lTaXZNOC84aUJOUTNmVndQNjNmdDBFSkxNVkd1MlJGWjQvVUFKL3JVUFNHTjh4aFhTazUrMWQ1NmEKL2thSDlyWDBIYVJJSEhseEE3aVBVS3hBajQ0eDlMS21xUEhUb0wzWGxXWTFBWHp2aWNXOWQrR00yRmFRZWUrSQpsZWFxTGJ6MEFadmxudTI3MVoxQ2VhQUN1VTlHbGp1anZ5aVRURTluYUhVRXF2SGdTcFB0aWxKYWx5SjUveklsClo5RjArVVd0M1RPWU1zNWcrU0N0ME13SFROYmlzYm1ld3BjRkZKemp0Mmt2dHJjOXQ5ZGtGODF4aGNTMTl3N3EKaDFBZVAzUlJsTGw3YnY5RUFWWEVtSWF2aWgvMjlQQTNaU3krcGJZTlc3ak5KSGpNUTRoUTBFK3hjQ2F6VS9PNAp5cFdHYWFudlBnPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
 statsConfig:
   statsTags:
   - regex: ^grpc\.((.+)\.)
diff --git a/pkg/xds/bootstrap/testdata/generator.custom-config.golden.yaml b/pkg/xds/bootstrap/testdata/generator.custom-config.golden.yaml
index 42b5dd124..c02dd1832 100644
--- a/pkg/xds/bootstrap/testdata/generator.custom-config.golden.yaml
+++ b/pkg/xds/bootstrap/testdata/generator.custom-config.golden.yaml
@@ -116,11 +116,8 @@ staticResources:
         commonTlsContext:
           tlsParams:
             tlsMinimumProtocolVersion: TLSv1_2
-          validationContext:
-            matchSubjectAltNames:
-            - exact: localhost
-            trustedCa:
-              inlineBytes: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURNekNDQWh1Z0F3SUJBZ0lRRGhsSW5mc1hZSGFtS04rMjlxblF2ekFOQmdrcWhraUc5dzBCQVFzRkFEQVAKTVEwd0N3WURWUVFERXdScmRXMWhNQjRYRFRJeE1EUXdNakV3TWpJeU5sb1hEVE14TURNek1URXdNakl5TmxvdwpEekVOTUFzR0ExVUVBeE1FYTNWdFlUQ0NBU0l3RFFZSktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0NnZ0VCCkFMNEdHZytlMk83ZUExMkYwRjZ2MnJyOGoyaVZTRktlcG5adEwxNWxyQ2RzNmxxSzUwc1hXT3c4UEtacDJpaEEKWEpWVFNaekthc3lMRFRBUjlWWVFqVHBFNTI2RXp2dGR0aFNhZ2YzMlFXVyt3WTZMTXBFZGV4S09PQ3gyc2U1NQpSZDk3TDMzeVlQZmdYMTVPWWxpSFBEMDU2ampob3RITGROMmxweTcrU1REdlF5Um5YQXU3M1lrWTM3RWQ0aEk0CnQvVjZzb0h5RUdOY0RobTlwNWZCR3F6MG5qQmJRa3AybFRZNS9rajQycUI3UTZyQ00ydGJQc0VNb29lQUF3NW0KaHlZNHhqMHRQOXVjcWxVejhnYys2bzhIRE5zdDhOZUpYWmt0V24rQ095dGpyL056R2dTMjJrdlNEcGhpc0pvdApvMEZ5b0lPZEF0eEMxcXhYWFIrWHVVVUNBd0VBQWFPQmlqQ0JoekFPQmdOVkhROEJBZjhFQkFNQ0FxUXdIUVlEClZSMGxCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQk1BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0hRWUQKVlIwT0JCWUVGS1JMa2dJelgvT2pLdzlpZGVwdVEvUk10VCtBTUNZR0ExVWRFUVFmTUIyQ0NXeHZZMkZzYUc5egpkSWNRL1FDaEl3QUFBQUFBQUFBQUFBQUFBVEFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBUHM1eUpaaG9ZbEdXCkNwQThkU0lTaXZNOC84aUJOUTNmVndQNjNmdDBFSkxNVkd1MlJGWjQvVUFKL3JVUFNHTjh4aFhTazUrMWQ1NmEKL2thSDlyWDBIYVJJSEhseEE3aVBVS3hBajQ0eDlMS21xUEhUb0wzWGxXWTFBWHp2aWNXOWQrR00yRmFRZWUrSQpsZWFxTGJ6MEFadmxudTI3MVoxQ2VhQUN1VTlHbGp1anZ5aVRURTluYUhVRXF2SGdTcFB0aWxKYWx5SjUveklsClo5RjArVVd0M1RPWU1zNWcrU0N0ME13SFROYmlzYm1ld3BjRkZKemp0Mmt2dHJjOXQ5ZGtGODF4aGNTMTl3N3EKaDFBZVAzUlJsTGw3YnY5RUFWWEVtSWF2aWgvMjlQQTNaU3krcGJZTlc3ak5KSGpNUTRoUTBFK3hjQ2F6VS9PNAp5cFdHYWFudlBnPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
+          validationContextSdsSecretConfig:
+            name: cp_validation_ctx
         sni: localhost
     type: STRICT_DNS
     upstreamConnectionOptions:
@@ -128,6 +125,13 @@ staticResources:
         keepaliveInterval: 10
         keepaliveProbes: 3
         keepaliveTime: 10
+  secrets:
+  - name: cp_validation_ctx
+    validationContext:
+      matchSubjectAltNames:
+      - exact: localhost
+      trustedCa:
+        inlineBytes: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURNekNDQWh1Z0F3SUJBZ0lRRGhsSW5mc1hZSGFtS04rMjlxblF2ekFOQmdrcWhraUc5dzBCQVFzRkFEQVAKTVEwd0N3WURWUVFERXdScmRXMWhNQjRYRFRJeE1EUXdNakV3TWpJeU5sb1hEVE14TURNek1URXdNakl5TmxvdwpEekVOTUFzR0ExVUVBeE1FYTNWdFlUQ0NBU0l3RFFZSktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0NnZ0VCCkFMNEdHZytlMk83ZUExMkYwRjZ2MnJyOGoyaVZTRktlcG5adEwxNWxyQ2RzNmxxSzUwc1hXT3c4UEtacDJpaEEKWEpWVFNaekthc3lMRFRBUjlWWVFqVHBFNTI2RXp2dGR0aFNhZ2YzMlFXVyt3WTZMTXBFZGV4S09PQ3gyc2U1NQpSZDk3TDMzeVlQZmdYMTVPWWxpSFBEMDU2ampob3RITGROMmxweTcrU1REdlF5Um5YQXU3M1lrWTM3RWQ0aEk0CnQvVjZzb0h5RUdOY0RobTlwNWZCR3F6MG5qQmJRa3AybFRZNS9rajQycUI3UTZyQ00ydGJQc0VNb29lQUF3NW0KaHlZNHhqMHRQOXVjcWxVejhnYys2bzhIRE5zdDhOZUpYWmt0V24rQ095dGpyL056R2dTMjJrdlNEcGhpc0pvdApvMEZ5b0lPZEF0eEMxcXhYWFIrWHVVVUNBd0VBQWFPQmlqQ0JoekFPQmdOVkhROEJBZjhFQkFNQ0FxUXdIUVlEClZSMGxCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQk1BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0hRWUQKVlIwT0JCWUVGS1JMa2dJelgvT2pLdzlpZGVwdVEvUk10VCtBTUNZR0ExVWRFUVFmTUIyQ0NXeHZZMkZzYUc5egpkSWNRL1FDaEl3QUFBQUFBQUFBQUFBQUFBVEFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBUHM1eUpaaG9ZbEdXCkNwQThkU0lTaXZNOC84aUJOUTNmVndQNjNmdDBFSkxNVkd1MlJGWjQvVUFKL3JVUFNHTjh4aFhTazUrMWQ1NmEKL2thSDlyWDBIYVJJSEhseEE3aVBVS3hBajQ0eDlMS21xUEhUb0wzWGxXWTFBWHp2aWNXOWQrR00yRmFRZWUrSQpsZWFxTGJ6MEFadmxudTI3MVoxQ2VhQUN1VTlHbGp1anZ5aVRURTluYUhVRXF2SGdTcFB0aWxKYWx5SjUveklsClo5RjArVVd0M1RPWU1zNWcrU0N0ME13SFROYmlzYm1ld3BjRkZKemp0Mmt2dHJjOXQ5ZGtGODF4aGNTMTl3N3EKaDFBZVAzUlJsTGw3YnY5RUFWWEVtSWF2aWgvMjlQQTNaU3krcGJZTlc3ak5KSGpNUTRoUTBFK3hjQ2F6VS9PNAp5cFdHYWFudlBnPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
 statsConfig:
   statsTags:
   - regex: ^grpc\.((.+)\.)
diff --git a/pkg/xds/bootstrap/testdata/generator.default-config-minimal-request.golden.yaml b/pkg/xds/bootstrap/testdata/generator.default-config-minimal-request.golden.yaml
index 11f68b6b2..61ac5e057 100644
--- a/pkg/xds/bootstrap/testdata/generator.default-config-minimal-request.golden.yaml
+++ b/pkg/xds/bootstrap/testdata/generator.default-config-minimal-request.golden.yaml
@@ -79,11 +79,8 @@ staticResources:
         commonTlsContext:
           tlsParams:
             tlsMinimumProtocolVersion: TLSv1_2
-          validationContext:
-            matchSubjectAltNames:
-            - exact: localhost
-            trustedCa:
-              inlineBytes: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURNekNDQWh1Z0F3SUJBZ0lRRGhsSW5mc1hZSGFtS04rMjlxblF2ekFOQmdrcWhraUc5dzBCQVFzRkFEQVAKTVEwd0N3WURWUVFERXdScmRXMWhNQjRYRFRJeE1EUXdNakV3TWpJeU5sb1hEVE14TURNek1URXdNakl5TmxvdwpEekVOTUFzR0ExVUVBeE1FYTNWdFlUQ0NBU0l3RFFZSktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0NnZ0VCCkFMNEdHZytlMk83ZUExMkYwRjZ2MnJyOGoyaVZTRktlcG5adEwxNWxyQ2RzNmxxSzUwc1hXT3c4UEtacDJpaEEKWEpWVFNaekthc3lMRFRBUjlWWVFqVHBFNTI2RXp2dGR0aFNhZ2YzMlFXVyt3WTZMTXBFZGV4S09PQ3gyc2U1NQpSZDk3TDMzeVlQZmdYMTVPWWxpSFBEMDU2ampob3RITGROMmxweTcrU1REdlF5Um5YQXU3M1lrWTM3RWQ0aEk0CnQvVjZzb0h5RUdOY0RobTlwNWZCR3F6MG5qQmJRa3AybFRZNS9rajQycUI3UTZyQ00ydGJQc0VNb29lQUF3NW0KaHlZNHhqMHRQOXVjcWxVejhnYys2bzhIRE5zdDhOZUpYWmt0V24rQ095dGpyL056R2dTMjJrdlNEcGhpc0pvdApvMEZ5b0lPZEF0eEMxcXhYWFIrWHVVVUNBd0VBQWFPQmlqQ0JoekFPQmdOVkhROEJBZjhFQkFNQ0FxUXdIUVlEClZSMGxCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQk1BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0hRWUQKVlIwT0JCWUVGS1JMa2dJelgvT2pLdzlpZGVwdVEvUk10VCtBTUNZR0ExVWRFUVFmTUIyQ0NXeHZZMkZzYUc5egpkSWNRL1FDaEl3QUFBQUFBQUFBQUFBQUFBVEFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBUHM1eUpaaG9ZbEdXCkNwQThkU0lTaXZNOC84aUJOUTNmVndQNjNmdDBFSkxNVkd1MlJGWjQvVUFKL3JVUFNHTjh4aFhTazUrMWQ1NmEKL2thSDlyWDBIYVJJSEhseEE3aVBVS3hBajQ0eDlMS21xUEhUb0wzWGxXWTFBWHp2aWNXOWQrR00yRmFRZWUrSQpsZWFxTGJ6MEFadmxudTI3MVoxQ2VhQUN1VTlHbGp1anZ5aVRURTluYUhVRXF2SGdTcFB0aWxKYWx5SjUveklsClo5RjArVVd0M1RPWU1zNWcrU0N0ME13SFROYmlzYm1ld3BjRkZKemp0Mmt2dHJjOXQ5ZGtGODF4aGNTMTl3N3EKaDFBZVAzUlJsTGw3YnY5RUFWWEVtSWF2aWgvMjlQQTNaU3krcGJZTlc3ak5KSGpNUTRoUTBFK3hjQ2F6VS9PNAp5cFdHYWFudlBnPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
+          validationContextSdsSecretConfig:
+            name: cp_validation_ctx
         sni: localhost
     type: STRICT_DNS
     upstreamConnectionOptions:
@@ -91,6 +88,13 @@ staticResources:
         keepaliveInterval: 10
         keepaliveProbes: 3
         keepaliveTime: 10
+  secrets:
+  - name: cp_validation_ctx
+    validationContext:
+      matchSubjectAltNames:
+      - exact: localhost
+      trustedCa:
+        inlineBytes: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURNekNDQWh1Z0F3SUJBZ0lRRGhsSW5mc1hZSGFtS04rMjlxblF2ekFOQmdrcWhraUc5dzBCQVFzRkFEQVAKTVEwd0N3WURWUVFERXdScmRXMWhNQjRYRFRJeE1EUXdNakV3TWpJeU5sb1hEVE14TURNek1URXdNakl5TmxvdwpEekVOTUFzR0ExVUVBeE1FYTNWdFlUQ0NBU0l3RFFZSktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0NnZ0VCCkFMNEdHZytlMk83ZUExMkYwRjZ2MnJyOGoyaVZTRktlcG5adEwxNWxyQ2RzNmxxSzUwc1hXT3c4UEtacDJpaEEKWEpWVFNaekthc3lMRFRBUjlWWVFqVHBFNTI2RXp2dGR0aFNhZ2YzMlFXVyt3WTZMTXBFZGV4S09PQ3gyc2U1NQpSZDk3TDMzeVlQZmdYMTVPWWxpSFBEMDU2ampob3RITGROMmxweTcrU1REdlF5Um5YQXU3M1lrWTM3RWQ0aEk0CnQvVjZzb0h5RUdOY0RobTlwNWZCR3F6MG5qQmJRa3AybFRZNS9rajQycUI3UTZyQ00ydGJQc0VNb29lQUF3NW0KaHlZNHhqMHRQOXVjcWxVejhnYys2bzhIRE5zdDhOZUpYWmt0V24rQ095dGpyL056R2dTMjJrdlNEcGhpc0pvdApvMEZ5b0lPZEF0eEMxcXhYWFIrWHVVVUNBd0VBQWFPQmlqQ0JoekFPQmdOVkhROEJBZjhFQkFNQ0FxUXdIUVlEClZSMGxCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQk1BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0hRWUQKVlIwT0JCWUVGS1JMa2dJelgvT2pLdzlpZGVwdVEvUk10VCtBTUNZR0ExVWRFUVFmTUIyQ0NXeHZZMkZzYUc5egpkSWNRL1FDaEl3QUFBQUFBQUFBQUFBQUFBVEFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBUHM1eUpaaG9ZbEdXCkNwQThkU0lTaXZNOC84aUJOUTNmVndQNjNmdDBFSkxNVkd1MlJGWjQvVUFKL3JVUFNHTjh4aFhTazUrMWQ1NmEKL2thSDlyWDBIYVJJSEhseEE3aVBVS3hBajQ0eDlMS21xUEhUb0wzWGxXWTFBWHp2aWNXOWQrR00yRmFRZWUrSQpsZWFxTGJ6MEFadmxudTI3MVoxQ2VhQUN1VTlHbGp1anZ5aVRURTluYUhVRXF2SGdTcFB0aWxKYWx5SjUveklsClo5RjArVVd0M1RPWU1zNWcrU0N0ME13SFROYmlzYm1ld3BjRkZKemp0Mmt2dHJjOXQ5ZGtGODF4aGNTMTl3N3EKaDFBZVAzUlJsTGw3YnY5RUFWWEVtSWF2aWgvMjlQQTNaU3krcGJZTlc3ak5KSGpNUTRoUTBFK3hjQ2F6VS9PNAp5cFdHYWFudlBnPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
 statsConfig:
   statsTags:
   - regex: ^grpc\.((.+)\.)
diff --git a/pkg/xds/bootstrap/testdata/generator.default-config.golden.yaml b/pkg/xds/bootstrap/testdata/generator.default-config.golden.yaml
index b13d85060..1b607bc5f 100644
--- a/pkg/xds/bootstrap/testdata/generator.default-config.golden.yaml
+++ b/pkg/xds/bootstrap/testdata/generator.default-config.golden.yaml
@@ -94,11 +94,8 @@ staticResources:
         commonTlsContext:
           tlsParams:
             tlsMinimumProtocolVersion: TLSv1_2
-          validationContext:
-            matchSubjectAltNames:
-            - exact: localhost
-            trustedCa:
-              inlineBytes: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURNekNDQWh1Z0F3SUJBZ0lRRGhsSW5mc1hZSGFtS04rMjlxblF2ekFOQmdrcWhraUc5dzBCQVFzRkFEQVAKTVEwd0N3WURWUVFERXdScmRXMWhNQjRYRFRJeE1EUXdNakV3TWpJeU5sb1hEVE14TURNek1URXdNakl5TmxvdwpEekVOTUFzR0ExVUVBeE1FYTNWdFlUQ0NBU0l3RFFZSktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0NnZ0VCCkFMNEdHZytlMk83ZUExMkYwRjZ2MnJyOGoyaVZTRktlcG5adEwxNWxyQ2RzNmxxSzUwc1hXT3c4UEtacDJpaEEKWEpWVFNaekthc3lMRFRBUjlWWVFqVHBFNTI2RXp2dGR0aFNhZ2YzMlFXVyt3WTZMTXBFZGV4S09PQ3gyc2U1NQpSZDk3TDMzeVlQZmdYMTVPWWxpSFBEMDU2ampob3RITGROMmxweTcrU1REdlF5Um5YQXU3M1lrWTM3RWQ0aEk0CnQvVjZzb0h5RUdOY0RobTlwNWZCR3F6MG5qQmJRa3AybFRZNS9rajQycUI3UTZyQ00ydGJQc0VNb29lQUF3NW0KaHlZNHhqMHRQOXVjcWxVejhnYys2bzhIRE5zdDhOZUpYWmt0V24rQ095dGpyL056R2dTMjJrdlNEcGhpc0pvdApvMEZ5b0lPZEF0eEMxcXhYWFIrWHVVVUNBd0VBQWFPQmlqQ0JoekFPQmdOVkhROEJBZjhFQkFNQ0FxUXdIUVlEClZSMGxCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQk1BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0hRWUQKVlIwT0JCWUVGS1JMa2dJelgvT2pLdzlpZGVwdVEvUk10VCtBTUNZR0ExVWRFUVFmTUIyQ0NXeHZZMkZzYUc5egpkSWNRL1FDaEl3QUFBQUFBQUFBQUFBQUFBVEFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBUHM1eUpaaG9ZbEdXCkNwQThkU0lTaXZNOC84aUJOUTNmVndQNjNmdDBFSkxNVkd1MlJGWjQvVUFKL3JVUFNHTjh4aFhTazUrMWQ1NmEKL2thSDlyWDBIYVJJSEhseEE3aVBVS3hBajQ0eDlMS21xUEhUb0wzWGxXWTFBWHp2aWNXOWQrR00yRmFRZWUrSQpsZWFxTGJ6MEFadmxudTI3MVoxQ2VhQUN1VTlHbGp1anZ5aVRURTluYUhVRXF2SGdTcFB0aWxKYWx5SjUveklsClo5RjArVVd0M1RPWU1zNWcrU0N0ME13SFROYmlzYm1ld3BjRkZKemp0Mmt2dHJjOXQ5ZGtGODF4aGNTMTl3N3EKaDFBZVAzUlJsTGw3YnY5RUFWWEVtSWF2aWgvMjlQQTNaU3krcGJZTlc3ak5KSGpNUTRoUTBFK3hjQ2F6VS9PNAp5cFdHYWFudlBnPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
+          validationContextSdsSecretConfig:
+            name: cp_validation_ctx
         sni: localhost
     type: STRICT_DNS
     upstreamConnectionOptions:
@@ -106,6 +103,13 @@ staticResources:
         keepaliveInterval: 10
         keepaliveProbes: 3
         keepaliveTime: 10
+  secrets:
+  - name: cp_validation_ctx
+    validationContext:
+      matchSubjectAltNames:
+      - exact: localhost
+      trustedCa:
+        inlineBytes: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURNekNDQWh1Z0F3SUJBZ0lRRGhsSW5mc1hZSGFtS04rMjlxblF2ekFOQmdrcWhraUc5dzBCQVFzRkFEQVAKTVEwd0N3WURWUVFERXdScmRXMWhNQjRYRFRJeE1EUXdNakV3TWpJeU5sb1hEVE14TURNek1URXdNakl5TmxvdwpEekVOTUFzR0ExVUVBeE1FYTNWdFlUQ0NBU0l3RFFZSktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0NnZ0VCCkFMNEdHZytlMk83ZUExMkYwRjZ2MnJyOGoyaVZTRktlcG5adEwxNWxyQ2RzNmxxSzUwc1hXT3c4UEtacDJpaEEKWEpWVFNaekthc3lMRFRBUjlWWVFqVHBFNTI2RXp2dGR0aFNhZ2YzMlFXVyt3WTZMTXBFZGV4S09PQ3gyc2U1NQpSZDk3TDMzeVlQZmdYMTVPWWxpSFBEMDU2ampob3RITGROMmxweTcrU1REdlF5Um5YQXU3M1lrWTM3RWQ0aEk0CnQvVjZzb0h5RUdOY0RobTlwNWZCR3F6MG5qQmJRa3AybFRZNS9rajQycUI3UTZyQ00ydGJQc0VNb29lQUF3NW0KaHlZNHhqMHRQOXVjcWxVejhnYys2bzhIRE5zdDhOZUpYWmt0V24rQ095dGpyL056R2dTMjJrdlNEcGhpc0pvdApvMEZ5b0lPZEF0eEMxcXhYWFIrWHVVVUNBd0VBQWFPQmlqQ0JoekFPQmdOVkhROEJBZjhFQkFNQ0FxUXdIUVlEClZSMGxCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQk1BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0hRWUQKVlIwT0JCWUVGS1JMa2dJelgvT2pLdzlpZGVwdVEvUk10VCtBTUNZR0ExVWRFUVFmTUIyQ0NXeHZZMkZzYUc5egpkSWNRL1FDaEl3QUFBQUFBQUFBQUFBQUFBVEFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBUHM1eUpaaG9ZbEdXCkNwQThkU0lTaXZNOC84aUJOUTNmVndQNjNmdDBFSkxNVkd1MlJGWjQvVUFKL3JVUFNHTjh4aFhTazUrMWQ1NmEKL2thSDlyWDBIYVJJSEhseEE3aVBVS3hBajQ0eDlMS21xUEhUb0wzWGxXWTFBWHp2aWNXOWQrR00yRmFRZWUrSQpsZWFxTGJ6MEFadmxudTI3MVoxQ2VhQUN1VTlHbGp1anZ5aVRURTluYUhVRXF2SGdTcFB0aWxKYWx5SjUveklsClo5RjArVVd0M1RPWU1zNWcrU0N0ME13SFROYmlzYm1ld3BjRkZKemp0Mmt2dHJjOXQ5ZGtGODF4aGNTMTl3N3EKaDFBZVAzUlJsTGw3YnY5RUFWWEVtSWF2aWgvMjlQQTNaU3krcGJZTlc3ak5KSGpNUTRoUTBFK3hjQ2F6VS9PNAp5cFdHYWFudlBnPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
 statsConfig:
   statsTags:
   - regex: ^grpc\.((.+)\.)
diff --git a/pkg/xds/bootstrap/testdata/generator.default-config.kubernetes.golden.yaml b/pkg/xds/bootstrap/testdata/generator.default-config.kubernetes.golden.yaml
index 222dddb6d..99fe16df9 100644
--- a/pkg/xds/bootstrap/testdata/generator.default-config.kubernetes.golden.yaml
+++ b/pkg/xds/bootstrap/testdata/generator.default-config.kubernetes.golden.yaml
@@ -82,11 +82,8 @@ staticResources:
         commonTlsContext:
           tlsParams:
             tlsMinimumProtocolVersion: TLSv1_2
-          validationContext:
-            matchSubjectAltNames:
-            - exact: localhost
-            trustedCa:
-              inlineBytes: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURNekNDQWh1Z0F3SUJBZ0lRRGhsSW5mc1hZSGFtS04rMjlxblF2ekFOQmdrcWhraUc5dzBCQVFzRkFEQVAKTVEwd0N3WURWUVFERXdScmRXMWhNQjRYRFRJeE1EUXdNakV3TWpJeU5sb1hEVE14TURNek1URXdNakl5TmxvdwpEekVOTUFzR0ExVUVBeE1FYTNWdFlUQ0NBU0l3RFFZSktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0NnZ0VCCkFMNEdHZytlMk83ZUExMkYwRjZ2MnJyOGoyaVZTRktlcG5adEwxNWxyQ2RzNmxxSzUwc1hXT3c4UEtacDJpaEEKWEpWVFNaekthc3lMRFRBUjlWWVFqVHBFNTI2RXp2dGR0aFNhZ2YzMlFXVyt3WTZMTXBFZGV4S09PQ3gyc2U1NQpSZDk3TDMzeVlQZmdYMTVPWWxpSFBEMDU2ampob3RITGROMmxweTcrU1REdlF5Um5YQXU3M1lrWTM3RWQ0aEk0CnQvVjZzb0h5RUdOY0RobTlwNWZCR3F6MG5qQmJRa3AybFRZNS9rajQycUI3UTZyQ00ydGJQc0VNb29lQUF3NW0KaHlZNHhqMHRQOXVjcWxVejhnYys2bzhIRE5zdDhOZUpYWmt0V24rQ095dGpyL056R2dTMjJrdlNEcGhpc0pvdApvMEZ5b0lPZEF0eEMxcXhYWFIrWHVVVUNBd0VBQWFPQmlqQ0JoekFPQmdOVkhROEJBZjhFQkFNQ0FxUXdIUVlEClZSMGxCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQk1BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0hRWUQKVlIwT0JCWUVGS1JMa2dJelgvT2pLdzlpZGVwdVEvUk10VCtBTUNZR0ExVWRFUVFmTUIyQ0NXeHZZMkZzYUc5egpkSWNRL1FDaEl3QUFBQUFBQUFBQUFBQUFBVEFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBUHM1eUpaaG9ZbEdXCkNwQThkU0lTaXZNOC84aUJOUTNmVndQNjNmdDBFSkxNVkd1MlJGWjQvVUFKL3JVUFNHTjh4aFhTazUrMWQ1NmEKL2thSDlyWDBIYVJJSEhseEE3aVBVS3hBajQ0eDlMS21xUEhUb0wzWGxXWTFBWHp2aWNXOWQrR00yRmFRZWUrSQpsZWFxTGJ6MEFadmxudTI3MVoxQ2VhQUN1VTlHbGp1anZ5aVRURTluYUhVRXF2SGdTcFB0aWxKYWx5SjUveklsClo5RjArVVd0M1RPWU1zNWcrU0N0ME13SFROYmlzYm1ld3BjRkZKemp0Mmt2dHJjOXQ5ZGtGODF4aGNTMTl3N3EKaDFBZVAzUlJsTGw3YnY5RUFWWEVtSWF2aWgvMjlQQTNaU3krcGJZTlc3ak5KSGpNUTRoUTBFK3hjQ2F6VS9PNAp5cFdHYWFudlBnPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
+          validationContextSdsSecretConfig:
+            name: cp_validation_ctx
         sni: localhost
     type: STRICT_DNS
     upstreamConnectionOptions:
@@ -94,6 +91,13 @@ staticResources:
         keepaliveInterval: 10
         keepaliveProbes: 3
         keepaliveTime: 10
+  secrets:
+  - name: cp_validation_ctx
+    validationContext:
+      matchSubjectAltNames:
+      - exact: localhost
+      trustedCa:
+        inlineBytes: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURNekNDQWh1Z0F3SUJBZ0lRRGhsSW5mc1hZSGFtS04rMjlxblF2ekFOQmdrcWhraUc5dzBCQVFzRkFEQVAKTVEwd0N3WURWUVFERXdScmRXMWhNQjRYRFRJeE1EUXdNakV3TWpJeU5sb1hEVE14TURNek1URXdNakl5TmxvdwpEekVOTUFzR0ExVUVBeE1FYTNWdFlUQ0NBU0l3RFFZSktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0NnZ0VCCkFMNEdHZytlMk83ZUExMkYwRjZ2MnJyOGoyaVZTRktlcG5adEwxNWxyQ2RzNmxxSzUwc1hXT3c4UEtacDJpaEEKWEpWVFNaekthc3lMRFRBUjlWWVFqVHBFNTI2RXp2dGR0aFNhZ2YzMlFXVyt3WTZMTXBFZGV4S09PQ3gyc2U1NQpSZDk3TDMzeVlQZmdYMTVPWWxpSFBEMDU2ampob3RITGROMmxweTcrU1REdlF5Um5YQXU3M1lrWTM3RWQ0aEk0CnQvVjZzb0h5RUdOY0RobTlwNWZCR3F6MG5qQmJRa3AybFRZNS9rajQycUI3UTZyQ00ydGJQc0VNb29lQUF3NW0KaHlZNHhqMHRQOXVjcWxVejhnYys2bzhIRE5zdDhOZUpYWmt0V24rQ095dGpyL056R2dTMjJrdlNEcGhpc0pvdApvMEZ5b0lPZEF0eEMxcXhYWFIrWHVVVUNBd0VBQWFPQmlqQ0JoekFPQmdOVkhROEJBZjhFQkFNQ0FxUXdIUVlEClZSMGxCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQk1BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0hRWUQKVlIwT0JCWUVGS1JMa2dJelgvT2pLdzlpZGVwdVEvUk10VCtBTUNZR0ExVWRFUVFmTUIyQ0NXeHZZMkZzYUc5egpkSWNRL1FDaEl3QUFBQUFBQUFBQUFBQUFBVEFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBUHM1eUpaaG9ZbEdXCkNwQThkU0lTaXZNOC84aUJOUTNmVndQNjNmdDBFSkxNVkd1MlJGWjQvVUFKL3JVUFNHTjh4aFhTazUrMWQ1NmEKL2thSDlyWDBIYVJJSEhseEE3aVBVS3hBajQ0eDlMS21xUEhUb0wzWGxXWTFBWHp2aWNXOWQrR00yRmFRZWUrSQpsZWFxTGJ6MEFadmxudTI3MVoxQ2VhQUN1VTlHbGp1anZ5aVRURTluYUhVRXF2SGdTcFB0aWxKYWx5SjUveklsClo5RjArVVd0M1RPWU1zNWcrU0N0ME13SFROYmlzYm1ld3BjRkZKemp0Mmt2dHJjOXQ5ZGtGODF4aGNTMTl3N3EKaDFBZVAzUlJsTGw3YnY5RUFWWEVtSWF2aWgvMjlQQTNaU3krcGJZTlc3ak5KSGpNUTRoUTBFK3hjQ2F6VS9PNAp5cFdHYWFudlBnPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
 statsConfig:
   statsTags:
   - regex: ^grpc\.((.+)\.)
diff --git a/pkg/xds/bootstrap/testdata/generator.default-config.kubernetes.ipv6.golden.yaml b/pkg/xds/bootstrap/testdata/generator.default-config.kubernetes.ipv6.golden.yaml
index fd3d03ad4..7124f75e7 100644
--- a/pkg/xds/bootstrap/testdata/generator.default-config.kubernetes.ipv6.golden.yaml
+++ b/pkg/xds/bootstrap/testdata/generator.default-config.kubernetes.ipv6.golden.yaml
@@ -82,11 +82,8 @@ staticResources:
         commonTlsContext:
           tlsParams:
             tlsMinimumProtocolVersion: TLSv1_2
-          validationContext:
-            matchSubjectAltNames:
-            - exact: fd00:a123::1
-            trustedCa:
-              inlineBytes: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURNekNDQWh1Z0F3SUJBZ0lRRGhsSW5mc1hZSGFtS04rMjlxblF2ekFOQmdrcWhraUc5dzBCQVFzRkFEQVAKTVEwd0N3WURWUVFERXdScmRXMWhNQjRYRFRJeE1EUXdNakV3TWpJeU5sb1hEVE14TURNek1URXdNakl5TmxvdwpEekVOTUFzR0ExVUVBeE1FYTNWdFlUQ0NBU0l3RFFZSktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0NnZ0VCCkFMNEdHZytlMk83ZUExMkYwRjZ2MnJyOGoyaVZTRktlcG5adEwxNWxyQ2RzNmxxSzUwc1hXT3c4UEtacDJpaEEKWEpWVFNaekthc3lMRFRBUjlWWVFqVHBFNTI2RXp2dGR0aFNhZ2YzMlFXVyt3WTZMTXBFZGV4S09PQ3gyc2U1NQpSZDk3TDMzeVlQZmdYMTVPWWxpSFBEMDU2ampob3RITGROMmxweTcrU1REdlF5Um5YQXU3M1lrWTM3RWQ0aEk0CnQvVjZzb0h5RUdOY0RobTlwNWZCR3F6MG5qQmJRa3AybFRZNS9rajQycUI3UTZyQ00ydGJQc0VNb29lQUF3NW0KaHlZNHhqMHRQOXVjcWxVejhnYys2bzhIRE5zdDhOZUpYWmt0V24rQ095dGpyL056R2dTMjJrdlNEcGhpc0pvdApvMEZ5b0lPZEF0eEMxcXhYWFIrWHVVVUNBd0VBQWFPQmlqQ0JoekFPQmdOVkhROEJBZjhFQkFNQ0FxUXdIUVlEClZSMGxCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQk1BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0hRWUQKVlIwT0JCWUVGS1JMa2dJelgvT2pLdzlpZGVwdVEvUk10VCtBTUNZR0ExVWRFUVFmTUIyQ0NXeHZZMkZzYUc5egpkSWNRL1FDaEl3QUFBQUFBQUFBQUFBQUFBVEFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBUHM1eUpaaG9ZbEdXCkNwQThkU0lTaXZNOC84aUJOUTNmVndQNjNmdDBFSkxNVkd1MlJGWjQvVUFKL3JVUFNHTjh4aFhTazUrMWQ1NmEKL2thSDlyWDBIYVJJSEhseEE3aVBVS3hBajQ0eDlMS21xUEhUb0wzWGxXWTFBWHp2aWNXOWQrR00yRmFRZWUrSQpsZWFxTGJ6MEFadmxudTI3MVoxQ2VhQUN1VTlHbGp1anZ5aVRURTluYUhVRXF2SGdTcFB0aWxKYWx5SjUveklsClo5RjArVVd0M1RPWU1zNWcrU0N0ME13SFROYmlzYm1ld3BjRkZKemp0Mmt2dHJjOXQ5ZGtGODF4aGNTMTl3N3EKaDFBZVAzUlJsTGw3YnY5RUFWWEVtSWF2aWgvMjlQQTNaU3krcGJZTlc3ak5KSGpNUTRoUTBFK3hjQ2F6VS9PNAp5cFdHYWFudlBnPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
+          validationContextSdsSecretConfig:
+            name: cp_validation_ctx
         sni: fd00:a123::1
     type: STATIC
     upstreamConnectionOptions:
@@ -94,6 +91,13 @@ staticResources:
         keepaliveInterval: 10
         keepaliveProbes: 3
         keepaliveTime: 10
+  secrets:
+  - name: cp_validation_ctx
+    validationContext:
+      matchSubjectAltNames:
+      - exact: fd00:a123::1
+      trustedCa:
+        inlineBytes: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURNekNDQWh1Z0F3SUJBZ0lRRGhsSW5mc1hZSGFtS04rMjlxblF2ekFOQmdrcWhraUc5dzBCQVFzRkFEQVAKTVEwd0N3WURWUVFERXdScmRXMWhNQjRYRFRJeE1EUXdNakV3TWpJeU5sb1hEVE14TURNek1URXdNakl5TmxvdwpEekVOTUFzR0ExVUVBeE1FYTNWdFlUQ0NBU0l3RFFZSktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0NnZ0VCCkFMNEdHZytlMk83ZUExMkYwRjZ2MnJyOGoyaVZTRktlcG5adEwxNWxyQ2RzNmxxSzUwc1hXT3c4UEtacDJpaEEKWEpWVFNaekthc3lMRFRBUjlWWVFqVHBFNTI2RXp2dGR0aFNhZ2YzMlFXVyt3WTZMTXBFZGV4S09PQ3gyc2U1NQpSZDk3TDMzeVlQZmdYMTVPWWxpSFBEMDU2ampob3RITGROMmxweTcrU1REdlF5Um5YQXU3M1lrWTM3RWQ0aEk0CnQvVjZzb0h5RUdOY0RobTlwNWZCR3F6MG5qQmJRa3AybFRZNS9rajQycUI3UTZyQ00ydGJQc0VNb29lQUF3NW0KaHlZNHhqMHRQOXVjcWxVejhnYys2bzhIRE5zdDhOZUpYWmt0V24rQ095dGpyL056R2dTMjJrdlNEcGhpc0pvdApvMEZ5b0lPZEF0eEMxcXhYWFIrWHVVVUNBd0VBQWFPQmlqQ0JoekFPQmdOVkhROEJBZjhFQkFNQ0FxUXdIUVlEClZSMGxCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQk1BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0hRWUQKVlIwT0JCWUVGS1JMa2dJelgvT2pLdzlpZGVwdVEvUk10VCtBTUNZR0ExVWRFUVFmTUIyQ0NXeHZZMkZzYUc5egpkSWNRL1FDaEl3QUFBQUFBQUFBQUFBQUFBVEFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBUHM1eUpaaG9ZbEdXCkNwQThkU0lTaXZNOC84aUJOUTNmVndQNjNmdDBFSkxNVkd1MlJGWjQvVUFKL3JVUFNHTjh4aFhTazUrMWQ1NmEKL2thSDlyWDBIYVJJSEhseEE3aVBVS3hBajQ0eDlMS21xUEhUb0wzWGxXWTFBWHp2aWNXOWQrR00yRmFRZWUrSQpsZWFxTGJ6MEFadmxudTI3MVoxQ2VhQUN1VTlHbGp1anZ5aVRURTluYUhVRXF2SGdTcFB0aWxKYWx5SjUveklsClo5RjArVVd0M1RPWU1zNWcrU0N0ME13SFROYmlzYm1ld3BjRkZKemp0Mmt2dHJjOXQ5ZGtGODF4aGNTMTl3N3EKaDFBZVAzUlJsTGw3YnY5RUFWWEVtSWF2aWgvMjlQQTNaU3krcGJZTlc3ak5KSGpNUTRoUTBFK3hjQ2F6VS9PNAp5cFdHYWFudlBnPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
 statsConfig:
   statsTags:
   - regex: ^grpc\.((.+)\.)
diff --git a/pkg/xds/envoy/listeners/v3/server_mtls__with_cp_configurer_test.go b/pkg/xds/envoy/listeners/v3/server_mtls__with_cp_configurer_test.go
new file mode 100644
index 000000000..ede03a4dd
--- /dev/null
+++ b/pkg/xds/envoy/listeners/v3/server_mtls__with_cp_configurer_test.go
@@ -0,0 +1,162 @@
+package v3_test
+
+import (
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/ginkgo/extensions/table"
+	. "github.com/onsi/gomega"
+
+	mesh_proto "github.com/kumahq/kuma/api/mesh/v1alpha1"
+	core_mesh "github.com/kumahq/kuma/pkg/core/resources/apis/mesh"
+	core_xds "github.com/kumahq/kuma/pkg/core/xds"
+	test_model "github.com/kumahq/kuma/pkg/test/resources/model"
+	"github.com/kumahq/kuma/pkg/tls"
+	util_proto "github.com/kumahq/kuma/pkg/util/proto"
+	xds_context "github.com/kumahq/kuma/pkg/xds/context"
+	envoy_common "github.com/kumahq/kuma/pkg/xds/envoy"
+	. "github.com/kumahq/kuma/pkg/xds/envoy/listeners"
+)
+
+var _ = Describe("ServerMtlsConfigurer", func() {
+
+	type testCase struct {
+		listenerName     string
+		listenerProtocol core_xds.SocketAddressProtocol
+		listenerAddress  string
+		listenerPort     uint32
+		statsName        string
+		clusters         []envoy_common.Cluster
+		ctx              xds_context.Context
+		expected         string
+	}
+
+	DescribeTable("should generate proper Envoy config",
+		func(given testCase) {
+			// when
+			listener, err := NewListenerBuilder(envoy_common.APIV3).
+				Configure(InboundListener(given.listenerName, given.listenerAddress, given.listenerPort, given.listenerProtocol)).
+				Configure(FilterChain(NewFilterChainBuilder(envoy_common.APIV3).
+					Configure(ServerSideMTLSWithCP(given.ctx)).
+					Configure(TcpProxy(given.statsName, given.clusters...)))).
+				Build()
+			// then
+			Expect(err).ToNot(HaveOccurred())
+
+			// when
+			actual, err := util_proto.ToYAML(listener)
+			Expect(err).ToNot(HaveOccurred())
+			// and
+			Expect(actual).To(MatchYAML(given.expected))
+		},
+		Entry("mTLS with CP when Mesh mTLS is enabled", testCase{
+			listenerName:    "inbound:192.168.0.1:8080",
+			listenerAddress: "192.168.0.1",
+			listenerPort:    8080,
+			statsName:       "localhost:8080",
+			clusters: []envoy_common.Cluster{envoy_common.NewCluster(
+				envoy_common.WithService("localhost:8080"),
+				envoy_common.WithWeight(200),
+			)},
+			ctx: xds_context.Context{
+				ControlPlane: &xds_context.ControlPlaneContext{},
+				Mesh: xds_context.MeshContext{
+					Resource: &core_mesh.MeshResource{
+						Meta: &test_model.ResourceMeta{
+							Name: "default",
+						},
+						Spec: &mesh_proto.Mesh{
+							Mtls: &mesh_proto.Mesh_Mtls{
+								EnabledBackend: "builtin",
+								Backends: []*mesh_proto.CertificateAuthorityBackend{
+									{
+										Name: "builtin",
+										Type: "builtin",
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+			expected: `
+            address:
+              socketAddress:
+                address: 192.168.0.1
+                portValue: 8080
+            filterChains:
+            - filters:
+              - name: envoy.filters.network.tcp_proxy
+                typedConfig:
+                  '@type': type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy
+                  cluster: localhost:8080
+                  statPrefix: localhost_8080
+              transportSocket:
+                name: envoy.transport_sockets.tls
+                typedConfig:
+                  '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
+                  commonTlsContext:
+                    tlsCertificateSdsSecretConfigs:
+                    - name: identity_cert
+                      sdsConfig:
+                        ads: {}
+                        resourceApiVersion: V3
+                    validationContextSdsSecretConfig:
+                      name: cp_validation_ctx
+                  requireClientCertificate: true
+            name: inbound:192.168.0.1:8080
+            trafficDirection: INBOUND`,
+		}),
+		Entry("mTLS with CP when Mesh mTLS is disabled", testCase{
+			listenerName:    "inbound:192.168.0.1:8080",
+			listenerAddress: "192.168.0.1",
+			listenerPort:    8080,
+			statsName:       "localhost:8080",
+			clusters: []envoy_common.Cluster{envoy_common.NewCluster(
+				envoy_common.WithService("localhost:8080"),
+				envoy_common.WithWeight(200),
+			)},
+			ctx: xds_context.Context{
+				ControlPlane: &xds_context.ControlPlaneContext{
+					AdminProxyKeyPair: &tls.KeyPair{
+						CertPEM: []byte("cert"),
+						KeyPEM:  []byte("key"),
+					},
+				},
+				Mesh: xds_context.MeshContext{
+					Resource: &core_mesh.MeshResource{
+						Meta: &test_model.ResourceMeta{
+							Name: "default",
+						},
+						Spec: &mesh_proto.Mesh{},
+					},
+				},
+			},
+			expected: `
+            address:
+              socketAddress:
+                address: 192.168.0.1
+                portValue: 8080
+            filterChains:
+            - filters:
+              - name: envoy.filters.network.tcp_proxy
+                typedConfig:
+                  '@type': type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy
+                  cluster: localhost:8080
+                  statPrefix: localhost_8080
+              transportSocket:
+                name: envoy.transport_sockets.tls
+                typedConfig:
+                  '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
+                  commonTlsContext:
+                    tlsCertificates:
+                    - certificateChain:
+                        inlineBytes: Y2VydA==
+                      privateKey:
+                        inlineBytes: a2V5
+                    validationContextSdsSecretConfig:
+                      name: cp_validation_ctx
+                  requireClientCertificate: true
+            name: inbound:192.168.0.1:8080
+            trafficDirection: INBOUND`,
+		}),
+	)
+})
diff --git a/pkg/xds/generator/testdata/admin/01.envoy-config.golden.yaml b/pkg/xds/generator/testdata/admin/01.envoy-config.golden.yaml
index 9ca1fbc84..c12e87c51 100644
--- a/pkg/xds/generator/testdata/admin/01.envoy-config.golden.yaml
+++ b/pkg/xds/generator/testdata/admin/01.envoy-config.golden.yaml
@@ -42,40 +42,6 @@ resources:
                   cluster: kuma:envoy:admin
                   prefixRewrite: /ready
           statPrefix: kuma_envoy_admin
-    - filterChainMatch:
-        transportProtocol: tls
-      filters:
-      - name: envoy.filters.network.http_connection_manager
-        typedConfig:
-          '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
-          httpFilters:
-          - name: envoy.filters.http.router
-          routeConfig:
-            validateClusters: false
-            virtualHosts:
-            - domains:
-              - '*'
-              name: kuma:envoy:admin
-              routes:
-              - match:
-                  headers:
-                  - exactMatch: Bearer token
-                    name: Authorization
-                  prefix: /
-                route:
-                  cluster: kuma:envoy:admin
-                  prefixRewrite: /
-          statPrefix: kuma_envoy_admin
-      transportSocket:
-        name: envoy.transport_sockets.tls
-        typedConfig:
-          '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
-          commonTlsContext:
-            tlsCertificates:
-            - certificateChain:
-                inlineBytes: TFMwPT0=
-              privateKey:
-                inlineBytes: TFMwPT0=
     listenerFilters:
     - name: envoy.filters.listener.tls_inspector
       typedConfig:
diff --git a/pkg/xds/generator/testdata/profile-source/1-envoy-config.golden.yaml b/pkg/xds/generator/testdata/profile-source/1-envoy-config.golden.yaml
index 110a774f1..c89d6835b 100644
--- a/pkg/xds/generator/testdata/profile-source/1-envoy-config.golden.yaml
+++ b/pkg/xds/generator/testdata/profile-source/1-envoy-config.golden.yaml
@@ -209,40 +209,6 @@ resources:
                   cluster: kuma:envoy:admin
                   prefixRewrite: /ready
           statPrefix: kuma_envoy_admin
-    - filterChainMatch:
-        transportProtocol: tls
-      filters:
-      - name: envoy.filters.network.http_connection_manager
-        typedConfig:
-          '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
-          httpFilters:
-          - name: envoy.filters.http.router
-          routeConfig:
-            validateClusters: false
-            virtualHosts:
-            - domains:
-              - '*'
-              name: kuma:envoy:admin
-              routes:
-              - match:
-                  headers:
-                  - exactMatch: Bearer token
-                    name: Authorization
-                  prefix: /
-                route:
-                  cluster: kuma:envoy:admin
-                  prefixRewrite: /
-          statPrefix: kuma_envoy_admin
-      transportSocket:
-        name: envoy.transport_sockets.tls
-        typedConfig:
-          '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
-          commonTlsContext:
-            tlsCertificates:
-            - certificateChain:
-                inlineBytes: TFMwPT0=
-              privateKey:
-                inlineBytes: TFMwPT0=
     listenerFilters:
     - name: envoy.filters.listener.tls_inspector
       typedConfig:
diff --git a/pkg/xds/generator/testdata/profile-source/2-envoy-config.golden.yaml b/pkg/xds/generator/testdata/profile-source/2-envoy-config.golden.yaml
index 2e74986e3..e334ec3d9 100644
--- a/pkg/xds/generator/testdata/profile-source/2-envoy-config.golden.yaml
+++ b/pkg/xds/generator/testdata/profile-source/2-envoy-config.golden.yaml
@@ -247,40 +247,6 @@ resources:
                   cluster: kuma:envoy:admin
                   prefixRewrite: /ready
           statPrefix: kuma_envoy_admin
-    - filterChainMatch:
-        transportProtocol: tls
-      filters:
-      - name: envoy.filters.network.http_connection_manager
-        typedConfig:
-          '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
-          httpFilters:
-          - name: envoy.filters.http.router
-          routeConfig:
-            validateClusters: false
-            virtualHosts:
-            - domains:
-              - '*'
-              name: kuma:envoy:admin
-              routes:
-              - match:
-                  headers:
-                  - exactMatch: Bearer token
-                    name: Authorization
-                  prefix: /
-                route:
-                  cluster: kuma:envoy:admin
-                  prefixRewrite: /
-          statPrefix: kuma_envoy_admin
-      transportSocket:
-        name: envoy.transport_sockets.tls
-        typedConfig:
-          '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
-          commonTlsContext:
-            tlsCertificates:
-            - certificateChain:
-                inlineBytes: TFMwPT0=
-              privateKey:
-                inlineBytes: TFMwPT0=
     listenerFilters:
     - name: envoy.filters.listener.tls_inspector
       typedConfig:
diff --git a/pkg/xds/generator/testdata/profile-source/3-envoy-config.golden.yaml b/pkg/xds/generator/testdata/profile-source/3-envoy-config.golden.yaml
index 35a5c6d35..efe2cba2c 100644
--- a/pkg/xds/generator/testdata/profile-source/3-envoy-config.golden.yaml
+++ b/pkg/xds/generator/testdata/profile-source/3-envoy-config.golden.yaml
@@ -242,40 +242,6 @@ resources:
                   cluster: kuma:envoy:admin
                   prefixRewrite: /ready
           statPrefix: kuma_envoy_admin
-    - filterChainMatch:
-        transportProtocol: tls
-      filters:
-      - name: envoy.filters.network.http_connection_manager
-        typedConfig:
-          '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
-          httpFilters:
-          - name: envoy.filters.http.router
-          routeConfig:
-            validateClusters: false
-            virtualHosts:
-            - domains:
-              - '*'
-              name: kuma:envoy:admin
-              routes:
-              - match:
-                  headers:
-                  - exactMatch: Bearer token
-                    name: Authorization
-                  prefix: /
-                route:
-                  cluster: kuma:envoy:admin
-                  prefixRewrite: /
-          statPrefix: kuma_envoy_admin
-      transportSocket:
-        name: envoy.transport_sockets.tls
-        typedConfig:
-          '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
-          commonTlsContext:
-            tlsCertificates:
-            - certificateChain:
-                inlineBytes: TFMwPT0=
-              privateKey:
-                inlineBytes: TFMwPT0=
     listenerFilters:
     - name: envoy.filters.listener.tls_inspector
       typedConfig:
diff --git a/pkg/xds/generator/testdata/profile-source/4-envoy-config.golden.yaml b/pkg/xds/generator/testdata/profile-source/4-envoy-config.golden.yaml
index 9e37d47cc..708e7ca40 100644
--- a/pkg/xds/generator/testdata/profile-source/4-envoy-config.golden.yaml
+++ b/pkg/xds/generator/testdata/profile-source/4-envoy-config.golden.yaml
@@ -280,40 +280,6 @@ resources:
                   cluster: kuma:envoy:admin
                   prefixRewrite: /ready
           statPrefix: kuma_envoy_admin
-    - filterChainMatch:
-        transportProtocol: tls
-      filters:
-      - name: envoy.filters.network.http_connection_manager
-        typedConfig:
-          '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
-          httpFilters:
-          - name: envoy.filters.http.router
-          routeConfig:
-            validateClusters: false
-            virtualHosts:
-            - domains:
-              - '*'
-              name: kuma:envoy:admin
-              routes:
-              - match:
-                  headers:
-                  - exactMatch: Bearer token
-                    name: Authorization
-                  prefix: /
-                route:
-                  cluster: kuma:envoy:admin
-                  prefixRewrite: /
-          statPrefix: kuma_envoy_admin
-      transportSocket:
-        name: envoy.transport_sockets.tls
-        typedConfig:
-          '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
-          commonTlsContext:
-            tlsCertificates:
-            - certificateChain:
-                inlineBytes: TFMwPT0=
-              privateKey:
-                inlineBytes: TFMwPT0=
     listenerFilters:
     - name: envoy.filters.listener.tls_inspector
       typedConfig:
diff --git a/test/e2e/hybrid/globaluniversal/kuma_hybrid.go b/test/e2e/hybrid/globaluniversal/kuma_hybrid.go
index e0ed2b9e5..25479aa82 100644
--- a/test/e2e/hybrid/globaluniversal/kuma_hybrid.go
+++ b/test/e2e/hybrid/globaluniversal/kuma_hybrid.go
@@ -263,18 +263,4 @@ metadata:
 		Expect(err).ToNot(HaveOccurred())
 		Expect(stdout).To(ContainSubstring("HTTP/1.1 200 OK"))
 	})
-
-	It("should support jobs with a sidecar", func() {
-		// when deploy job that connects to a service on other K8S cluster
-		err := DemoClientJobK8s(TestNamespace, nonDefaultMesh, "test-server_kuma-test_svc_80.mesh")(zone1)
-
-		// then job is properly cleaned up and finished
-		Expect(err).ToNot(HaveOccurred())
-
-		// when deploy job that connects to a service on other Universal cluster
-		err = DemoClientJobK8s(TestNamespace, nonDefaultMesh, "test-server.mesh")(zone2)
-
-		// then job is properly cleaned up and finished
-		Expect(err).ToNot(HaveOccurred())
-	})
 }
diff --git a/test/e2e/jobs/jobs.go b/test/e2e/jobs/jobs.go
new file mode 100644
index 000000000..acbd2f9fd
--- /dev/null
+++ b/test/e2e/jobs/jobs.go
@@ -0,0 +1,69 @@
+package jobs
+
+import (
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+
+	config_core "github.com/kumahq/kuma/pkg/config/core"
+	"github.com/kumahq/kuma/pkg/core/resources/model"
+	. "github.com/kumahq/kuma/test/framework"
+	"github.com/kumahq/kuma/test/framework/deployments/testserver"
+)
+
+func Jobs() {
+	var kubernetes Cluster
+	var kubernetesOps = KumaK8sDeployOpts
+
+	E2EBeforeSuite(func() {
+		k8sClusters, err := NewK8sClusters([]string{Kuma1}, Silent)
+		Expect(err).ToNot(HaveOccurred())
+
+		kubernetes = k8sClusters.GetCluster(Kuma1)
+		err = NewClusterSetup().
+			Install(Kuma(config_core.Standalone, kubernetesOps...)).
+			Install(NamespaceWithSidecarInjection(TestNamespace)).
+			Install(testserver.Install()).
+			Setup(kubernetes)
+		Expect(err).ToNot(HaveOccurred())
+		err = kubernetes.VerifyKuma()
+		Expect(err).ToNot(HaveOccurred())
+	})
+
+	E2EAfterSuite(func() {
+		Expect(kubernetes.DeleteKuma(kubernetesOps...)).To(Succeed())
+		Expect(kubernetes.DeleteNamespace(TestNamespace)).To(Succeed())
+		Expect(kubernetes.DismissCluster()).To(Succeed())
+	})
+
+	It("should terminate jobs without mTLS", func() {
+		// when
+		err := DemoClientJobK8s(TestNamespace, model.DefaultMesh, "test-server_kuma-test_svc_80.mesh")(kubernetes)
+
+		// then CP terminates the job by sending /quitquitquit to Envoy Admin and verifies connection using self-signed certs
+		Expect(err).ToNot(HaveOccurred())
+	})
+
+	It("should terminate jobs with mTLS", func() {
+		// given mTLS in the Mesh
+		meshDefaulMtlsOn := `
+apiVersion: kuma.io/v1alpha1
+kind: Mesh
+metadata:
+  name: default
+spec:
+  mtls:
+    enabledBackend: ca-1
+    backends:
+      - name: ca-1
+        type: builtin
+`
+		err := YamlK8s(meshDefaulMtlsOn)(kubernetes)
+		Expect(err).ToNot(HaveOccurred())
+
+		// when
+		err = DemoClientJobK8s(TestNamespace, model.DefaultMesh, "test-server_kuma-test_svc_80.mesh")(kubernetes)
+
+		// then CP terminates the job by sending /quitquitquit to Envoy Admin and verifies connection using mTLS certs
+		Expect(err).ToNot(HaveOccurred())
+	})
+}
diff --git a/test/e2e/jobs/jobs_suite_test.go b/test/e2e/jobs/jobs_suite_test.go
new file mode 100644
index 000000000..1d1bc3016
--- /dev/null
+++ b/test/e2e/jobs/jobs_suite_test.go
@@ -0,0 +1,16 @@
+package jobs_test
+
+import (
+	"testing"
+
+	"github.com/kumahq/kuma/pkg/test"
+	"github.com/kumahq/kuma/test/framework"
+)
+
+func TestE2EJobsKubernetes(t *testing.T) {
+	if framework.IsK8sClustersStarted() {
+		test.RunSpecs(t, "Jobs Kubernetes Suite")
+	} else {
+		t.SkipNow()
+	}
+}
diff --git a/test/e2e/jobs/jobs_test.go b/test/e2e/jobs/jobs_test.go
new file mode 100644
index 000000000..438658781
--- /dev/null
+++ b/test/e2e/jobs/jobs_test.go
@@ -0,0 +1,9 @@
+package jobs_test
+
+import (
+	. "github.com/onsi/ginkgo"
+
+	"github.com/kumahq/kuma/test/e2e/jobs"
+)
+
+var _ = Describe("Jobs on Kubernetes", jobs.Jobs)